├── 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 | Jekyll 2025-09-03T13:23:50+03:00 https://puikinsh.github.io/gentelella/feed.xml Gentelella Admin Template Modern 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 |
104 |
105 |
106 | Message sent successfully!
107 |
108 |
109 |
110 |
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 |
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 |
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 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | © 2025 All Rights Reserved. Gentelella is a Bootstrap 5 template.
103 |
104 |
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 |
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 |
82 |
83 |
84 |
85 |
Or search for what you need:
86 |
94 |
95 |
96 |
97 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | © 2025 All Rights Reserved. Gentelella is a Bootstrap 5 template.
111 |
112 |
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 |
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 |
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 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | © 2025 All Rights Reserved. Gentelella is a Bootstrap 5 template.
119 |
120 |
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 | 
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 |
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 |
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.
--------------------------------------------------------------------------------