├── docs ├── _site │ ├── robots.txt │ ├── favicon.ico │ ├── assets │ │ └── images │ │ │ └── favicon.ico │ ├── feed.xml │ ├── sitemap.xml │ ├── bundle-analysis.md │ └── daterangepicker-fix.md ├── favicon.ico ├── assets │ └── images │ │ └── favicon.ico ├── Gemfile ├── .github │ └── workflows │ │ └── pages.yml ├── _config.yml ├── bundle-analysis.md ├── index.md ├── daterangepicker-fix.md ├── README.md └── installation.md ├── production ├── images │ ├── img.jpg │ ├── inbox.png │ ├── media.jpg │ ├── paypal.png │ ├── prod-1.jpg │ ├── prod-2.jpg │ ├── prod-3.jpg │ ├── prod-4.jpg │ ├── prod-5.jpg │ ├── user.png │ ├── visa.png │ ├── cropper.jpg │ ├── favicon.ico │ ├── picture.jpg │ ├── mastercard.png │ ├── american-express.png │ ├── logo-icon.svg │ ├── favicon-16x16.svg │ ├── android-chrome-192x192.svg │ ├── android-chrome-512x512.svg │ ├── apple-touch-icon.svg │ ├── favicon.svg │ ├── favicon-32x32.svg │ └── logo.svg ├── site.webmanifest ├── page_403.html ├── page_404.html ├── page_500.html └── landing.html ├── tests └── README.md ├── src ├── modules │ ├── dashboard.js │ ├── charts.js │ ├── forms.js │ ├── weather.js │ └── ui-components.js ├── main-form-basic-simple.js ├── jquery-setup.js ├── main.scss ├── js │ ├── require-shim.js │ ├── helpers │ │ └── smartresize-modern.js │ ├── page │ │ └── index3-analytics.js │ └── sidebar-modern.js ├── scss │ ├── index4.scss │ ├── font-optimization.scss │ ├── daterangepicker.scss │ └── index2.scss ├── utils │ ├── security.js │ ├── table-optimizer.js │ └── dom-modern.js ├── main-form-advanced.js ├── main-inbox.js ├── main-tables.js ├── main-form-basic.js ├── main-upload.js └── main-core.js ├── .prettierignore ├── .gitignore ├── .prettierrc ├── dev-watch.sh ├── .npmignore ├── LICENSE.txt ├── .editorconfig ├── .github └── workflows │ └── jekyll-gh-pages.yml ├── eslint.config.js ├── package.json ├── README_CN.md ├── vite.config.js └── changelog.md /docs/_site/robots.txt: -------------------------------------------------------------------------------- 1 | Sitemap: https://puikinsh.github.io/gentelella/sitemap.xml 2 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /docs/_site/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/docs/_site/favicon.ico -------------------------------------------------------------------------------- /production/images/img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/img.jpg -------------------------------------------------------------------------------- /production/images/inbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/inbox.png -------------------------------------------------------------------------------- /production/images/media.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/media.jpg -------------------------------------------------------------------------------- /production/images/paypal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/paypal.png -------------------------------------------------------------------------------- /production/images/prod-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/prod-1.jpg -------------------------------------------------------------------------------- /production/images/prod-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/prod-2.jpg -------------------------------------------------------------------------------- /production/images/prod-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/prod-3.jpg -------------------------------------------------------------------------------- /production/images/prod-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/prod-4.jpg -------------------------------------------------------------------------------- /production/images/prod-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/prod-5.jpg -------------------------------------------------------------------------------- /production/images/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/user.png -------------------------------------------------------------------------------- /production/images/visa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/visa.png -------------------------------------------------------------------------------- /docs/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/docs/assets/images/favicon.ico -------------------------------------------------------------------------------- /production/images/cropper.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/cropper.jpg -------------------------------------------------------------------------------- /production/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/favicon.ico -------------------------------------------------------------------------------- /production/images/picture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/picture.jpg -------------------------------------------------------------------------------- /production/images/mastercard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/mastercard.png -------------------------------------------------------------------------------- /docs/_site/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/docs/_site/assets/images/favicon.ico -------------------------------------------------------------------------------- /production/images/american-express.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColorlibHQ/gentelella/HEAD/production/images/american-express.png -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # Test Directory 2 | 3 | This directory contains secure test files for development purposes. 4 | Test files should never contain hardcoded URLs or sensitive information. 5 | -------------------------------------------------------------------------------- /src/modules/dashboard.js: -------------------------------------------------------------------------------- 1 | // Dashboard Module - Only loaded on dashboard pages 2 | 3 | // Dashboard-specific widgets and functionality 4 | // Uses Chart.js for charting (imported via charts module) 5 | // Uses modern JavaScript APIs instead of jQuery where possible 6 | 7 | export default { 8 | initialized: true 9 | }; 10 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | 4 | # Build outputs 5 | dist/ 6 | docs/_site/ 7 | 8 | # Generated files 9 | *.min.js 10 | *.min.css 11 | package-lock.json 12 | 13 | # Images and binary files 14 | production/images/ 15 | *.jpg 16 | *.jpeg 17 | *.png 18 | *.gif 19 | *.svg 20 | *.ico 21 | 22 | # Logs 23 | *.log 24 | 25 | # Legacy files (to be cleaned up later) 26 | production/*.html -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nbproject 2 | npm-debug.log 3 | node_modules 4 | .sass-cache 5 | CLAUDE.md 6 | 7 | # Build outputs 8 | dist/ 9 | 10 | # Vendor dependencies (generated from node_modules) 11 | vendors/ 12 | bower_components/ 13 | 14 | # IDE files 15 | .vscode/ 16 | .idea/ 17 | *.swp 18 | *.swo 19 | 20 | # OS files 21 | .DS_Store 22 | Thumbs.db 23 | 24 | # Logs 25 | *.log 26 | JQUERY_PHASE_OUT_PLAN.md 27 | COMPREHENSIVE_IMPROVEMENT_PLAN.md 28 | release.md 29 | JQUERY_ELIMINATION_PLAN.md -------------------------------------------------------------------------------- /src/main-form-basic-simple.js: -------------------------------------------------------------------------------- 1 | // Simplified main.js for testing 2 | 3 | // Test if we can set a simple global variable 4 | window.testVariable = 'Hello from module!'; 5 | 6 | // Try importing just jQuery first 7 | import $ from 'jquery'; 8 | 9 | // Set jQuery globally 10 | window.jQuery = window.$ = $; 11 | 12 | // Import basic styles 13 | import './main.scss'; 14 | 15 | // Set a flag when everything is ready 16 | window.moduleReady = true; 17 | 18 | // Dispatch event 19 | window.dispatchEvent(new CustomEvent('simple-module-ready')); 20 | -------------------------------------------------------------------------------- /docs/_site/feed.xml: -------------------------------------------------------------------------------- 1 | Jekyll2025-09-03T13:23:50+03:00https://puikinsh.github.io/gentelella/feed.xmlGentelella Admin TemplateModern Bootstrap 5 Admin Dashboard Template with Performance Optimizations -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "quoteProps": "as-needed", 8 | "trailingComma": "none", 9 | "bracketSpacing": true, 10 | "bracketSameLine": false, 11 | "arrowParens": "avoid", 12 | "endOfLine": "lf", 13 | "overrides": [ 14 | { 15 | "files": "*.html", 16 | "options": { 17 | "printWidth": 120, 18 | "tabWidth": 2 19 | } 20 | }, 21 | { 22 | "files": "*.scss", 23 | "options": { 24 | "printWidth": 120, 25 | "singleQuote": false 26 | } 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /dev-watch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Dev server auto-restart script 4 | # This script will automatically restart the dev server if it crashes 5 | 6 | echo "🚀 Starting Gentelella dev server with auto-restart..." 7 | 8 | while true; do 9 | echo "📡 Starting dev server on http://localhost:3000" 10 | npm run dev 11 | 12 | exit_code=$? 13 | echo "⚠️ Dev server stopped with exit code: $exit_code" 14 | 15 | if [ $exit_code -eq 0 ]; then 16 | echo "✅ Dev server stopped normally" 17 | break 18 | else 19 | echo "🔄 Dev server crashed, restarting in 2 seconds..." 20 | sleep 2 21 | fi 22 | done -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Development files 2 | .editorconfig 3 | .prettierignore 4 | .prettierrc 5 | .github/ 6 | .claude/ 7 | eslint.config.js 8 | vite.config.js 9 | GITHUB_RELEASE_TEMPLATE.md 10 | RELEASE_NOTES_2.1.0.md 11 | 12 | # Documentation 13 | docs/ 14 | README_CN.md 15 | 16 | # Test files 17 | tests/ 18 | production/debug-test.html 19 | production/browser-test.html 20 | production/csp-template.html 21 | production/sidebar_test.html 22 | 23 | # Build artifacts 24 | dist/ 25 | *.tgz 26 | 27 | # Other 28 | .DS_Store 29 | node_modules/ 30 | npm-debug.log* 31 | yarn-debug.log* 32 | yarn-error.log* 33 | *.log 34 | 35 | # Keep only essential files for npm package -------------------------------------------------------------------------------- /production/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Gentelella Admin", 3 | "short_name": "Gentelella", 4 | "description": "Bootstrap 5 Admin Template", 5 | "icons": [ 6 | { 7 | "src": "images/android-chrome-192x192.svg", 8 | "sizes": "192x192", 9 | "type": "image/svg+xml" 10 | }, 11 | { 12 | "src": "images/android-chrome-512x512.svg", 13 | "sizes": "512x512", 14 | "type": "image/svg+xml" 15 | } 16 | ], 17 | "theme_color": "rgb(42, 63, 84)", 18 | "background_color": "rgb(42, 63, 84)", 19 | "display": "standalone", 20 | "start_url": "/index.html" 21 | } -------------------------------------------------------------------------------- /src/modules/charts.js: -------------------------------------------------------------------------------- 1 | // Charts Module - Only loaded when needed 2 | 3 | // Chart.js v4 - Modern charting library 4 | import { Chart, registerables } from 'chart.js'; 5 | Chart.register(...registerables); 6 | window.Chart = Chart; 7 | 8 | // Leaflet for maps 9 | import 'leaflet'; 10 | import 'leaflet/dist/leaflet.css'; 11 | 12 | // Skycons for animated weather icons (used in some charts) 13 | import SkyconsFactory from 'skycons'; 14 | const Skycons = SkyconsFactory(window); 15 | window.Skycons = Skycons; 16 | 17 | // Mini charts now handled by Chart.js instead of jQuery Sparkline 18 | 19 | 20 | export default { 21 | Chart, 22 | Skycons, 23 | L: window.L, 24 | initialized: true 25 | }; 26 | -------------------------------------------------------------------------------- /src/jquery-setup.js: -------------------------------------------------------------------------------- 1 | // jQuery Global Setup with Enhanced Browser Compatibility 2 | import $ from 'jquery'; 3 | 4 | // Ensure jQuery is available globally with multiple assignment methods 5 | if (typeof window !== 'undefined') { 6 | // Primary assignment 7 | window.jQuery = window.$ = $; 8 | 9 | // Additional assignment methods for stricter browsers 10 | globalThis.jQuery = globalThis.$ = $; 11 | 12 | // Force assignment to global scope (Safari compatibility) 13 | if (typeof global !== 'undefined') { 14 | global.jQuery = global.$ = $; 15 | } 16 | 17 | // Verify the assignment worked 18 | if (!window.jQuery || !window.$) { 19 | console.error('CRITICAL: jQuery global assignment failed!'); 20 | } 21 | } 22 | 23 | export default $; 24 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # GitHub Pages compatible Jekyll version 4 | gem "github-pages", group: :jekyll_plugins 5 | 6 | # Override nokogiri to get latest security patches 7 | gem "nokogiri", ">= 1.16.0" 8 | 9 | # Theme 10 | gem "just-the-docs" 11 | 12 | # Jekyll plugins 13 | group :jekyll_plugins do 14 | gem "jekyll-feed" 15 | gem "jekyll-sitemap" 16 | gem "jekyll-seo-tag" 17 | end 18 | 19 | # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem 20 | # and associated library. 21 | platforms :mingw, :x64_mingw, :mswin, :jruby do 22 | gem "tzinfo", "~> 1.2" 23 | gem "tzinfo-data" 24 | end 25 | 26 | # Performance-booster for watching directories on Windows 27 | gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin] -------------------------------------------------------------------------------- /production/images/logo-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /production/images/favicon-16x16.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /production/images/android-chrome-192x192.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /production/images/android-chrome-512x512.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /production/images/apple-touch-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /production/images/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /production/images/favicon-32x32.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main.scss: -------------------------------------------------------------------------------- 1 | // SCSS files using @use (must come first) 2 | @use "bootstrap/scss/bootstrap"; 3 | @use "./scss/custom.scss"; 4 | @use "./scss/font-optimization.scss"; 5 | @use "./scss/index2.scss"; 6 | @use "./scss/index4.scss"; 7 | @use "./scss/landing.scss"; 8 | 9 | // CSS files using @import (legacy approach for .css files) 10 | @import "@fortawesome/fontawesome-free/css/all.min.css"; 11 | @import "nprogress/nprogress.css"; 12 | // @import "jquery-ui/dist/themes/ui-lightness/jquery-ui.css"; // Package not installed 13 | // @import "select2/dist/css/select2.min.css"; // Package not installed 14 | // @import "ion-rangeslider/css/ion.rangeSlider.css"; // Package not installed 15 | // Switchery CSS is now imported directly from node_modules via JS imports 16 | @import "@eonasdan/tempus-dominus/dist/css/tempus-dominus.min.css"; 17 | 18 | // Import Leaflet's CSS 19 | @import "leaflet/dist/leaflet.css"; 20 | 21 | // jqvmap 22 | // @import "jqvmap/dist/jqvmap.min.css"; 23 | 24 | // Custom Theme Style is now handled with @use at the top 25 | 26 | // Page-specific styles are already imported with @use above 27 | -------------------------------------------------------------------------------- /production/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Gentelella Alela! 23 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 Aigars Silkalns & Colorlib 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/_site/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | https://puikinsh.github.io/gentelella/api-integration/ 5 | 6 | 7 | https://puikinsh.github.io/gentelella/components/ 8 | 9 | 10 | https://puikinsh.github.io/gentelella/configuration/ 11 | 12 | 13 | https://puikinsh.github.io/gentelella/customization/ 14 | 15 | 16 | https://puikinsh.github.io/gentelella/deployment/ 17 | 18 | 19 | https://puikinsh.github.io/gentelella/ 20 | 21 | 22 | https://puikinsh.github.io/gentelella/installation/ 23 | 24 | 25 | https://puikinsh.github.io/gentelella/bundle-analysis/ 26 | 27 | 28 | https://puikinsh.github.io/gentelella/daterangepicker-fix/ 29 | 30 | 31 | https://puikinsh.github.io/gentelella/jquery-elimination-complete/ 32 | 33 | 34 | https://puikinsh.github.io/gentelella/security-headers/ 35 | 36 | 37 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps maintain consistent coding styles for multiple developers 2 | # working on the same project across various editors and IDEs 3 | # See https://editorconfig.org 4 | 5 | root = true 6 | 7 | # Unix-style newlines with a newline ending every file 8 | [*] 9 | charset = utf-8 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | 14 | # JavaScript files 15 | [*.{js,mjs,jsx,ts,tsx}] 16 | indent_style = space 17 | indent_size = 2 18 | max_line_length = 100 19 | 20 | # JSON files 21 | [*.{json,jsonc}] 22 | indent_style = space 23 | indent_size = 2 24 | 25 | # HTML files 26 | [*.html] 27 | indent_style = space 28 | indent_size = 2 29 | max_line_length = 120 30 | 31 | # CSS/SCSS files 32 | [*.{css,scss,sass}] 33 | indent_style = space 34 | indent_size = 2 35 | max_line_length = 120 36 | 37 | # Markdown files 38 | [*.{md,mdx}] 39 | indent_style = space 40 | indent_size = 2 41 | trim_trailing_whitespace = false 42 | max_line_length = 120 43 | 44 | # YAML files 45 | [*.{yml,yaml}] 46 | indent_style = space 47 | indent_size = 2 48 | 49 | # Package.json - use 2 spaces 50 | [package.json] 51 | indent_style = space 52 | indent_size = 2 53 | 54 | # Makefiles - use tabs 55 | [Makefile] 56 | indent_style = tab 57 | 58 | # Batch files 59 | [*.{bat,cmd}] 60 | end_of_line = crlf -------------------------------------------------------------------------------- /docs/.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Documentation to GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | paths: 7 | - 'docs/**' 8 | workflow_dispatch: 9 | 10 | permissions: 11 | contents: read 12 | pages: write 13 | id-token: write 14 | 15 | concurrency: 16 | group: "pages" 17 | cancel-in-progress: false 18 | 19 | jobs: 20 | deploy: 21 | environment: 22 | name: github-pages 23 | url: ${{ steps.deployment.outputs.page_url }} 24 | runs-on: ubuntu-latest 25 | 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | 30 | - name: Setup Ruby 31 | uses: ruby/setup-ruby@v1 32 | with: 33 | ruby-version: '3.1' 34 | bundler-cache: true 35 | working-directory: docs 36 | 37 | - name: Setup Pages 38 | uses: actions/configure-pages@v3 39 | 40 | - name: Build with Jekyll 41 | run: | 42 | cd docs 43 | bundle exec jekyll build --baseurl "/gentelella" 44 | env: 45 | JEKYLL_ENV: production 46 | 47 | - name: Upload artifact 48 | uses: actions/upload-pages-artifact@v2 49 | with: 50 | path: docs/_site 51 | 52 | - name: Deploy to GitHub Pages 53 | id: deployment 54 | uses: actions/deploy-pages@v2 -------------------------------------------------------------------------------- /src/modules/forms.js: -------------------------------------------------------------------------------- 1 | // Forms Module - Only loaded on form pages 2 | 3 | // Tempus Dominus DateTimePicker (Bootstrap 5 compatible) 4 | import { TempusDominus, DateTime } from '@eonasdan/tempus-dominus'; 5 | window.TempusDominus = TempusDominus; 6 | window.DateTime = DateTime; 7 | 8 | // Choices.js (Enhanced select boxes - jQuery-free replacement for Select2) 9 | import Choices from 'choices.js'; 10 | window.Choices = Choices; 11 | 12 | // NoUiSlider (Range slider - jQuery-free replacement for Ion Range Slider) 13 | import noUiSlider from 'nouislider'; 14 | window.noUiSlider = noUiSlider; 15 | 16 | // Autosize (Auto-resizing textareas) 17 | import autosize from 'autosize'; 18 | window.autosize = autosize; 19 | 20 | // Switchery (iOS-style toggle switches) 21 | import Switchery from 'switchery'; 22 | window.Switchery = Switchery; 23 | 24 | // Import CSS for the new libraries 25 | import 'choices.js/public/assets/styles/choices.min.css'; 26 | import 'nouislider/dist/nouislider.css'; 27 | 28 | // Modern alternatives: 29 | // - Progress bars: Bootstrap 5 native progress components 30 | // - Date pickers: TempusDominus (already imported above) 31 | // - Sliders: NoUiSlider (already imported above) 32 | // - Select dropdowns: Choices.js (already imported above) 33 | 34 | // Form validation libraries 35 | // Note: Parsley.js and other form validators can be added here 36 | 37 | export default { 38 | TempusDominus, 39 | DateTime, 40 | Choices, 41 | noUiSlider, 42 | autosize, 43 | Switchery, 44 | initialized: true 45 | }; 46 | -------------------------------------------------------------------------------- /src/js/require-shim.js: -------------------------------------------------------------------------------- 1 | // Minimal CommonJS/AMD compatibility shim for third-party libraries in the browser. 2 | // Must be loaded *after* jQuery so that `window.jQuery` / `window.$` already exist. 3 | // 1. Exposes a global `require()` that returns jQuery when asked for "jquery". 4 | // 2. Ensures `module` / `exports` point to jQuery so libraries taking the CommonJS 5 | // branch receive the real jQuery function. 6 | // 3. Provides stubbed AMD helpers so AMD checks don't error out. 7 | (function (global) { 8 | // Helper – link module.exports to jQuery if possible 9 | function ensureModuleExports() { 10 | if ( 11 | typeof global.jQuery !== 'undefined' && 12 | (typeof global.module === 'undefined' || !global.module.exports) 13 | ) { 14 | global.module = { exports: global.jQuery }; 15 | global.exports = global.jQuery; 16 | } 17 | } 18 | 19 | // Define require() if it doesn't already exist 20 | if (typeof global.require === 'undefined') { 21 | global.require = function (name) { 22 | if (name === 'jquery' || name === 'jquery') { 23 | return global.jQuery || global.$; 24 | } 25 | return {}; 26 | }; 27 | global.require.config = function () {}; 28 | global.require.amd = {}; 29 | } 30 | 31 | // Define module/exports if they're missing, pointing them at jQuery. 32 | ensureModuleExports(); 33 | 34 | // If jQuery is loaded later (unlikely but safe), run again after the load event. 35 | if (typeof global.jQuery === 'undefined') { 36 | global.addEventListener('load', ensureModuleExports); 37 | } 38 | })(typeof window !== 'undefined' ? window : this); 39 | -------------------------------------------------------------------------------- /src/scss/index4.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * index4.scss 3 | * ------------------ 4 | * Styles for the Store Analytics dashboard page (index4.html). 5 | */ 6 | 7 | // Custom styles for the new widgets will go here. 8 | 9 | .top_products_scroll { 10 | max-height: 340px; 11 | overflow-y: auto; 12 | 13 | &::-webkit-scrollbar { 14 | width: 6px; 15 | } 16 | 17 | &::-webkit-scrollbar-track { 18 | background: #f1f1f1; 19 | } 20 | 21 | &::-webkit-scrollbar-thumb { 22 | background: #888; 23 | border-radius: 3px; 24 | 25 | &:hover { 26 | background: #555; 27 | } 28 | } 29 | 30 | .media.event { 31 | padding-bottom: 10px; 32 | border-bottom: 1px solid #f0f0f0; 33 | 34 | &:last-child { 35 | border-bottom: none; 36 | } 37 | 38 | .product_img { 39 | width: 60px; 40 | height: 60px; 41 | object-fit: cover; 42 | border-radius: 6px; 43 | margin-right: 15px; 44 | } 45 | 46 | .media-body { 47 | .title { 48 | font-weight: 600; 49 | font-size: 14px; 50 | color: #333; 51 | text-decoration: none; 52 | display: block; 53 | margin-bottom: 5px; 54 | 55 | &:hover { 56 | color: #1abb9c; 57 | } 58 | } 59 | 60 | p { 61 | margin: 0; 62 | font-size: 13px; 63 | color: #666; 64 | 65 | strong { 66 | color: #1abb9c; 67 | font-weight: 600; 68 | } 69 | } 70 | } 71 | } 72 | } 73 | 74 | // Custom badge color for 'Pending' status 75 | .badge.bg-orange { 76 | background-color: #f39c12 !important; 77 | color: white; 78 | } 79 | -------------------------------------------------------------------------------- /.github/workflows/jekyll-gh-pages.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Jekyll site to GitHub Pages 2 | name: Deploy Jekyll with GitHub Pages dependencies preinstalled 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["master"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Build job 26 | build: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v4 31 | - name: Setup Pages 32 | uses: actions/configure-pages@v5 33 | - name: Build with Jekyll 34 | uses: actions/jekyll-build-pages@v1 35 | with: 36 | source: ./ 37 | destination: ./_site 38 | - name: Upload artifact 39 | uses: actions/upload-pages-artifact@v3 40 | 41 | # Deployment job 42 | deploy: 43 | environment: 44 | name: github-pages 45 | url: ${{ steps.deployment.outputs.page_url }} 46 | runs-on: ubuntu-latest 47 | needs: build 48 | steps: 49 | - name: Deploy to GitHub Pages 50 | id: deployment 51 | uses: actions/deploy-pages@v4 52 | -------------------------------------------------------------------------------- /src/js/helpers/smartresize-modern.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Modern SmartResize - jQuery-free version 3 | * Debounced resize event handler for better performance 4 | */ 5 | 6 | // Debounce function to limit resize event frequency 7 | function debounce(func, wait = 250, immediate = false) { 8 | let timeout; 9 | return function executedFunction(...args) { 10 | const later = () => { 11 | timeout = null; 12 | if (!immediate) { 13 | func(...args); 14 | } 15 | }; 16 | const callNow = immediate && !timeout; 17 | clearTimeout(timeout); 18 | timeout = setTimeout(later, wait); 19 | if (callNow) { 20 | func(...args); 21 | } 22 | }; 23 | } 24 | 25 | // Smart resize functionality 26 | const smartResize = { 27 | handlers: new Set(), 28 | 29 | // Add a resize handler 30 | add(handler, wait = 250) { 31 | const debouncedHandler = debounce(handler, wait); 32 | this.handlers.add(debouncedHandler); 33 | window.addEventListener('resize', debouncedHandler); 34 | return debouncedHandler; 35 | }, 36 | 37 | // Remove a resize handler 38 | remove(handler) { 39 | window.removeEventListener('resize', handler); 40 | this.handlers.delete(handler); 41 | }, 42 | 43 | // Clear all handlers 44 | clear() { 45 | this.handlers.forEach(handler => { 46 | window.removeEventListener('resize', handler); 47 | }); 48 | this.handlers.clear(); 49 | } 50 | }; 51 | 52 | // Extend Window prototype for jQuery-like API 53 | if (!window.smartResize) { 54 | window.smartResize = smartResize; 55 | } 56 | 57 | // Also provide a simple function for direct use 58 | window.addSmartResize = (handler, wait) => smartResize.add(handler, wait); 59 | window.removeSmartResize = handler => smartResize.remove(handler); 60 | 61 | console.log('✅ Modern smart resize initialized (jQuery-free)'); 62 | 63 | export default smartResize; 64 | -------------------------------------------------------------------------------- /src/scss/font-optimization.scss: -------------------------------------------------------------------------------- 1 | // Font Display Optimization 2 | // This file adds font-display: swap to all @font-face declarations 3 | // to improve perceived performance and avoid FOIT (Flash of Invisible Text) 4 | 5 | // Override Font Awesome font-display 6 | @font-face { 7 | font-family: "Font Awesome 6 Free"; 8 | font-display: swap; 9 | } 10 | 11 | @font-face { 12 | font-family: "Font Awesome 6 Brands"; 13 | font-display: swap; 14 | } 15 | 16 | @font-face { 17 | font-family: "Font Awesome 6 Pro"; 18 | font-display: swap; 19 | } 20 | 21 | @font-face { 22 | font-family: "FontAwesome"; 23 | font-display: swap; 24 | } 25 | 26 | // Force font-display: swap on all font-face declarations 27 | @supports (font-display: swap) { 28 | @font-face { 29 | font-display: swap !important; 30 | } 31 | } 32 | 33 | // Preload critical fonts hint 34 | // Add this to HTML head for critical fonts: 35 | // 36 | 37 | // Optimize web font loading with CSS Font Loading API fallback 38 | .font-loading { 39 | // Hide text briefly while custom fonts load 40 | .wf-loading & { 41 | visibility: hidden; 42 | } 43 | 44 | // Show text when fonts are loaded 45 | .wf-active &, 46 | .wf-inactive & { 47 | visibility: visible; 48 | } 49 | } 50 | 51 | // System font stack fallback for better performance 52 | $system-font-stack: 53 | -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, 54 | "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 55 | 56 | // Ensure good fallbacks for custom fonts 57 | body { 58 | // This ensures text is visible even if custom fonts fail to load 59 | font-family: $system-font-stack; 60 | 61 | // When custom fonts load, they'll override this 62 | .wf-active & { 63 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 64 | } 65 | } 66 | 67 | // Performance hint: Use font subsetting for icon fonts 68 | // Only load the characters/icons you actually use 69 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | title: Gentelella Admin Template 2 | description: Modern Bootstrap 5 Admin Dashboard Template with Performance Optimizations 3 | url: "https://puikinsh.github.io" 4 | baseurl: "/gentelella" 5 | 6 | # Theme 7 | theme: just-the-docs 8 | 9 | # Just the Docs configuration 10 | color_scheme: light 11 | search_enabled: true 12 | search: 13 | heading_level: 2 14 | previews: 3 15 | preview_words_before: 5 16 | preview_words_after: 10 17 | tokenizer_separator: /[\s/]+/ 18 | rel_url: true 19 | button: false 20 | 21 | # Navigation structure 22 | nav_sort: case_insensitive 23 | 24 | # Footer content 25 | footer_content: "Copyright © {{ 'now' | date: '%Y' }} Colorlib. Distributed under the MIT license." 26 | 27 | # Google Analytics (optional) 28 | # ga_tracking: UA-XXXXXXXX-X 29 | 30 | # Social links in footer 31 | aux_links: 32 | "Gentelella on GitHub": 33 | - "//github.com/puikinsh/gentelella" 34 | "Colorlib": 35 | - "//colorlib.com" 36 | 37 | # Back to top link 38 | back_to_top: true 39 | back_to_top_text: "Back to top" 40 | 41 | # Plugins 42 | plugins: 43 | - jekyll-feed 44 | - jekyll-sitemap 45 | - jekyll-seo-tag 46 | 47 | # Build settings 48 | markdown: kramdown 49 | highlighter: rouge 50 | permalink: pretty 51 | 52 | # Exclude from processing 53 | exclude: 54 | - README.md 55 | - Gemfile 56 | - Gemfile.lock 57 | - node_modules 58 | - vendor 59 | - .bundle 60 | - .sass-cache 61 | - .jekyll-cache 62 | - .jekyll-metadata 63 | 64 | # Collections for organizing documentation 65 | collections: 66 | docs: 67 | permalink: "/:collection/:name/" 68 | output: true 69 | 70 | # Default values 71 | defaults: 72 | - scope: 73 | path: "" 74 | type: "pages" 75 | values: 76 | layout: "default" 77 | - scope: 78 | path: "" 79 | type: "docs" 80 | values: 81 | layout: "default" 82 | - scope: 83 | path: "" 84 | values: 85 | image: "/assets/images/gentelella-preview.jpg" 86 | 87 | # Add site icon/favicon 88 | favicon_ico: "/favicon.ico" -------------------------------------------------------------------------------- /src/utils/security.js: -------------------------------------------------------------------------------- 1 | // Security utilities for XSS prevention 2 | import DOMPurify from 'dompurify'; 3 | 4 | /** 5 | * Sanitizes HTML content to prevent XSS attacks 6 | * @param {string} html - The HTML content to sanitize 7 | * @param {Object} options - DOMPurify configuration options 8 | * @returns {string} - Sanitized HTML 9 | */ 10 | export function sanitizeHtml(html, options = {}) { 11 | if (!html || typeof html !== 'string') { 12 | return ''; 13 | } 14 | 15 | const config = { 16 | ALLOWED_TAGS: [ 17 | 'div', 18 | 'span', 19 | 'p', 20 | 'h1', 21 | 'h2', 22 | 'h3', 23 | 'h4', 24 | 'h5', 25 | 'h6', 26 | 'strong', 27 | 'em', 28 | 'br', 29 | 'img', 30 | 'a' 31 | ], 32 | ALLOWED_ATTR: ['class', 'id', 'src', 'alt', 'href', 'target', 'title'], 33 | ALLOW_DATA_ATTR: false, 34 | ...options 35 | }; 36 | 37 | return DOMPurify.sanitize(html, config); 38 | } 39 | 40 | /** 41 | * Sanitizes text content (removes all HTML tags) 42 | * @param {string} text - The text to sanitize 43 | * @returns {string} - Plain text without HTML 44 | */ 45 | export function sanitizeText(text) { 46 | if (!text || typeof text !== 'string') { 47 | return ''; 48 | } 49 | 50 | // Strip all HTML tags and decode HTML entities 51 | const div = document.createElement('div'); 52 | div.innerHTML = DOMPurify.sanitize(text, { ALLOWED_TAGS: [] }); 53 | return div.textContent || div.innerText || ''; 54 | } 55 | 56 | /** 57 | * Creates a safe innerHTML setter that automatically sanitizes content 58 | * @param {HTMLElement} element - The element to set innerHTML on 59 | * @param {string} html - The HTML content to set 60 | * @param {Object} options - DOMPurify configuration options 61 | */ 62 | export function setSafeInnerHTML(element, html, options = {}) { 63 | if (!element || !html) { 64 | return; 65 | } 66 | 67 | element.innerHTML = sanitizeHtml(html, options); 68 | } 69 | 70 | /** 71 | * Make security utilities available globally for legacy code 72 | */ 73 | if (typeof window !== 'undefined') { 74 | window.sanitizeHtml = sanitizeHtml; 75 | window.sanitizeText = sanitizeText; 76 | window.setSafeInnerHTML = setSafeInnerHTML; 77 | } 78 | 79 | export default { 80 | sanitizeHtml, 81 | sanitizeText, 82 | setSafeInnerHTML 83 | }; 84 | -------------------------------------------------------------------------------- /src/js/page/index3-analytics.js: -------------------------------------------------------------------------------- 1 | // Sales Analytics Widget Initialization 2 | 3 | // Get security utilities from window if available 4 | const sanitizeHtml = 5 | window.sanitizeHtml || 6 | function (html) { 7 | return html; 8 | }; 9 | 10 | function initSalesAnalytics() { 11 | // Animate progress bars on page load 12 | const progressBars = document.querySelectorAll('.sales-progress .progress-bar'); 13 | 14 | if (progressBars.length > 0) { 15 | // Reset all progress bars to 0 width initially 16 | progressBars.forEach(bar => { 17 | bar.style.width = '0%'; 18 | }); 19 | 20 | // Animate them to their target width with a staggered delay 21 | setTimeout(() => { 22 | progressBars.forEach((bar, index) => { 23 | setTimeout(() => { 24 | const targetWidth = 25 | bar.style.getPropertyValue('--final-width') || bar.getAttribute('data-width'); 26 | if (targetWidth) { 27 | bar.style.width = targetWidth; 28 | } else { 29 | // Fallback to reading from parent element's data or style 30 | const parentProgress = bar.closest('.progress'); 31 | if (parentProgress) { 32 | const widthMatch = bar.className.match(/width:\s*(\d+)%/); 33 | if (widthMatch) { 34 | bar.style.width = widthMatch[1] + '%'; 35 | } 36 | } 37 | } 38 | }, index * 150); // Stagger animation by 150ms per bar 39 | }); 40 | }, 500); // Initial delay to ensure page is loaded 41 | } 42 | 43 | // Add hover effects to the View Details button 44 | const viewDetailsBtn = document 45 | .querySelector('.sales-progress') 46 | .closest('.card') 47 | .querySelector('.btn-outline-success'); 48 | if (viewDetailsBtn) { 49 | viewDetailsBtn.addEventListener('click', function (e) { 50 | e.preventDefault(); 51 | 52 | // Simple animation feedback 53 | this.innerHTML = sanitizeHtml('Loading...'); 54 | 55 | setTimeout(() => { 56 | this.innerHTML = sanitizeHtml('View Details'); 57 | // Here you could open a modal or navigate to details page 58 | alert('Sales details would be displayed here'); 59 | }, 1000); 60 | }); 61 | } 62 | } 63 | 64 | // Auto-initialize when DOM is ready 65 | document.addEventListener('DOMContentLoaded', function () { 66 | // Small delay to ensure styles are loaded 67 | setTimeout(initSalesAnalytics, 200); 68 | }); 69 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js'; 2 | import tsPlugin from '@typescript-eslint/eslint-plugin'; 3 | import tsParser from '@typescript-eslint/parser'; 4 | import prettierConfig from 'eslint-config-prettier'; 5 | 6 | export default [ 7 | js.configs.recommended, 8 | prettierConfig, 9 | { 10 | files: ['**/*.js', '**/*.mjs', '**/*.jsx'], 11 | languageOptions: { 12 | ecmaVersion: 2022, 13 | sourceType: 'module', 14 | globals: { 15 | window: 'readonly', 16 | document: 'readonly', 17 | console: 'readonly', 18 | globalThis: 'readonly', 19 | $: 'readonly', 20 | jQuery: 'readonly', 21 | bootstrap: 'readonly', 22 | Chart: 'readonly', 23 | echarts: 'readonly', 24 | NProgress: 'readonly', 25 | dayjs: 'readonly' 26 | } 27 | }, 28 | rules: { 29 | // Code Quality 30 | 'no-unused-vars': ['error', { argsIgnorePattern: '^_' }], 31 | 'no-console': 'warn', 32 | 'no-debugger': 'error', 33 | 'no-alert': 'warn', 34 | 35 | // Best Practices 36 | 'eqeqeq': ['error', 'always'], 37 | 'curly': ['error', 'all'], 38 | 'no-eval': 'error', 39 | 'no-implied-eval': 'error', 40 | 'no-new-func': 'error', 41 | 42 | // Security 43 | 'no-script-url': 'error', 44 | 'no-void': 'error', 45 | 46 | // Style (basic) 47 | 'semi': ['error', 'always'], 48 | 'quotes': ['error', 'single', { avoidEscape: true }], 49 | 'indent': ['warn', 2, { SwitchCase: 1 }], 50 | 'comma-dangle': ['error', 'never'], 51 | 'no-trailing-spaces': 'error', 52 | 'eol-last': 'error' 53 | } 54 | }, 55 | { 56 | files: ['**/*.ts', '**/*.tsx'], 57 | languageOptions: { 58 | parser: tsParser, 59 | parserOptions: { 60 | ecmaVersion: 2022, 61 | sourceType: 'module' 62 | } 63 | }, 64 | plugins: { 65 | '@typescript-eslint': tsPlugin 66 | }, 67 | rules: { 68 | ...tsPlugin.configs.recommended.rules, 69 | '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], 70 | '@typescript-eslint/no-explicit-any': 'warn', 71 | '@typescript-eslint/explicit-function-return-type': 'off', 72 | '@typescript-eslint/explicit-module-boundary-types': 'off' 73 | } 74 | }, 75 | { 76 | ignores: [ 77 | 'node_modules/**', 78 | 'dist/**', 79 | 'docs/_site/**', 80 | 'production/images/**', 81 | '**/*.min.js', 82 | 'vite.config.js' 83 | ] 84 | } 85 | ]; -------------------------------------------------------------------------------- /src/main-form-advanced.js: -------------------------------------------------------------------------------- 1 | // Minimal main.js for form_advanced.html 2 | 3 | // Import jQuery setup first 4 | import $ from './jquery-setup.js'; 5 | window.jQuery = window.$ = $; 6 | globalThis.jQuery = globalThis.$ = $; 7 | 8 | // Bootstrap 5 - No jQuery dependency needed 9 | import * as bootstrap from 'bootstrap'; 10 | window.bootstrap = bootstrap; 11 | globalThis.bootstrap = bootstrap; 12 | 13 | // Switchery (iOS-style toggle switches) 14 | import Switchery from 'switchery'; 15 | window.Switchery = Switchery; 16 | globalThis.Switchery = Switchery; 17 | 18 | // TempusDominus DateTimePicker (Bootstrap 5 compatible) 19 | import { TempusDominus } from '@eonasdan/tempus-dominus'; 20 | window.TempusDominus = TempusDominus; 21 | globalThis.TempusDominus = TempusDominus; 22 | 23 | // Global styles (Bootstrap 5 + custom) 24 | import './main.scss'; 25 | 26 | // TempusDominus CSS 27 | import '@eonasdan/tempus-dominus/dist/css/tempus-dominus.min.css'; 28 | 29 | // Additional CSS for form components 30 | import '@simonwep/pickr/dist/themes/classic.min.css'; 31 | import 'ion-rangeslider/css/ion.rangeSlider.min.css'; 32 | import 'cropper/dist/cropper.min.css'; 33 | 34 | // Add the essential JavaScript functionality 35 | try { 36 | // Import helpers and sidebar 37 | await import('./js/helpers/smartresize-modern.js'); 38 | await import('./js/sidebar-modern.js'); 39 | await import('./js/init-modern.js'); 40 | } catch (error) { 41 | } 42 | 43 | // Create a library availability checker for inline scripts 44 | window.waitForLibraries = function (libraries, callback, timeout = 5000) { 45 | const startTime = Date.now(); 46 | 47 | function check() { 48 | const allAvailable = libraries.every(lib => { 49 | return typeof window[lib] !== 'undefined' || typeof globalThis[lib] !== 'undefined'; 50 | }); 51 | 52 | if (allAvailable) { 53 | callback(); 54 | } else if (Date.now() - startTime < timeout) { 55 | setTimeout(check, 50); 56 | } else { 57 | 'Timeout waiting for libraries:', 58 | libraries.filter( 59 | lib => typeof window[lib] === 'undefined' && typeof globalThis[lib] === 'undefined' 60 | ) 61 | ); 62 | callback(); // Call anyway to prevent hanging 63 | } 64 | } 65 | 66 | check(); 67 | }; 68 | 69 | // Only add form-specific libraries after core is loaded 70 | document.addEventListener('DOMContentLoaded', async function () { 71 | try { 72 | // Input Mask 73 | const { default: Inputmask } = await import('inputmask'); 74 | window.Inputmask = Inputmask; 75 | globalThis.Inputmask = Inputmask; 76 | 77 | // Modern Color Picker 78 | const { default: Pickr } = await import('@simonwep/pickr'); 79 | window.Pickr = Pickr; 80 | globalThis.Pickr = Pickr; 81 | 82 | // Ion Range Slider 83 | await import('ion-rangeslider'); 84 | 85 | // jQuery Knob 86 | await import('jquery-knob'); 87 | 88 | // Cropper.js 89 | await import('cropper'); 90 | } catch (error) { 91 | } 92 | }); 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gentelella", 3 | "version": "2.1.1", 4 | "type": "module", 5 | "description": "Gentelella Admin is a free to use Bootstrap admin template", 6 | "scripts": { 7 | "dev": "vite --clearScreen false", 8 | "dev:watch": "./dev-watch.sh", 9 | "dev:debug": "DEBUG=vite:* vite --clearScreen false", 10 | "build": "NODE_ENV=production vite build", 11 | "build:dev": "NODE_ENV=development vite build", 12 | "preview": "vite preview", 13 | "lint": "eslint src/", 14 | "lint:fix": "eslint src/ --fix", 15 | "format": "prettier --write src/", 16 | "format:check": "prettier --check src/", 17 | "analyze": "npm run build && open dist/stats.html", 18 | "analyze:ci": "npm run build" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/puikinsh/gentelella.git" 23 | }, 24 | "keywords": [ 25 | "css", 26 | "js", 27 | "html", 28 | "template", 29 | "admin", 30 | "bootstrap", 31 | "bootstrap5", 32 | "theme", 33 | "backend", 34 | "responsive", 35 | "modern" 36 | ], 37 | "author": "Aigars Silkalns", 38 | "contributors": [ 39 | "Christian Esperar" 40 | ], 41 | "license": "MIT", 42 | "main": "src/main.js", 43 | "bugs": { 44 | "url": "https://github.com/puikinsh/gentelella/issues" 45 | }, 46 | "homepage": "https://github.com/puikinsh/gentelella#readme", 47 | "devDependencies": { 48 | "@eslint/js": "^9.35.0", 49 | "@typescript-eslint/eslint-plugin": "^8.43.0", 50 | "@typescript-eslint/parser": "^8.43.0", 51 | "eslint": "^9.35.0", 52 | "eslint-config-prettier": "^10.1.8", 53 | "glob": "^11.0.3", 54 | "prettier": "^3.6.2", 55 | "rollup-plugin-visualizer": "^6.0.3", 56 | "sass": "^1.92.1", 57 | "terser": "^5.44.0", 58 | "typescript": "^5.9.2", 59 | "vite": "^7.1.5" 60 | }, 61 | "dependencies": { 62 | "@eonasdan/tempus-dominus": "^6.10.4", 63 | "@fortawesome/fontawesome-free": "^7.0.1", 64 | "@fullcalendar/core": "^6.1.19", 65 | "@fullcalendar/daygrid": "^6.1.19", 66 | "@fullcalendar/interaction": "^6.1.19", 67 | "@fullcalendar/timegrid": "^6.1.19", 68 | "@popperjs/core": "^2.11.8", 69 | "@simonwep/pickr": "^1.9.1", 70 | "autosize": "^6.0.1", 71 | "bootstrap": "^5.3.8", 72 | "chart.js": "^4.5.0", 73 | "choices.js": "^11.1.0", 74 | "cropperjs": "^2.0.1", 75 | "datatables.net": "^2.3.4", 76 | "datatables.net-bs5": "^2.3.4", 77 | "datatables.net-buttons": "^3.2.5", 78 | "datatables.net-buttons-bs5": "^3.2.5", 79 | "datatables.net-fixedheader": "^4.0.3", 80 | "datatables.net-keytable": "^2.12.1", 81 | "datatables.net-responsive": "^3.0.6", 82 | "datatables.net-responsive-bs5": "^3.0.6", 83 | "dayjs": "^1.11.18", 84 | "dompurify": "^3.2.6", 85 | "dropzone": "^6.0.0-beta.2", 86 | "echarts": "^6.0.0", 87 | "flot": "^4.2.6", 88 | "inputmask": "^5.0.9", 89 | "jszip": "^3.10.1", 90 | "leaflet": "^1.9.4", 91 | "moment": "^2.30.1", 92 | "nouislider": "^15.8.1", 93 | "nprogress": "^0.2.0", 94 | "skycons": "^1.0.0", 95 | "switchery": "^0.0.2" 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /docs/bundle-analysis.md: -------------------------------------------------------------------------------- 1 | # Bundle Analysis Guide 2 | 3 | This guide explains how to use the bundle analyzer to monitor and optimize the bundle size of the Gentelella admin template. 4 | 5 | ## Quick Start 6 | 7 | ```bash 8 | # Build and generate bundle analysis 9 | npm run analyze 10 | 11 | # Build without opening the stats file (for CI) 12 | npm run analyze:ci 13 | ``` 14 | 15 | ## Analysis File Location 16 | 17 | After running the build, the bundle analysis is saved to: 18 | - `dist/stats.html` - Interactive treemap visualization 19 | 20 | ## Understanding the Analysis 21 | 22 | ### Treemap View 23 | The default treemap view shows: 24 | - **Size of boxes** = Bundle size (larger boxes = larger bundles) 25 | - **Colors** = Different modules and dependencies 26 | - **Nested structure** = Module hierarchy and dependencies 27 | 28 | ### Key Metrics to Monitor 29 | 30 | 1. **Vendor Chunks** (largest bundles): 31 | - `vendor-charts` (~1.4MB) - Chart.js, ECharts, Leaflet 32 | - `vendor-core` (~168KB) - jQuery, Bootstrap, Popper.js 33 | - `vendor-forms` (~128KB) - Select2, Date pickers, Sliders 34 | - `vendor-ui` (~100KB) - jQuery UI, DataTables 35 | 36 | 2. **Application Code**: 37 | - `init` (~54KB) - Main initialization code 38 | - Page-specific bundles (2-3KB each) 39 | 40 | 3. **CSS Bundles**: 41 | - `init.css` (~510KB) - Main stylesheet bundle 42 | - Page-specific CSS (4-67KB each) 43 | 44 | ## Optimization Strategies 45 | 46 | ### 1. Identify Large Dependencies 47 | - Look for unexpectedly large vendor chunks 48 | - Check if dependencies are being tree-shaken properly 49 | - Consider lighter alternatives for heavy libraries 50 | 51 | ### 2. Monitor Bundle Growth 52 | - Track changes in bundle sizes over time 53 | - Set up alerts for significant size increases 54 | - Use gzip/brotli compressed sizes for realistic network transfer sizes 55 | 56 | ### 3. Code Splitting Optimization 57 | Current manual chunks are optimized for: 58 | - **vendor-core**: Essential libraries loaded on every page 59 | - **vendor-charts**: Chart functionality (loaded only on chart pages) 60 | - **vendor-forms**: Form enhancements (loaded only on form pages) 61 | - **vendor-ui**: UI components (loaded as needed)/ 62 | 63 | ### 4. Dynamic Import Opportunities 64 | Consider converting large features to dynamic imports: 65 | ```javascript 66 | // Instead of static import 67 | import { Chart } from 'chart.js'; 68 | 69 | // Use dynamic import for conditional loading 70 | if (document.querySelector('.chart-container')) { 71 | const { Chart } = await import('chart.js'); 72 | } 73 | ``` 74 | 75 | ## Performance Targets 76 | 77 | ### Current Performance (as of latest build): 78 | - **JavaScript Total**: ~2.4MB uncompressed, ~800KB gzipped 79 | - **CSS Total**: ~610KB uncompressed, ~110KB gzipped 80 | - **Page Load Impact**: Core bundle (168KB) loads on every page 81 | 82 | ### Recommended Targets: 83 | - **Core Bundle**: <200KB (currently 168KB ✅) 84 | - **Feature Bundles**: <150KB each (charts: 1.4MB ❌) 85 | - **Total Initial Load**: <300KB gzipped (currently ~150KB ✅) 86 | 87 | ## Bundle Size Warnings 88 | 89 | The build process will warn about chunks larger than 1000KB: 90 | - This is currently triggered by the `vendor-charts` bundle 91 | - Consider splitting chart libraries further or using dynamic imports 92 | - Adjust the warning limit in `vite.config.js` if needed -------------------------------------------------------------------------------- /docs/_site/bundle-analysis.md: -------------------------------------------------------------------------------- 1 | # Bundle Analysis Guide 2 | 3 | This guide explains how to use the bundle analyzer to monitor and optimize the bundle size of the Gentelella admin template. 4 | 5 | ## Quick Start 6 | 7 | ```bash 8 | # Build and generate bundle analysis 9 | npm run analyze 10 | 11 | # Build without opening the stats file (for CI) 12 | npm run analyze:ci 13 | ``` 14 | 15 | ## Analysis File Location 16 | 17 | After running the build, the bundle analysis is saved to: 18 | - `dist/stats.html` - Interactive treemap visualization 19 | 20 | ## Understanding the Analysis 21 | 22 | ### Treemap View 23 | The default treemap view shows: 24 | - **Size of boxes** = Bundle size (larger boxes = larger bundles) 25 | - **Colors** = Different modules and dependencies 26 | - **Nested structure** = Module hierarchy and dependencies 27 | 28 | ### Key Metrics to Monitor 29 | 30 | 1. **Vendor Chunks** (largest bundles): 31 | - `vendor-charts` (~1.4MB) - Chart.js, ECharts, Leaflet 32 | - `vendor-core` (~168KB) - jQuery, Bootstrap, Popper.js 33 | - `vendor-forms` (~128KB) - Select2, Date pickers, Sliders 34 | - `vendor-ui` (~100KB) - jQuery UI, DataTables 35 | 36 | 2. **Application Code**: 37 | - `init` (~54KB) - Main initialization code 38 | - Page-specific bundles (2-3KB each) 39 | 40 | 3. **CSS Bundles**: 41 | - `init.css` (~510KB) - Main stylesheet bundle 42 | - Page-specific CSS (4-67KB each) 43 | 44 | ## Optimization Strategies 45 | 46 | ### 1. Identify Large Dependencies 47 | - Look for unexpectedly large vendor chunks 48 | - Check if dependencies are being tree-shaken properly 49 | - Consider lighter alternatives for heavy libraries 50 | 51 | ### 2. Monitor Bundle Growth 52 | - Track changes in bundle sizes over time 53 | - Set up alerts for significant size increases 54 | - Use gzip/brotli compressed sizes for realistic network transfer sizes 55 | 56 | ### 3. Code Splitting Optimization 57 | Current manual chunks are optimized for: 58 | - **vendor-core**: Essential libraries loaded on every page 59 | - **vendor-charts**: Chart functionality (loaded only on chart pages) 60 | - **vendor-forms**: Form enhancements (loaded only on form pages) 61 | - **vendor-ui**: UI components (loaded as needed)/ 62 | 63 | ### 4. Dynamic Import Opportunities 64 | Consider converting large features to dynamic imports: 65 | ```javascript 66 | // Instead of static import 67 | import { Chart } from 'chart.js'; 68 | 69 | // Use dynamic import for conditional loading 70 | if (document.querySelector('.chart-container')) { 71 | const { Chart } = await import('chart.js'); 72 | } 73 | ``` 74 | 75 | ## Performance Targets 76 | 77 | ### Current Performance (as of latest build): 78 | - **JavaScript Total**: ~2.4MB uncompressed, ~800KB gzipped 79 | - **CSS Total**: ~610KB uncompressed, ~110KB gzipped 80 | - **Page Load Impact**: Core bundle (168KB) loads on every page 81 | 82 | ### Recommended Targets: 83 | - **Core Bundle**: <200KB (currently 168KB ✅) 84 | - **Feature Bundles**: <150KB each (charts: 1.4MB ❌) 85 | - **Total Initial Load**: <300KB gzipped (currently ~150KB ✅) 86 | 87 | ## Bundle Size Warnings 88 | 89 | The build process will warn about chunks larger than 1000KB: 90 | - This is currently triggered by the `vendor-charts` bundle 91 | - Consider splitting chart libraries further or using dynamic imports 92 | - Adjust the warning limit in `vite.config.js` if needed -------------------------------------------------------------------------------- /src/scss/daterangepicker.scss: -------------------------------------------------------------------------------- 1 | .daterangepicker { 2 | .ranges { 3 | li { 4 | color: #73879c; 5 | 6 | &.active, 7 | &:hover { 8 | background: #536a7f; 9 | border: 1px solid #536a7f; 10 | color: #fff; 11 | } 12 | } 13 | } 14 | 15 | .input-mini { 16 | background-color: #eee; 17 | border: 1px solid #ccc; 18 | box-shadow: none !important; 19 | 20 | &.active { 21 | border: 1px solid #ccc; 22 | } 23 | } 24 | 25 | select { 26 | &.monthselect, 27 | &.yearselect, 28 | &.hourselect, 29 | &.minuteselect, 30 | &.secondselect, 31 | &.ampmselect { 32 | font-size: 12px; 33 | padding: 1px; 34 | height: auto; 35 | margin: 0; 36 | cursor: default; 37 | height: 30px; 38 | border: 1px solid #adb2b5; 39 | line-height: 30px; 40 | border-radius: 0px !important; 41 | } 42 | 43 | &.monthselect { 44 | margin-right: 2%; 45 | } 46 | } 47 | 48 | td { 49 | &.in-range { 50 | background: #e4e7ea; 51 | color: #73879c; 52 | } 53 | 54 | &.active, 55 | &.active:hover { 56 | background-color: #536a7f; 57 | color: #fff; 58 | } 59 | } 60 | 61 | th.available:hover { 62 | background: #eee; 63 | color: #34495e; 64 | } 65 | 66 | &:before, 67 | &:after { 68 | content: none; 69 | } 70 | 71 | .calendar.single { 72 | margin: 0 0 4px 0; 73 | 74 | .calendar-table { 75 | width: 224px; 76 | padding: 0 0 4px 0 !important; 77 | 78 | thead { 79 | & tr:first-child { 80 | th { 81 | padding: 8px 5px; 82 | } 83 | } 84 | 85 | th { 86 | border-radius: 0; 87 | } 88 | } 89 | } 90 | } 91 | 92 | &.picker_1 { 93 | color: #fff; 94 | background: #34495e; 95 | 96 | .calendar-table { 97 | background: #34495e; 98 | 99 | thead { 100 | & tr { 101 | background: #213345; 102 | } 103 | 104 | & tr:first-child { 105 | background: #1abb9c; 106 | } 107 | } 108 | 109 | td.off { 110 | background: #34495e; 111 | color: #999; 112 | } 113 | 114 | td.available:hover { 115 | color: #34495e; 116 | } 117 | } 118 | } 119 | 120 | &.picker_2 { 121 | .calendar-table { 122 | thead { 123 | & tr { 124 | color: #1abb9c; 125 | } 126 | 127 | & tr:first-child { 128 | color: #73879c; 129 | } 130 | } 131 | } 132 | } 133 | 134 | &.picker_3 { 135 | .calendar-table { 136 | thead { 137 | & tr:first-child { 138 | color: #fff; 139 | background: #1abb9c; 140 | } 141 | } 142 | } 143 | } 144 | 145 | &.picker_4 { 146 | .calendar-table { 147 | thead { 148 | & tr:first-child { 149 | color: #fff; 150 | background: #34495e; 151 | } 152 | } 153 | 154 | td, 155 | td.off { 156 | background: #ecf0f1; 157 | border: 1px solid #fff; 158 | border-radius: 0; 159 | } 160 | 161 | td.active { 162 | background: #34495e; 163 | } 164 | } 165 | } 166 | } 167 | 168 | .calendar-exibit { 169 | .show-calendar { 170 | float: none; 171 | display: block; 172 | position: relative; 173 | background-color: #fff; 174 | border: 1px solid #ccc; 175 | margin-bottom: 20px; 176 | border: 1px solid rgba(0, 0, 0, 0.15); 177 | overflow: hidden; 178 | 179 | .calendar { 180 | margin: 0 0 4px 0; 181 | } 182 | 183 | &.picker_1 { 184 | background: #34495e; 185 | } 186 | } 187 | 188 | .calendar-table { 189 | padding: 0 0 4px 0; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/main-inbox.js: -------------------------------------------------------------------------------- 1 | // Inbox.html specific JavaScript with Bootstrap WYSIWYG editor 2 | 3 | // Import jQuery setup first 4 | import $ from './jquery-setup.js'; 5 | window.jQuery = window.$ = $; 6 | globalThis.jQuery = globalThis.$ = $; 7 | 8 | // Import security utilities 9 | import { sanitizeHtml } from './utils/security.js'; 10 | 11 | // Bootstrap 5 12 | import * as bootstrap from 'bootstrap'; 13 | window.bootstrap = bootstrap; 14 | globalThis.bootstrap = bootstrap; 15 | 16 | // Global styles 17 | import './main.scss'; 18 | 19 | // Essential scripts for layout 20 | import './js/helpers/smartresize-modern.js'; 21 | import './js/sidebar-modern.js'; 22 | import './js/init-modern.js'; 23 | 24 | // Bootstrap WYSIWYG Editor 25 | // bootstrap-wysiwyg removed - was unused dependency 26 | 27 | // Initialize WYSIWYG editor when DOM is ready 28 | document.addEventListener('DOMContentLoaded', function () { 29 | // Check if we have the required elements 30 | const editorEl = document.getElementById('editor'); 31 | const toolbarEl = document.querySelector('[data-role="editor-toolbar"]'); 32 | 33 | if (editorEl && toolbarEl && window.jQuery) { 34 | try { 35 | // Initialize the WYSIWYG editor 36 | $(editorEl).wysiwyg({ 37 | toolbarSelector: '[data-role="editor-toolbar"]', 38 | activeToolbarClass: 'btn-info', 39 | hotKeys: { 40 | 'ctrl+b meta+b': 'bold', 41 | 'ctrl+i meta+i': 'italic', 42 | 'ctrl+u meta+u': 'underline', 43 | 'ctrl+z meta+z': 'undo', 44 | 'ctrl+y meta+y meta+shift+z': 'redo' 45 | } 46 | }); 47 | 48 | // Style the editor 49 | $(editorEl).css({ 50 | 'min-height': '200px', 51 | padding: '10px', 52 | border: '1px solid #E6E9ED', 53 | 'border-radius': '4px', 54 | 'background-color': '#fff' 55 | }); 56 | 57 | // Add some default content 58 | $(editorEl).html(sanitizeHtml('

Start typing your message here...

')); 59 | 60 | // Handle toolbar button states 61 | $(editorEl).on('keyup mouseup', function () { 62 | // Update toolbar button states based on current selection 63 | $('[data-role="editor-toolbar"] [data-edit]').each(function () { 64 | const command = $(this).data('edit'); 65 | if (document.queryCommandState(command)) { 66 | $(this).addClass('active btn-info'); 67 | } else { 68 | $(this).removeClass('active btn-info'); 69 | } 70 | }); 71 | }); 72 | 73 | // Handle file upload for images 74 | $('#file-upload').on('change', function (e) { 75 | const file = e.target.files[0]; 76 | if (file && file.type.match('image.*')) { 77 | const reader = new FileReader(); 78 | reader.onload = function (event) { 79 | const img = 80 | ''; 83 | $(editorEl).append(img); 84 | }; 85 | reader.readAsDataURL(file); 86 | } 87 | }); 88 | } catch (error) { 89 | } 90 | } else { 91 | } 92 | }); 93 | 94 | // Handle send button 95 | document.addEventListener('click', function (e) { 96 | if (e.target.matches('[data-action="send"]')) { 97 | e.preventDefault(); 98 | const content = document.getElementById('editor').innerHTML; 99 | 100 | // Show success message 101 | if (window.bootstrap && window.bootstrap.Toast) { 102 | const toastHtml = ` 103 | 111 | `; 112 | 113 | const toastContainer = document.createElement('div'); 114 | toastContainer.innerHTML = sanitizeHtml(toastHtml); 115 | document.body.appendChild(toastContainer); 116 | 117 | const toast = new bootstrap.Toast(toastContainer.querySelector('.toast')); 118 | toast.show(); 119 | } else { 120 | alert('Message sent successfully!'); 121 | } 122 | } 123 | }); 124 | -------------------------------------------------------------------------------- /src/main-tables.js: -------------------------------------------------------------------------------- 1 | // Dedicated entry point for tables_dynamic.html 2 | // Ensures proper DataTables initialization without conflicts 3 | 4 | // Import security utilities 5 | import './utils/security.js'; 6 | 7 | // Bootstrap 5 - No jQuery dependency 8 | import * as bootstrap from 'bootstrap'; 9 | window.bootstrap = bootstrap; 10 | globalThis.bootstrap = bootstrap; 11 | 12 | // Global styles 13 | import './main.scss'; 14 | 15 | // DataTables with all extensions - LOAD FIRST 16 | import DataTable from 'datatables.net-bs5'; 17 | import 'datatables.net-responsive-bs5'; 18 | import 'datatables.net-buttons-bs5'; 19 | import 'datatables.net-buttons/js/buttons.html5.js'; 20 | import 'datatables.net-buttons/js/buttons.print.js'; 21 | import 'datatables.net-fixedheader'; 22 | import 'datatables.net-keytable'; 23 | 24 | // Required for export functionality 25 | import JSZip from 'jszip'; 26 | window.JSZip = JSZip; 27 | 28 | // Make DataTable globally available immediately 29 | window.DataTable = DataTable; 30 | globalThis.DataTable = DataTable; 31 | 32 | // DOM utilities for vanilla JS operations 33 | const DOM = { 34 | ready: (callback) => { 35 | if (document.readyState === 'loading') { 36 | document.addEventListener('DOMContentLoaded', callback); 37 | } else { 38 | callback(); 39 | } 40 | }, 41 | select: (selector) => document.querySelector(selector), 42 | selectAll: (selector) => [...document.querySelectorAll(selector)] 43 | }; 44 | 45 | window.DOM = DOM; 46 | 47 | // Essential JavaScript functionality - modern versions 48 | import './js/helpers/smartresize-modern.js'; 49 | import './js/sidebar-modern.js'; 50 | import './js/init-modern.js'; 51 | 52 | // Initialize DataTables immediately when DOM is ready 53 | DOM.ready(() => { 54 | 55 | // Small delay to ensure all modules are loaded 56 | setTimeout(() => { 57 | 58 | if (typeof window.DataTable === 'undefined') { 59 | return; 60 | } 61 | 62 | try { 63 | // Initialize basic DataTable 64 | const basicTable = DOM.select('#datatable'); 65 | if (basicTable) { 66 | const dt1 = new DataTable(basicTable, { 67 | responsive: true, 68 | pageLength: 10, 69 | lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]], 70 | order: [[0, 'asc']], 71 | language: { 72 | search: 'Search employees:', 73 | lengthMenu: 'Show _MENU_ entries per page', 74 | info: 'Showing _START_ to _END_ of _TOTAL_ entries', 75 | paginate: { 76 | first: 'First', 77 | last: 'Last', 78 | next: 'Next', 79 | previous: 'Previous' 80 | } 81 | } 82 | }); 83 | } 84 | 85 | // Initialize DataTable with Buttons 86 | const buttonsTable = DOM.select('#datatable-buttons'); 87 | if (buttonsTable) { 88 | const dt2 = new DataTable(buttonsTable, { 89 | responsive: true, 90 | pageLength: 10, 91 | dom: 'Bfrtip', 92 | buttons: [ 93 | { 94 | extend: 'copy', 95 | text: ' Copy', 96 | className: 'btn btn-secondary btn-sm' 97 | }, 98 | { 99 | extend: 'csv', 100 | text: ' CSV', 101 | className: 'btn btn-success btn-sm' 102 | }, 103 | { 104 | extend: 'excel', 105 | text: ' Excel', 106 | className: 'btn btn-primary btn-sm' 107 | }, 108 | { 109 | extend: 'print', 110 | text: ' Print', 111 | className: 'btn btn-info btn-sm' 112 | } 113 | ], 114 | language: { 115 | search: 'Search records:', 116 | lengthMenu: 'Show _MENU_ entries per page', 117 | info: 'Showing _START_ to _END_ of _TOTAL_ entries' 118 | } 119 | }); 120 | } 121 | 122 | // Initialize Responsive DataTable 123 | const responsiveTable = DOM.select('#datatable-responsive'); 124 | if (responsiveTable) { 125 | const dt3 = new DataTable(responsiveTable, { 126 | responsive: true, 127 | pageLength: 10, 128 | order: [[0, 'asc']], 129 | language: { 130 | search: 'Search records:', 131 | lengthMenu: 'Show _MENU_ entries per page', 132 | info: 'Showing _START_ to _END_ of _TOTAL_ entries' 133 | }, 134 | columnDefs: [ 135 | { responsivePriority: 1, targets: 0 }, 136 | { responsivePriority: 2, targets: -1 } 137 | ] 138 | }); 139 | } 140 | 141 | 142 | } catch (error) { 143 | } 144 | }, 300); 145 | }); 146 | 147 | -------------------------------------------------------------------------------- /src/scss/index2.scss: -------------------------------------------------------------------------------- 1 | /* index2.html specific styles */ 2 | 3 | /* Top Profiles Styling */ 4 | .top_profiles { 5 | .media.event { 6 | position: relative; 7 | padding: 10px 0; 8 | border-bottom: 1px solid #f0f0f0; 9 | 10 | &:last-child { 11 | border-bottom: none; 12 | } 13 | 14 | .pull-left { 15 | margin-right: 15px; 16 | } 17 | 18 | .profile_thumb { 19 | width: 50px; 20 | height: 50px; 21 | border-radius: 50%; 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | text-decoration: none; 26 | padding: 3px; 27 | 28 | .profile_img { 29 | width: 100%; 30 | height: 100%; 31 | object-fit: cover; 32 | border-radius: 50%; 33 | } 34 | 35 | &.border-aero { 36 | background-color: #1abb9c; 37 | } 38 | 39 | &.border-green { 40 | background-color: #26b99a; 41 | } 42 | 43 | &.border-blue { 44 | background-color: #3498db; 45 | } 46 | 47 | &.border-red { 48 | background-color: #e74c3c; 49 | } 50 | 51 | &.border-purple { 52 | background-color: #9b59b6; 53 | } 54 | } 55 | 56 | .media-body { 57 | flex: 1; 58 | 59 | .title { 60 | font-weight: 600; 61 | font-size: 16px; 62 | color: #333; 63 | text-decoration: none; 64 | display: block; 65 | margin-bottom: 5px; 66 | 67 | &:hover { 68 | color: #1abb9c; 69 | } 70 | } 71 | 72 | p { 73 | margin: 0; 74 | font-size: 14px; 75 | color: #666; 76 | 77 | strong { 78 | color: #1abb9c; 79 | font-weight: 600; 80 | } 81 | 82 | small { 83 | color: #999; 84 | font-size: 12px; 85 | } 86 | } 87 | } 88 | } 89 | } 90 | 91 | /* Event Date Styling for Timeline */ 92 | .media.event { 93 | .pull-left.date { 94 | width: 60px; 95 | height: 60px; 96 | background: #1abb9c; 97 | border-radius: 10px; 98 | text-align: center; 99 | color: white; 100 | text-decoration: none; 101 | display: flex; 102 | flex-direction: column; 103 | justify-content: center; 104 | margin-right: 15px; 105 | 106 | .month { 107 | font-size: 12px; 108 | margin: 0; 109 | text-transform: uppercase; 110 | font-weight: 500; 111 | } 112 | 113 | .day { 114 | font-size: 20px; 115 | font-weight: bold; 116 | margin: 0; 117 | line-height: 1; 118 | } 119 | } 120 | } 121 | 122 | /* Engagement Metrics Styling */ 123 | .metric { 124 | margin-bottom: 15px; 125 | 126 | p { 127 | margin: 0 0 5px 0; 128 | font-size: 14px; 129 | 130 | .metric-label { 131 | color: #73879c; 132 | } 133 | 134 | .metric-value { 135 | font-weight: 600; 136 | color: #333; 137 | } 138 | } 139 | 140 | .progress.progress_sm { 141 | height: 8px; 142 | border-radius: 4px; 143 | } 144 | } 145 | 146 | /* Tiles Styling for Statistics */ 147 | .tiles { 148 | .tile { 149 | background: white; 150 | padding: 20px; 151 | margin-bottom: 20px; 152 | border-left: 3px solid #1abb9c; 153 | 154 | span:first-child { 155 | font-size: 14px; 156 | color: #999; 157 | text-transform: uppercase; 158 | font-weight: 500; 159 | } 160 | 161 | h2 { 162 | font-size: 32px; 163 | font-weight: bold; 164 | color: #333; 165 | margin: 10px 0; 166 | } 167 | 168 | .graph { 169 | margin-top: 10px; 170 | } 171 | } 172 | } 173 | 174 | /* Scroll View for Lists - Only apply to Top Profiles */ 175 | .top_profiles.scroll-view.page-index2-profiles { 176 | max-height: 385px; 177 | overflow-y: auto; 178 | 179 | &::-webkit-scrollbar { 180 | width: 6px; 181 | } 182 | 183 | &::-webkit-scrollbar-track { 184 | background: #f1f1f1; 185 | } 186 | 187 | &::-webkit-scrollbar-thumb { 188 | background: #888; 189 | border-radius: 3px; 190 | 191 | &:hover { 192 | background: #555; 193 | } 194 | } 195 | } 196 | 197 | /* Media Object Flexbox Support */ 198 | .media { 199 | display: flex; 200 | align-items: center; 201 | } 202 | 203 | .media-body { 204 | flex: 1; 205 | } 206 | 207 | /* Color Classes for Icons */ 208 | .aero { 209 | color: #1abb9c !important; 210 | } 211 | .green { 212 | color: #26b99a !important; 213 | } 214 | .blue { 215 | color: #3498db !important; 216 | } 217 | .red { 218 | color: #e74c3c !important; 219 | } 220 | .purple { 221 | color: #9b59b6 !important; 222 | } 223 | 224 | /* Panel Improvements */ 225 | .x_panel { 226 | box-shadow: 227 | 0 1px 3px rgba(0, 0, 0, 0.12), 228 | 0 1px 2px rgba(0, 0, 0, 0.24); 229 | border-radius: 5px; 230 | } 231 | 232 | /* Chart Container Improvements */ 233 | .demo-container { 234 | background: white; 235 | border-radius: 5px; 236 | padding: 10px; 237 | } 238 | 239 | /* Weekly Summary Styling */ 240 | .canvasDoughnut { 241 | border-radius: 50%; 242 | } 243 | -------------------------------------------------------------------------------- /production/page_403.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 403 - Access Forbidden | Gentelella 11 | 12 | 13 | 14 | 15 | 29 | 30 | 31 | 32 |
33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 | Gentelella 41 |

Gentelella

42 |
43 |
44 | 45 | 46 |
47 | 48 |

403

49 |
50 | 51 | 52 |
53 |

Access Forbidden

54 |

You don't have permission to access this resource. Please contact your administrator if you believe this is an error.

55 |
56 | 57 | 58 |
59 | 60 | Go Home 61 | 62 | 65 |
66 | 67 | 68 |
69 |
70 |
71 |
72 | 73 |
Authentication Required
74 | Please log in to access this content 75 |
76 |
77 |
78 |
79 | 80 |
Insufficient Privileges
81 | Contact admin for access rights 82 |
83 |
84 |
85 |
86 | 87 | 88 |
89 | 90 | Need help? 91 | Contact Administrator | 92 | Login | 93 | Request Access 94 | 95 |
96 |
97 |
98 | 99 | 100 |
101 |

102 | © 2025 All Rights Reserved. Gentelella is a Bootstrap 5 template. 103 |

104 |
105 | Privacy 106 | Terms 107 | Support 108 |
109 |
110 | 111 |
112 |
113 |
114 | 115 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Gentelella Admin Template Documentation 4 | nav_order: 1 5 | description: "Modern Bootstrap 5 Admin Dashboard Template with Performance Optimizations" 6 | permalink: / 7 | --- 8 | 9 | # Gentelella Admin Template Documentation 10 | {: .fs-9 } 11 | 12 | Modern Bootstrap 5 Admin Dashboard Template with Vite Build System & Performance Optimizations 13 | {: .fs-6 .fw-300 } 14 | 15 | [Get Started Now](#quick-start){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } 16 | [View on GitHub](https://github.com/puikinsh/gentelella){: .btn .fs-5 .mb-4 .mb-md-0 } 17 | 18 | --- 19 | 20 | ## Welcome to Gentelella v2.0 21 | 22 | Gentelella is a modern, powerful, and completely free Bootstrap 5 admin template that has been completely rebuilt with **Vite**, **performance optimizations**, and the latest web technologies. 23 | 24 | ### ✨ What's New in Version 2.0 25 | 26 | - **🚀 90% smaller initial bundle** (779KB → 79KB) 27 | - **⚡ 40-70% faster page loads** with intelligent code splitting 28 | - **📦 Modern Build System** with Vite 6.3.5 29 | - **🎨 Bootstrap 5.3.7** with updated design system 30 | - **🧩 Smart Module Loading** - Load only what you need 31 | - **📱 Mobile-First** responsive design 32 | 33 | ### 📊 Performance Metrics 34 | 35 | | Metric | Before | After | Improvement | 36 | |--------|--------|-------|-------------| 37 | | Initial Bundle Size | 779 KB | 79 KB | **90% smaller** | 38 | | Total Page Load | 1.3 MB | 770 KB | **40% reduction** | 39 | | First Contentful Paint | 2.1s | 0.8s | **62% faster** | 40 | | Time to Interactive | 3.5s | 1.2s | **66% faster** | 41 | 42 | --- 43 | 44 | ## Quick Start 45 | 46 | ### Prerequisites 47 | - [Node.js](https://nodejs.org/) (v16 or higher) 48 | - npm, yarn, or pnpm package manager 49 | 50 | ### Installation 51 | 52 | ```bash 53 | # Clone the repository 54 | git clone https://github.com/puikinsh/gentelella.git 55 | cd gentelella 56 | 57 | # Install dependencies 58 | npm install 59 | 60 | # Start development server 61 | npm run dev 62 | # Your server will be running at http://localhost:3000 63 | ``` 64 | 65 | ### Alternative Installation 66 | 67 | ```bash 68 | # npm package 69 | npm install gentelella --save 70 | 71 | # yarn package 72 | yarn add gentelella 73 | ``` 74 | 75 | --- 76 | 77 | ## Features Overview 78 | 79 | ### 🏠 Dashboard Components 80 | - **3 Dashboard Layouts** - Different styles for various use cases 81 | - **Widget Cards** - Revenue, stats, progress indicators 82 | - **Real-time Charts** - Live data visualization 83 | - **Activity Feeds** - User activity and notifications 84 | 85 | ### 📊 Data Visualization 86 | - **Chart.js Integration** - Modern, responsive charts 87 | - **Morris.js Charts** - Beautiful time-series graphs 88 | - **Interactive Maps** - World maps with jVectorMap 89 | - **Gauge Charts** - Animated gauge displays 90 | 91 | ### 📝 Form Components 92 | - **Multi-step Wizards** - Complex form workflows 93 | - **Rich Text Editors** - WYSIWYG content editing 94 | - **File Upload** - Drag & drop with progress tracking 95 | - **Advanced Selects** - Searchable, multi-select dropdowns 96 | 97 | ### 📋 Table Components 98 | - **DataTables** - Advanced sorting, filtering, pagination 99 | - **Responsive Tables** - Mobile-optimized displays 100 | - **Export Functions** - PDF, Excel, CSV export options 101 | 102 | --- 103 | 104 | ## Technology Stack 105 | 106 | ### Core Technologies 107 | - **Bootstrap 5.3.7** - CSS Framework 108 | - **Vite 6.3.5** - Build Tool 109 | - **SASS** - CSS Preprocessor 110 | - **jQuery 3.6.1** - DOM Manipulation* 111 | 112 | *jQuery is being phased out in favor of vanilla JavaScript 113 | 114 | ### Chart Libraries 115 | - **Chart.js 4.5.0** - Modern responsive charts 116 | - **Morris.js** - Time-series line graphs 117 | - **jVectorMap** - Interactive world maps 118 | - **Gauge.js** - Beautiful animated gauges 119 | 120 | ### Form Libraries 121 | - **Select2** - Enhanced dropdown selections 122 | - **Tempus Dominus** - Bootstrap 5 date/time picker 123 | - **Ion.RangeSlider** - Advanced range controls 124 | - **DataTables** - Advanced table functionality 125 | 126 | --- 127 | 128 | ## Browser Support 129 | 130 | | Browser | Version | 131 | |---------|---------| 132 | | Chrome | 88+ | 133 | | Firefox | 85+ | 134 | | Safari | 14+ | 135 | | Edge | 88+ | 136 | | Opera | 74+ | 137 | 138 | **Internet Explorer is not supported** - We focus on modern browsers for the best performance and features. 139 | 140 | --- 141 | 142 | ## License 143 | 144 | **MIT License** - Free for personal and commercial use with attribution to [Colorlib](https://colorlib.com/). 145 | 146 | --- 147 | 148 | ## Next Steps 149 | 150 | - **[Installation Guide]({{ site.baseurl }}/installation/)** - Detailed setup instructions 151 | - **[Configuration]({{ site.baseurl }}/configuration/)** - Customize the template 152 | - **[Components]({{ site.baseurl }}/components/)** - Explore all available components 153 | - **[Performance]({{ site.baseurl }}/performance/)** - Optimization strategies 154 | - **[Deployment]({{ site.baseurl }}/deployment/)** - Deploy to production 155 | 156 | --- 157 | 158 |
159 |

Made with ❤️ by Colorlib

160 |
-------------------------------------------------------------------------------- /production/page_404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 404 - Page Not Found | Gentelella 11 | 12 | 13 | 14 | 15 | 45 | 46 | 47 | 48 |
49 |
50 |
51 |
52 |
53 | 54 |
55 |
56 | Gentelella 57 |

Gentelella

58 |
59 |
60 | 61 | 62 |
63 | 64 |

404

65 |
66 | 67 | 68 |
69 |

Page Not Found

70 |

Sorry, the page you are looking for doesn't exist or has been moved.

71 |
72 | 73 | 74 |
75 | 76 | Go Home 77 | 78 | 81 |
82 | 83 | 84 |
85 |
Or search for what you need:
86 |
87 |
88 | 89 | 92 |
93 |
94 |
95 | 96 | 97 |
98 | 99 | Need help? 100 | Contact Support | 101 | Report Issue 102 | 103 |
104 |
105 |
106 | 107 | 108 |
109 |

110 | © 2025 All Rights Reserved. Gentelella is a Bootstrap 5 template. 111 |

112 |
113 | Privacy 114 | Terms 115 | Support 116 |
117 |
118 | 119 |
120 |
121 |
122 | 123 | 124 | -------------------------------------------------------------------------------- /docs/daterangepicker-fix.md: -------------------------------------------------------------------------------- 1 | # Date Range Picker Fix Documentation 2 | 3 | ## Issue 4 | The daterangepicker plugin was throwing an error: 5 | ``` 6 | Error setting default dates for date range picker: TypeError: Cannot read properties of undefined (reading 'clone') 7 | ``` 8 | 9 | ## Root Cause 10 | The daterangepicker library was designed to work with moment.js, which has a native `clone()` method. The project initially used Day.js as a modern replacement for moment.js, but Day.js doesn't have the exact same API as moment.js. Attempts to create a compatibility layer were unsuccessful due to subtle API differences. 11 | 12 | ## Final Solution Implemented 13 | 14 | ### 1. Installed required packages 15 | ```bash 16 | npm install daterangepicker moment 17 | ``` 18 | 19 | ### 2. Dual Date Library Setup in main.js 20 | Configured both Day.js (primary) and moment.js (for daterangepicker) to coexist: 21 | 22 | ```javascript 23 | // Day.js for modern date manipulation (primary library) 24 | import dayjs from 'dayjs'; 25 | 26 | // Day.js plugins for enhanced functionality 27 | import duration from 'dayjs/plugin/duration'; 28 | import relativeTime from 'dayjs/plugin/relativeTime'; 29 | import utc from 'dayjs/plugin/utc'; 30 | import timezone from 'dayjs/plugin/timezone'; 31 | import customParseFormat from 'dayjs/plugin/customParseFormat'; 32 | import advancedFormat from 'dayjs/plugin/advancedFormat'; 33 | import isBetween from 'dayjs/plugin/isBetween'; 34 | import weekOfYear from 'dayjs/plugin/weekOfYear'; 35 | import dayOfYear from 'dayjs/plugin/dayOfYear'; 36 | 37 | // Enable Day.js plugins 38 | dayjs.extend(duration); 39 | dayjs.extend(relativeTime); 40 | dayjs.extend(utc); 41 | dayjs.extend(timezone); 42 | dayjs.extend(customParseFormat); 43 | dayjs.extend(advancedFormat); 44 | dayjs.extend(isBetween); 45 | dayjs.extend(weekOfYear); 46 | dayjs.extend(dayOfYear); 47 | 48 | // Enhanced dayjs wrapper for consistency 49 | const createDayjsWithClone = function(...args) { 50 | const instance = dayjs(...args); 51 | if (!instance.clone) { 52 | instance.clone = function() { return dayjs(this); }; 53 | } 54 | return instance; 55 | }; 56 | 57 | Object.keys(dayjs).forEach(key => { 58 | createDayjsWithClone[key] = dayjs[key]; 59 | }); 60 | createDayjsWithClone.prototype = dayjs.prototype; 61 | createDayjsWithClone.fn = dayjs.prototype; 62 | 63 | // Make Day.js available globally (primary date library) 64 | window.dayjs = createDayjsWithClone; 65 | globalThis.dayjs = createDayjsWithClone; 66 | 67 | // Import real moment.js for daterangepicker compatibility 68 | import moment from 'moment'; 69 | 70 | // Make moment.js available globally for daterangepicker 71 | window.moment = moment; 72 | globalThis.moment = moment; 73 | ``` 74 | 75 | ### 3. Import daterangepicker after setup 76 | ```javascript 77 | // Import daterangepicker AFTER both libraries are configured 78 | import 'daterangepicker'; 79 | import 'daterangepicker/daterangepicker.css'; 80 | 81 | // Verification logging 82 | console.log('Date libraries setup complete:', { 83 | dayjs: typeof window.dayjs, 84 | moment: typeof window.moment, 85 | momentClone: typeof window.moment().clone 86 | }); 87 | ``` 88 | 89 | ## Files Modified 90 | - `/src/main.js` - Added Day.js plugins and daterangepicker imports 91 | - `/package.json` - Added daterangepicker dependency 92 | 93 | ## Verification 94 | After implementing this fix: 95 | - ✅ Build completes successfully 96 | - ✅ No more clone() method errors 97 | - ✅ Daterangepicker functionality restored 98 | - ✅ Day.js compatibility maintained 99 | 100 | ## Why This Solution Works 101 | 102 | ### **Dual Library Approach** 103 | - **Day.js**: Primary date library for modern date manipulation (lighter, faster) 104 | - **Moment.js**: Specifically for daterangepicker compatibility (full API support) 105 | - **Coexistence**: Both libraries work together without conflicts 106 | 107 | ### **Benefits** 108 | 1. **100% Compatibility**: Real moment.js ensures daterangepicker works perfectly 109 | 2. **Modern Development**: Day.js available for new code and general date operations 110 | 3. **No API Gaps**: Eliminates compatibility layer complexity 111 | 4. **Clean Separation**: Each library serves its specific purpose 112 | 113 | ## Alternative Solutions Attempted 114 | 1. **Day.js Compatibility Layer**: Failed due to subtle API differences 115 | 2. **Enhanced Clone Method**: Couldn't replicate full moment.js behavior 116 | 3. **Wrapper Functions**: Daterangepicker still couldn't access required methods 117 | 4. **Replace daterangepicker**: Would require extensive code rewriting 118 | 5. **Full moment.js migration**: Would lose Day.js performance benefits 119 | 120 | ## Why This Solution is Optimal 121 | - **Pragmatic**: Uses the right tool for each job 122 | - **Maintainable**: Clear separation of concerns 123 | - **Performance**: Day.js for new code, moment.js only where needed 124 | - **Future-proof**: Easy to migrate daterangepicker when Day.js-compatible alternatives emerge 125 | 126 | ## Testing 127 | To test the daterangepicker functionality: 128 | 1. Navigate to pages with date range pickers (e.g., reports, analytics) 129 | 2. Verify that date pickers open and function correctly 130 | 3. Check browser console for absence of clone() errors 131 | 4. Test date selection and range functionality 132 | 133 | ## Future Considerations 134 | - Consider migrating to a Day.js native date picker in future major versions 135 | - Monitor daterangepicker updates for native Day.js support 136 | - Evaluate bundle size impact of daterangepicker dependency -------------------------------------------------------------------------------- /docs/_site/daterangepicker-fix.md: -------------------------------------------------------------------------------- 1 | # Date Range Picker Fix Documentation 2 | 3 | ## Issue 4 | The daterangepicker plugin was throwing an error: 5 | ``` 6 | Error setting default dates for date range picker: TypeError: Cannot read properties of undefined (reading 'clone') 7 | ``` 8 | 9 | ## Root Cause 10 | The daterangepicker library was designed to work with moment.js, which has a native `clone()` method. The project initially used Day.js as a modern replacement for moment.js, but Day.js doesn't have the exact same API as moment.js. Attempts to create a compatibility layer were unsuccessful due to subtle API differences. 11 | 12 | ## Final Solution Implemented 13 | 14 | ### 1. Installed required packages 15 | ```bash 16 | npm install daterangepicker moment 17 | ``` 18 | 19 | ### 2. Dual Date Library Setup in main.js 20 | Configured both Day.js (primary) and moment.js (for daterangepicker) to coexist: 21 | 22 | ```javascript 23 | // Day.js for modern date manipulation (primary library) 24 | import dayjs from 'dayjs'; 25 | 26 | // Day.js plugins for enhanced functionality 27 | import duration from 'dayjs/plugin/duration'; 28 | import relativeTime from 'dayjs/plugin/relativeTime'; 29 | import utc from 'dayjs/plugin/utc'; 30 | import timezone from 'dayjs/plugin/timezone'; 31 | import customParseFormat from 'dayjs/plugin/customParseFormat'; 32 | import advancedFormat from 'dayjs/plugin/advancedFormat'; 33 | import isBetween from 'dayjs/plugin/isBetween'; 34 | import weekOfYear from 'dayjs/plugin/weekOfYear'; 35 | import dayOfYear from 'dayjs/plugin/dayOfYear'; 36 | 37 | // Enable Day.js plugins 38 | dayjs.extend(duration); 39 | dayjs.extend(relativeTime); 40 | dayjs.extend(utc); 41 | dayjs.extend(timezone); 42 | dayjs.extend(customParseFormat); 43 | dayjs.extend(advancedFormat); 44 | dayjs.extend(isBetween); 45 | dayjs.extend(weekOfYear); 46 | dayjs.extend(dayOfYear); 47 | 48 | // Enhanced dayjs wrapper for consistency 49 | const createDayjsWithClone = function(...args) { 50 | const instance = dayjs(...args); 51 | if (!instance.clone) { 52 | instance.clone = function() { return dayjs(this); }; 53 | } 54 | return instance; 55 | }; 56 | 57 | Object.keys(dayjs).forEach(key => { 58 | createDayjsWithClone[key] = dayjs[key]; 59 | }); 60 | createDayjsWithClone.prototype = dayjs.prototype; 61 | createDayjsWithClone.fn = dayjs.prototype; 62 | 63 | // Make Day.js available globally (primary date library) 64 | window.dayjs = createDayjsWithClone; 65 | globalThis.dayjs = createDayjsWithClone; 66 | 67 | // Import real moment.js for daterangepicker compatibility 68 | import moment from 'moment'; 69 | 70 | // Make moment.js available globally for daterangepicker 71 | window.moment = moment; 72 | globalThis.moment = moment; 73 | ``` 74 | 75 | ### 3. Import daterangepicker after setup 76 | ```javascript 77 | // Import daterangepicker AFTER both libraries are configured 78 | import 'daterangepicker'; 79 | import 'daterangepicker/daterangepicker.css'; 80 | 81 | // Verification logging 82 | console.log('Date libraries setup complete:', { 83 | dayjs: typeof window.dayjs, 84 | moment: typeof window.moment, 85 | momentClone: typeof window.moment().clone 86 | }); 87 | ``` 88 | 89 | ## Files Modified 90 | - `/src/main.js` - Added Day.js plugins and daterangepicker imports 91 | - `/package.json` - Added daterangepicker dependency 92 | 93 | ## Verification 94 | After implementing this fix: 95 | - ✅ Build completes successfully 96 | - ✅ No more clone() method errors 97 | - ✅ Daterangepicker functionality restored 98 | - ✅ Day.js compatibility maintained 99 | 100 | ## Why This Solution Works 101 | 102 | ### **Dual Library Approach** 103 | - **Day.js**: Primary date library for modern date manipulation (lighter, faster) 104 | - **Moment.js**: Specifically for daterangepicker compatibility (full API support) 105 | - **Coexistence**: Both libraries work together without conflicts 106 | 107 | ### **Benefits** 108 | 1. **100% Compatibility**: Real moment.js ensures daterangepicker works perfectly 109 | 2. **Modern Development**: Day.js available for new code and general date operations 110 | 3. **No API Gaps**: Eliminates compatibility layer complexity 111 | 4. **Clean Separation**: Each library serves its specific purpose 112 | 113 | ## Alternative Solutions Attempted 114 | 1. **Day.js Compatibility Layer**: Failed due to subtle API differences 115 | 2. **Enhanced Clone Method**: Couldn't replicate full moment.js behavior 116 | 3. **Wrapper Functions**: Daterangepicker still couldn't access required methods 117 | 4. **Replace daterangepicker**: Would require extensive code rewriting 118 | 5. **Full moment.js migration**: Would lose Day.js performance benefits 119 | 120 | ## Why This Solution is Optimal 121 | - **Pragmatic**: Uses the right tool for each job 122 | - **Maintainable**: Clear separation of concerns 123 | - **Performance**: Day.js for new code, moment.js only where needed 124 | - **Future-proof**: Easy to migrate daterangepicker when Day.js-compatible alternatives emerge 125 | 126 | ## Testing 127 | To test the daterangepicker functionality: 128 | 1. Navigate to pages with date range pickers (e.g., reports, analytics) 129 | 2. Verify that date pickers open and function correctly 130 | 3. Check browser console for absence of clone() errors 131 | 4. Test date selection and range functionality 132 | 133 | ## Future Considerations 134 | - Consider migrating to a Day.js native date picker in future major versions 135 | - Monitor daterangepicker updates for native Day.js support 136 | - Evaluate bundle size impact of daterangepicker dependency -------------------------------------------------------------------------------- /src/modules/weather.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Weather Module 3 | * Handles Skycons weather icon animations 4 | * Already modern JavaScript - extracted from init.js 5 | */ 6 | 7 | /** 8 | * Initialize Skycons weather icons 9 | * Modern JavaScript implementation 10 | */ 11 | export function initializeSkycons() { 12 | if (typeof window.Skycons === 'undefined') { 13 | console.warn('⚠️ Skycons library not available'); 14 | return; 15 | } 16 | 17 | try { 18 | const skycons = new window.Skycons({ color: '#73879C' }); 19 | 20 | // Index.html specific weather icons (actual IDs from the HTML) 21 | const weatherIcons = [ 22 | { id: 'partly-cloudy-day', type: window.Skycons.PARTLY_CLOUDY_DAY }, 23 | { id: 'clear-day', type: window.Skycons.CLEAR_DAY }, 24 | { id: 'rain', type: window.Skycons.RAIN }, 25 | { id: 'snow', type: window.Skycons.SNOW }, 26 | { id: 'sleet', type: window.Skycons.SLEET }, 27 | { id: 'wind', type: window.Skycons.WIND }, 28 | { id: 'cloudy', type: window.Skycons.CLOUDY } 29 | ]; 30 | 31 | let initializedCount = 0; 32 | 33 | weatherIcons.forEach(icon => { 34 | const element = document.getElementById(icon.id); 35 | if (element) { 36 | skycons.add(element, icon.type); 37 | initializedCount++; 38 | console.log(`✅ Skycon initialized: ${icon.id}`); 39 | } 40 | }); 41 | 42 | // Legacy support: Temperature widget (if exists) 43 | const tempElement = document.getElementById('canvas-temperature'); 44 | if (tempElement) { 45 | skycons.add(tempElement, window.Skycons.PARTLY_CLOUDY_DAY); 46 | initializedCount++; 47 | } 48 | 49 | // Legacy support: Humidity widget (if exists) 50 | const humidityElement = document.getElementById('canvas-humidity'); 51 | if (humidityElement) { 52 | skycons.add(humidityElement, window.Skycons.CLOUDY); 53 | initializedCount++; 54 | } 55 | 56 | // Legacy support: Wind widget (if exists) 57 | const windElement = document.getElementById('canvas-wind'); 58 | if (windElement) { 59 | skycons.add(windElement, window.Skycons.WIND); 60 | initializedCount++; 61 | } 62 | 63 | // Legacy support: Rain widget (if exists) 64 | const rainElement = document.getElementById('canvas-rain'); 65 | if (rainElement) { 66 | skycons.add(rainElement, window.Skycons.RAIN); 67 | initializedCount++; 68 | } 69 | 70 | // Generic weather icons with data attributes 71 | document.querySelectorAll('[data-weather]').forEach(element => { 72 | const weatherType = element.getAttribute('data-weather').toUpperCase(); 73 | if (window.Skycons[weatherType]) { 74 | skycons.add(element, window.Skycons[weatherType]); 75 | initializedCount++; 76 | } 77 | }); 78 | 79 | if (initializedCount > 0) { 80 | // Start the animation 81 | skycons.play(); 82 | console.log(`✅ ${initializedCount} Skycons weather icons initialized and animated`); 83 | } else { 84 | console.log('ℹ️ No weather icon elements found on this page'); 85 | } 86 | 87 | // Return skycons instance for external control 88 | return skycons; 89 | } catch (error) { 90 | console.error('❌ Failed to initialize Skycons:', error); 91 | } 92 | } 93 | 94 | /** 95 | * Weather Data Simulation 96 | * For demonstration purposes - replace with real API calls 97 | */ 98 | export function simulateWeatherData() { 99 | const weatherData = { 100 | temperature: Math.round(Math.random() * 30 + 10) + '°C', 101 | humidity: Math.round(Math.random() * 50 + 30) + '%', 102 | windSpeed: Math.round(Math.random() * 20 + 5) + ' km/h', 103 | rainfall: Math.round(Math.random() * 10) + ' mm' 104 | }; 105 | 106 | // Update weather displays if they exist 107 | const tempDisplay = document.querySelector('[data-weather-temp]'); 108 | if (tempDisplay) { 109 | tempDisplay.textContent = weatherData.temperature; 110 | } 111 | 112 | const humidityDisplay = document.querySelector('[data-weather-humidity]'); 113 | if (humidityDisplay) { 114 | humidityDisplay.textContent = weatherData.humidity; 115 | } 116 | 117 | const windDisplay = document.querySelector('[data-weather-wind]'); 118 | if (windDisplay) { 119 | windDisplay.textContent = weatherData.windSpeed; 120 | } 121 | 122 | const rainDisplay = document.querySelector('[data-weather-rain]'); 123 | if (rainDisplay) { 124 | rainDisplay.textContent = weatherData.rainfall; 125 | } 126 | 127 | return weatherData; 128 | } 129 | 130 | /** 131 | * Weather API Integration Helper 132 | * Template for real weather API integration 133 | */ 134 | export async function fetchWeatherData(location = 'New York') { 135 | try { 136 | // Replace with your weather API endpoint 137 | // const response = await fetch(`https://api.weather.com/current?location=${location}`); 138 | // const data = await response.json(); 139 | 140 | // For now, return simulated data 141 | return simulateWeatherData(); 142 | } catch (error) { 143 | console.error('❌ Failed to fetch weather data:', error); 144 | return simulateWeatherData(); // Fallback to simulated data 145 | } 146 | } 147 | 148 | // Auto-initialize when module loads (only if weather elements exist) 149 | if (typeof document !== 'undefined') { 150 | document.addEventListener('DOMContentLoaded', () => { 151 | const weatherElements = document.querySelectorAll( 152 | '.weather-icon, [data-weather], ' + 153 | '#canvas-temperature, #canvas-humidity, #canvas-wind, #canvas-rain, ' + 154 | '#partly-cloudy-day, #clear-day, #rain, #snow, #sleet, #wind, #cloudy' 155 | ); 156 | if (weatherElements.length > 0) { 157 | initializeSkycons(); 158 | } 159 | }); 160 | } 161 | -------------------------------------------------------------------------------- /production/page_500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 500 - Internal Server Error | Gentelella 11 | 12 | 13 | 14 | 15 | 29 | 30 | 31 | 32 |
33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 | Gentelella 41 |

Gentelella

42 |
43 |
44 | 45 | 46 |
47 | 48 |

500

49 |
50 | 51 | 52 |
53 |

Internal Server Error

54 |

Something went wrong on our end. We're working to fix this issue. Please try again later.

55 |
56 | 57 | 58 |
59 | 60 | Go Home 61 | 62 | 65 |
66 | 67 | 68 |
69 |
70 |
71 |
72 | 73 |
Fixing
74 | Our team is on it 75 |
76 |
77 |
78 |
79 | 80 |
Please Wait
81 | We'll be back soon 82 |
83 |
84 |
85 |
86 | 87 |
Support
88 | We're here to help 89 |
90 |
91 |
92 |
93 | 94 | 95 |
96 |
97 |
Error Details:
98 | Error ID: ERR-500- 99 | Timestamp: 100 |
101 |
102 | 103 | 104 |
105 | 106 | Need immediate help? 107 | Contact Support | 108 | Status Page | 109 | Report Issue 110 | 111 |
112 |
113 |
114 | 115 | 116 |
117 |

118 | © 2025 All Rights Reserved. Gentelella is a Bootstrap 5 template. 119 |

120 |
121 | Privacy 122 | Terms 123 | Support 124 |
125 |
126 | 127 |
128 |
129 |
130 | 131 | 141 | 142 | -------------------------------------------------------------------------------- /src/main-form-basic.js: -------------------------------------------------------------------------------- 1 | // jQuery-free main.js for form_advanced.html - modern alternatives 2 | 3 | // Import security utilities 4 | import { sanitizeHtml } from './utils/security.js'; 5 | 6 | // Bootstrap 5 - No jQuery dependency needed 7 | import * as bootstrap from 'bootstrap'; 8 | window.bootstrap = bootstrap; 9 | globalThis.bootstrap = bootstrap; 10 | 11 | // Global styles (Bootstrap 5 + custom) - most important for layout 12 | import './main.scss'; 13 | 14 | // Essential scripts for layout - modern versions 15 | import './js/helpers/smartresize-modern.js'; 16 | import './js/sidebar-modern.js'; 17 | import './js/init-modern.js'; 18 | 19 | // TempusDominus for date/time pickers 20 | import { TempusDominus } from '@eonasdan/tempus-dominus'; 21 | window.TempusDominus = TempusDominus; 22 | 23 | // TempusDominus CSS 24 | import '@eonasdan/tempus-dominus/dist/css/tempus-dominus.min.css'; 25 | 26 | // Switchery for iOS-style toggles 27 | import Switchery from 'switchery'; 28 | window.Switchery = Switchery; 29 | 30 | // Input Mask for input formatting 31 | import Inputmask from 'inputmask'; 32 | window.Inputmask = Inputmask; 33 | 34 | // NoUiSlider (Ion Range Slider replacement) 35 | import noUiSlider from 'nouislider'; 36 | window.noUiSlider = noUiSlider; 37 | 38 | // NoUiSlider CSS 39 | import 'nouislider/dist/nouislider.css'; 40 | 41 | // Choices.js (Select2 replacement) 42 | import Choices from 'choices.js'; 43 | window.Choices = Choices; 44 | 45 | // Choices.js CSS 46 | import 'choices.js/public/assets/styles/choices.min.css'; 47 | 48 | // Modern Color Picker (Pickr) 49 | import * as PickrModule from '@simonwep/pickr'; 50 | const Pickr = PickrModule.default || PickrModule.Pickr || PickrModule; 51 | window.Pickr = Pickr; 52 | 53 | // Pickr CSS - Classic theme 54 | import '@simonwep/pickr/dist/themes/classic.min.css'; 55 | 56 | // Chart.js for circular progress (jQuery Knob replacement) 57 | import { Chart, registerables } from 'chart.js'; 58 | Chart.register(...registerables); 59 | window.Chart = Chart; 60 | 61 | // Cropper.js 2.0 for image cropping (using cropperjs package) 62 | import * as CropperModule from 'cropperjs'; 63 | 64 | // Create a library availability checker for inline scripts 65 | window.waitForLibraries = function (libraries, callback, timeout = 5000) { 66 | const startTime = Date.now(); 67 | 68 | function check() { 69 | const allAvailable = libraries.every(lib => { 70 | return typeof window[lib] !== 'undefined' || typeof globalThis[lib] !== 'undefined'; 71 | }); 72 | 73 | if (allAvailable) { 74 | callback(); 75 | } else if (Date.now() - startTime < timeout) { 76 | setTimeout(check, 50); 77 | } else { 78 | callback(); // Call anyway to prevent hanging 79 | } 80 | } 81 | 82 | check(); 83 | }; 84 | 85 | // Dispatch a custom event when all modules are loaded 86 | window.dispatchEvent( 87 | new CustomEvent('form-libraries-loaded', { 88 | detail: { 89 | timestamp: Date.now(), 90 | libraries: { 91 | jQuery: typeof window.$, 92 | TempusDominus: typeof window.TempusDominus, 93 | Cropper: typeof window.Cropper, 94 | Pickr: typeof window.Pickr, 95 | Inputmask: typeof window.Inputmask, 96 | Switchery: typeof window.Switchery 97 | } 98 | } 99 | }) 100 | ); 101 | 102 | // Also immediately trigger initialization when DOM is ready 103 | document.addEventListener('DOMContentLoaded', () => { 104 | // Try to initialize directly 105 | if (typeof window.initializeFormComponents === 'function') { 106 | window.initializeFormComponents(); 107 | } 108 | 109 | // ----------------------------- 110 | // Cropper.js v2 demo 111 | // ----------------------------- 112 | const sourceImg = document.getElementById('cropper-source'); 113 | if (sourceImg && window.Cropper) { 114 | const cropperInstance = new window.Cropper(sourceImg, { 115 | container: sourceImg.parentElement 116 | }); 117 | window.cropper = cropperInstance; // expose globally for debugging 118 | 119 | // Helper to refresh preview canvas 120 | const previewEl = document.getElementById('cropper-preview'); 121 | const refreshPreview = () => { 122 | if (!previewEl) { 123 | return; 124 | } 125 | const currentSel = cropperInstance.getCropperSelection(); 126 | if (!currentSel || currentSel.hidden) { 127 | previewEl.innerHTML = sanitizeHtml('No selection'); 128 | return; 129 | } 130 | currentSel 131 | .$toCanvas() 132 | .then(canvas => { 133 | previewEl.innerHTML = sanitizeHtml(''); 134 | canvas.style.width = '100%'; 135 | canvas.style.height = 'auto'; 136 | previewEl.appendChild(canvas); 137 | }); 138 | }; 139 | 140 | // Rotate button 141 | const rotateBtn = document.getElementById('cropper-rotate'); 142 | if (rotateBtn) { 143 | rotateBtn.addEventListener('click', () => { 144 | try { 145 | const imgElement = cropperInstance.getCropperImage(); 146 | imgElement && imgElement.$rotate(90); 147 | refreshPreview(); 148 | } catch (err) { 149 | } 150 | }); 151 | } 152 | 153 | // Reset button 154 | const resetBtn = document.getElementById('cropper-reset'); 155 | if (resetBtn) { 156 | resetBtn.addEventListener('click', () => { 157 | try { 158 | cropperInstance.getCropperImage()?.$resetTransform(); 159 | cropperInstance.getCropperSelection()?.$reset(); 160 | refreshPreview(); 161 | } catch (err) { 162 | } 163 | }); 164 | } 165 | 166 | // Download button 167 | const downloadBtn = document.getElementById('cropper-download'); 168 | if (downloadBtn) { 169 | downloadBtn.addEventListener('click', () => { 170 | const selection = cropperInstance.getCropperSelection(); 171 | if (!selection) { 172 | return; 173 | } 174 | selection 175 | .$toCanvas() 176 | .then(canvas => { 177 | const link = document.createElement('a'); 178 | link.href = canvas.toDataURL('image/jpeg'); 179 | link.download = 'cropped-image.jpg'; 180 | link.click(); 181 | }); 182 | }); 183 | } 184 | 185 | // Listen for any selection change events on the canvas 186 | cropperInstance.getCropperCanvas()?.addEventListener('change', refreshPreview); 187 | 188 | // Initial preview render after load 189 | setTimeout(refreshPreview, 600); 190 | } 191 | }); 192 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # gentelella 2 | 3 | Gentelella 管理后台是一个免费使用的Bootstrap管理模版。 4 | 这个模版默认使用Bootstrap 3 风格,还有一系列强大的jQuery插件和工具去创造一个强大的框架,用来创建管理面板或者后端仪表盘。 5 | 该主题使用了不同的库,用来创建表格,日历,表单验证,引导式风格的接口,导航菜单,文本表格,日期范围,上传区域,表格自动填充,范围滑块,进度条,提示以及更多。 6 | 我们很乐意看到你使用这个令人惊叹的管理模版。你可以通过tweet [@colorlib](https://twitter.com/colorlib)告知我们你的网站,app或者服务。一旦列表量够了,我们将会写一篇文章去展示这个最佳案例[this](https://colorlib.com/wp/avada-theme-examples/)。 7 | 8 | ## 主题例子 9 | ![Gentelella Bootstrap 管理模版](https://cdn.colorlib.com/wp/wp-content/uploads/sites/2/gentelella-admin-template-preview.jpg 10 | "Gentelella 主题浏览器预览") 11 | 12 | **[模版例子](https://colorlib.com/polygon/gentelella/index.html)** 13 | 14 | ## 文档 15 | 16 | **[文档](https://puikinsh.github.io/gentelella/)** 17 | 18 | ## 通过Package Manager安装 19 | 20 | 我们的目标是使它在不同的包管理器中都可以安装!你有你倾向使用的包管理器还有你知道为什么吗?随时随地通过pull request告诉我们! 21 | 现在这是一些可以安装的包管理器: 22 | 23 | **Bower** 24 | 25 | ``` 26 | bower install gentelella --save 27 | ``` 28 | 29 | **npm** 30 | 31 | ``` 32 | npm install gentelella --save 33 | ``` 34 | 35 | **yarn** 36 | 37 | ``` 38 | yarn add gentelella 39 | ``` 40 | ## 如何贡献 41 | 为了贡献,请确保你安装有稳定的 [Node.js](https://nodejs.org/) 和[npm](https://npmjs.com) 42 | 测试Gulp CLI 是否安装,可以运行`gulp --version`.如果命令行没有找到,运行`npm install -g gulp`。关于更多如何安装Gulp,可以看一下Gulp的使用指南[Getting Started](https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md)。 43 | 44 | 安装所有的gulp依赖,可以运行```npm install``` 45 | 46 | 如果`gulp` 已经安装了,遵循一下的步骤。 47 | 48 | 1. Fork和克隆这个仓库 49 | 2. 运行`gulp`,会在你的默认浏览器中运行 gentelella 。 50 | 3. 现在你可以开始写代码了! 51 | 4. 提交一个pull request 52 | 53 | ## Gentelella 在其它平台和框架 54 | 55 | * [Gentelella on Ruby on Rails 4](https://github.com/iogbole/gentelella_on_rails) thanks to Israel Ogbole. 56 | * [Gentelella on Rails 5.x](https://github.com/mwlang/gentelella-rails) thanks to Michael Lang 57 | * [Gentelella on Smarty 3](https://github.com/microvb/otp-thing) with one time password generator, validator, and QR code generator that has no web dependencies (self-contained) in PHP thanks to MicroVB INC 58 | * [Gentelella integrated into Symfony 3](https://github.com/krzysiekpiasecki/Gentelella) full stack PHP framework thanks to Krzysztof Piasecki. 59 | * [Gentelella on Yii framework 2](https://github.com/yiister/yii2-gentelella) with an asset bundle, a layout template and some widgets. 60 | * [Gentelella on Angular 2](https://github.com/kmkatsma/angular2-webpack-starter-gentelella) Angular Webpack Starter modified to utilize the Gentelella. 61 | * [Gentelella on Aurelia](https://github.com/kmkatsma/aurelia-gentelella) Typescript webpack skeleton modified to utilize the Gentelella. 62 | * [Gentelella on Laravel](https://github.com/Labs64/laravel-boilerplate) PHP / Laravel 5 boilerplate project with Gentelella Admin theme support. 63 | * [Gentelella on Django](https://github.com/GiriB/django-gentelella) Gentelella modified to fit as a Django app 64 | * [Gentelella on Flask](https://github.com/afourmy/flask-gentelella) Gentelella modified to fit as a Flask app 65 | * [Gentelella on CakePHP 3](https://github.com/backstageel/cakephp-gentelella-theme) Gentelella modified to work on CakePHP 66 | * [Gentelella right to left](https://github.com/mortezakarimi/gentelella-rtl) Gentelella modified to work with right to left languages like Persian 67 | * [Gentelella-rtl on Yii framework 2](https://github.com/mortezakarimi/yii2-gentelella-rtl) with an asset bundle, a layout template and some widgets. inspired from [Gentelella on Yii framework 2](https://github.com/yiister/yii2-gentelella) 68 | 69 | 让我们知道你是否为其它管理模版或者平台、框架集成了Gentelella,我们会很乐意分享你的工作。 70 | 71 | ## Scripts 包括: 72 | * Bootstrap 73 | * Font Awesome 74 | * jQuery-Autocomplete 75 | * FullCalendar 76 | * Charts.js 77 | * Bootstrap Colorpicker 78 | * Cropper 79 | * dataTables 80 | * Date Range Picker for Bootstrap 81 | * Dropzone 82 | * easyPieChart 83 | * ECharts 84 | * bootstrap-wysiwyg 85 | * Flot - Javascript plotting library for jQuery. 86 | * gauge.js 87 | * jquery.inputmask plugin 88 | * Ion.RangeSlider 89 | * jQuery 90 | * jVectorMap 91 | * moment.js 92 | * Chart.js - 现代响应式图表 93 | * PNotify - Awesome JavaScript notifications 94 | * NProgress 95 | * Pace 96 | * Parsley 97 | * bootstrap-progressbar 98 | * select2 99 | * Sidebar Transitions - simple off-canvas navigations 100 | * Skycons - canvas based wather icons 101 | * jQuery Sparklines plugin 102 | * switchery - Turns HTML checkbox inputs into beautiful iOS style switches 103 | * jQuery Tags Input Plugin 104 | * Autosize - resizes text area to fit text 105 | * validator - HTML from validator using jQuery 106 | * jQuery Smart Wizard 107 | 108 | ## 其它模版和有用的资源 109 | * [Free Bootstrap Admin Templates](https://colorlib.com/wp/free-bootstrap-admin-dashboard-templates/ "Bootstrap Admin Templates on Colorlib") - List of the best Free Bootstrap admin dashboard templates that are available for free for personal and commercial use. 110 | * [Free Admin Templates](https://colorlib.com/wp/free-html5-admin-dashboard-templates/ "List of free HTML based admin templates by Colorlib") - Long list of the best free HTML5 powered admin dashboard templates. Available for personal and commercial use. 111 | * [Angular Templates](https://colorlib.com/wp/angularjs-admin-templates/ "Angular Admin Templates on Colorlib") - List of the most popular admin templates based on AngularJS. 112 | * [HTML Admin Templates](https://colorlib.com/wp/html-admin-templates/ "Material Design Admin Templates on Colorlib") - Most of these templates are based on AngularJS and uses a stunning Material design. 113 | * [Bootstrap Admin Templates](https://colorlib.com/wp/bootstrap-admin-templates/ "List of Premium Bootstrap Admin Templates by Colorlib") - List of premium Bootstrap admin templates that uses a minimal flat or material design. Majority of these themes uses AngularJS but HTML5 versions are also available. 114 | * [WordPress Admin Templates](https://colorlib.com/wp/wordpress-admin-dashboard-themes-plugins/ "List of WordPress Admin Dashboard Templates and Plugins by Colorlib") - List of the best WordPress admin dashboard templates and plugins that will add a personal touch to your WordPress dashboard. 115 | * [WordPress Themes](https://colorlib.com/wp/free-wordpress-themes/ "List of Free WordPress themes by Colorlib") - A huge selection of the best free WordPress themes that are all licensed under GPL and are available for personal and commercial use without restrictions. 116 | 117 | ## License information 118 | Gentelella is licensed under The MIT License (MIT). Which means that you can use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software. But you always need to state that Colorlib is the original author of this template. 119 | 120 | Project is developed and maintained by [Colorlib](https://colorlib.com/ "Colorlib - Make Your First Blog") and Aigars Silkalns 121 | -------------------------------------------------------------------------------- /src/main-upload.js: -------------------------------------------------------------------------------- 1 | // Form Upload.html specific JavaScript with Dropzone integration 2 | 3 | // Import jQuery setup first 4 | import $ from './jquery-setup.js'; 5 | window.jQuery = window.$ = $; 6 | globalThis.jQuery = globalThis.$ = $; 7 | 8 | // Bootstrap 5 9 | import * as bootstrap from 'bootstrap'; 10 | window.bootstrap = bootstrap; 11 | globalThis.bootstrap = bootstrap; 12 | 13 | // Global styles 14 | import './main.scss'; 15 | 16 | // Essential scripts for layout 17 | import './js/helpers/smartresize-modern.js'; 18 | import './js/sidebar-modern.js'; 19 | import './js/init-modern.js'; 20 | 21 | // Dropzone for file uploads 22 | import Dropzone from 'dropzone'; 23 | import 'dropzone/dist/dropzone.css'; 24 | 25 | // Make Dropzone available globally 26 | window.Dropzone = Dropzone; 27 | globalThis.Dropzone = Dropzone; 28 | 29 | // Configure Dropzone defaults 30 | Dropzone.autoDiscover = false; 31 | 32 | // Initialize Dropzone when DOM is ready 33 | document.addEventListener('DOMContentLoaded', function () { 34 | const dropzoneElement = document.querySelector('.dropzone'); 35 | 36 | if (dropzoneElement) { 37 | try { 38 | const myDropzone = new Dropzone(dropzoneElement, { 39 | url: '#', // Since this is a demo, we'll use a dummy URL 40 | maxFilesize: 20, // MB 41 | acceptedFiles: 'image/*,application/pdf,.psd,.doc,.docx,.xls,.xlsx,.ppt,.pptx', 42 | addRemoveLinks: true, 43 | dictDefaultMessage: ` 44 |
45 | 46 |

Drop files here or click to upload

47 |

Maximum file size: 20MB

48 |
49 | `, 50 | dictRemoveFile: 'Remove file', 51 | dictCancelUpload: 'Cancel upload', 52 | dictUploadCanceled: 'Upload canceled', 53 | dictCancelUploadConfirmation: 'Are you sure you want to cancel this upload?', 54 | dictRemoveFileConfirmation: 'Are you sure you want to remove this file?', 55 | 56 | // Custom styling 57 | previewTemplate: ` 58 |
59 |
60 | 61 |
62 |
63 |
64 |
65 |
66 |
67 | 68 |
69 |
70 |
71 | 72 | Check 73 | 74 | 75 | 76 | 77 |
78 |
79 | 80 | Error 81 | 82 | 83 | 84 | 85 | 86 | 87 |
88 |
89 |
90 | `, 91 | 92 | init: function () { 93 | this.on('addedfile', function (file) {}); 94 | 95 | this.on('removedfile', function (file) {}); 96 | 97 | this.on('success', function (file, response) {}); 98 | 99 | this.on('error', function (file, errorMessage) {}); 100 | 101 | // Since this is a demo, simulate successful uploads 102 | this.on('sending', function (file, xhr, formData) { 103 | // Simulate upload success after 2 seconds 104 | setTimeout(() => { 105 | this.emit('success', file, 'Upload successful (demo)'); 106 | this.emit('complete', file); 107 | }, 2000); 108 | 109 | // Prevent actual sending since this is demo 110 | xhr.abort(); 111 | }); 112 | } 113 | }); 114 | 115 | // Store reference globally 116 | window.myDropzone = myDropzone; 117 | globalThis.myDropzone = myDropzone; 118 | } catch (error) { 119 | } 120 | } else { 121 | } 122 | }); 123 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { visualizer } from 'rollup-plugin-visualizer'; 3 | 4 | export default defineConfig({ 5 | root: '.', 6 | publicDir: 'production', 7 | logLevel: 'info', 8 | clearScreen: false, 9 | build: { 10 | outDir: 'dist', 11 | emptyOutDir: true, 12 | chunkSizeWarningLimit: 1000, 13 | // Optimize source maps: 'hidden' for production (generates but doesn't reference in bundle) 14 | // This allows debugging in production without exposing source maps to users 15 | sourcemap: process.env.NODE_ENV === 'production' ? 'hidden' : true, 16 | target: 'es2022', 17 | rollupOptions: { 18 | plugins: [ 19 | // Bundle analyzer - generates stats.html file 20 | visualizer({ 21 | filename: 'dist/stats.html', 22 | open: false, 23 | gzipSize: true, 24 | brotliSize: true, 25 | template: 'treemap' // 'treemap', 'sunburst', 'network' 26 | }) 27 | ], 28 | output: { 29 | manualChunks: { 30 | // Core UI framework - used on all pages 31 | 'vendor-core': ['bootstrap', '@popperjs/core'], 32 | 33 | // Chart libraries - only loaded on chart pages 34 | 'vendor-charts': ['chart.js', 'echarts'], 35 | 36 | // Maps - separate since it's large and only used on map pages 37 | 'vendor-maps': ['leaflet'], 38 | 39 | // Form libraries - loaded on form pages 40 | 'vendor-forms': ['choices.js', 'nouislider', 'autosize', 'switchery', '@eonasdan/tempus-dominus'], 41 | 42 | // DataTables core - frequently used 43 | 'vendor-tables': ['datatables.net', 'datatables.net-bs5'], 44 | 45 | // DataTables extensions - only loaded when needed 46 | 'vendor-tables-ext': ['jszip'], 47 | 48 | // UI utilities and progress 49 | 'vendor-ui': ['nprogress'], 50 | 51 | // Date/time and small utilities 52 | 'vendor-utils': ['dayjs', 'skycons'] 53 | }, 54 | assetFileNames: (assetInfo) => { 55 | const info = assetInfo.name.split('.'); 56 | const extType = info[info.length - 1]; 57 | if (/\.(png|jpe?g|svg|gif|tiff|bmp|ico)$/i.test(assetInfo.name)) { 58 | return `images/[name]-[hash][extname]`; 59 | } 60 | if (/\.(woff2?|eot|ttf|otf)$/i.test(assetInfo.name)) { 61 | return `fonts/[name]-[hash][extname]`; 62 | } 63 | return `assets/[name]-[hash][extname]`; 64 | }, 65 | chunkFileNames: 'js/[name]-[hash].js', 66 | entryFileNames: 'js/[name]-[hash].js' 67 | }, 68 | input: { 69 | main: 'production/index.html', 70 | index2: 'production/index2.html', 71 | index3: 'production/index3.html', 72 | index4: 'production/index4.html', 73 | 74 | form: 'production/form.html', 75 | form_advanced: 'production/form_advanced.html', 76 | form_buttons: 'production/form_buttons.html', 77 | form_upload: 'production/form_upload.html', 78 | form_validation: 'production/form_validation.html', 79 | form_wizards: 'production/form_wizards.html', 80 | 81 | general_elements: 'production/general_elements.html', 82 | media_gallery: 'production/media_gallery.html', 83 | typography: 'production/typography.html', 84 | icons: 'production/icons.html', 85 | 86 | widgets: 'production/widgets.html', 87 | invoice: 'production/invoice.html', 88 | inbox: 'production/inbox.html', 89 | calendar: 'production/calendar.html', 90 | 91 | tables: 'production/tables.html', 92 | tables_dynamic: 'production/tables_dynamic.html', 93 | 94 | chartjs: 'production/chartjs.html', 95 | chartjs2: 'production/chartjs2.html', 96 | chart3: 'production/chart3.html', 97 | echarts: 'production/echarts.html', 98 | other_charts: 'production/other_charts.html', 99 | 100 | fixed_sidebar: 'production/fixed_sidebar.html', 101 | fixed_footer: 'production/fixed_footer.html', 102 | 103 | e_commerce: 'production/e_commerce.html', 104 | projects: 'production/projects.html', 105 | project_detail: 'production/project_detail.html', 106 | contacts: 'production/contacts.html', 107 | profile: 'production/profile.html', 108 | 109 | page_403: 'production/page_403.html', 110 | page_404: 'production/page_404.html', 111 | page_500: 'production/page_500.html', 112 | plain_page: 'production/plain_page.html', 113 | login: 'production/login.html', 114 | pricing_tables: 'production/pricing_tables.html', 115 | 116 | level2: 'production/level2.html', 117 | map: 'production/map.html', 118 | landing: 'production/landing.html' 119 | } 120 | }, 121 | minify: 'terser', 122 | terserOptions: { 123 | compress: { 124 | drop_console: true, 125 | drop_debugger: true, 126 | unsafe_comps: true, 127 | passes: 2 128 | }, 129 | mangle: { 130 | safari10: true 131 | } 132 | } 133 | }, 134 | esbuild: { 135 | target: 'es2022' 136 | }, 137 | server: { 138 | open: '/index.html', 139 | port: 3000, 140 | host: true, 141 | watch: { 142 | usePolling: false, 143 | interval: 100, 144 | ignored: ['**/node_modules/**', '**/dist/**'] 145 | }, 146 | hmr: { 147 | overlay: false 148 | } 149 | }, 150 | optimizeDeps: { 151 | include: [ 152 | 'bootstrap', 153 | '@popperjs/core', 154 | 'dayjs', 155 | 'nprogress' 156 | ], 157 | force: false, 158 | exclude: ['@simonwep/pickr'] 159 | }, 160 | resolve: { 161 | // Modern build without jQuery aliases 162 | }, 163 | css: { 164 | // Enable CSS source maps in development 165 | devSourcemap: true, 166 | preprocessorOptions: { 167 | scss: { 168 | // Silence Sass deprecation warnings 169 | silenceDeprecations: ['legacy-js-api', 'import', 'global-builtin', 'color-functions'], 170 | // Additional settings for better performance 171 | includePaths: ['node_modules'], 172 | // Generate source maps for better debugging 173 | sourceMap: true, 174 | sourceMapContents: true 175 | } 176 | } 177 | }, 178 | define: { 179 | global: 'globalThis', 180 | process: JSON.stringify({ 181 | env: { 182 | NODE_ENV: 'production' 183 | } 184 | }), 185 | 'process.env': JSON.stringify({ 186 | NODE_ENV: 'production' 187 | }), 188 | 'process.env.NODE_ENV': '"production"' 189 | } 190 | }); -------------------------------------------------------------------------------- /src/utils/table-optimizer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Table Performance Optimizer 3 | * Fixes performance issues with large DataTables 4 | */ 5 | 6 | /** 7 | * Optimize large tables by implementing lazy loading and progressive enhancement 8 | */ 9 | export function optimizeTablePerformance() { 10 | // Add loading skeleton for tables while DataTables initializes 11 | addTableLoadingSkeletons(); 12 | 13 | // Implement progressive table initialization with delays between each table 14 | initializeTablesProgressively(); 15 | 16 | // Add intersection observer for lazy loading of off-screen tables 17 | implementLazyTableLoading(); 18 | 19 | // Optimize table dimensions to prevent layout shifts 20 | optimizeTableDimensions(); 21 | } 22 | 23 | /** 24 | * Add loading skeletons to prevent layout shifts 25 | */ 26 | function addTableLoadingSkeletons() { 27 | const tables = document.querySelectorAll('table[id^="datatable"]'); 28 | 29 | tables.forEach(table => { 30 | // Add a minimum height to prevent layout shift 31 | table.style.minHeight = '400px'; 32 | 33 | // Add loading indicator 34 | const loadingDiv = document.createElement('div'); 35 | loadingDiv.className = 'table-loading-overlay'; 36 | loadingDiv.innerHTML = ` 37 |
38 |
39 | Loading table... 40 |
41 | Initializing table... 42 |
43 | `; 44 | loadingDiv.style.cssText = ` 45 | position: absolute; 46 | top: 0; 47 | left: 0; 48 | right: 0; 49 | bottom: 0; 50 | background: rgba(255, 255, 255, 0.9); 51 | z-index: 10; 52 | display: flex; 53 | align-items: center; 54 | justify-content: center; 55 | min-height: 200px; 56 | `; 57 | 58 | // Make parent container relative 59 | const parent = table.closest('.table-responsive') || table.parentElement; 60 | parent.style.position = 'relative'; 61 | parent.appendChild(loadingDiv); 62 | 63 | // Store reference for removal later 64 | table.loadingOverlay = loadingDiv; 65 | }); 66 | } 67 | 68 | /** 69 | * Initialize tables progressively with delays to prevent blocking 70 | */ 71 | function initializeTablesProgressively() { 72 | const tableConfigs = [ 73 | { id: 'datatable', delay: 0 }, 74 | { id: 'datatable-checkbox', delay: 300 }, 75 | { id: 'datatable-buttons', delay: 600 }, 76 | { id: 'datatable-responsive', delay: 900 } 77 | ]; 78 | 79 | tableConfigs.forEach(config => { 80 | setTimeout(() => { 81 | initializeSpecificTable(config.id); 82 | }, config.delay); 83 | }); 84 | } 85 | 86 | /** 87 | * Initialize a specific table with optimized settings 88 | */ 89 | function initializeSpecificTable(tableId) { 90 | const table = document.getElementById(tableId); 91 | if (!table || table.dataTableInstance) { 92 | return; 93 | } 94 | 95 | try { 96 | // Basic configuration optimized for performance 97 | const config = { 98 | pageLength: 25, // Smaller page size for better performance 99 | processing: true, 100 | deferRender: true, // Only render visible rows 101 | stateSave: true, // Save user state 102 | responsive: true, 103 | autoWidth: false, 104 | dom: 105 | '<"row"<"col-sm-12 col-md-6"l><"col-sm-12 col-md-6"f>>' + 106 | '<"row"<"col-sm-12"tr>>' + 107 | '<"row"<"col-sm-12 col-md-5"i><"col-sm-12 col-md-7"p>>', 108 | language: { 109 | processing: 'Loading...', 110 | loadingRecords: 'Loading...', 111 | emptyTable: 'No data available' 112 | }, 113 | initComplete: function () { 114 | // Remove loading overlay when table is ready 115 | if (table.loadingOverlay) { 116 | table.loadingOverlay.remove(); 117 | delete table.loadingOverlay; 118 | } 119 | console.log(`✅ Table ${tableId} initialized successfully`); 120 | } 121 | }; 122 | 123 | // Add specific features based on table type 124 | if (tableId === 'datatable-buttons') { 125 | config.buttons = ['copy', 'csv', 'excel', 'pdf', 'print']; 126 | config.dom = 'Bfrtip'; 127 | } 128 | 129 | // Initialize DataTable - check both global DataTable and jQuery DataTable 130 | if (typeof DataTable !== 'undefined') { 131 | const dataTable = new DataTable(table, config); 132 | table.dataTableInstance = dataTable; 133 | } else if (typeof window.$ !== 'undefined' && $.fn.DataTable) { 134 | const dataTable = $(table).DataTable(config); 135 | table.dataTableInstance = dataTable; 136 | } 137 | } catch (error) { 138 | console.error(`❌ Failed to initialize table ${tableId}:`, error); 139 | // Remove loading overlay even on error 140 | if (table.loadingOverlay) { 141 | table.loadingOverlay.remove(); 142 | delete table.loadingOverlay; 143 | } 144 | } 145 | } 146 | 147 | /** 148 | * Implement lazy loading for tables not in viewport 149 | */ 150 | function implementLazyTableLoading() { 151 | if ('IntersectionObserver' in window) { 152 | const observer = new IntersectionObserver( 153 | entries => { 154 | entries.forEach(entry => { 155 | if (entry.isIntersecting) { 156 | const table = entry.target; 157 | const tableId = table.id; 158 | 159 | // Initialize table when it becomes visible 160 | if (!table.dataTableInstance) { 161 | setTimeout(() => { 162 | initializeSpecificTable(tableId); 163 | }, 100); 164 | } 165 | 166 | // Stop observing once initialized 167 | observer.unobserve(table); 168 | } 169 | }); 170 | }, 171 | { 172 | rootMargin: '100px' // Start loading 100px before element is visible 173 | } 174 | ); 175 | 176 | // Observe tables that aren't in the initial viewport 177 | document.querySelectorAll('table[id^="datatable"]').forEach((table, index) => { 178 | if (index > 1) { 179 | // Only lazy load tables after the first 2 (since we now have 4 total) 180 | observer.observe(table); 181 | } 182 | }); 183 | } 184 | } 185 | 186 | /** 187 | * Optimize table dimensions to prevent layout shifts 188 | */ 189 | function optimizeTableDimensions() { 190 | const style = document.createElement('style'); 191 | style.textContent = ` 192 | /* Prevent layout shifts during DataTable initialization */ 193 | .table-responsive { 194 | min-height: 400px; 195 | } 196 | 197 | table[id^="datatable"] { 198 | width: 100% !important; 199 | min-height: 300px; 200 | } 201 | 202 | /* Smooth transitions for DataTable state changes */ 203 | .dataTables_wrapper { 204 | transition: opacity 0.3s ease; 205 | } 206 | 207 | /* Loading overlay styles */ 208 | .table-loading-overlay { 209 | transition: opacity 0.3s ease; 210 | } 211 | 212 | /* Performance optimizations */ 213 | .dataTables_processing { 214 | background: rgba(255, 255, 255, 0.8); 215 | border: 1px solid #ddd; 216 | border-radius: 4px; 217 | color: #666; 218 | font-size: 14px; 219 | padding: 10px 20px; 220 | } 221 | `; 222 | document.head.appendChild(style); 223 | } 224 | 225 | // Auto-initialize when DOM is ready 226 | if (typeof document !== 'undefined') { 227 | document.addEventListener('DOMContentLoaded', () => { 228 | // Only run on tables_dynamic.html page 229 | if (window.location.pathname.includes('tables_dynamic.html')) { 230 | optimizeTablePerformance(); 231 | } 232 | }); 233 | } 234 | -------------------------------------------------------------------------------- /src/main-core.js: -------------------------------------------------------------------------------- 1 | // CORE ESSENTIALS - Only what every page needs (jQuery-free) 2 | 3 | // Import and expose security utilities globally 4 | import { sanitizeHtml, sanitizeText, setSafeInnerHTML } from './utils/security.js'; 5 | window.sanitizeHtml = sanitizeHtml; 6 | window.sanitizeText = sanitizeText; 7 | window.setSafeInnerHTML = setSafeInnerHTML; 8 | 9 | // Import and expose validation utilities globally 10 | import * as ValidationUtils from './utils/validation.js'; 11 | window.ValidationUtils = ValidationUtils; 12 | 13 | // Import modern DOM utilities 14 | import DOM from './utils/dom-modern.js'; 15 | 16 | // Bootstrap 5 - Essential for all pages 17 | import * as bootstrap from 'bootstrap'; 18 | window.bootstrap = bootstrap; 19 | 20 | // Initialize Bootstrap tooltips and popovers 21 | document.addEventListener('DOMContentLoaded', function () { 22 | // Initialize all tooltips 23 | const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); 24 | const tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { 25 | return new bootstrap.Tooltip(tooltipTriggerEl); 26 | }); 27 | 28 | // Initialize all popovers 29 | const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')); 30 | const popoverList = popoverTriggerList.map(function (popoverTriggerEl) { 31 | return new bootstrap.Popover(popoverTriggerEl); 32 | }); 33 | }); 34 | 35 | // Day.js for basic date manipulation - lightweight alternative to moment.js 36 | import dayjs from 'dayjs'; 37 | window.dayjs = dayjs; 38 | 39 | // NProgress (Loading bar) - used across many pages 40 | import NProgress from 'nprogress'; 41 | window.NProgress = NProgress; 42 | 43 | // Essential UI components are now handled by Bootstrap 5 and custom modules 44 | 45 | // Add global error boundary to catch and handle errors gracefully 46 | window.addEventListener('error', event => { 47 | // Only log to console in development 48 | if (process.env.NODE_ENV === 'development') { 49 | console.error({ 50 | message: event.message, 51 | filename: event.filename, 52 | lineno: event.lineno, 53 | colno: event.colno, 54 | error: event.error 55 | }); 56 | } 57 | 58 | // Could send to error tracking service in production 59 | if (process.env.NODE_ENV === 'production') { 60 | // Example: sendErrorToService(event.error); 61 | } 62 | }); 63 | 64 | // Performance monitoring for module loading 65 | window.moduleLoadTimes = new Map(); 66 | 67 | // Console logging in development only 68 | if (process.env.NODE_ENV === 'development') { 69 | const originalLog = console.log; 70 | const originalError = console.error; 71 | const originalWarn = console.warn; 72 | 73 | console.log = (...args) => { 74 | originalLog(`[${new Date().toLocaleTimeString()}]`, ...args); 75 | }; 76 | 77 | console.error = (...args) => { 78 | originalError(`[${new Date().toLocaleTimeString()}] ❌`, ...args); 79 | }; 80 | 81 | console.warn = (...args) => { 82 | originalWarn(`[${new Date().toLocaleTimeString()}] ⚠️`, ...args); 83 | }; 84 | } 85 | 86 | // Global styles (Bootstrap 5 + custom) 87 | import './main.scss'; 88 | 89 | // Core scripts that all pages need 90 | import './js/helpers/smartresize-modern.js'; 91 | import './js/sidebar-modern.js'; 92 | import './js/init-modern.js'; 93 | 94 | // Module loading cache to prevent duplicate loads 95 | window.moduleCache = new Map(); 96 | 97 | // Loading states for better UX 98 | window.showModuleLoadingState = function (moduleName) { 99 | const indicator = document.createElement('div'); 100 | indicator.id = `loading-${moduleName}`; 101 | indicator.style.cssText = ` 102 | position: fixed; 103 | top: 20px; 104 | right: 20px; 105 | background: #1ABB9C; 106 | color: white; 107 | padding: 10px 15px; 108 | border-radius: 4px; 109 | font-size: 13px; 110 | z-index: 10000; 111 | box-shadow: 0 2px 10px rgba(0,0,0,0.1); 112 | `; 113 | indicator.innerHTML = ` Loading ${moduleName}...`; 114 | document.body.appendChild(indicator); 115 | return indicator; 116 | }; 117 | 118 | window.hideModuleLoadingState = function (indicator) { 119 | if (indicator && indicator.parentNode) { 120 | indicator.style.opacity = '0'; 121 | indicator.style.transform = 'translateX(100%)'; 122 | indicator.style.transition = 'all 0.3s ease'; 123 | setTimeout(() => indicator.remove(), 300); 124 | } 125 | }; 126 | 127 | // Enhanced dynamic loader for page-specific modules 128 | window.loadModule = async function (moduleName, showLoading = true) { 129 | // Check cache first 130 | if (window.moduleCache.has(moduleName)) { 131 | return window.moduleCache.get(moduleName); 132 | } 133 | 134 | let loadingIndicator; 135 | if (showLoading) { 136 | loadingIndicator = window.showModuleLoadingState(moduleName); 137 | } 138 | 139 | try { 140 | const startTime = performance.now(); 141 | let module; 142 | 143 | switch (moduleName) { 144 | case 'charts': 145 | module = await import('./modules/charts.js'); 146 | break; 147 | case 'forms': 148 | module = await import('./modules/forms.js'); 149 | break; 150 | case 'tables': 151 | module = await import('./modules/tables-modern.js'); 152 | break; 153 | case 'tables-modern': 154 | module = await import('./modules/tables-modern.js'); 155 | break; 156 | case 'ui': 157 | module = await import('./modules/ui-components.js'); 158 | break; 159 | case 'dashboard': 160 | module = await import('./modules/dashboard.js'); 161 | break; 162 | case 'weather': 163 | module = await import('./modules/weather.js'); 164 | break; 165 | case 'maps': 166 | module = await import('./modules/maps.js'); 167 | break; 168 | case 'echarts': 169 | module = await import('./modules/echarts-modern.js'); 170 | break; 171 | default: 172 | return null; 173 | } 174 | 175 | // Cache the module and record load time 176 | window.moduleCache.set(moduleName, module); 177 | const loadTime = performance.now() - startTime; 178 | window.moduleLoadTimes.set(moduleName, loadTime); 179 | 180 | return module; 181 | } catch (error) { 182 | if (process.env.NODE_ENV === 'development') { 183 | } 184 | return null; 185 | } finally { 186 | if (loadingIndicator) { 187 | window.hideModuleLoadingState(loadingIndicator); 188 | } 189 | } 190 | }; 191 | 192 | // Utility to preload modules for better performance 193 | window.preloadModules = async function (moduleNames) { 194 | const promises = moduleNames.map(name => window.loadModule(name, false)); 195 | const results = await Promise.allSettled(promises); 196 | return results; 197 | }; 198 | 199 | // Debug utility to show module loading stats (development only) 200 | window.getModuleStats = function () { 201 | if (process.env.NODE_ENV === 'development') { 202 | 203 | Array.from(window.moduleLoadTimes.entries()) 204 | .sort((a, b) => b[1] - a[1]) 205 | .forEach(([module, time]) => { 206 | }); 207 | 208 | const totalTime = Array.from(window.moduleLoadTimes.values()).reduce((a, b) => a + b, 0); 209 | } 210 | }; 211 | 212 | // Enhanced page readiness detector 213 | window.waitForPageReady = function (callback, timeout = 10000) { 214 | const startTime = Date.now(); 215 | 216 | function checkReady() { 217 | const basicReady = document.readyState === 'complete'; 218 | const bootstrapReady = typeof window.bootstrap !== 'undefined'; 219 | const scriptsReady = typeof window.loadModule !== 'undefined'; 220 | 221 | if (basicReady && bootstrapReady && scriptsReady) { 222 | callback(); 223 | } else if (Date.now() - startTime < timeout) { 224 | setTimeout(checkReady, 50); 225 | } else { 226 | callback(); 227 | } 228 | } 229 | 230 | checkReady(); 231 | }; 232 | -------------------------------------------------------------------------------- /src/js/sidebar-modern.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Modern Sidebar - jQuery-free version 3 | * Enhanced sidebar menu with proper multilevel support 4 | */ 5 | 6 | // Define DOM utilities locally if not available globally 7 | const DOM = window.DOM || { 8 | select: (selector) => document.querySelector(selector), 9 | selectAll: (selector) => [...document.querySelectorAll(selector)], 10 | addClass: (element, className) => element?.classList.add(className), 11 | removeClass: (element, className) => element?.classList.remove(className), 12 | toggleClass: (element, className) => element?.classList.toggle(className), 13 | hasClass: (element, className) => element?.classList.contains(className), 14 | closest: (element, selector) => element?.closest(selector), 15 | find: (element, selector) => element?.querySelector(selector), 16 | findAll: (element, selector) => [...(element?.querySelectorAll(selector) || [])], 17 | css: (element, property, value) => { 18 | if (element) { 19 | if (value !== undefined) { 20 | element.style[property] = value; 21 | } else { 22 | return window.getComputedStyle(element)[property]; 23 | } 24 | } 25 | }, 26 | height: (element) => element ? element.offsetHeight : 0, 27 | outerHeight: (element) => element ? element.offsetHeight : 0, 28 | slideUp: (element, duration = 300) => { 29 | if (element) { 30 | element.style.transition = `height ${duration}ms ease`; 31 | element.style.height = element.offsetHeight + 'px'; 32 | element.style.overflow = 'hidden'; 33 | 34 | requestAnimationFrame(() => { 35 | element.style.height = '0px'; 36 | setTimeout(() => { 37 | element.style.display = 'none'; 38 | element.style.transition = ''; 39 | element.style.overflow = ''; 40 | }, duration); 41 | }); 42 | } 43 | }, 44 | slideDown: (element, duration = 300) => { 45 | if (element) { 46 | element.style.display = ''; 47 | element.style.overflow = 'hidden'; 48 | element.style.height = '0px'; 49 | element.style.transition = `height ${duration}ms ease`; 50 | 51 | requestAnimationFrame(() => { 52 | element.style.height = element.scrollHeight + 'px'; 53 | setTimeout(() => { 54 | element.style.height = ''; 55 | element.style.transition = ''; 56 | element.style.overflow = ''; 57 | }, duration); 58 | }); 59 | } 60 | }, 61 | slideToggle: (element, duration = 300) => { 62 | if (element) { 63 | const isVisible = element.offsetHeight > 0; 64 | if (isVisible) { 65 | DOM.slideUp(element, duration); 66 | } else { 67 | DOM.slideDown(element, duration); 68 | } 69 | } 70 | } 71 | }; 72 | 73 | function initSidebar() { 74 | // Helper function to set the content height 75 | const setContentHeight = function () { 76 | const body = document.body; 77 | const rightCol = DOM.select('.right_col'); 78 | const sidebarFooter = DOM.select('.sidebar-footer'); 79 | const leftCol = DOM.select('.left_col'); 80 | const navMenu = DOM.select('.nav_menu'); 81 | const footer = DOM.select('footer'); 82 | 83 | if (!rightCol) { 84 | return; 85 | } 86 | 87 | // reset height 88 | DOM.css(rightCol, 'min-height', window.innerHeight + 'px'); 89 | 90 | const bodyHeight = DOM.outerHeight(body); 91 | const footerHeight = DOM.hasClass(body, 'footer_fixed') ? -10 : footer ? DOM.height(footer) : 0; 92 | const leftColHeight = 93 | (leftCol ? DOM.height(leftCol) : 0) + (sidebarFooter ? DOM.height(sidebarFooter) : 0); 94 | let contentHeight = bodyHeight < leftColHeight ? leftColHeight : bodyHeight; 95 | 96 | // normalize content 97 | contentHeight -= (navMenu ? DOM.height(navMenu) : 0) + footerHeight; 98 | 99 | DOM.css(rightCol, 'min-height', contentHeight + 'px'); 100 | }; 101 | 102 | const sidebarMenu = DOM.select('#sidebar-menu'); 103 | const body = document.body; 104 | const currentUrl = window.location.href.split('#')[0].split('?')[0]; 105 | 106 | if (!sidebarMenu) { 107 | return; 108 | } 109 | 110 | // Enhanced sidebar menu click handler 111 | sidebarMenu.addEventListener('click', function (ev) { 112 | const target = ev.target.closest('a'); 113 | if (!target) { 114 | return; 115 | } 116 | 117 | const li = target.parentElement; 118 | const submenu = li.querySelector('ul.child_menu'); 119 | 120 | // If this link has no submenu, allow normal navigation 121 | if (!submenu) { 122 | return true; 123 | } 124 | 125 | // Prevent default for menu toggles 126 | ev.preventDefault(); 127 | ev.stopPropagation(); 128 | 129 | // Check if submenu is currently visible 130 | const isVisible = submenu.style.display !== 'none' && submenu.offsetHeight > 0; 131 | 132 | // Close all other submenus at the same level 133 | const parentMenu = li.parentElement; 134 | if (parentMenu) { 135 | DOM.selectAll('li', parentMenu).forEach(sibling => { 136 | if (sibling !== li) { 137 | const siblingSubmenu = sibling.querySelector('ul.child_menu'); 138 | if (siblingSubmenu) { 139 | DOM.slideUp(siblingSubmenu); 140 | DOM.removeClass(sibling, 'active'); 141 | } 142 | } 143 | }); 144 | } 145 | 146 | // Toggle current submenu 147 | if (isVisible) { 148 | DOM.slideUp(submenu); 149 | DOM.removeClass(li, 'active'); 150 | } else { 151 | DOM.slideDown(submenu); 152 | DOM.addClass(li, 'active'); 153 | } 154 | 155 | setContentHeight(); 156 | }); 157 | 158 | // Menu toggle functionality 159 | const menuToggle = DOM.select('#menu_toggle'); 160 | if (menuToggle) { 161 | menuToggle.addEventListener('click', function (ev) { 162 | ev.preventDefault(); 163 | 164 | if (DOM.hasClass(body, 'nav-md')) { 165 | DOM.removeClass(body, 'nav-md'); 166 | DOM.addClass(body, 'nav-sm'); 167 | 168 | // Hide full logo, show icon logo 169 | const logoFull = DOM.select('.logo-full'); 170 | const logoIcon = DOM.select('.logo-icon'); 171 | if (logoFull) { 172 | logoFull.style.display = 'none'; 173 | } 174 | if (logoIcon) { 175 | logoIcon.style.display = 'inline-block'; 176 | } 177 | 178 | // Close all submenus when collapsing 179 | DOM.selectAll('#sidebar-menu ul.child_menu').forEach(submenu => { 180 | submenu.style.display = 'none'; 181 | }); 182 | DOM.selectAll('#sidebar-menu li.active').forEach(li => { 183 | DOM.removeClass(li, 'active'); 184 | }); 185 | } else { 186 | DOM.removeClass(body, 'nav-sm'); 187 | DOM.addClass(body, 'nav-md'); 188 | 189 | // Show full logo, hide icon logo 190 | const logoFull = DOM.select('.logo-full'); 191 | const logoIcon = DOM.select('.logo-icon'); 192 | if (logoFull) { 193 | logoFull.style.display = 'inline-block'; 194 | } 195 | if (logoIcon) { 196 | logoIcon.style.display = 'none'; 197 | } 198 | } 199 | 200 | setContentHeight(); 201 | }); 202 | } 203 | 204 | // Highlight current page in menu 205 | if (currentUrl) { 206 | DOM.selectAll('#sidebar-menu a').forEach(link => { 207 | if (link.href && link.href === currentUrl) { 208 | DOM.addClass(link.parentElement, 'current-page'); 209 | 210 | // Open parent menus if this is a submenu item 211 | let parent = link.closest('ul.child_menu'); 212 | while (parent) { 213 | parent.style.display = 'block'; 214 | const parentLi = parent.closest('li'); 215 | if (parentLi) { 216 | DOM.addClass(parentLi, 'active'); 217 | } 218 | parent = parent.parentElement.closest('ul.child_menu'); 219 | } 220 | } 221 | }); 222 | } 223 | 224 | // Initialize content height 225 | setContentHeight(); 226 | 227 | // Recalculate on window resize 228 | window.addEventListener('resize', setContentHeight); 229 | } 230 | 231 | // Initialize when DOM is ready 232 | if (document.readyState === 'loading') { 233 | document.addEventListener('DOMContentLoaded', initSidebar); 234 | } else { 235 | initSidebar(); 236 | } 237 | 238 | export default { initSidebar }; 239 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Gentelella Admin Template Documentation 2 | 3 | This directory contains the complete documentation for Gentelella Admin Template, built with Jekyll and deployable to GitHub Pages. 4 | 5 | ## 📚 Documentation Structure 6 | 7 | - **[index.md](index.md)** - Main landing page with overview and quick start 8 | - **[installation.md](installation.md)** - Detailed installation guide 9 | - **[configuration.md](configuration.md)** - Configuration and setup options 10 | - **[components.md](components.md)** - Complete component reference 11 | - **[performance.md](performance.md)** - Performance optimization guide 12 | - **[deployment.md](deployment.md)** - Production deployment instructions 13 | - **[customization.md](customization.md)** - Advanced customization techniques 14 | - **[api-integration.md](api-integration.md)** - API integration and data management 15 | 16 | ## 🚀 Local Development 17 | 18 | ### Prerequisites 19 | 20 | - Ruby 3.1 or higher 21 | - Bundler gem 22 | - Git 23 | 24 | ### Setup 25 | 26 | 1. **Clone the repository:** 27 | ```bash 28 | git clone https://github.com/puikinsh/gentelella.git 29 | cd gentelella/docs 30 | ``` 31 | 32 | 2. **Install dependencies:** 33 | ```bash 34 | bundle install 35 | ``` 36 | 37 | 3. **Start the development server:** 38 | ```bash 39 | bundle exec jekyll serve 40 | ``` 41 | 42 | 4. **Open your browser:** 43 | Navigate to `http://localhost:4000` 44 | 45 | ### Development Commands 46 | 47 | ```bash 48 | # Start development server 49 | bundle exec jekyll serve 50 | 51 | # Start with live reload 52 | bundle exec jekyll serve --livereload 53 | 54 | # Build for production 55 | bundle exec jekyll build 56 | 57 | # Build with specific base URL 58 | bundle exec jekyll build --baseurl "/gentelella" 59 | 60 | # Clean generated files 61 | bundle exec jekyll clean 62 | ``` 63 | 64 | ## 🌐 GitHub Pages Deployment 65 | 66 | The documentation is automatically deployed to GitHub Pages using GitHub Actions. 67 | 68 | ### Automatic Deployment 69 | 70 | The site automatically deploys when changes are pushed to the `main` branch in the `docs/` directory. The workflow is defined in `.github/workflows/pages.yml`. 71 | 72 | ### Manual Deployment 73 | 74 | To deploy manually: 75 | 76 | 1. **Enable GitHub Pages:** 77 | - Go to repository Settings → Pages 78 | - Set Source to "GitHub Actions" 79 | 80 | 2. **Trigger deployment:** 81 | - Push changes to the `main` branch 82 | - Or manually trigger the workflow in the Actions tab 83 | 84 | ### Custom Domain Setup 85 | 86 | To use a custom domain: 87 | 88 | 1. **Add CNAME file:** 89 | ```bash 90 | echo "docs.yoursite.com" > CNAME 91 | ``` 92 | 93 | 2. **Configure DNS:** 94 | Add CNAME record pointing to `username.github.io` 95 | 96 | 3. **Update _config.yml:** 97 | ```yaml 98 | url: "https://docs.yoursite.com" 99 | baseurl: "" 100 | ``` 101 | 102 | ## 📝 Content Management 103 | 104 | ### Adding New Pages 105 | 106 | 1. **Create new markdown file:** 107 | ```bash 108 | touch new-page.md 109 | ``` 110 | 111 | 2. **Add front matter:** 112 | ```yaml 113 | --- 114 | layout: default 115 | title: Your Page Title 116 | nav_order: 9 117 | --- 118 | ``` 119 | 120 | 3. **Write content using markdown** 121 | 122 | ### Page Structure 123 | 124 | Each page should include: 125 | 126 | ```yaml 127 | --- 128 | layout: default 129 | title: Page Title 130 | nav_order: 1 131 | description: "Page description for SEO" 132 | permalink: /custom-url/ 133 | --- 134 | 135 | # Page Title 136 | {: .no_toc } 137 | 138 | Page description 139 | {: .fs-6 .fw-300 } 140 | 141 | ## Table of contents 142 | {: .no_toc .text-delta } 143 | 144 | 1. TOC 145 | {:toc} 146 | 147 | --- 148 | 149 | ## Your Content Here 150 | ``` 151 | 152 | ### Navigation 153 | 154 | Navigation is automatically generated based on: 155 | - `nav_order` - Controls position in menu 156 | - `title` - Displays in navigation 157 | - File structure for sub-pages 158 | 159 | ### Styling and Components 160 | 161 | The documentation uses [Just the Docs](https://just-the-docs.github.io/just-the-docs/) theme with custom styling. 162 | 163 | #### Available Components 164 | 165 | **Callouts:** 166 | ```markdown 167 | {: .highlight } 168 | 💡 **Pro Tip**: Your tip content here 169 | 170 | {: .warning } 171 | ⚠️ **Warning**: Important warning here 172 | ``` 173 | 174 | **Code Blocks:** 175 | ```markdown 176 | ```javascript 177 | // Code with syntax highlighting 178 | const example = 'hello world'; 179 | ``` 180 | 181 | **Tables:** 182 | ```markdown 183 | | Column 1 | Column 2 | 184 | |----------|----------| 185 | | Data 1 | Data 2 | 186 | ``` 187 | 188 | **Buttons:** 189 | ```markdown 190 | [Button Text](link){: .btn .btn-primary } 191 | ``` 192 | 193 | ## 🔧 Configuration 194 | 195 | ### _config.yml 196 | 197 | Key configuration options: 198 | 199 | ```yaml 200 | # Site settings 201 | title: Gentelella Admin Template 202 | description: Modern Bootstrap 5 Admin Dashboard Template 203 | url: "https://puikinsh.github.io" 204 | baseurl: "/gentelella" 205 | 206 | # Theme 207 | theme: just-the-docs 208 | 209 | # Search 210 | search_enabled: true 211 | 212 | # Navigation 213 | nav_sort: case_insensitive 214 | 215 | # Footer 216 | footer_content: "Copyright © 2025 Colorlib" 217 | 218 | # External links 219 | aux_links: 220 | "GitHub": "//github.com/puikinsh/gentelella" 221 | "Colorlib": "//colorlib.com" 222 | ``` 223 | 224 | ### Gemfile 225 | 226 | Dependencies managed through Bundler: 227 | 228 | ```ruby 229 | source "https://rubygems.org" 230 | 231 | # GitHub Pages compatible Jekyll version 232 | gem "github-pages", group: :jekyll_plugins 233 | 234 | # Theme 235 | gem "just-the-docs" 236 | 237 | # Jekyll plugins 238 | group :jekyll_plugins do 239 | gem "jekyll-feed" 240 | gem "jekyll-sitemap" 241 | gem "jekyll-seo-tag" 242 | end 243 | ``` 244 | 245 | ## 📊 Analytics and SEO 246 | 247 | ### Google Analytics 248 | 249 | Add tracking ID to `_config.yml`: 250 | 251 | ```yaml 252 | ga_tracking: UA-XXXXXXXX-X 253 | ``` 254 | 255 | ### SEO Optimization 256 | 257 | - All pages include meta descriptions 258 | - Structured data for documentation 259 | - Sitemap automatically generated 260 | - Social media meta tags 261 | 262 | ### Performance 263 | 264 | - Static site generation for fast loading 265 | - Image optimization 266 | - Minified CSS and HTML 267 | - CDN-ready assets 268 | 269 | ## 🤝 Contributing 270 | 271 | ### Documentation Guidelines 272 | 273 | 1. **Clear and concise writing** 274 | 2. **Include code examples** 275 | 3. **Add table of contents for long pages** 276 | 4. **Use consistent formatting** 277 | 5. **Include links to related sections** 278 | 279 | ### Editing Process 280 | 281 | 1. **Fork the repository** 282 | 2. **Create feature branch** 283 | 3. **Make changes to documentation** 284 | 4. **Test locally with Jekyll** 285 | 5. **Submit pull request** 286 | 287 | ### Style Guide 288 | 289 | - Use sentence case for headings 290 | - Include code examples for all features 291 | - Add screenshots when helpful 292 | - Link to external resources appropriately 293 | - Keep paragraphs concise 294 | 295 | ## 🐛 Troubleshooting 296 | 297 | ### Common Issues 298 | 299 | **Bundle install fails:** 300 | ```bash 301 | # Update RubyGems 302 | gem update --system 303 | 304 | # Clear bundle cache 305 | bundle clean --force 306 | rm Gemfile.lock 307 | bundle install 308 | ``` 309 | 310 | **Jekyll serve fails:** 311 | ```bash 312 | # Clear Jekyll cache 313 | bundle exec jekyll clean 314 | 315 | # Regenerate 316 | bundle exec jekyll serve --trace 317 | ``` 318 | 319 | **GitHub Pages build fails:** 320 | - Check Jekyll build logs in Actions tab 321 | - Ensure all gems are GitHub Pages compatible 322 | - Validate YAML front matter 323 | 324 | ### Getting Help 325 | 326 | - Check [Jekyll documentation](https://jekyllrb.com/docs/) 327 | - Review [Just the Docs guide](https://just-the-docs.github.io/just-the-docs/) 328 | - Open issue in repository 329 | 330 | ## 📄 License 331 | 332 | Documentation is licensed under MIT License - see main repository LICENSE file for details. 333 | 334 | ## 🙏 Acknowledgments 335 | 336 | - Built with [Jekyll](https://jekyllrb.com/) 337 | - Styled with [Just the Docs](https://just-the-docs.github.io/just-the-docs/) 338 | - Hosted on [GitHub Pages](https://pages.github.com/) 339 | - Template by [Colorlib](https://colorlib.com/) -------------------------------------------------------------------------------- /src/modules/ui-components.js: -------------------------------------------------------------------------------- 1 | /** 2 | * UI Components Module 3 | * Handles panel toolbox, progress bars, and toast notifications 4 | * Modernized from jQuery to vanilla JavaScript 5 | */ 6 | 7 | // Modern DOM utilities (jQuery replacement) 8 | const DOM = { 9 | select: selector => document.querySelector(selector), 10 | selectAll: selector => [...document.querySelectorAll(selector)], 11 | on: (element, event, handler) => element.addEventListener(event, handler), 12 | find: (element, selector) => element.querySelector(selector), 13 | closest: (element, selector) => element.closest(selector), 14 | addClass: (element, className) => element.classList.add(className), 15 | removeClass: (element, className) => element.classList.remove(className), 16 | toggle: (element, className) => element.classList.toggle(className), 17 | slideUp: (element, duration = 300) => { 18 | element.style.transition = `height ${duration}ms ease`; 19 | element.style.overflow = 'hidden'; 20 | element.style.height = element.offsetHeight + 'px'; 21 | element.offsetHeight; // Force reflow 22 | element.style.height = '0px'; 23 | setTimeout(() => { 24 | element.style.display = 'none'; 25 | }, duration); 26 | }, 27 | slideDown: (element, duration = 300) => { 28 | element.style.display = 'block'; 29 | const height = element.scrollHeight; 30 | element.style.height = '0px'; 31 | element.style.transition = `height ${duration}ms ease`; 32 | element.style.overflow = 'hidden'; 33 | element.offsetHeight; // Force reflow 34 | element.style.height = height + 'px'; 35 | setTimeout(() => { 36 | element.style.height = ''; 37 | element.style.overflow = ''; 38 | element.style.transition = ''; 39 | }, duration); 40 | } 41 | }; 42 | 43 | /** 44 | * Panel Toolbox Functionality 45 | * Modernized from jQuery event handlers 46 | */ 47 | export function initializePanelToolbox() { 48 | // Close panel functionality - MODERNIZED 49 | DOM.selectAll('.close-link').forEach(link => { 50 | DOM.on(link, 'click', function (e) { 51 | e.preventDefault(); 52 | const panel = DOM.closest(this, '.x_panel'); 53 | if (panel) { 54 | DOM.slideUp(panel); 55 | } 56 | }); 57 | }); 58 | 59 | // Collapse panel functionality - MODERNIZED 60 | DOM.selectAll('.collapse-link').forEach(link => { 61 | DOM.on(link, 'click', function (e) { 62 | e.preventDefault(); 63 | const panel = DOM.closest(this, '.x_panel'); 64 | const content = DOM.find(panel, '.x_content'); 65 | const icon = DOM.find(this, 'i'); 66 | 67 | if (content && icon) { 68 | if (content.style.display === 'none') { 69 | DOM.slideDown(content); 70 | DOM.removeClass(icon, 'fa-chevron-down'); 71 | DOM.addClass(icon, 'fa-chevron-up'); 72 | } else { 73 | DOM.slideUp(content); 74 | DOM.removeClass(icon, 'fa-chevron-up'); 75 | DOM.addClass(icon, 'fa-chevron-down'); 76 | } 77 | } 78 | }); 79 | }); 80 | 81 | console.log('✅ Panel toolbox initialized (jQuery-free)'); 82 | } 83 | 84 | /** 85 | * Progress Bar Animations 86 | * Already modern - moved from init.js 87 | */ 88 | export function initializeProgressBars() { 89 | // Animate all progress bars with data-transitiongoal 90 | const progressBars = DOM.selectAll('.progress-bar[data-transitiongoal]'); 91 | 92 | progressBars.forEach(bar => { 93 | const targetPercent = parseInt(bar.getAttribute('data-transitiongoal'), 10); 94 | const displayText = bar.getAttribute('data-display-text') !== 'false'; 95 | 96 | // Remove any inline width styles to allow animation 97 | bar.style.removeProperty('width'); 98 | 99 | // Set initial state with !important to override any CSS 100 | bar.style.setProperty('width', '0%', 'important'); 101 | bar.setAttribute('aria-valuenow', '0'); 102 | 103 | // Animate to target value 104 | setTimeout(() => { 105 | bar.style.setProperty('transition', 'width 1s ease-in-out', 'important'); 106 | bar.style.setProperty('width', targetPercent + '%', 'important'); 107 | bar.setAttribute('aria-valuenow', targetPercent); 108 | 109 | if (displayText) { 110 | bar.textContent = targetPercent + '%'; 111 | } 112 | }, 100); 113 | }); 114 | 115 | // Handle App Versions progress bars (widget_summary containers) 116 | const appVersionBars = DOM.selectAll('.widget_summary .progress-bar'); 117 | appVersionBars.forEach(bar => { 118 | // Skip if already processed with data-transitiongoal 119 | if (bar.getAttribute('data-transitiongoal')) { 120 | return; 121 | } 122 | 123 | // Extract target percentage from inline style 124 | const inlineWidth = bar.style.width; 125 | if (inlineWidth && inlineWidth.includes('%')) { 126 | const targetPercent = parseInt(inlineWidth.replace('%', ''), 10); 127 | 128 | // Remove inline width and animate 129 | bar.style.removeProperty('width'); 130 | bar.style.setProperty('width', '0%', 'important'); 131 | bar.setAttribute('aria-valuenow', '0'); 132 | 133 | // Animate to target value with delay for staggered effect 134 | const delay = Array.from(appVersionBars).indexOf(bar) * 100 + 200; 135 | setTimeout(() => { 136 | bar.style.setProperty('transition', 'width 1s ease-in-out', 'important'); 137 | bar.style.setProperty('width', targetPercent + '%', 'important'); 138 | bar.setAttribute('aria-valuenow', targetPercent); 139 | }, delay); 140 | } 141 | }); 142 | 143 | // For other progress bars without data-transitiongoal, just show them immediately 144 | const staticProgressBars = DOM.selectAll('.progress-bar:not([data-transitiongoal])'); 145 | staticProgressBars.forEach(bar => { 146 | // Skip App Versions bars as they're handled above 147 | if (DOM.closest(bar, '.widget_summary')) { 148 | return; 149 | } 150 | 151 | const currentPercent = bar.style.width || bar.getAttribute('aria-valuenow') + '%' || '0%'; 152 | bar.style.width = currentPercent; 153 | }); 154 | 155 | console.log('✅ Progress bars initialized (jQuery-free)'); 156 | } 157 | 158 | /** 159 | * Toast Notifications 160 | * Bootstrap 5 native implementation - MODERNIZED 161 | */ 162 | export function initializeToasts() { 163 | // Initialize all toast elements 164 | const toastElements = DOM.selectAll('.toast'); 165 | 166 | toastElements.forEach(toastEl => { 167 | // Use Bootstrap 5 native Toast API instead of jQuery plugin 168 | if (typeof bootstrap !== 'undefined' && bootstrap.Toast) { 169 | const toast = new bootstrap.Toast(toastEl); 170 | 171 | // Auto-show toasts marked with data-show 172 | if (toastEl.getAttribute('data-show') === 'true') { 173 | toast.show(); 174 | } 175 | } 176 | }); 177 | 178 | console.log('✅ Toasts initialized (jQuery-free)'); 179 | } 180 | 181 | /** 182 | * Bootstrap Component Initialization 183 | * Using Bootstrap 5 native APIs - MODERNIZED 184 | */ 185 | export function initializeBootstrapComponents() { 186 | // Initialize tooltips - MODERNIZED from jQuery 187 | if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) { 188 | DOM.selectAll('[data-bs-toggle="tooltip"]').forEach(element => { 189 | new bootstrap.Tooltip(element); 190 | }); 191 | } 192 | 193 | // Initialize popovers - MODERNIZED from jQuery 194 | if (typeof bootstrap !== 'undefined' && bootstrap.Popover) { 195 | DOM.selectAll('[data-bs-toggle="popover"]').forEach(element => { 196 | new bootstrap.Popover(element); 197 | }); 198 | } 199 | 200 | console.log('✅ Bootstrap components initialized (jQuery-free)'); 201 | } 202 | 203 | /** 204 | * Switchery Toggle Switches 205 | * Modern implementation - ALREADY MODERN 206 | */ 207 | export function initializeSwitchery() { 208 | if (typeof Switchery === 'undefined') { 209 | console.warn('⚠️ Switchery library not available'); 210 | return; 211 | } 212 | 213 | DOM.selectAll('.js-switch').forEach(element => { 214 | if (!element.switchery) { 215 | // Avoid double initialization 216 | new Switchery(element, { 217 | color: '#26B99A', 218 | size: 'small' 219 | }); 220 | } 221 | }); 222 | 223 | console.log('✅ Switchery initialized (jQuery-free)'); 224 | } 225 | 226 | // Export DOM utilities for other modules 227 | export { DOM }; 228 | 229 | // Auto-initialize when module loads 230 | if (typeof document !== 'undefined') { 231 | document.addEventListener('DOMContentLoaded', () => { 232 | initializePanelToolbox(); 233 | initializeProgressBars(); 234 | initializeToasts(); 235 | initializeBootstrapComponents(); 236 | initializeSwitchery(); 237 | }); 238 | } 239 | -------------------------------------------------------------------------------- /production/landing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Gentelella - Free Bootstrap Admin Dashboard Template by Colorlib 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 |
25 |

Gentelella

26 |

Free Bootstrap Admin Dashboard Template

27 |

by Colorlib

28 |

29 | A beautiful, responsive admin template built with Bootstrap 5. Perfect for building 30 | modern dashboards, admin panels, and web applications with elegant design and rich functionality. 31 |

32 | 40 |
41 |
42 |
43 | 44 | 45 |
46 |
47 |
48 |

Powerful Features

49 |

Everything you need to build modern admin dashboards and web applications with confidence

50 |
51 | 52 |
53 |
54 |
55 | 56 |
57 |

Fully Responsive

58 |

Works flawlessly on all devices - desktop, tablet, and mobile. Built with a mobile-first approach for optimal user experience across all screen sizes.

59 |
60 | 61 |
62 |
63 | 64 |
65 |

Rich Components

66 |

Includes interactive charts, data tables, forms, widgets, and dozens of UI components to build comprehensive admin panels and dashboards.

67 |
68 | 69 |
70 |
71 | 72 |
73 |

Clean Code

74 |

Well-structured, semantic HTML with clean CSS and organized JavaScript. Easy to customize, extend, and maintain for long-term projects.

75 |
76 | 77 |
78 |
79 | 80 |
81 |

Modern Design

82 |

Contemporary flat design with beautiful color schemes, smooth animations, and intuitive user interface for engaging user experiences.

83 |
84 | 85 |
86 |
87 | 88 |
89 |

Fast Performance

90 |

Optimized for speed with minimal CSS/JS footprint and modern build tools. Loads quickly and runs smoothly on all modern browsers.

91 |
92 | 93 |
94 |
95 | 96 |
97 |

Easy Customization

98 |

Modular SCSS files and well-documented code make it simple to customize colors, layouts, and components to match your brand.

99 |
100 |
101 |
102 |
103 | 104 | 105 |
106 |
107 |
108 |

Dashboard Preview

109 |

See how your admin dashboard will look with real data and modern interface design

110 |
111 | 112 |
113 |
114 |
115 |
116 |

Analytics Dashboard

117 |

Real-time insights and performance metrics

118 |
119 |
120 |
JD
121 |
122 |
John Doe
123 |
Administrator
124 |
125 |
126 |
127 | 128 |
129 |
130 | 2,847 131 |
Total Users
132 |
133 |
134 | $28,567 135 |
Revenue
136 |
137 |
138 | 1,428 139 |
Orders
140 |
141 |
142 | 143 |
144 |
145 | 146 |

Interactive Charts & Analytics

147 | Real-time data visualization with Chart.js, ECharts & more 148 |
149 |
150 |
151 |
152 |
153 |
154 | 155 | 156 |
157 |
158 |
159 |
160 |

50+

161 |

UI Components

162 |
163 |
164 |

15+

165 |

Sample Pages

166 |
167 |
168 |

100%

169 |

Responsive

170 |
171 |
172 |

Free

173 |

Open Source

174 |
175 |
176 |
177 |
178 | 179 | 180 |
181 |
182 |

Ready to Build Something Amazing?

183 |

Join thousands of developers who trust Gentelella for building beautiful admin dashboards and web applications

184 | 192 |
193 |
194 | 195 | 196 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /src/utils/dom-modern.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Modern DOM Utilities - jQuery-free DOM manipulation 3 | * Provides a consistent API for DOM operations across the codebase 4 | */ 5 | 6 | const DOM = { 7 | // Basic selection 8 | select: selector => document.querySelector(selector), 9 | selectAll: selector => [...document.querySelectorAll(selector)], 10 | exists: selector => document.querySelector(selector) !== null, 11 | 12 | // Event handling 13 | on: (element, event, handler) => element.addEventListener(event, handler), 14 | off: (element, event, handler) => element.removeEventListener(event, handler), 15 | trigger: (element, event, data = {}) => { 16 | const customEvent = new CustomEvent(event, { detail: data }); 17 | element.dispatchEvent(customEvent); 18 | }, 19 | 20 | // DOM traversal 21 | find: (element, selector) => element.querySelector(selector), 22 | findAll: (element, selector) => [...element.querySelectorAll(selector)], 23 | closest: (element, selector) => element.closest(selector), 24 | parent: element => element.parentElement, 25 | children: element => [...element.children], 26 | siblings: element => [...element.parentElement.children].filter(el => el !== element), 27 | 28 | // Class manipulation 29 | hasClass: (element, className) => element.classList.contains(className), 30 | addClass: (element, className) => element.classList.add(className), 31 | removeClass: (element, className) => element.classList.remove(className), 32 | toggleClass: (element, className) => element.classList.toggle(className), 33 | 34 | // Style manipulation 35 | css: (element, property, value) => { 36 | if (typeof property === 'object') { 37 | // Set multiple styles: DOM.css(el, {color: 'red', fontSize: '14px'}) 38 | Object.entries(property).forEach(([prop, val]) => { 39 | element.style[prop] = val; 40 | }); 41 | } else if (value !== undefined) { 42 | // Set single style: DOM.css(el, 'color', 'red') 43 | element.style[property] = value; 44 | } else { 45 | // Get style: DOM.css(el, 'color') 46 | return getComputedStyle(element)[property]; 47 | } 48 | }, 49 | 50 | // Dimensions 51 | width: element => element.offsetWidth, 52 | height: element => element.offsetHeight, 53 | outerWidth: element => { 54 | const rect = element.getBoundingClientRect(); 55 | const computedStyle = getComputedStyle(element); 56 | return ( 57 | rect.width + parseFloat(computedStyle.marginLeft) + parseFloat(computedStyle.marginRight) 58 | ); 59 | }, 60 | outerHeight: element => { 61 | const rect = element.getBoundingClientRect(); 62 | const computedStyle = getComputedStyle(element); 63 | return ( 64 | rect.height + parseFloat(computedStyle.marginTop) + parseFloat(computedStyle.marginBottom) 65 | ); 66 | }, 67 | 68 | // Content manipulation 69 | html: (element, content) => { 70 | if (content !== undefined) { 71 | element.innerHTML = content; 72 | } else { 73 | return element.innerHTML; 74 | } 75 | }, 76 | text: (element, content) => { 77 | if (content !== undefined) { 78 | element.textContent = content; 79 | } else { 80 | return element.textContent; 81 | } 82 | }, 83 | val: (element, value) => { 84 | if (value !== undefined) { 85 | element.value = value; 86 | } else { 87 | return element.value; 88 | } 89 | }, 90 | 91 | // Attributes 92 | attr: (element, name, value) => { 93 | if (value !== undefined) { 94 | element.setAttribute(name, value); 95 | } else { 96 | return element.getAttribute(name); 97 | } 98 | }, 99 | removeAttr: (element, name) => element.removeAttribute(name), 100 | data: (element, key, value) => { 101 | const dataKey = `data-${key}`; 102 | if (value !== undefined) { 103 | element.setAttribute(dataKey, value); 104 | } else { 105 | return element.getAttribute(dataKey); 106 | } 107 | }, 108 | 109 | // DOM manipulation 110 | append: (parent, child) => { 111 | if (typeof child === 'string') { 112 | parent.insertAdjacentHTML('beforeend', child); 113 | } else { 114 | parent.appendChild(child); 115 | } 116 | }, 117 | prepend: (parent, child) => { 118 | if (typeof child === 'string') { 119 | parent.insertAdjacentHTML('afterbegin', child); 120 | } else { 121 | parent.insertBefore(child, parent.firstChild); 122 | } 123 | }, 124 | after: (element, newElement) => { 125 | if (typeof newElement === 'string') { 126 | element.insertAdjacentHTML('afterend', newElement); 127 | } else { 128 | element.parentNode.insertBefore(newElement, element.nextSibling); 129 | } 130 | }, 131 | before: (element, newElement) => { 132 | if (typeof newElement === 'string') { 133 | element.insertAdjacentHTML('beforebegin', newElement); 134 | } else { 135 | element.parentNode.insertBefore(newElement, element); 136 | } 137 | }, 138 | remove: element => element.remove(), 139 | clone: (element, deep = true) => element.cloneNode(deep), 140 | 141 | // Visibility 142 | show: element => { 143 | element.style.display = ''; 144 | }, 145 | hide: element => { 146 | element.style.display = 'none'; 147 | }, 148 | toggle: element => { 149 | element.style.display = element.style.display === 'none' ? '' : 'none'; 150 | }, 151 | 152 | // Animations (jQuery-like slide effects) 153 | slideDown: (element, duration = 300) => { 154 | element.style.height = '0px'; 155 | element.style.overflow = 'hidden'; 156 | element.style.transition = `height ${duration}ms ease`; 157 | element.style.display = 'block'; 158 | 159 | // Get the natural height 160 | const height = element.scrollHeight + 'px'; 161 | 162 | // Animate to natural height 163 | requestAnimationFrame(() => { 164 | element.style.height = height; 165 | }); 166 | 167 | // Clean up after animation 168 | setTimeout(() => { 169 | element.style.height = 'auto'; 170 | element.style.overflow = ''; 171 | element.style.transition = ''; 172 | }, duration); 173 | }, 174 | 175 | slideUp: (element, duration = 300) => { 176 | element.style.height = element.scrollHeight + 'px'; 177 | element.style.overflow = 'hidden'; 178 | element.style.transition = `height ${duration}ms ease`; 179 | 180 | // Animate to zero height 181 | requestAnimationFrame(() => { 182 | element.style.height = '0px'; 183 | }); 184 | 185 | // Hide element after animation 186 | setTimeout(() => { 187 | element.style.display = 'none'; 188 | element.style.height = ''; 189 | element.style.overflow = ''; 190 | element.style.transition = ''; 191 | }, duration); 192 | }, 193 | 194 | slideToggle: (element, duration = 300) => { 195 | if (element.style.display === 'none' || element.offsetHeight === 0) { 196 | DOM.slideDown(element, duration); 197 | } else { 198 | DOM.slideUp(element, duration); 199 | } 200 | }, 201 | 202 | fadeIn: (element, duration = 300) => { 203 | element.style.opacity = '0'; 204 | element.style.display = 'block'; 205 | element.style.transition = `opacity ${duration}ms ease`; 206 | 207 | requestAnimationFrame(() => { 208 | element.style.opacity = '1'; 209 | }); 210 | 211 | setTimeout(() => { 212 | element.style.transition = ''; 213 | }, duration); 214 | }, 215 | 216 | fadeOut: (element, duration = 300) => { 217 | element.style.transition = `opacity ${duration}ms ease`; 218 | element.style.opacity = '0'; 219 | 220 | setTimeout(() => { 221 | element.style.display = 'none'; 222 | element.style.transition = ''; 223 | element.style.opacity = ''; 224 | }, duration); 225 | }, 226 | 227 | // Ready state 228 | ready: callback => { 229 | if (document.readyState === 'loading') { 230 | document.addEventListener('DOMContentLoaded', callback); 231 | } else { 232 | callback(); 233 | } 234 | }, 235 | 236 | // Position and offset 237 | offset: element => { 238 | const rect = element.getBoundingClientRect(); 239 | return { 240 | top: rect.top + window.scrollY, 241 | left: rect.left + window.scrollX 242 | }; 243 | }, 244 | 245 | position: element => { 246 | return { 247 | top: element.offsetTop, 248 | left: element.offsetLeft 249 | }; 250 | }, 251 | 252 | // Scroll 253 | scrollTop: (element, value) => { 254 | if (value !== undefined) { 255 | element.scrollTop = value; 256 | } else { 257 | return element.scrollTop; 258 | } 259 | }, 260 | 261 | scrollLeft: (element, value) => { 262 | if (value !== undefined) { 263 | element.scrollLeft = value; 264 | } else { 265 | return element.scrollLeft; 266 | } 267 | } 268 | }; 269 | 270 | // Make it available globally 271 | window.DOM = DOM; 272 | globalThis.DOM = DOM; 273 | 274 | // Export for module usage 275 | export default DOM; 276 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Gentelella Changelog 2 | 3 | ## 2.1.1 - 11.09.2025 4 | 5 | **Maintenance Release - Dependency Updates, Chart Fixes & UI Improvements** 6 | 7 | ### Dependency Updates 8 | - **Latest Dependencies**: All dependencies updated to latest versions for security and performance 9 | - Vite 7.1.4 → 7.1.5 10 | - Bootstrap 5.3.6 → 5.3.8 11 | - ECharts 5.6.0 → 6.0.0 (major version upgrade) 12 | - Chart.js 4.4.2 → 4.5.0 13 | - jQuery 3.6.1 → 3.7.1 14 | - TypeScript 5.8.3 → 5.9.2 15 | - ESLint 9.34.0 → 9.35.0 16 | - SASS 1.92.0 → 1.92.1 17 | - DataTables 2.3.3 → 2.3.4 with all related packages 18 | - Font Awesome 7.0.0 → 7.0.1 19 | - **Security Updates**: Ruby 3.3.9 and Nokogiri 1.18.9 resolve all CVE vulnerabilities 20 | 21 | ### Chart & Widget Improvements 22 | - **ECharts Functionality**: Fixed all missing charts on echarts.html page 23 | - Added missing pyramid sales funnel chart with improved readability 24 | - Fixed world map visualization 25 | - Enhanced chart sizing and positioning 26 | - **Widget System Enhancement**: Improved content density in widgets.html 27 | - Enhanced metric cards with additional context information 28 | - Added growth indicators and supplementary metrics 29 | - Professional styling with hover effects and better typography 30 | - **Chart Color Consistency**: Fixed Device Usage chart colors to match label indicators 31 | - **Interactive Maps**: Fixed visitors location map and skycons weather icons on index.html 32 | 33 | ### UI/UX Improvements 34 | - **Sidebar Profile Enhancement**: Improved sidebar name section for better scalability 35 | - Reduced font size from default h4 to 14px for optimal space utilization 36 | - Added proper typography with font-weight 400 and line-height 1.2 37 | - Enhanced profile_info container with flexbox layout for better vertical centering 38 | - Added word-wrapping and break-word support for long names 39 | - Limited to 2.4em max-height to prevent sidebar expansion while allowing up to 2 lines 40 | - Gracefully handles both short names and longer names without breaking layout 41 | 42 | ### Developer Experience 43 | - **Dev Server Stability**: Fixed development server crashes with auto-restart capability 44 | - Enhanced Vite configuration for better stability 45 | - Added dev:watch script for automatic server restart 46 | - Improved file watching and HMR reliability 47 | - **Console Log Cleanup**: Production builds now clean with comprehensive console statement removal 48 | - Enhanced Terser configuration for complete console removal 49 | - Development-only console logging with environment checks 50 | - **Build Optimization**: Enhanced production build configuration 51 | - Better chunk splitting and manual chunks optimization 52 | - Improved Terser settings with additional compression options 53 | 54 | ### Technical Enhancements 55 | - **ES Module Support**: Added "type": "module" to package.json for modern JavaScript 56 | - **Code Quality**: Enhanced ESLint and Prettier configurations 57 | - **Bundle Analysis**: Improved build analysis tools and documentation 58 | 59 | ## 2.1.0 - 28.07.2025 60 | 61 | **Enhancement Release - jQuery-Free Core System & Brand Refresh** 62 | 63 | ### New Features 64 | - **jQuery-Free Core System**: Complete main-core.js modernization with vanilla JavaScript 65 | - Dynamic module loading with caching and performance monitoring 66 | - Loading states and visual indicators for better UX 67 | - Enhanced error handling and development debugging tools 68 | - **Brand-Consistent Favicon Suite**: Modern favicon system with complete browser support 69 | - SVG-first approach for sharp display across all devices 70 | - Apple Touch Icon, Android Chrome icons, and PWA manifest 71 | - Modern standard implementation with proper fallbacks 72 | 73 | ### UI/UX Improvements 74 | - **Top Navigation Alignment**: Perfect vertical centering of user profile and notification elements 75 | - **Modern DOM Utilities**: Comprehensive jQuery-free DOM manipulation library 76 | - Slide animations, fade effects, and smooth transitions 77 | - Event handling and element manipulation without jQuery dependency 78 | - **Enhanced Visual Consistency**: Improved spacing and alignment throughout interface 79 | 80 | ### Technical Enhancements 81 | - **Console Log Cleanup**: Production-ready code with clean, professional output 82 | - Development-only logging wrapped in environment checks 83 | - Removed verbose initialization messages and debug output 84 | - **Code Quality**: Streamlined codebase with reduced development artifacts 85 | - **Performance Optimizations**: Further improvements to module loading system 86 | 87 | ### Bug Fixes 88 | - Fixed loadModule reference errors when using main-minimal.js 89 | - Resolved favicon display issues in legacy browsers 90 | - Corrected navigation element positioning and alignment 91 | - Eliminated development console noise in production builds 92 | 93 | ### File Structure 94 | - Added centralized DOM utilities (`src/utils/dom-modern.js`) 95 | - Updated favicon implementation with proper size variants 96 | - Cleaned development files and reduced repository size 97 | 98 | ### Developer Experience 99 | - Improved error boundaries and debugging capabilities 100 | - Enhanced module performance monitoring and statistics 101 | - Better development vs production environment handling 102 | 103 | ## 2.0.0 - 20.06.2025 🎉 104 | 105 | **Major Stable Release - Bootstrap 5 with Modern Build System** 106 | 107 | ### 🚀 New Features 108 | - **Vite Build System**: Lightning-fast development with hot-reload and optimized production builds 109 | - **Bootstrap 5.3.7**: Complete migration to latest Bootstrap with modern design system 110 | - **Smart Code Splitting**: 90% smaller initial bundle (79KB vs 779KB) with conditional module loading 111 | - **Modern JavaScript**: ES6+ modules with dynamic imports and tree shaking 112 | - **Performance Optimized**: 40-70% faster page loads with intelligent caching 113 | 114 | ### 🔧 Major Improvements 115 | - **Morris.js Complete Removal**: Replaced with modern Chart.js implementation 116 | - Renamed `morisjs.html` → `chart3.html` with updated navigation 117 | - Removed all Morris.js CSS and JavaScript code 118 | - Updated 35+ HTML files with new Chart.js references 119 | - **jQuery Easing Fixes**: Resolved all `TypeError: jQuery.easing[this.easing] is not a function` errors 120 | - Added jQuery UI easing effects with fallback functions 121 | - Fixed EasyPieChart and progress bar animations 122 | - **Enhanced Navigation**: Consistent search bar implementation across all pages 123 | - **Error Page Redesign**: Modern 403, 404, 500 pages with consistent branding 124 | 125 | ### 🎨 UI/UX Enhancements 126 | - **Responsive Design**: Mobile-first approach with optimized touch interfaces 127 | - **Login Page**: Complete redesign with modern card layout and form validation 128 | - **Pricing Tables**: Pure Bootstrap 5 implementation with interactive features 129 | - **Fixed Sidebar/Footer**: Proper Bootstrap 5 compatibility and positioning 130 | 131 | ### 🛠️ Technical Updates 132 | - **Dependencies**: Updated all packages to latest stable versions 133 | - **SASS Structure**: Organized and optimized stylesheet architecture 134 | - **TypeScript Ready**: Full TypeScript support available 135 | - **Cross-Browser**: Tested compatibility with modern browsers 136 | 137 | ### 📦 Bundle Optimization 138 | - **Core Bundle**: 79KB essential libraries 139 | - **Chart Module**: 219KB (loads only on chart pages) 140 | - **Form Module**: 200KB (loads only on form pages) 141 | - **Table Module**: DataTables functionality on demand 142 | - **Dashboard Module**: Dashboard-specific widgets 143 | 144 | ### 🐛 Bug Fixes 145 | - Fixed all reported Bootstrap 4 to 5 migration issues 146 | - Resolved SASS deprecation warnings 147 | - Fixed dropdown functionality across all pages 148 | - Corrected responsive behavior on mobile devices 149 | - Eliminated JavaScript console errors 150 | 151 | ### 💻 Developer Experience 152 | - Hot-reload development server 153 | - Optimized build process with cache-busting 154 | - Smart asset optimization 155 | - Improved documentation and examples 156 | 157 | ### Note 158 | 159 | Earlier there were no changelog at all and we have introduced one now and we will start from version 1.0.0. However, keep in mind that this is far from being first version as there have been dozens of commits. 160 | 161 | ### 1.0.0 - 25.03.2016 162 | 163 | * Fixed dataTables 164 | * Added new dataTable variations 165 | 166 | ### 1.1.0 - 26.04.2016 167 | 168 | * Add multilevel menu 169 | * Mobile comptibility enhancement 170 | 171 | ### 1.2.0 - 19.05.2016 172 | 173 | * Fix menu not become active if url contains parameters 174 | * Fix form upload form not adjust on large number of files 175 | * Remove invalid css 176 | * Add compose message functionalities 177 | * Add fixed sidebar functionalities 178 | 179 | ### 1.3.0 - 01.06.2016 180 | 181 | * Fix menu not become active if url contains parameters 182 | * Fix form upload form not adjust on large number of files 183 | * Remove invalid css 184 | * Add compose message functionalities 185 | * Add fixed footer functionalities 186 | 187 | ### Gentelella 2.0-beta1 188 | 189 | * Updated Bootstrap to 4.3.1 190 | * Updated all dependencies 191 | * Fixed all reported bugs 192 | 193 | This version is tested but we would recommend not to use it in production right away. Please install it for testing purposes and report back if there are any problems to be fixed. 194 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Installation Guide 4 | nav_order: 2 5 | --- 6 | 7 | # Installation Guide 8 | {: .no_toc } 9 | 10 | Complete installation and setup instructions for Gentelella Admin Template 11 | {: .fs-6 .fw-300 } 12 | 13 | ## Table of contents 14 | {: .no_toc .text-delta } 15 | 16 | 1. TOC 17 | {:toc} 18 | 19 | --- 20 | 21 | ## System Requirements 22 | 23 | ### Prerequisites 24 | 25 | Before installing Gentelella, ensure you have the following installed: 26 | 27 | - **Node.js** (v16 or higher) - [Download here](https://nodejs.org/) 28 | - **npm** (comes with Node.js) or **yarn** package manager 29 | - **Git** (for cloning the repository) 30 | - A modern code editor (VS Code recommended) 31 | 32 | ### Browser Support 33 | 34 | Gentelella supports all modern browsers: 35 | 36 | | Browser | Minimum Version | 37 | |---------|-----------------| 38 | | Chrome | 88+ | 39 | | Firefox | 85+ | 40 | | Safari | 14+ | 41 | | Edge | 88+ | 42 | | Opera | 74+ | 43 | 44 | **Note:** Internet Explorer is not supported. 45 | 46 | --- 47 | 48 | ## Installation Methods 49 | 50 | ### Method 1: Git Clone (Recommended) 51 | 52 | This is the recommended method for development and customization: 53 | 54 | ```bash 55 | # Clone the repository 56 | git clone https://github.com/puikinsh/gentelella.git 57 | 58 | # Navigate to the project directory 59 | cd gentelella 60 | 61 | # Install dependencies 62 | npm install 63 | 64 | # Start the development server 65 | npm run dev 66 | ``` 67 | 68 | Your development server will be running at `http://localhost:3000` 69 | 70 | ### Method 2: Download ZIP 71 | 72 | 1. Visit [GitHub repository](https://github.com/puikinsh/gentelella) 73 | 2. Click "Code" → "Download ZIP" 74 | 3. Extract the ZIP file 75 | 4. Open terminal in the extracted folder 76 | 5. Run `npm install` 77 | 6. Run `npm run dev` 78 | 79 | ### Method 3: npm Package 80 | 81 | Install as a dependency in your existing project: 82 | 83 | ```bash 84 | npm install gentelella --save 85 | ``` 86 | 87 | ### Method 4: Yarn Package 88 | 89 | If you prefer Yarn: 90 | 91 | ```bash 92 | yarn add gentelella 93 | ``` 94 | 95 | ### Method 5: Bower (Legacy) 96 | 97 | For legacy projects using Bower: 98 | 99 | ```bash 100 | bower install gentelella --save 101 | ``` 102 | 103 | --- 104 | 105 | ## Project Structure 106 | 107 | After installation, your project structure will look like this: 108 | 109 | ``` 110 | gentelella/ 111 | ├── 📁 docs/ # Documentation files 112 | ├── 📁 production/ # HTML templates & assets 113 | │ ├── 📄 index.html # Main dashboard 114 | │ ├── 📄 form.html # Form examples 115 | │ ├── 📄 tables.html # Table examples 116 | │ ├── 📄 charts.html # Chart examples 117 | │ ├── 📄 [38 more pages] # Complete admin coverage 118 | │ └── 📁 images/ # Image assets 119 | ├── 📁 src/ # Source files 120 | │ ├── 📄 main-core.js # Core bundle (79KB) 121 | │ ├── 📄 main.js # Full bundle (779KB) 122 | │ ├── 📄 main.scss # Styles entry point 123 | │ ├── 📁 js/ # Custom JavaScript 124 | │ ├── 📁 scss/ # Custom SASS files 125 | │ └── 📁 modules/ # Smart loading modules 126 | │ ├── 📄 charts.js # Chart libraries (219KB) 127 | │ ├── 📄 forms.js # Form enhancements (200KB) 128 | │ ├── 📄 tables.js # DataTables functionality 129 | │ ├── 📄 dashboard.js # Dashboard widgets 130 | │ └── 📄 utils.js # Utility functions 131 | ├── 📁 dist/ # Production build output 132 | ├── 📁 scripts/ # Build & optimization tools 133 | ├── 📁 vendors/ # Third-party libraries 134 | ├── 📄 vite.config.js # Vite configuration 135 | ├── 📄 package.json # Dependencies & scripts 136 | └── 📄 README.md # Basic documentation 137 | ``` 138 | 139 | --- 140 | 141 | ## Development Commands 142 | 143 | ### Basic Commands 144 | 145 | ```bash 146 | # Start development server with hot reload 147 | npm run dev 148 | 149 | # Build for production 150 | npm run build 151 | 152 | # Preview production build locally 153 | npm run preview 154 | ``` 155 | 156 | ### Advanced Commands 157 | 158 | ```bash 159 | # Build with bundle analysis 160 | npm run build:analyze 161 | 162 | # Performance optimization analysis 163 | npm run optimize 164 | 165 | # SASS compilation only 166 | npm run sass:watch 167 | 168 | # JavaScript linting 169 | npm run lint 170 | 171 | # Code formatting 172 | npm run format 173 | ``` 174 | 175 | --- 176 | 177 | ## Configuration 178 | 179 | ### Environment Setup 180 | 181 | 1. **Development Environment** 182 | ```bash 183 | npm run dev 184 | ``` 185 | - Hot reload enabled 186 | - Source maps available 187 | - All modules loaded for development 188 | 189 | 2. **Production Environment** 190 | ```bash 191 | npm run build 192 | npm run preview 193 | ``` 194 | - Optimized bundles 195 | - Minified assets 196 | - Smart code splitting 197 | 198 | ### Vite Configuration 199 | 200 | The template includes an optimized `vite.config.js` with: 201 | 202 | - **Entry Points**: All 42 HTML files configured 203 | - **Code Splitting**: Automatic vendor/app separation 204 | - **Asset Optimization**: Images, fonts, and static files 205 | - **Development Features**: Hot reload, source maps 206 | - **Production Optimizations**: Minification, compression 207 | 208 | ### SASS Configuration 209 | 210 | SASS is configured in `src/main.scss`: 211 | 212 | ```scss 213 | // Modern @use syntax (recommended) 214 | @use "bootstrap/scss/bootstrap"; 215 | @use "./scss/custom.scss"; 216 | 217 | // Legacy @import syntax (deprecated but still works) 218 | // @import "bootstrap/scss/bootstrap"; 219 | // @import "./scss/custom.scss"; 220 | ``` 221 | 222 | --- 223 | 224 | ## Verification 225 | 226 | ### Check Installation 227 | 228 | After installation, verify everything is working: 229 | 230 | 1. **Start the development server:** 231 | ```bash 232 | npm run dev 233 | ``` 234 | 235 | 2. **Open your browser** and navigate to `http://localhost:3000` 236 | 237 | 3. **You should see** the Gentelella dashboard 238 | 239 | ### Test All Pages 240 | 241 | Navigate through different pages to ensure all modules load correctly: 242 | 243 | - Dashboard pages (index.html, index2.html, index3.html) 244 | - Form pages (form.html, form_advanced.html, form_validation.html) 245 | - Table pages (tables.html, tables_dynamic.html) 246 | - Chart pages (chartjs.html, chartjs2.html, chart3.html) 247 | 248 | ### Performance Check 249 | 250 | Run the optimization analysis: 251 | 252 | ```bash 253 | npm run optimize 254 | ``` 255 | 256 | This will show you: 257 | - Bundle sizes 258 | - Loading times 259 | - Optimization recommendations 260 | 261 | --- 262 | 263 | ## Troubleshooting 264 | 265 | ### Common Issues 266 | 267 | #### 1. Node.js Version Issues 268 | 269 | **Error:** `npm ERR! engine Unsupported engine` 270 | 271 | **Solution:** Update Node.js to version 16 or higher: 272 | ```bash 273 | # Check current version 274 | node --version 275 | 276 | # Update Node.js from https://nodejs.org/ 277 | ``` 278 | 279 | #### 2. Port Already in Use 280 | 281 | **Error:** `Port 3000 is already in use` 282 | 283 | **Solution:** Either stop the conflicting process or use a different port: 284 | ```bash 285 | # Use different port 286 | npm run dev -- --port 3001 287 | ``` 288 | 289 | #### 3. SASS Compilation Errors 290 | 291 | **Error:** SASS deprecation warnings 292 | 293 | **Solution:** These are mainly from Bootstrap internal files and can be safely ignored. Our project code uses modern SASS syntax. 294 | 295 | #### 4. Module Not Found 296 | 297 | **Error:** `Cannot resolve module` 298 | 299 | **Solution:** Clear cache and reinstall: 300 | ```bash 301 | # Delete node_modules and package-lock.json 302 | rm -rf node_modules package-lock.json 303 | 304 | # Reinstall dependencies 305 | npm install 306 | ``` 307 | 308 | #### 5. Build Failures 309 | 310 | **Error:** Build process fails 311 | 312 | **Solution:** Check for file permission issues and ensure all dependencies are installed: 313 | ```bash 314 | # Clear cache 315 | npm cache clean --force 316 | 317 | # Reinstall 318 | npm install 319 | 320 | # Try building again 321 | npm run build 322 | ``` 323 | 324 | ### Getting Help 325 | 326 | If you encounter issues not covered here: 327 | 328 | 1. **Check GitHub Issues**: [github.com/puikinsh/gentelella/issues](https://github.com/puikinsh/gentelella/issues) 329 | 2. **Create New Issue**: Provide detailed error messages and system information 330 | 3. **Community Support**: Join discussions on GitHub 331 | 4. **Documentation**: Check other sections of this documentation 332 | 333 | --- 334 | 335 | ## Next Steps 336 | 337 | After successful installation: 338 | 339 | 1. **[Configuration Guide]({{ site.baseurl }}/docs/configuration/)** - Customize the template 340 | 2. **[Components Overview]({{ site.baseurl }}/docs/components/)** - Explore available components 341 | 3. **[Performance Guide]({{ site.baseurl }}/docs/performance/)** - Optimize your build 342 | 4. **[Customization]({{ site.baseurl }}/docs/customization/)** - Add your own styles and features 343 | 344 | --- 345 | 346 | {: .highlight } 347 | 💡 **Pro Tip**: Use `npm run dev` during development for the best experience with hot reload and source maps. Only use `npm run build` when you're ready to deploy to production. --------------------------------------------------------------------------------