├── .nvmrc ├── src ├── assets │ ├── styles │ │ ├── spec │ │ │ ├── components │ │ │ │ ├── masonry.scss │ │ │ │ ├── footer.scss │ │ │ │ ├── progressBar.scss │ │ │ │ ├── easyPieChart.scss │ │ │ │ ├── index.scss │ │ │ │ ├── loader.scss │ │ │ │ └── pageContainer.scss │ │ │ ├── generic │ │ │ │ ├── index.scss │ │ │ │ └── base.scss │ │ │ ├── tools │ │ │ │ ├── index.scss │ │ │ │ └── mixins │ │ │ │ │ ├── index.scss │ │ │ │ │ ├── clearfix.scss │ │ │ │ │ ├── placeholder.scss │ │ │ │ │ └── mediaQueriesRanges.scss │ │ │ ├── screens │ │ │ │ ├── index.scss │ │ │ │ ├── email.scss │ │ │ │ └── chat.scss │ │ │ ├── utils │ │ │ │ ├── index.scss │ │ │ │ ├── layout │ │ │ │ │ ├── mixins │ │ │ │ │ │ ├── index.scss │ │ │ │ │ │ ├── mediaQueryCondition.scss │ │ │ │ │ │ └── generateResponsive.scss │ │ │ │ │ ├── index.scss │ │ │ │ │ ├── utils │ │ │ │ │ │ ├── index.scss │ │ │ │ │ │ ├── layers.scss │ │ │ │ │ │ ├── peers.scss │ │ │ │ │ │ ├── center.scss │ │ │ │ │ │ └── gap.scss │ │ │ │ │ └── helpers │ │ │ │ │ │ ├── pseudo.scss │ │ │ │ │ │ ├── index.scss │ │ │ │ │ │ ├── lists.scss │ │ │ │ │ │ ├── border.scss │ │ │ │ │ │ ├── objects.scss │ │ │ │ │ │ ├── positions.scss │ │ │ │ │ │ ├── typography.scss │ │ │ │ │ │ ├── padding.scss │ │ │ │ │ │ ├── layout.scss │ │ │ │ │ │ └── sizes.scss │ │ │ │ └── colors.scss │ │ │ ├── index.scss │ │ │ └── settings │ │ │ │ ├── index.scss │ │ │ │ ├── fonts.scss │ │ │ │ ├── borders.scss │ │ │ │ ├── breakpoints.scss │ │ │ │ └── baseColors.scss │ │ ├── vendor │ │ │ ├── perfectScrollbar.scss │ │ │ ├── sparkline.scss │ │ │ ├── index.scss │ │ │ ├── jquery.datatables.scss │ │ │ ├── datepicker.scss │ │ │ └── fullcalendar.scss │ │ └── utils │ │ │ └── theme.css │ ├── scripts │ │ ├── charts │ │ │ ├── index.js │ │ │ ├── chartJS │ │ │ │ └── index.js │ │ │ ├── easyPieChart │ │ │ │ └── index.js │ │ │ └── sparkline │ │ │ │ └── index.js │ │ ├── scrollbar │ │ │ └── index.js │ │ ├── chat │ │ │ └── index.js │ │ ├── masonry │ │ │ └── index.js │ │ ├── search │ │ │ └── index.js │ │ ├── index.js │ │ ├── email │ │ │ └── index.js │ │ ├── skycons │ │ │ └── index.js │ │ ├── utils │ │ │ ├── index.js │ │ │ └── theme.js │ │ ├── googleMaps │ │ │ └── index.js │ │ ├── fullcalendar │ │ │ └── index.js │ │ ├── popover │ │ │ └── index.js │ │ └── sidebar │ │ │ └── index.js │ └── static │ │ ├── images │ │ ├── bg.jpg │ │ ├── 404.png │ │ ├── 500.png │ │ ├── logo.png │ │ ├── datatables │ │ │ ├── sort_asc.png │ │ │ ├── sort_both.png │ │ │ ├── sort_desc.png │ │ │ ├── sort_asc_disabled.png │ │ │ └── sort_desc_disabled.png │ │ ├── logo-circle.svg │ │ ├── logo-outline.svg │ │ ├── logo-gradient.svg │ │ └── logo.svg │ │ └── fonts │ │ └── icons │ │ ├── themify │ │ ├── themify.eot │ │ ├── themify.ttf │ │ └── themify.woff │ │ └── fontawesome │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 ├── 404.html ├── 500.html ├── signup.html └── signin.html ├── .babelrc ├── ci ├── getVersion.sh └── verifyVersion.sh ├── webpack.config.js ├── docs ├── assets │ └── images │ │ ├── adminator-dark-mode.avif │ │ └── adminator-light-mode.avif ├── examples.md ├── api.md ├── customization.md ├── getting-started.md ├── _config.yml ├── index.md ├── _sass │ └── custom.scss ├── README.md └── getting-started │ └── installation.md ├── webpack ├── plugins │ ├── dashboardPlugin.js │ ├── caseSensitivePlugin.js │ ├── extractPlugin.js │ ├── copyPlugin.js │ ├── index.js │ ├── htmlPlugin.js │ └── internal.js ├── rules │ ├── fonts.js │ ├── js.js │ ├── index.js │ ├── images.js │ ├── css.js │ └── sass.js ├── devServer.js ├── manifest.js └── config.js ├── browserslist ├── .claude └── settings.local 2.json ├── .npmignore ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md └── workflows │ ├── merge.yml │ └── release.yml ├── LICENSE ├── .gitignore ├── .gitattributes ├── .stylelintrc.json ├── CODE_OF_CONDUCT.md ├── package.json └── eslint.config.mjs /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /src/assets/styles/spec/components/masonry.scss: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } -------------------------------------------------------------------------------- /src/assets/styles/spec/generic/index.scss: -------------------------------------------------------------------------------- 1 | @use 'base' as *; 2 | -------------------------------------------------------------------------------- /src/assets/styles/spec/tools/index.scss: -------------------------------------------------------------------------------- 1 | @use 'mixins/index' as *; 2 | -------------------------------------------------------------------------------- /ci/getVersion.sh: -------------------------------------------------------------------------------- 1 | echo $(sed 's/.*"version": "\(.*\)".*/\1/;t;d' ./package.json) -------------------------------------------------------------------------------- /src/assets/styles/spec/screens/index.scss: -------------------------------------------------------------------------------- 1 | @use 'chat' as *; 2 | @use 'email' as *; 3 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/index.scss: -------------------------------------------------------------------------------- 1 | @use 'layout/index' as *; 2 | @use 'colors' as *; 3 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const config = require('./webpack/config'); 2 | 3 | module.exports = config; 4 | -------------------------------------------------------------------------------- /src/assets/styles/spec/components/footer.scss: -------------------------------------------------------------------------------- 1 | footer { 2 | z-index: 1; 3 | position: relative; 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/scripts/charts/index.js: -------------------------------------------------------------------------------- 1 | import './chartJS'; 2 | import './easyPieChart'; 3 | import './sparkline'; 4 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/mixins/index.scss: -------------------------------------------------------------------------------- 1 | @use 'mediaQueryCondition' as *; 2 | @use 'generateResponsive' as *; 3 | -------------------------------------------------------------------------------- /src/assets/styles/vendor/perfectScrollbar.scss: -------------------------------------------------------------------------------- 1 | .ps__rail-y { 2 | right: 0 !important; 3 | left: auto !important; 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/static/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/images/bg.jpg -------------------------------------------------------------------------------- /src/assets/static/images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/images/404.png -------------------------------------------------------------------------------- /src/assets/static/images/500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/images/500.png -------------------------------------------------------------------------------- /src/assets/static/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/images/logo.png -------------------------------------------------------------------------------- /src/assets/styles/spec/tools/mixins/index.scss: -------------------------------------------------------------------------------- 1 | @use 'placeholder' as *; 2 | @use 'clearfix' as *; 3 | @use 'mediaQueriesRanges' as *; 4 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/index.scss: -------------------------------------------------------------------------------- 1 | @use 'mixins/index' as *; 2 | @use 'utils/index' as *; 3 | @use 'helpers/index' as *; 4 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/utils/index.scss: -------------------------------------------------------------------------------- 1 | @use 'center' as *; 2 | @use 'gap' as *; 3 | @use 'peers' as *; 4 | @use 'layers' as *; 5 | 6 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/utils/layers.scss: -------------------------------------------------------------------------------- 1 | .layers { 2 | display: flex; 3 | flex-flow: column nowrap; 4 | align-items: center; 5 | } 6 | -------------------------------------------------------------------------------- /docs/assets/images/adminator-dark-mode.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/docs/assets/images/adminator-dark-mode.avif -------------------------------------------------------------------------------- /docs/assets/images/adminator-light-mode.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/docs/assets/images/adminator-light-mode.avif -------------------------------------------------------------------------------- /src/assets/styles/spec/index.scss: -------------------------------------------------------------------------------- 1 | @use 'generic/index' as *; 2 | @use 'components/index' as *; 3 | @use 'screens/index' as *; 4 | @use 'utils/index' as *; 5 | -------------------------------------------------------------------------------- /webpack/plugins/dashboardPlugin.js: -------------------------------------------------------------------------------- 1 | const 2 | DashboardPlugin = require('webpack-dashboard/plugin'); 3 | 4 | module.exports = new DashboardPlugin(); 5 | -------------------------------------------------------------------------------- /webpack/rules/fonts.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | test: /\.(eot|svg|ttf|woff|woff2)$/, 3 | exclude : /(node_modules)/, 4 | type: 'asset/resource', 5 | }; -------------------------------------------------------------------------------- /webpack/rules/js.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | test : /\.(js|jsx)$/, 3 | exclude : /(node_modules|build|dist\/)/, 4 | use : ['babel-loader'], 5 | }; 6 | -------------------------------------------------------------------------------- /src/assets/static/fonts/icons/themify/themify.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/fonts/icons/themify/themify.eot -------------------------------------------------------------------------------- /src/assets/static/fonts/icons/themify/themify.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/fonts/icons/themify/themify.ttf -------------------------------------------------------------------------------- /src/assets/static/images/datatables/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/images/datatables/sort_asc.png -------------------------------------------------------------------------------- /src/assets/static/images/datatables/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/images/datatables/sort_both.png -------------------------------------------------------------------------------- /src/assets/static/images/datatables/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/images/datatables/sort_desc.png -------------------------------------------------------------------------------- /src/assets/static/fonts/icons/themify/themify.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/fonts/icons/themify/themify.woff -------------------------------------------------------------------------------- /webpack/rules/index.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | require('./js'), 3 | require('./images'), 4 | require('./css'), 5 | require('./sass'), 6 | require('./fonts'), 7 | ]; 8 | -------------------------------------------------------------------------------- /src/assets/static/fonts/icons/fontawesome/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/fonts/icons/fontawesome/FontAwesome.otf -------------------------------------------------------------------------------- /src/assets/static/images/datatables/sort_asc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/images/datatables/sort_asc_disabled.png -------------------------------------------------------------------------------- /src/assets/static/images/datatables/sort_desc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/images/datatables/sort_desc_disabled.png -------------------------------------------------------------------------------- /src/assets/styles/spec/components/progressBar.scss: -------------------------------------------------------------------------------- 1 | .progress { 2 | height: 4px; 3 | background-color: var(--c-border); 4 | border-radius: 4px; 5 | margin-bottom: 10px; 6 | } 7 | -------------------------------------------------------------------------------- /webpack/plugins/caseSensitivePlugin.js: -------------------------------------------------------------------------------- 1 | const 2 | CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); 3 | 4 | module.exports = new CaseSensitivePathsPlugin(); 5 | -------------------------------------------------------------------------------- /src/assets/styles/spec/settings/index.scss: -------------------------------------------------------------------------------- 1 | @use 'breakpoints' as *; 2 | @use 'brand-colors/dist/latest/scss/brand-colors.latest.scss' as *; 3 | @use 'baseColors' as *; 4 | @use 'borders' as *; 5 | -------------------------------------------------------------------------------- /src/assets/styles/vendor/sparkline.scss: -------------------------------------------------------------------------------- 1 | #jqstooltip { 2 | width: auto !important; 3 | height: auto !important; 4 | padding: 5px 10px !important; 5 | border-radius: 2px !important; 6 | } 7 | -------------------------------------------------------------------------------- /src/assets/static/fonts/icons/fontawesome/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/fonts/icons/fontawesome/fontawesome-webfont.eot -------------------------------------------------------------------------------- /src/assets/static/fonts/icons/fontawesome/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/fonts/icons/fontawesome/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /src/assets/static/fonts/icons/fontawesome/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/fonts/icons/fontawesome/fontawesome-webfont.woff -------------------------------------------------------------------------------- /src/assets/static/fonts/icons/fontawesome/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puikinsh/Adminator-admin-dashboard/HEAD/src/assets/static/fonts/icons/fontawesome/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /webpack/rules/images.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | test : /\.(png|gif|jpg?g|svg)$/i, 3 | exclude : /(node_modules)/, 4 | type : 'asset/resource', 5 | generator: { 6 | filename: 'assets/[name][ext]', 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /browserslist: -------------------------------------------------------------------------------- 1 | # https://github.com/browserslist/browserslist#readme 2 | 3 | >= 0.5% 4 | last 2 major versions 5 | not dead 6 | Chrome >= 60 7 | Firefox >= 60 8 | Firefox ESR 9 | iOS >= 12 10 | Safari >= 12 11 | not Explorer <= 11 -------------------------------------------------------------------------------- /src/assets/styles/spec/settings/fonts.scss: -------------------------------------------------------------------------------- 1 | $font-primary: 2 | Roboto, -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif; 3 | $font-secondary: $font-primary; 4 | $font-size-base: 0.875rem; 5 | -------------------------------------------------------------------------------- /src/assets/styles/spec/settings/borders.scss: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------- 2 | // @Borders 3 | // --------------------------------------------------------- 4 | 5 | $border-width: 1px; 6 | $border-color: rgba(0, 0, 0, 0.0625); 7 | -------------------------------------------------------------------------------- /src/assets/styles/spec/components/easyPieChart.scss: -------------------------------------------------------------------------------- 1 | .easy-pie-chart { 2 | position: relative; 3 | 4 | span { 5 | position: absolute; 6 | top: 50%; 7 | left: 50%; 8 | transform: translate(-50%, -50%); 9 | line-height: 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /webpack/plugins/extractPlugin.js: -------------------------------------------------------------------------------- 1 | const 2 | manifest = require('../manifest'), 3 | ExtractTextPlugin = require('mini-css-extract-plugin'); 4 | 5 | module.exports = new ExtractTextPlugin({ 6 | filename: manifest.outputFiles.css, 7 | // allChunks: true, 8 | }); 9 | -------------------------------------------------------------------------------- /src/assets/styles/spec/components/index.scss: -------------------------------------------------------------------------------- 1 | @use 'sidebar' as *; 2 | @use 'topbar' as *; 3 | @use 'pageContainer' as *; 4 | @use 'progressBar' as *; 5 | @use 'easyPieChart' as *; 6 | @use 'forms' as *; 7 | @use 'masonry' as *; 8 | @use 'loader' as *; 9 | @use 'footer' as *; 10 | -------------------------------------------------------------------------------- /docs/examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Examples 4 | nav_order: 5 5 | has_children: true 6 | --- 7 | 8 | # Examples 9 | 10 | Practical examples and integration guides. 11 | 12 | This section provides real-world examples of using Adminator in various scenarios and frameworks. -------------------------------------------------------------------------------- /ci/verifyVersion.sh: -------------------------------------------------------------------------------- 1 | VERSION=$1 2 | TAG_EXISTS=$(git ls-remote --tags origin $VERSION | wc -l) 3 | 4 | if [ $TAG_EXISTS -eq "1" ]; then 5 | echo "The tag '$VERSION' already exists. Please update version in package.json."; 6 | exit 1; 7 | fi 8 | 9 | echo "The tag '$VERSION' does not exist - success."; -------------------------------------------------------------------------------- /src/assets/styles/vendor/index.scss: -------------------------------------------------------------------------------- 1 | @use 'perfect-scrollbar/css/perfect-scrollbar' as *; 2 | @use 'themify-icons' as *; 3 | @use 'font-awesome' as *; 4 | @use 'perfectScrollbar' as *; 5 | @use 'sparkline' as *; 6 | @use 'jquery.datatables' as *; 7 | @use 'fullcalendar' as *; 8 | @use 'datepicker' as *; 9 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/helpers/pseudo.scss: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------- 2 | // @Pseudo Elements 3 | // --------------------------------------------------------- 4 | 5 | .no-after::after { display: none !important; } 6 | .no-before::before { display: none !important; } 7 | -------------------------------------------------------------------------------- /docs/api.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: API Reference 4 | nav_order: 4 5 | has_children: true 6 | --- 7 | 8 | # API Reference 9 | 10 | Complete JavaScript API documentation for Adminator. 11 | 12 | This section provides detailed documentation for all JavaScript APIs, utility functions, and integration methods. -------------------------------------------------------------------------------- /docs/customization.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Customization 4 | nav_order: 3 5 | has_children: true 6 | --- 7 | 8 | # Customization 9 | 10 | Learn how to customize Adminator's appearance and behavior. 11 | 12 | This section covers the dark mode system, theming, CSS variables, and custom component creation. -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Getting Started 4 | nav_order: 2 5 | has_children: true 6 | --- 7 | 8 | # Getting Started 9 | 10 | Everything you need to know to start building with Adminator. 11 | 12 | This section covers installation, project setup, development workflow, and deployment options. -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/helpers/index.scss: -------------------------------------------------------------------------------- 1 | @use 'flex' as *; 2 | @use 'layout' as *; 3 | @use 'lists' as *; 4 | @use 'margin' as *; 5 | @use 'objects' as *; 6 | @use 'padding' as *; 7 | @use 'positions' as *; 8 | @use 'sizes' as *; 9 | @use 'typography' as *; 10 | @use 'border' as *; 11 | @use 'pseudo' as *; 12 | -------------------------------------------------------------------------------- /src/assets/scripts/scrollbar/index.js: -------------------------------------------------------------------------------- 1 | import PerfectScrollbar from 'perfect-scrollbar'; 2 | 3 | export default (function () { 4 | const scrollables = document.querySelectorAll('.scrollable'); 5 | if (scrollables.length > 0) { 6 | scrollables.forEach(el => { 7 | new PerfectScrollbar(el); 8 | }); 9 | } 10 | }()); 11 | -------------------------------------------------------------------------------- /src/assets/styles/spec/tools/mixins/clearfix.scss: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------- 2 | // @Clearfix 3 | //---------------------------------------------------------- 4 | 5 | @mixin clearfix { 6 | &::before, 7 | &::after { 8 | content: ' '; 9 | display: table; 10 | } 11 | 12 | &::after { 13 | clear: both; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/assets/styles/spec/tools/mixins/placeholder.scss: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------- 2 | // @Placeholder 3 | //---------------------------------------------------------- 4 | 5 | @mixin placeholder { 6 | &::-webkit-input-placeholder { @content; } 7 | &:-moz-placeholder { @content; } 8 | &::-moz-placeholder { @content; } 9 | &:-ms-input-placeholder { @content; } 10 | } 11 | -------------------------------------------------------------------------------- /src/assets/static/images/logo-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | Adminator Logo – Circle 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /webpack/plugins/copyPlugin.js: -------------------------------------------------------------------------------- 1 | const 2 | path = require('path'), 3 | manifest = require('../manifest'), 4 | CopyWebpackPlugin = require('copy-webpack-plugin'); 5 | 6 | module.exports = new CopyWebpackPlugin({ 7 | patterns: [ 8 | { 9 | from : path.join(manifest.paths.src, 'assets/static'), 10 | to : path.join(manifest.paths.build, 'assets/static'), 11 | }, 12 | ], 13 | }); 14 | -------------------------------------------------------------------------------- /src/assets/scripts/chat/index.js: -------------------------------------------------------------------------------- 1 | export default (function () { 2 | const chatSidebarToggle = document.getElementById('chat-sidebar-toggle'); 3 | const chatSidebar = document.getElementById('chat-sidebar'); 4 | 5 | if (chatSidebarToggle && chatSidebar) { 6 | chatSidebarToggle.addEventListener('click', e => { 7 | chatSidebar.classList.toggle('open'); 8 | e.preventDefault(); 9 | }); 10 | } 11 | }()) 12 | -------------------------------------------------------------------------------- /src/assets/scripts/masonry/index.js: -------------------------------------------------------------------------------- 1 | import Masonry from 'masonry-layout'; 2 | 3 | export default (function () { 4 | window.addEventListener('load', () => { 5 | const masonryElement = document.querySelector('.masonry'); 6 | if (masonryElement) { 7 | new Masonry(masonryElement, { 8 | itemSelector: '.masonry-item', 9 | columnWidth: '.masonry-sizer', 10 | percentPosition: true, 11 | }); 12 | } 13 | }); 14 | }()); 15 | -------------------------------------------------------------------------------- /src/assets/static/images/logo-outline.svg: -------------------------------------------------------------------------------- 1 | 2 | Adminator Logo – Outline 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /webpack/plugins/index.js: -------------------------------------------------------------------------------- 1 | const 2 | manifest = require('../manifest'); 3 | 4 | const plugins = []; 5 | 6 | plugins.push( 7 | ...(require('./htmlPlugin')), 8 | ...(require('./internal')), 9 | require('./caseSensitivePlugin'), 10 | require('./extractPlugin'), 11 | require('./copyPlugin') 12 | ); 13 | 14 | if (manifest.IS_DEVELOPMENT) { 15 | plugins.push(require('./dashboardPlugin')); 16 | } 17 | 18 | if (manifest.IS_PRODUCTION) { 19 | plugins.push(require('./copyPlugin')); 20 | } 21 | 22 | module.exports = plugins; 23 | -------------------------------------------------------------------------------- /.claude/settings.local 2.json: -------------------------------------------------------------------------------- 1 | { 2 | "permissions": { 3 | "allow": [ 4 | "Bash(npm run build:*)", 5 | "Bash(npm install)", 6 | "Bash(npm run lint)", 7 | "Bash(rm:*)", 8 | "Bash(ls:*)", 9 | "Bash(pkill:*)", 10 | "Bash(true)", 11 | "Bash(npm start)", 12 | "Bash(grep:*)", 13 | "Bash(sudo rm:*)", 14 | "Bash(npx eslint:*)", 15 | "Bash(npm run lint:*)", 16 | "Bash(gh release create:*)", 17 | "Bash(npm search:*)", 18 | "Bash(npm pack:*)", 19 | "Bash(npm:*)", 20 | "WebFetch(domain:keenthemes.com)" 21 | ], 22 | "deny": [] 23 | } 24 | } -------------------------------------------------------------------------------- /src/assets/scripts/search/index.js: -------------------------------------------------------------------------------- 1 | export default (function () { 2 | const searchToggle = document.querySelector('.search-toggle'); 3 | const searchBox = document.querySelector('.search-box'); 4 | const searchInput = document.querySelector('.search-input'); 5 | const searchInputField = document.querySelector('.search-input input'); 6 | 7 | if (searchToggle && searchBox && searchInput && searchInputField) { 8 | searchToggle.addEventListener('click', e => { 9 | searchBox.classList.toggle('active'); 10 | searchInput.classList.toggle('active'); 11 | searchInputField.focus(); 12 | e.preventDefault(); 13 | }); 14 | } 15 | }()); 16 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/utils/peers.scss: -------------------------------------------------------------------------------- 1 | .peers { 2 | box-sizing: border-box; 3 | display: flex !important; 4 | align-items: flex-start; 5 | justify-content: flex-start; 6 | flex-flow: row wrap; 7 | height: auto; 8 | max-width: 100%; 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | .peer { 14 | display: block; 15 | height: auto; 16 | flex: 0 0 auto; 17 | } 18 | 19 | .peer-greed { 20 | flex: 1 1 auto; 21 | // overflow: hidden; 22 | } 23 | 24 | .peers-greed > .peer, 25 | .peers-greed > .peers { 26 | flex: 1 1 auto; 27 | } 28 | 29 | .peer > img { 30 | max-width: none; 31 | } 32 | 33 | .peer-greed > img { 34 | max-width: 100%; 35 | } 36 | -------------------------------------------------------------------------------- /src/assets/scripts/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Adminator Admin Template 3 | * Modern Entry Point - Phase 2 Modernization 4 | */ 5 | 6 | // Import the modern application 7 | import './app.js'; 8 | 9 | // Legacy imports that haven't been modernized yet 10 | // These will be gradually replaced in future iterations 11 | import './datatable'; 12 | // import './datepicker'; // REMOVED: Replaced with modern day.js implementation in app.js 13 | 14 | // Note: The following have been modernized and are now handled by app.js: 15 | // - sidebar (now Sidebar component) 16 | // - charts (now ChartComponent using Chart.js instead of jQuery Sparkline) 17 | // - Basic DOM utilities (now DOM utils) 18 | 19 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Development files 2 | node_modules/ 3 | .git/ 4 | .gitignore 5 | .eslintrc.js 6 | .stylelintrc.json 7 | 8 | # Build artifacts that aren't needed 9 | webpack.config.js 10 | babel.config.js 11 | 12 | # Documentation that's not essential for npm users 13 | .github/ 14 | docs/ 15 | 16 | # IDE files 17 | .vscode/ 18 | .idea/ 19 | *.swp 20 | *.swo 21 | *~ 22 | 23 | # OS files 24 | .DS_Store 25 | Thumbs.db 26 | 27 | # Logs 28 | *.log 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | 33 | # Runtime data 34 | pids 35 | *.pid 36 | *.seed 37 | *.pid.lock 38 | 39 | # Coverage directory used by tools like istanbul 40 | coverage/ 41 | 42 | # Temporary folders 43 | tmp/ 44 | temp/ -------------------------------------------------------------------------------- /src/assets/static/images/logo-gradient.svg: -------------------------------------------------------------------------------- 1 | 2 | Adminator Logo – Gradient 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # ----------------------------- 2 | # General 3 | # ----------------------------- 4 | 5 | # EditorConfig helps developers define and maintain consistent 6 | # coding styles between different editors and IDEs 7 | # editorconfig.org 8 | 9 | # top-most EditorConfig file 10 | root = true 11 | 12 | 13 | 14 | # ----------------------------- 15 | # All Files 16 | # ----------------------------- 17 | 18 | [*] 19 | end_of_line = lf 20 | charset = utf-8 21 | insert_final_newline = true 22 | trim_trailing_whitespace = true 23 | indent_style = space 24 | indent_size = 2 25 | 26 | 27 | 28 | # ----------------------------- 29 | # Markdown Files 30 | # ----------------------------- 31 | 32 | [*.md] 33 | trim_trailing_whitespace = false 34 | -------------------------------------------------------------------------------- /src/assets/static/images/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/mixins/mediaQueryCondition.scss: -------------------------------------------------------------------------------- 1 | @use '../../../settings/breakpoints' as *; 2 | 3 | // --------------------------------------------------------- 4 | // @Media Queries Generator 5 | // --------------------------------------------------------- 6 | 7 | // Mixin used to generate responsive versions of css rules. 8 | 9 | @mixin mediaQueryCondition($mq) { 10 | $breakpointFound: false; 11 | 12 | @each $breakpoint in $breakpoints { 13 | $alias: nth($breakpoint, 1); 14 | $condition: nth($breakpoint, 2); 15 | 16 | @if $mq == $alias and $condition { 17 | $breakpointFound: true; 18 | 19 | @media #{$condition} { 20 | @content; 21 | } 22 | } 23 | } 24 | 25 | @if $breakpointFound == false { 26 | @warn "Oops! Breakpoint ‘#{$mq}’ does not exist \:"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/mixins/generateResponsive.scss: -------------------------------------------------------------------------------- 1 | @use '../../../settings/breakpoints' as *; 2 | @use 'mediaQueryCondition' as *; 3 | 4 | // Initialize global variable to avoid deprecation warning 5 | $breakpointAlias: null; 6 | 7 | // --------------------------------------------------------- 8 | // @Responsive Suffix Generator 9 | // --------------------------------------------------------- 10 | 11 | // Mixin used to generate responsive suffixes for classes (i.e. m-10@sm+). 12 | 13 | 14 | @mixin generateResponsive() { 15 | @each $breakpoint in $breakpoints { 16 | $breakpointAlias : nth($breakpoint, 1) !global; 17 | $breakpointCondition : nth($breakpoint, 2); 18 | 19 | @include mediaQueryCondition($breakpointAlias) { 20 | @content; 21 | } 22 | 23 | $breakpointAlias: null !global; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/helpers/lists.scss: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------- 2 | // @TOC 3 | // --------------------------------------------------------- 4 | 5 | // + @List Style Position 6 | // + @List Style Type 7 | 8 | // --------------------------------------------------------- 9 | // @List Style Position 10 | // --------------------------------------------------------- 11 | 12 | .lisp-i { list-style-position: inside; } 13 | .lisp-o { list-style-position: outside; } 14 | 15 | // --------------------------------------------------------- 16 | // @List Style Type 17 | // --------------------------------------------------------- 18 | 19 | .lis-n { list-style: none; } 20 | .list-c { list-style-type: circle; } 21 | .list-s { list-style-type: square; } 22 | .list-u { list-style-type: upper-roman; } 23 | .list-l { list-style-type: lower-alpha; } 24 | -------------------------------------------------------------------------------- /src/assets/scripts/email/index.js: -------------------------------------------------------------------------------- 1 | export default (function () { 2 | // Email side toggle functionality 3 | const emailSideToggle = document.querySelector('.email-side-toggle'); 4 | const emailApp = document.querySelector('.email-app'); 5 | 6 | if (emailSideToggle && emailApp) { 7 | emailSideToggle.addEventListener('click', e => { 8 | emailApp.classList.toggle('side-active'); 9 | e.preventDefault(); 10 | }); 11 | } 12 | 13 | // Email list item and back to mailbox functionality 14 | const emailListItems = document.querySelectorAll('.email-list-item, .back-to-mailbox'); 15 | const emailContent = document.querySelector('.email-content'); 16 | 17 | if (emailListItems.length > 0 && emailContent) { 18 | emailListItems.forEach(item => { 19 | item.addEventListener('click', e => { 20 | emailContent.classList.toggle('open'); 21 | e.preventDefault(); 22 | }); 23 | }); 24 | } 25 | }()) 26 | -------------------------------------------------------------------------------- /src/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 404 7 | 8 | 9 |
10 |
11 | # 12 |
13 | 14 |
15 |

404

16 |

Oops Page Not Found

17 |

The page you are looking for does not exist or has been moved.

18 |
19 | Go to Home 20 |
21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /src/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 500 7 | 8 | 9 |
10 |
11 | # 12 |
13 | 14 |
15 |

500

16 |

Internal server error

17 |

Something goes wrong with our servers, please try again later.

18 |
19 | Go to Home 20 |
21 |
22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 |

Bug report

10 |

Feature request

11 |

Support request

12 |

Documentation issue

13 | 14 | 15 | 16 | # Current behavior 17 | 18 | 19 | # Expected behavior 20 | 21 | 22 | 23 | # Environment 24 | Platform: 25 | Browser: 26 | 27 | For building issues: 28 | Node: 29 | NPM: 30 | 31 | 32 | -------------------------------------------------------------------------------- /.github/workflows/merge.yml: -------------------------------------------------------------------------------- 1 | name: Merge checks 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | strategy: 13 | matrix: 14 | node-version: [22.x] 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | cache: 'npm' 25 | 26 | - name: Install dependencies 27 | run: npm ci 28 | 29 | - name: Run linting 30 | run: npm run lint 31 | 32 | - name: Build 33 | run: npm run build 34 | 35 | - name: Get version 36 | id: version 37 | run: echo "version=v$(./ci/getVersion.sh)" >> $GITHUB_OUTPUT 38 | 39 | - name: Verify version 40 | run: ./ci/verifyVersion.sh ${{ steps.version.outputs.version }} 41 | -------------------------------------------------------------------------------- /src/assets/styles/spec/components/loader.scss: -------------------------------------------------------------------------------- 1 | #loader { 2 | transition: all 0.3s ease-in-out; 3 | opacity: 1; 4 | visibility: visible; 5 | position: fixed; 6 | height: 100vh; 7 | width: 100%; 8 | background: var(--loader-bg); 9 | z-index: 90000; 10 | } 11 | 12 | #loader.fadeOut { 13 | opacity: 0; 14 | visibility: hidden; 15 | } 16 | 17 | .spinner { 18 | width: 40px; 19 | height: 40px; 20 | position: absolute; 21 | top: calc(50% - 20px); 22 | left: calc(50% - 20px); 23 | background-color: var(--spinner-bg); 24 | border-radius: 100%; 25 | -webkit-animation: sk-scaleout 1.0s infinite ease-in-out; 26 | animation: sk-scaleout 1.0s infinite ease-in-out; 27 | } 28 | 29 | @-webkit-keyframes sk-scaleout { 30 | 0% { -webkit-transform: scale(0) } 31 | 100% { 32 | -webkit-transform: scale(1.0); 33 | opacity: 0; 34 | } 35 | } 36 | 37 | @keyframes sk-scaleout { 38 | 0% { 39 | -webkit-transform: scale(0); 40 | transform: scale(0); 41 | } 100% { 42 | -webkit-transform: scale(1.0); 43 | transform: scale(1.0); 44 | opacity: 0; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Aigars Silkalns 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/assets/styles/spec/settings/breakpoints.scss: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------- 2 | // @Breakpoints 3 | // --------------------------------------------------------- 4 | 5 | $breakpoint-xl : 1440px; 6 | $breakpoint-lg : 1200px; 7 | $breakpoint-md : 992px; 8 | $breakpoint-sm : 768px; 9 | $breakpoint-xs : 0; 10 | 11 | $breakpoints: ( 12 | "xl\\+" "screen and (min-width: #{$breakpoint-xl})", 13 | "lg\\+" "screen and (min-width: #{$breakpoint-lg})", 14 | "md\\+" "screen and (min-width: #{$breakpoint-md})", 15 | "sm\\+" "screen and (min-width: #{$breakpoint-sm})", 16 | "xs\\+" "screen and (min-width: #{$breakpoint-xs})", 17 | 18 | "xl-" "screen and (max-width: #{$breakpoint-xl - 1px})", 19 | "lg-" "screen and (max-width: #{$breakpoint-lg - 1px})", 20 | "md-" "screen and (max-width: #{$breakpoint-md - 1px})", 21 | "sm-" "screen and (max-width: #{$breakpoint-sm - 1px})", 22 | 23 | "lg" "screen and (min-width: #{$breakpoint-lg - 1px}) and (max-width: #{$breakpoint-xl - 1px})", 24 | "md" "screen and (min-width: #{$breakpoint-md - 1px}) and (max-width: #{$breakpoint-lg - 1px})", 25 | "sm" "screen and (min-width: #{$breakpoint-sm - 1px}) and (max-width: #{$breakpoint-md - 1px})", 26 | ); 27 | -------------------------------------------------------------------------------- /webpack/devServer.js: -------------------------------------------------------------------------------- 1 | // --------------------- 2 | // @Loading Dependencies 3 | // --------------------- 4 | 5 | const 6 | manifest = require('./manifest'); 7 | 8 | 9 | // ------------------ 10 | // @DevServer Configs 11 | // ------------------ 12 | 13 | /** 14 | * [1] : To enable local network testing 15 | */ 16 | 17 | const devServer = { 18 | static: { 19 | directory: manifest.IS_PRODUCTION ? manifest.paths.build : manifest.paths.src, 20 | watch: true, 21 | }, 22 | historyApiFallback: true, 23 | port: manifest.IS_PRODUCTION ? 3001 : 4000, 24 | compress: manifest.IS_PRODUCTION, 25 | client: { 26 | overlay: true, 27 | progress: !manifest.IS_PRODUCTION, 28 | }, 29 | hot: !manifest.IS_PRODUCTION, 30 | host: '0.0.0.0', 31 | allowedHosts: 'all', // [1] 32 | devMiddleware: { 33 | stats: { 34 | assets: true, 35 | children: false, 36 | chunks: false, 37 | hash: false, 38 | modules: false, 39 | publicPath: false, 40 | timings: true, 41 | version: false, 42 | warnings: true, 43 | colors: true, 44 | }, 45 | }, 46 | }; 47 | 48 | 49 | // ----------------- 50 | // @Exporting Module 51 | // ----------------- 52 | 53 | module.exports = devServer; -------------------------------------------------------------------------------- /src/assets/scripts/skycons/index.js: -------------------------------------------------------------------------------- 1 | import SkyconsInit from 'skycons'; 2 | import Theme from '../utils/theme.js'; 3 | 4 | const Skycons = SkyconsInit(window); 5 | 6 | export default (function () { 7 | let icons; 8 | 9 | const initSkycons = () => { 10 | const skyconsColor = Theme.getCSSVar('--skycons-color'); 11 | 12 | if (icons) { 13 | icons.pause(); 14 | icons.remove('all'); 15 | } 16 | 17 | icons = new Skycons({ 'color': skyconsColor }); 18 | 19 | const list = [ 20 | 'clear-day', 21 | 'clear-night', 22 | 'partly-cloudy-day', 23 | 'partly-cloudy-night', 24 | 'cloudy', 25 | 'rain', 26 | 'sleet', 27 | 'snow', 28 | 'wind', 29 | 'fog', 30 | ]; 31 | let i = list.length; 32 | 33 | while (i--) { 34 | const 35 | weatherType = list[i], 36 | elements = document.getElementsByClassName(weatherType); 37 | let j = elements.length; 38 | 39 | while (j--) { 40 | icons.set(elements[j], weatherType); 41 | } 42 | } 43 | 44 | icons.play(); 45 | }; 46 | 47 | // Initialize skycons 48 | initSkycons(); 49 | 50 | // Listen for theme changes 51 | window.addEventListener('adminator:themeChanged', initSkycons); 52 | }()); 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ---------------------------- 2 | # Generated Files & Folders 3 | # ---------------------------- 4 | 5 | # [1] : Windows thumbnail cache files. 6 | # [2] : Folder config file. 7 | # [3] : Recycle Bin used on file shares. 8 | 9 | .idea 10 | .DS_Store 11 | .sass-cache 12 | Thumbs.db # [1] 13 | ehthumbs.db # [1] 14 | Desktop.ini # [2] 15 | $RECYCLE.BIN # [3] 16 | 17 | # ---------------------------- 18 | # Packaging 19 | # ---------------------------- 20 | 21 | logs 22 | *.log 23 | npm-debug.log* 24 | node_modules 25 | yarn.lock 26 | yarn-error.log* 27 | pnpm-lock.yaml 28 | package-lock.json 29 | 30 | # ---------------------------- 31 | # Project Folders 32 | # ---------------------------- 33 | 34 | build/ 35 | dist/ 36 | 37 | # ---------------------------- 38 | # Development Files 39 | # ---------------------------- 40 | 41 | *.map 42 | *.ts 43 | tsconfig.json 44 | .env 45 | .env.local 46 | .env.development 47 | .env.test 48 | .env.production 49 | 50 | # ---------------------------- 51 | # Editor Directories 52 | # ---------------------------- 53 | 54 | .vscode/ 55 | .idea/ 56 | *.swp 57 | *.swo 58 | *~ 59 | 60 | # ---------------------------- 61 | # OS Files 62 | # ---------------------------- 63 | 64 | .DS_Store 65 | .DS_Store? 66 | ._* 67 | .Spotlight-V100 68 | .Trashes 69 | CLAUDE.md 70 | release.md 71 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/utils/center.scss: -------------------------------------------------------------------------------- 1 | @use '../mixins/generateResponsive' as *; 2 | 3 | // --------------------------------------------------------- 4 | // @TOC 5 | // --------------------------------------------------------- 6 | 7 | // + @Variables 8 | // + @Centering 9 | 10 | // --------------------------------------------------------- 11 | // @Variables 12 | // --------------------------------------------------------- 13 | 14 | $responsive: true; 15 | 16 | // --------------------------------------------------------- 17 | // @Centering 18 | // --------------------------------------------------------- 19 | 20 | .centerY { 21 | top: 50%; 22 | transform: translateY(-50%); 23 | } 24 | 25 | .centerX { 26 | left: 50%; 27 | transform: translateX(-50%); 28 | } 29 | 30 | .centerXY { 31 | top: 50%; 32 | left: 50%; 33 | transform: translate(-50%, -50%); 34 | } 35 | 36 | @if ($responsive == true) { 37 | @include generateResponsive() { 38 | .centerY\@#{$breakpointAlias} { 39 | top: 50%; 40 | transform: translateY(-50%); 41 | } 42 | 43 | .centerX\@#{$breakpointAlias} { 44 | left: 50%; 45 | transform: translateX(-50%); 46 | } 47 | 48 | .centerXY\@#{$breakpointAlias} { 49 | top: 50%; 50 | left: 50%; 51 | transform: translate(-50%, -50%); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /webpack/plugins/htmlPlugin.js: -------------------------------------------------------------------------------- 1 | const 2 | path = require('path'), 3 | manifest = require('../manifest'), 4 | HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | 6 | const titles = { 7 | 'index': 'Dashboard', 8 | 'blank': 'Blank', 9 | 'buttons': 'Buttons', 10 | 'calendar': 'Calendar', 11 | 'charts': 'Charts', 12 | 'chat': 'Chat', 13 | 'compose': 'Compose', 14 | 'datatable': 'Datatable', 15 | 'email': 'Email', 16 | 'forms': 'Forms', 17 | 'google-maps': 'Google Maps', 18 | 'signin': 'Signin', 19 | 'signup': 'Signup', 20 | 'ui': 'UI', 21 | 'vector-maps': 'Vector Maps', 22 | '404': '404', 23 | '500': '500', 24 | 'basic-table': 'Basic Table', 25 | }; 26 | 27 | let minify = { 28 | collapseWhitespace: false, 29 | minifyCSS: false, 30 | minifyJS: false, 31 | removeComments: true, 32 | useShortDoctype: false, 33 | }; 34 | 35 | if (manifest.MINIFY) { 36 | minify = { 37 | collapseWhitespace: true, 38 | minifyCSS: true, 39 | minifyJS: true, 40 | removeComments: true, 41 | useShortDoctype: true, 42 | }; 43 | } 44 | 45 | 46 | module.exports = Object.keys(titles).map(title => { 47 | return new HtmlWebpackPlugin({ 48 | template: path.join(manifest.paths.src, `${title}.html`), 49 | path: manifest.paths.build, 50 | filename: `${title}.html`, 51 | inject: true, 52 | minify, 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # ----------------------------- 2 | # General 3 | # ----------------------------- 4 | 5 | # AUTO-DETECT - Handle line endings automatically for files detected 6 | # as text and leave all files detected as binary untouched. 7 | # This will handle all files NOT defined below. 8 | 9 | * text=auto 10 | 11 | 12 | 13 | # ----------------------------- 14 | # Source Code 15 | # ----------------------------- 16 | 17 | *.css text 18 | *.html text 19 | *.js text 20 | *.json text 21 | *.scss text 22 | 23 | 24 | 25 | # ----------------------------- 26 | # Documentation 27 | # ----------------------------- 28 | 29 | *.md text 30 | CHANGELOG text 31 | LICENSE text 32 | 33 | 34 | 35 | # ----------------------------- 36 | # Configs 37 | # ----------------------------- 38 | 39 | .editorconfig text 40 | .gitattributes text 41 | .gitconfig text 42 | .gitignore text 43 | .babelrc text 44 | .stylelintrc text 45 | .eslintrc text 46 | .yarnclean text 47 | *.yml text 48 | browserlist text 49 | yarn.lock text 50 | 51 | 52 | 53 | # ----------------------------- 54 | # Graphics 55 | # ----------------------------- 56 | 57 | *.gif binary 58 | *.ico binary 59 | *.jpg binary 60 | *.jpeg binary 61 | *.png binary 62 | *.svg text 63 | 64 | 65 | 66 | # ----------------------------- 67 | # Fonts 68 | # ----------------------------- 69 | 70 | *.ttf binary 71 | *.eot binary 72 | *.otf binary 73 | *.woff binary 74 | *.woff2 binary 75 | -------------------------------------------------------------------------------- /webpack/rules/css.js: -------------------------------------------------------------------------------- 1 | // ------------------ 2 | // @Table of Contents 3 | // ------------------ 4 | 5 | /** 6 | * + @Loading Dependencies 7 | * + @Common Loaders 8 | * + @Merging Production Loaders 9 | * + @Merging Development Loaders 10 | * + @Exporting Module 11 | */ 12 | 13 | 14 | // --------------------- 15 | // @Loading Dependencies 16 | // --------------------- 17 | 18 | const 19 | manifest = require('../manifest'), 20 | ExtractTextPlugin = require('mini-css-extract-plugin'); 21 | 22 | 23 | // --------------- 24 | // @Common Loaders 25 | // --------------- 26 | 27 | let rule = {}; 28 | 29 | const loaders = [ 30 | { 31 | loader: 'css-loader', 32 | options: { 33 | sourceMap : manifest.IS_DEVELOPMENT, 34 | importLoaders: 1, 35 | }, 36 | }, 37 | ]; 38 | 39 | 40 | // --------------------------- 41 | // @Merging Production Loaders 42 | // --------------------------- 43 | 44 | if (manifest.IS_PRODUCTION) { 45 | rule = { 46 | test: /\.css$/, 47 | use: [{ 48 | loader: ExtractTextPlugin.loader, 49 | }].concat(loaders), 50 | }; 51 | } 52 | 53 | 54 | // ---------------------------- 55 | // @Merging Development Loaders 56 | // ---------------------------- 57 | 58 | if (manifest.IS_DEVELOPMENT) { 59 | rule = { 60 | test: /\.css$/, 61 | use: [{ 62 | loader: 'style-loader', 63 | }].concat(loaders), 64 | }; 65 | } 66 | 67 | 68 | // ----------------- 69 | // @Exporting Module 70 | // ----------------- 71 | 72 | module.exports = rule; 73 | -------------------------------------------------------------------------------- /src/assets/scripts/utils/index.js: -------------------------------------------------------------------------------- 1 | export default (function () { 2 | // ------------------------------------------------------ 3 | // @Window Resize 4 | // ------------------------------------------------------ 5 | 6 | /** 7 | * NOTE: Register resize event for Masonry layout 8 | */ 9 | const EVENT = document.createEvent('UIEvents'); 10 | window.EVENT = EVENT; 11 | EVENT.initUIEvent('resize', true, false, window, 0); 12 | 13 | 14 | window.addEventListener('load', () => { 15 | /** 16 | * Trigger window resize event after page load 17 | * for recalculation of masonry layout. 18 | */ 19 | window.dispatchEvent(EVENT); 20 | }); 21 | 22 | // ------------------------------------------------------ 23 | // @External Links 24 | // ------------------------------------------------------ 25 | 26 | // Open external links in new window 27 | const externalLinks = document.querySelectorAll('a[href^="http"], a[href^="//"]'); 28 | 29 | externalLinks.forEach(link => { 30 | const href = link.getAttribute('href'); 31 | if (href && !href.includes(window.location.host)) { 32 | link.setAttribute('rel', 'noopener noreferrer'); 33 | link.setAttribute('target', '_blank'); 34 | } 35 | }); 36 | 37 | // ------------------------------------------------------ 38 | // @Resize Trigger 39 | // ------------------------------------------------------ 40 | 41 | // Trigger resize on any element click 42 | document.addEventListener('click', () => { 43 | window.dispatchEvent(window.EVENT); 44 | }); 45 | }()); 46 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/colors.scss: -------------------------------------------------------------------------------- 1 | @use '../settings/materialColors' as *; 2 | @use '../settings/baseColors' as *; 3 | 4 | // --------------------------------------------------------- 5 | // @TOC 6 | // --------------------------------------------------------- 7 | 8 | // + @Material Color 9 | // + @Grey Colors 10 | 11 | // --------------------------------------------------------- 12 | // @Material Color 13 | // --------------------------------------------------------- 14 | 15 | @each $item, $color in $md-colors { 16 | .c-#{"" + $item}, .cH-#{"" + $item}:hover { color: $color !important; } 17 | .bgc-#{"" + $item}, .bgcH-#{"" + $item}:hover { background-color: $color !important; } 18 | .bdc-#{"" + $item}, .bdcH-#{"" + $item}:hover { border-color: $color !important; } 19 | .fill-#{"" + $item}, .fillH-#{"" + $item}:hover { fill: $color !important; } 20 | .str-#{"" + $item}, .strH-#{"" + $item}:hover { stroke: $color !important; } 21 | } 22 | 23 | // --------------------------------------------------------- 24 | // @Grey Colors 25 | // --------------------------------------------------------- 26 | 27 | @each $item, $color in $grey-colors-alt { 28 | .c-#{"" + $item}, .cH-#{"" + $item}:hover { color: $color !important; } 29 | .bgc-#{"" + $item}, .bgcH-#{"" + $item}:hover { background-color: $color !important; } 30 | .bdc-#{"" + $item}, .bdcH-#{"" + $item}:hover { border-color: $color !important; } 31 | .fill-#{"" + $item}, .fillH-#{"" + $item}:hover { fill: $color !important; } 32 | .str-#{"" + $item}, .strH-#{"" + $item}:hover { stroke: $color !important; } 33 | } 34 | -------------------------------------------------------------------------------- /webpack/manifest.js: -------------------------------------------------------------------------------- 1 | // ------------------ 2 | // @Table of Contents 3 | // ------------------ 4 | 5 | /** 6 | * + @Loading Dependencies 7 | * + @Environment Holders 8 | * + @Utils 9 | * + @App Paths 10 | * + @Output Files Names 11 | * + @Entries Files Names 12 | * + @Exporting Module 13 | */ 14 | 15 | 16 | // --------------------- 17 | // @Loading Dependencies 18 | // --------------------- 19 | 20 | const path = require('path'); 21 | 22 | 23 | // -------------------- 24 | // @Environment Holders 25 | // -------------------- 26 | 27 | const 28 | NODE_ENV = process.env.NODE_ENV || 'development', 29 | IS_DEVELOPMENT = NODE_ENV === 'development', 30 | IS_PRODUCTION = NODE_ENV === 'production', 31 | MINIFY = process.env.MINIFY === 'true'; 32 | 33 | // ------ 34 | // @Utils 35 | // ------ 36 | 37 | const 38 | dir = src => path.join(__dirname, src); 39 | 40 | 41 | // ---------- 42 | // @App Paths 43 | // ---------- 44 | 45 | const 46 | paths = { 47 | src : dir('../src'), 48 | build : dir('../dist'), 49 | }; 50 | 51 | 52 | // ------------------- 53 | // @Output Files Names 54 | // ------------------- 55 | 56 | const 57 | outputFiles = { 58 | bundle : 'bundle.js', 59 | vendor : 'vendor.js', 60 | css : 'style.css', 61 | }; 62 | 63 | 64 | // -------------------- 65 | // @Entries Files Names 66 | // -------------------- 67 | 68 | const 69 | entries = { 70 | js : 'index.js', 71 | }; 72 | 73 | 74 | // ----------------- 75 | // @Exporting Module 76 | // ----------------- 77 | 78 | module.exports = { 79 | paths, 80 | outputFiles, 81 | entries, 82 | NODE_ENV, 83 | IS_DEVELOPMENT, 84 | IS_PRODUCTION, 85 | MINIFY, 86 | }; 87 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build and Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | strategy: 13 | matrix: 14 | node-version: [22.x] 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v4 19 | 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v4 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | cache: 'npm' 25 | 26 | - name: Install dependencies 27 | run: npm ci 28 | 29 | - name: Run linting 30 | run: npm run lint 31 | 32 | - name: Build minified release 33 | run: | 34 | npm run release:minified 35 | zip -r -j static_minified.zip dist/* 36 | 37 | - name: Build unminified release 38 | run: | 39 | npm run release:unminified 40 | zip -r -j static_unminified.zip dist/* 41 | 42 | - name: Get version 43 | id: version 44 | run: echo "version=v$(./ci/getVersion.sh)" >> $GITHUB_OUTPUT 45 | 46 | - name: Verify version 47 | run: ./ci/verifyVersion.sh ${{ steps.version.outputs.version }} 48 | 49 | - name: Create GitHub Release 50 | uses: softprops/action-gh-release@v2 51 | with: 52 | name: ${{ steps.version.outputs.version }} 53 | tag_name: ${{ steps.version.outputs.version }} 54 | files: | 55 | static_minified.zip 56 | static_unminified.zip 57 | fail_on_unmatched_files: true 58 | prerelease: false 59 | draft: false 60 | generate_release_notes: true 61 | -------------------------------------------------------------------------------- /src/assets/styles/spec/generic/base.scss: -------------------------------------------------------------------------------- 1 | @use '../settings/fonts' as *; 2 | @use '../settings/baseColors' as *; 3 | @use 'sass:color'; 4 | 5 | html, html a, body { 6 | -webkit-font-smoothing: antialiased; 7 | } 8 | 9 | a { 10 | transition: all 0.3s ease-in-out; 11 | text-decoration: none; 12 | } 13 | 14 | body { 15 | font-family: $font-primary; 16 | font-size: 14px; 17 | color: $default-text-color; 18 | line-height: 1.5; 19 | letter-spacing: 0.2px; 20 | overflow-x: hidden; 21 | } 22 | 23 | h1, 24 | h2, 25 | h3, 26 | h4, 27 | h5, 28 | h6 { 29 | font-family: $font-secondary; 30 | letter-spacing: 0.5px; 31 | line-height: 1.5; 32 | font-weight: 400; 33 | 34 | a { 35 | font-family: $font-secondary; 36 | } 37 | 38 | small { 39 | font-weight: 300; 40 | color: color.adjust($default-dark, $lightness: 5%); 41 | } 42 | } 43 | 44 | p { 45 | font-family: $font-primary; 46 | line-height: 1.9; 47 | } 48 | 49 | .lead { 50 | font-size: 18px; 51 | } 52 | 53 | ul { 54 | margin-bottom: 0; 55 | } 56 | 57 | a { 58 | color: $default-info; 59 | 60 | &:hover, 61 | &:focus { 62 | text-decoration: none; 63 | color: darken($default-info, 10%); 64 | } 65 | 66 | &:focus { 67 | outline: none; 68 | } 69 | 70 | &.text-gray { 71 | &:hover, 72 | &:focus, 73 | &.active { 74 | color: $default-dark !important; 75 | } 76 | } 77 | } 78 | 79 | :focus { 80 | outline: none; 81 | } 82 | 83 | hr { 84 | border-top: 1px solid $border-color; 85 | } 86 | 87 | 88 | a.btn { 89 | color:#fff; 90 | } 91 | 92 | a.btn:hover { 93 | color:#fff; 94 | } 95 | 96 | .btn-color { 97 | color:#fff; 98 | } 99 | 100 | .btn-color:hover { 101 | color:#fff; 102 | } 103 | -------------------------------------------------------------------------------- /src/assets/styles/spec/tools/mixins/mediaQueriesRanges.scss: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------- 2 | // @TOC 3 | // --------------------------------------------------------- 4 | 5 | // + @General Media Query 6 | // + @All Above Media Query 7 | // + @All Under Media Query 8 | // + @Between Two Devices Media Query 9 | 10 | // --------------------------------------------------------- 11 | // @General Media Query Mixin 12 | // --------------------------------------------------------- 13 | 14 | // Mixin used for custom rules that don't follow 15 | // any of the following premade media queries. 16 | 17 | @mixin mq($condition) { 18 | @media #{$condition} { 19 | @content; 20 | } 21 | } 22 | 23 | // --------------------------------------------------------- 24 | // @All Above Media Query Mixin 25 | // --------------------------------------------------------- 26 | 27 | // Mixin used to match certain breakpoint 28 | // and all devices above it. 29 | 30 | @mixin from($breakpoint) { 31 | @media screen and (min-width: $breakpoint){ 32 | @content; 33 | } 34 | } 35 | 36 | // --------------------------------------------------------- 37 | // @All Under Media Query Mixin 38 | // --------------------------------------------------------- 39 | 40 | // Mixin used to match all devices under certain breakpoint. 41 | 42 | @mixin to($breakpoint) { 43 | @media screen and (max-width: $breakpoint - 1px) { 44 | @content; 45 | } 46 | } 47 | 48 | // --------------------------------------------------------- 49 | // @Between Two Devices Media Query Mixin 50 | // --------------------------------------------------------- 51 | 52 | // Mixin used to match the devices between 2 breakpoints. 53 | 54 | @mixin between($start, $end){ 55 | @media screen and (min-width: $start) and (max-width: $end - 1px) { 56 | @content; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /webpack/rules/sass.js: -------------------------------------------------------------------------------- 1 | // ------------------ 2 | // @Table of Contents 3 | // ------------------ 4 | 5 | /** 6 | * + @Loading Dependencies 7 | * + @Common Loaders 8 | * + @Exporting Module 9 | */ 10 | 11 | 12 | // --------------------- 13 | // @Loading Dependencies 14 | // --------------------- 15 | 16 | const 17 | manifest = require('../manifest'), 18 | path = require('path'), 19 | cssNext = require('postcss-preset-env'), 20 | ExtractTextPlugin = require('mini-css-extract-plugin'); 21 | 22 | // --------------- 23 | // @Common Loaders 24 | // --------------- 25 | 26 | const loaders = [ 27 | { 28 | loader: 'css-loader', 29 | options: { 30 | sourceMap : manifest.IS_DEVELOPMENT, 31 | }, 32 | }, 33 | { 34 | loader: 'postcss-loader', 35 | options: { 36 | sourceMap: manifest.IS_DEVELOPMENT, 37 | postcssOptions: { 38 | plugins: [ 39 | [ 40 | cssNext(), 41 | ], 42 | ], 43 | }, 44 | }, 45 | }, 46 | { 47 | loader: 'sass-loader', 48 | options: { 49 | sourceMap: manifest.IS_DEVELOPMENT, 50 | api: 'modern-compiler', 51 | sassOptions: { 52 | outputStyle: manifest.MINIFY ? 'compressed' : 'expanded', 53 | includePaths: [ 54 | path.join('../../', 'node_modules'), 55 | path.join(manifest.paths.src, 'assets', 'styles'), 56 | path.join(manifest.paths.src, ''), 57 | ], 58 | quietDeps: true, 59 | verbose: false, 60 | }, 61 | }, 62 | }, 63 | ]; 64 | 65 | if (manifest.IS_PRODUCTION) { 66 | loaders.unshift(ExtractTextPlugin.loader); 67 | } else { 68 | loaders.unshift({ 69 | loader: 'style-loader', 70 | }); 71 | } 72 | 73 | const rule = { 74 | test: /\.scss$/, 75 | use: loaders, 76 | }; 77 | 78 | // ----------------- 79 | // @Exporting Module 80 | // ----------------- 81 | 82 | module.exports = rule; 83 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard-scss", 3 | "rules": { 4 | "at-rule-no-unknown": null, 5 | "scss/at-rule-no-unknown": true, 6 | "at-rule-empty-line-before": null, 7 | "no-descending-specificity": null, 8 | "selector-class-pattern": null, 9 | "scss/double-slash-comment-empty-line-before": null, 10 | "scss/no-global-function-names": null, 11 | "declaration-property-value-no-unknown": null, 12 | "scss/dollar-variable-empty-line-before": null, 13 | "scss/operator-no-newline-after": null, 14 | "scss/load-partial-extension": null, 15 | "media-feature-range-notation": null, 16 | "color-function-notation": null, 17 | "color-function-alias-notation": null, 18 | "alpha-value-notation": null, 19 | "color-hex-length": null, 20 | "no-duplicate-selectors": null, 21 | "shorthand-property-no-redundant-values": null, 22 | "scss/at-extend-no-missing-placeholder": null, 23 | "declaration-block-no-redundant-longhand-properties": null, 24 | "scss/at-mixin-argumentless-call-parentheses": null, 25 | "scss/at-rule-conditional-no-parentheses": null, 26 | "scss/dollar-variable-pattern": null, 27 | "scss/at-mixin-pattern": null, 28 | "scss/dollar-variable-colon-space-before": null, 29 | "length-zero-no-unit": null, 30 | "property-no-deprecated": null, 31 | "selector-not-notation": null, 32 | "import-notation": null, 33 | "scss/comment-no-empty": null, 34 | "value-keyword-case": null, 35 | "function-name-case": null, 36 | "declaration-property-value-keyword-no-deprecated": null, 37 | "property-no-vendor-prefix": null, 38 | "font-family-name-quotes": null, 39 | "font-family-no-missing-generic-family-keyword": null, 40 | "at-rule-no-vendor-prefix": null, 41 | "rule-empty-line-before": null, 42 | "declaration-empty-line-before": null, 43 | "no-empty-source": null, 44 | "scss/dollar-variable-colon-space-after": null, 45 | "scss/double-slash-comment-whitespace-inside": null, 46 | "selector-no-vendor-prefix": null, 47 | "declaration-block-single-line-max-declarations": null 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /webpack/plugins/internal.js: -------------------------------------------------------------------------------- 1 | // ------------------ 2 | // @Table of Contents 3 | // ------------------ 4 | 5 | /** 6 | * + @Loading Dependencies 7 | * + @Common Plugins 8 | * + @Merging Production Plugins 9 | * + @Merging Development Plugins 10 | * + @Exporting Module 11 | */ 12 | 13 | 14 | // --------------------- 15 | // @Loading Dependencies 16 | // --------------------- 17 | 18 | const 19 | manifest = require('../manifest'), 20 | webpack = require('webpack'); 21 | 22 | 23 | // --------------- 24 | // @Common Plugins 25 | // --------------- 26 | 27 | const 28 | plugins = []; 29 | 30 | plugins.push( 31 | // new webpack.DefinePlugin({ 32 | // 'process.env': { 33 | // NODE_ENV: JSON.stringify(manifest.NODE_ENV), 34 | // }, 35 | // }), 36 | 37 | 38 | // new webpack.optimize.CommonsChunkPlugin({ 39 | // name: 'vendor', 40 | // filename: manifest.outputFiles.vendor, 41 | // minChunks(module) { 42 | // const { context } = module; 43 | // return context && context.indexOf('node_modules') >= 0; 44 | // }, 45 | // }), 46 | 47 | new webpack.ProvidePlugin({ 48 | $: 'jquery', 49 | jQuery: 'jquery', 50 | 'window.jQuery': 'jquery', 51 | // Popper: ['popper.js', 'default'], 52 | }) 53 | ); 54 | 55 | 56 | // --------------------------- 57 | // @Merging Production Plugins 58 | // --------------------------- 59 | 60 | // if (manifest.IS_PRODUCTION) { 61 | // plugins.push( 62 | // new webpack.optimize.UglifyJsPlugin({ 63 | // compress: { 64 | // comparisons : true, 65 | // conditionals : true, 66 | // dead_code : true, 67 | // drop_debugger : true, 68 | // evaluate : true, 69 | // if_return : true, 70 | // join_vars : true, 71 | // screw_ie8 : true, 72 | // sequences : true, 73 | // unused : true, 74 | // warnings : false, 75 | // }, 76 | 77 | // output: { 78 | // comments: false, 79 | // }, 80 | // }) 81 | // ); 82 | // } 83 | 84 | 85 | // ---------------------------- 86 | // @Merging Development Plugins 87 | // ---------------------------- 88 | 89 | if (manifest.IS_DEVELOPMENT) { 90 | plugins.push( 91 | // new webpack.NoEmitOnErrorsPlugin(), 92 | // new webpack.NamedModulesPlugin(), 93 | new webpack.HotModuleReplacementPlugin() 94 | ); 95 | } 96 | 97 | 98 | // ----------------- 99 | // @Exporting Module 100 | // ----------------- 101 | 102 | module.exports = plugins; 103 | -------------------------------------------------------------------------------- /src/assets/styles/utils/theme.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --c-bkg-body: #f8f9fa; 3 | --c-bkg-card: #ffffff; 4 | --c-bkg-sidebar: #ffffff; 5 | 6 | --c-text-base: #212529; 7 | --c-text-muted: #6c757d; 8 | 9 | --c-border: #e2e5e8; 10 | 11 | --c-primary: #4b7cf3; 12 | --c-success: #2ecc71; 13 | --c-danger: #e74c3c; 14 | 15 | /* Vector map colors */ 16 | --vmap-bg-color: #ffffff; 17 | --vmap-border-color: #ffffff; 18 | --vmap-region-color: #e4ecef; 19 | --vmap-marker-fill: #ffffff; 20 | --vmap-marker-stroke: #000000; 21 | --vmap-hover-color: #ffffff; 22 | --vmap-selected-color: #c9dfaf; 23 | --vmap-scale-start: #03a9f3; 24 | --vmap-scale-end: #02a7f1; 25 | --vmap-scale-light: #b6d6ff; 26 | --vmap-scale-dark: #005ace; 27 | 28 | /* Skycons */ 29 | --skycons-color: #ff6849; 30 | 31 | /* Sparkline colors */ 32 | --sparkline-success: #4caf50; 33 | --sparkline-purple: #9675ce; 34 | --sparkline-info: #03a9f3; 35 | --sparkline-danger: #f96262; 36 | --sparkline-light: #aaf; 37 | 38 | /* Loader */ 39 | --loader-bg: #ffffff; 40 | --spinner-bg: #333333; 41 | 42 | /* Google Maps */ 43 | --gmap-landscape-hue: #FFBB00; 44 | --gmap-highway-hue: #FFC200; 45 | --gmap-road-hue: #FF0300; 46 | --gmap-water-hue: #0078FF; 47 | --gmap-poi-hue: #00FF6A; 48 | } 49 | 50 | [data-theme="dark"] { 51 | --c-bkg-body: #181a1f; 52 | --c-bkg-card: #20232a; 53 | --c-bkg-sidebar: #20232a; 54 | 55 | --c-text-base: #e8eaed; 56 | --c-text-muted: #9ca3af; 57 | 58 | --c-border: #2b2f36; 59 | 60 | --c-primary: #4b7cf3; 61 | --c-success: #32d48e; 62 | --c-danger: #ff6b6b; 63 | 64 | /* Vector map colors - dark theme */ 65 | --vmap-bg-color: #20232a; 66 | --vmap-border-color: #2b2f36; 67 | --vmap-region-color: #2b2f36; 68 | --vmap-marker-fill: #e8eaed; 69 | --vmap-marker-stroke: #9ca3af; 70 | --vmap-hover-color: #181a1f; 71 | --vmap-selected-color: #4b5563; 72 | --vmap-scale-start: #3b82f6; 73 | --vmap-scale-end: #1d4ed8; 74 | --vmap-scale-light: #1e40af; 75 | --vmap-scale-dark: #60a5fa; 76 | 77 | /* Skycons - dark theme */ 78 | --skycons-color: #f97316; 79 | 80 | /* Sparkline colors - dark theme */ 81 | --sparkline-success: #10b981; 82 | --sparkline-purple: #a855f7; 83 | --sparkline-info: #3b82f6; 84 | --sparkline-danger: #ef4444; 85 | --sparkline-light: #60a5fa; 86 | 87 | /* Loader - dark theme */ 88 | --loader-bg: #181a1f; 89 | --spinner-bg: #e8eaed; 90 | 91 | /* Google Maps - dark theme */ 92 | --gmap-landscape-hue: #D4AC0D; 93 | --gmap-highway-hue: #E6AC00; 94 | --gmap-road-hue: #CC2900; 95 | --gmap-water-hue: #1B4F72; 96 | --gmap-poi-hue: #148F77; 97 | } -------------------------------------------------------------------------------- /webpack/config.js: -------------------------------------------------------------------------------- 1 | // ------------------ 2 | // @Table of Contents 3 | // ------------------ 4 | 5 | /** 6 | * + @Loading Dependencies 7 | * + @Entry Point Setup 8 | * + @Path Resolving 9 | * + @Exporting Module 10 | */ 11 | 12 | 13 | // --------------------- 14 | // @Loading Dependencies 15 | // --------------------- 16 | 17 | const 18 | path = require('path'), 19 | manifest = require('./manifest'), 20 | devServer = require('./devServer'), 21 | rules = require('./rules'), 22 | plugins = require('./plugins'); 23 | 24 | const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); 25 | const TerserPlugin = require("terser-webpack-plugin"); 26 | 27 | // ------------------ 28 | // @Entry Point Setup 29 | // ------------------ 30 | 31 | const 32 | entry = [ 33 | path.join(manifest.paths.src, 'assets', 'scripts', manifest.entries.js), 34 | ]; 35 | 36 | 37 | // --------------- 38 | // @Path Resolving 39 | // --------------- 40 | 41 | const resolve = { 42 | extensions: ['.tsx', '.ts', '.webpack-loader.js', '.web-loader.js', '.loader.js', '.js', '.jsx'], 43 | modules: [ 44 | path.join(__dirname, '../node_modules'), 45 | path.join(manifest.paths.src, ''), 46 | ], 47 | alias: { 48 | '@': path.join(manifest.paths.src), 49 | '@/components': path.join(manifest.paths.src, 'assets', 'scripts', 'components'), 50 | '@/utils': path.join(manifest.paths.src, 'assets', 'scripts', 'utils'), 51 | '@/constants': path.join(manifest.paths.src, 'assets', 'scripts', 'constants'), 52 | }, 53 | }; 54 | 55 | const optimization = { 56 | minimize: manifest.MINIFY, 57 | }; 58 | 59 | if (manifest.MINIFY) { 60 | optimization.minimizer = [ 61 | new CssMinimizerPlugin(), 62 | new TerserPlugin(), 63 | ]; 64 | } 65 | 66 | // ----------------- 67 | // @Exporting Module 68 | // ----------------- 69 | module.exports = { 70 | devtool: manifest.IS_PRODUCTION ? false : 'source-map', 71 | context: path.join(manifest.paths.src, manifest.entries.js), 72 | // watch: !manifest.IS_PRODUCTION, 73 | entry, 74 | mode: manifest.NODE_ENV, 75 | // output: { 76 | // path: manifest.paths.build, 77 | // publicPath: '', 78 | // filename: manifest.outputFiles.bundle, 79 | // }, 80 | module: { 81 | rules, 82 | }, 83 | optimization, 84 | resolve, 85 | plugins, 86 | devServer, 87 | // Suppress Bootstrap SASS deprecation warnings (modern syntax) 88 | ignoreWarnings: [ 89 | /Deprecation Warning/, 90 | /node_modules\/bootstrap/, 91 | /repetitive deprecation warnings omitted/, 92 | /red\(\) is deprecated/, 93 | /green\(\) is deprecated/, 94 | /blue\(\) is deprecated/, 95 | /Global built-in functions are deprecated/, 96 | ], 97 | }; 98 | -------------------------------------------------------------------------------- /src/assets/styles/spec/screens/email.scss: -------------------------------------------------------------------------------- 1 | @use '../settings/baseColors' as *; 2 | @use '../settings/breakpoints' as *; 3 | @use '../tools/mixins/mediaQueriesRanges' as *; 4 | 5 | .email-app { 6 | .email-side-nav { 7 | background: var(--c-bkg-card); 8 | position: fixed; 9 | border-right: 1px solid var(--c-border); 10 | float: left; 11 | width: 250px; 12 | transition: all 0.3s ease-in-out; 13 | 14 | @include to($breakpoint-md) { 15 | z-index: 1; 16 | left: -250px; 17 | } 18 | } 19 | 20 | .email-wrapper { 21 | margin: 0; 22 | padding: 0; 23 | overflow: auto; 24 | min-height: 100%; 25 | transition: all 0.3s ease-in-out; 26 | 27 | @include to($breakpoint-md) { 28 | position: absolute; 29 | left: 0; 30 | width: 100%; 31 | overflow-x: hidden; 32 | } 33 | 34 | @include from($breakpoint-md) { 35 | margin-left: 250px; 36 | } 37 | 38 | .email-list { 39 | position: relative; 40 | padding: 0; 41 | width: 100%; 42 | overflow-y: hidden; 43 | background-color: var(--c-bkg-card); 44 | height: calc(100vh - #{$header-height}); 45 | 46 | @include to($breakpoint-md) { 47 | max-height: calc(100vh - 65px); 48 | } 49 | 50 | @include from($breakpoint-md) { 51 | width: 40%; 52 | border-right: 1px solid var(--c-border); 53 | float: left; 54 | } 55 | } 56 | 57 | .email-content { 58 | float: left; 59 | width: 60%; 60 | position: relative; 61 | padding: 0; 62 | background-color: var(--c-bkg-card); 63 | // min-height: calc(100vh - #{$header-height}); 64 | 65 | &.no-inbox-view { 66 | width: 100%; 67 | } 68 | 69 | @include to($breakpoint-md) { 70 | position: absolute; 71 | top: 0; 72 | left: 100%; 73 | width: 100%; 74 | height: 100%; 75 | transition: all 0.3s ease-in-out; 76 | max-height: calc(100vh - #{$header-height}); 77 | overflow-y: scroll; 78 | 79 | &.open { 80 | left: 0; 81 | } 82 | } 83 | } 84 | 85 | .email-compose { 86 | position: relative; 87 | .email-compose-body { 88 | padding: 30px 20px; 89 | 90 | } 91 | } 92 | } 93 | 94 | 95 | &.side-active { 96 | .email-side-nav { 97 | @include to($breakpoint-md) { 98 | left: 0; 99 | } 100 | } 101 | 102 | .email-wrapper { 103 | @include to($breakpoint-md) { 104 | left: 250px; 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | # GitHub Pages Configuration for Adminator Documentation 2 | 3 | # Site settings 4 | title: "Adminator Documentation" 5 | description: "Complete guide for the Adminator Bootstrap 5 Admin Dashboard Template with Dark Mode" 6 | url: "https://puikinsh.github.io" 7 | baseurl: "/Adminator-admin-dashboard" 8 | 9 | # Build settings 10 | markdown: kramdown 11 | highlighter: rouge 12 | remote_theme: just-the-docs/just-the-docs 13 | 14 | # Plugin settings 15 | plugins: 16 | - jekyll-feed 17 | - jekyll-sitemap 18 | - jekyll-seo-tag 19 | 20 | # Just the Docs theme configuration 21 | color_scheme: nil 22 | search_enabled: true 23 | search: 24 | heading_level: 2 25 | previews: 2 26 | preview_words_before: 3 27 | preview_words_after: 3 28 | tokenizer_separator: /[\s/]+/ 29 | rel_url: true 30 | button: false 31 | 32 | # Enable custom CSS 33 | sass: 34 | sass_dir: _sass 35 | style: compressed 36 | 37 | # Heading anchor links appear on hover over h1-h6 tags 38 | heading_anchors: true 39 | 40 | # Footer 41 | footer_content: "Copyright © 2025 Colorlib. Distributed by an MIT license." 42 | 43 | # Back to top link 44 | back_to_top: true 45 | back_to_top_text: "Back to top" 46 | 47 | # Navigation Structure 48 | nav_sort: case_insensitive 49 | 50 | # Aux links for the upper right navigation 51 | aux_links: 52 | "GitHub Repository": 53 | - "//github.com/puikinsh/Adminator-admin-dashboard" 54 | "Live Demo": 55 | - "//colorlib.com/polygon/adminator/index.html" 56 | 57 | # Makes Aux links open in a new tab 58 | aux_links_new_tab: true 59 | 60 | # SEO 61 | author: "Colorlib" 62 | twitter: 63 | username: colorlib 64 | social: 65 | name: "Colorlib" 66 | links: 67 | - https://github.com/puikinsh/Adminator-admin-dashboard 68 | - https://colorlib.com 69 | 70 | # Exclude from processing 71 | exclude: 72 | - Gemfile 73 | - Gemfile.lock 74 | - node_modules 75 | - vendor/bundle/ 76 | - vendor/cache/ 77 | - vendor/gems/ 78 | - vendor/ruby/ 79 | 80 | # Code highlighting 81 | kramdown: 82 | syntax_highlighter: rouge 83 | syntax_highlighter_opts: 84 | css_class: 'highlight' 85 | block: 86 | line_numbers: false 87 | start_line: 1 88 | 89 | # Include 90 | include: 91 | - _pages 92 | 93 | # Collections 94 | collections: 95 | pages: 96 | output: true 97 | permalink: /:name/ 98 | 99 | # Defaults 100 | defaults: 101 | - scope: 102 | path: "" 103 | type: "pages" 104 | values: 105 | layout: "page" 106 | - scope: 107 | path: "" 108 | values: 109 | layout: "default" -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/helpers/border.scss: -------------------------------------------------------------------------------- 1 | @use '../../../settings/baseColors' as colors; 2 | @use '../../../settings/borders' as borders; 3 | 4 | // --------------------------------------------------------- 5 | // @TOC 6 | // --------------------------------------------------------- 7 | 8 | // + @Quick Border Helpers 9 | // + @Border Width 10 | // + @Border Radius 11 | // + @Border Style 12 | 13 | // --------------------------------------------------------- 14 | // @Quick Border Helpers 15 | // --------------------------------------------------------- 16 | 17 | .bd { border: #{borders.$border-width} solid #{colors.$border-color} !important; } 18 | .bdT { border-top: #{borders.$border-width} solid #{colors.$border-color} !important; } 19 | .bdR { border-right: #{borders.$border-width} solid #{colors.$border-color} !important; } 20 | .bdB { border-bottom: #{borders.$border-width} solid #{colors.$border-color} !important; } 21 | .bdL { border-left: #{borders.$border-width} solid #{colors.$border-color} !important; } 22 | 23 | // --------------------------------------------------------- 24 | // @Border Width 25 | // --------------------------------------------------------- 26 | 27 | @for $i from 0 through 5 { 28 | .bdw-#{$i} { border-width: #{$i}px !important; } 29 | .bdwT-#{$i} { border-top-width: #{$i}px !important; } 30 | .bdwR-#{$i} { border-right-width: #{$i}px !important; } 31 | .bdwB-#{$i} { border-bottom-width: #{$i}px !important; } 32 | .bdwL-#{$i} { border-left-width: #{$i}px !important; } 33 | } 34 | 35 | // --------------------------------------------------------- 36 | // @Border Radius 37 | // --------------------------------------------------------- 38 | 39 | @for $i from 0 to 5 { 40 | .bdrs-#{$i} { border-radius: #{$i}px !important; } 41 | 42 | .bdrsT-#{$i} { 43 | border-top-left-radius: #{$i}px !important; 44 | border-top-right-radius: #{$i}px !important; 45 | } 46 | 47 | .bdrsR-#{$i} { 48 | border-top-right-radius: #{$i}px !important; 49 | border-bottom-right-radius: #{$i}px !important; 50 | } 51 | 52 | .bdrsB-#{$i} { 53 | border-bottom-left-radius: #{$i}px !important; 54 | border-bottom-right-radius: #{$i}px !important; 55 | } 56 | 57 | .bdrsL-#{$i} { 58 | border-top-left-radius: #{$i}px !important; 59 | border-bottom-left-radius: #{$i}px !important; 60 | } 61 | } 62 | 63 | .bdrs-50p { border-radius: 50% !important; } 64 | .bdrs-10em { border-radius: 10em !important; } 65 | 66 | // --------------------------------------------------------- 67 | // @Border Style 68 | // --------------------------------------------------------- 69 | 70 | .bds-n { border-style: none !important; } 71 | .bds-s { border-style: solid !important; } 72 | .bds-dt { border-style: dotted !important; } 73 | .bds-ds { border-style: dashed !important; } 74 | .bds-db { border-style: double !important; } 75 | .bds-g { border-style: groove !important; } 76 | .bds-r { border-style: ridge !important; } 77 | .bds-i { border-style: inset !important; } 78 | .bds-o { border-style: outset !important; } 79 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/helpers/objects.scss: -------------------------------------------------------------------------------- 1 | // --------------------------------------------------------- 2 | // @TOC 3 | // --------------------------------------------------------- 4 | 5 | // + @Background Size 6 | // + @Background Position 7 | // + @Background Repeat 8 | // + @Object Fit 9 | // + @Resize 10 | // + @Opacity 11 | // + @Cursor 12 | // + @Visibility 13 | 14 | // --------------------------------------------------------- 15 | // @Background Size 16 | // --------------------------------------------------------- 17 | 18 | .bgsz-cv { background-size: cover; } 19 | .bgsz-ct { background-size: contain; } 20 | .bgsz-full { background-size: 100% 100%; } 21 | 22 | // --------------------------------------------------------- 23 | // @Background Position 24 | // --------------------------------------------------------- 25 | 26 | .bgpX-c { background-position-x: center; } 27 | .bgpX-t { background-position-x: top; } 28 | .bgpX-r { background-position-x: right; } 29 | .bgpX-l { background-position-x: left; } 30 | .bgpX-b { background-position-x: bottom; } 31 | .bgpY-c { background-position-y: center; } 32 | .bgpY-t { background-position-y: top; } 33 | .bgpY-r { background-position-y: right; } 34 | .bgpY-l { background-position-y: left; } 35 | .bgpY-b { background-position-y: bottom; } 36 | 37 | // --------------------------------------------------------- 38 | // @Background Repeat 39 | // --------------------------------------------------------- 40 | 41 | .bgr-n { background-repeat: no-repeat; } 42 | .bgr-y { background-repeat: repeat-y; } 43 | .bgr-x { background-repeat: repeat-x; } 44 | 45 | // --------------------------------------------------------- 46 | // @Object Fit 47 | // --------------------------------------------------------- 48 | 49 | .of-ct { object-fit: contain; } 50 | .of-cv { object-fit: cover; } 51 | .of-f { object-fit: fill; } 52 | .of-n { object-fit: none; } 53 | .of-sd { object-fit: scale-down; } 54 | 55 | // --------------------------------------------------------- 56 | // @Resize 57 | // --------------------------------------------------------- 58 | 59 | .rsz-v { resize: vertical; } 60 | .rsz-h { resize: horizontal; } 61 | 62 | // --------------------------------------------------------- 63 | // @Opacity 64 | // --------------------------------------------------------- 65 | 66 | .op-0 { opacity: 0; } 67 | .op-10p { opacity: 0.1; } 68 | .op-20p { opacity: 0.2; } 69 | .op-30p { opacity: 0.3; } 70 | .op-40p { opacity: 0.4; } 71 | .op-50p { opacity: 0.5; } 72 | .op-60p { opacity: 0.6; } 73 | .op-70p { opacity: 0.7; } 74 | .op-80p { opacity: 0.8; } 75 | .op-90p { opacity: 0.9; } 76 | .op-100p { opacity: 1; } 77 | 78 | // --------------------------------------------------------- 79 | // @Cursor 80 | // --------------------------------------------------------- 81 | 82 | .cur-na { cursor: not-allowed; } 83 | .cur-p { cursor: pointer; } 84 | .cur-a { cursor: auto; } 85 | 86 | // --------------------------------------------------------- 87 | // @Visibility 88 | // --------------------------------------------------------- 89 | 90 | .vis-v { visibility: visible; } 91 | .vis-h { visibility: hidden; } 92 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at support at colorlib.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /src/assets/scripts/googleMaps/index.js: -------------------------------------------------------------------------------- 1 | import loadGoogleMapsAPI from 'load-google-maps-api'; 2 | import Theme from '../utils/theme.js'; 3 | 4 | export default (function () { 5 | let map, marker; 6 | 7 | const initGoogleMap = () => { 8 | const googleMapElement = document.getElementById('google-map'); 9 | if (googleMapElement) { 10 | loadGoogleMapsAPI({ 11 | key: 'AIzaSyDW8td30_gj6sGXjiMU0ALeMu1SDEwUnEA', 12 | }).then(() => { 13 | const latitude = 26.8206; 14 | const longitude = 30.8025; 15 | const mapZoom = 5; 16 | const { google } = window; 17 | 18 | const mapOptions = { 19 | center : new google.maps.LatLng(latitude, longitude), 20 | zoom : mapZoom, 21 | mapTypeId : google.maps.MapTypeId.ROADMAP, 22 | styles: [{ 23 | 'featureType': 'landscape', 24 | 'stylers': [ 25 | { 'hue' : Theme.getCSSVar('--gmap-landscape-hue') }, 26 | { 'saturation' : 43.400000000000006 }, 27 | { 'lightness' : 37.599999999999994 }, 28 | { 'gamma' : 1 }, 29 | ], 30 | }, { 31 | 'featureType': 'road.highway', 32 | 'stylers': [ 33 | { 'hue' : Theme.getCSSVar('--gmap-highway-hue') }, 34 | { 'saturation' : -61.8 }, 35 | { 'lightness' : 45.599999999999994 }, 36 | { 'gamma' : 1 }, 37 | ], 38 | }, { 39 | 'featureType': 'road.arterial', 40 | 'stylers': [ 41 | { 'hue' : Theme.getCSSVar('--gmap-road-hue') }, 42 | { 'saturation' : -100 }, 43 | { 'lightness' : 51.19999999999999 }, 44 | { 'gamma' : 1 }, 45 | ], 46 | }, { 47 | 'featureType': 'road.local', 48 | 'stylers': [ 49 | { 'hue' : Theme.getCSSVar('--gmap-road-hue') }, 50 | { 'saturation' : -100 }, 51 | { 'lightness' : 52 }, 52 | { 'gamma' : 1 }, 53 | ], 54 | }, { 55 | 'featureType': 'water', 56 | 'stylers': [ 57 | { 'hue' : Theme.getCSSVar('--gmap-water-hue') }, 58 | { 'saturation' : -13.200000000000003 }, 59 | { 'lightness' : 2.4000000000000057 }, 60 | { 'gamma' : 1 }, 61 | ], 62 | }, { 63 | 'featureType': 'poi', 64 | 'stylers': [ 65 | { 'hue' : Theme.getCSSVar('--gmap-poi-hue') }, 66 | { 'saturation' : -1.0989010989011234 }, 67 | { 'lightness' : 11.200000000000017 }, 68 | { 'gamma' : 1 }, 69 | ], 70 | }], 71 | }; 72 | 73 | map = new google.maps.Map(document.getElementById('google-map'), mapOptions); 74 | 75 | if (marker) { 76 | marker.setMap(null); 77 | } 78 | 79 | marker = new google.maps.Marker({ 80 | map, 81 | position : new google.maps.LatLng(latitude, longitude), 82 | visible : true, 83 | }); 84 | }); 85 | } 86 | }; 87 | 88 | // Initialize Google Maps 89 | initGoogleMap(); 90 | 91 | // Listen for theme changes 92 | window.addEventListener('adminator:themeChanged', initGoogleMap); 93 | }()) 94 | -------------------------------------------------------------------------------- /src/assets/styles/spec/settings/baseColors.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:color'; 2 | @use 'materialColors' as *; 3 | 4 | // --------------------------------------------------------- 5 | // @TOC 6 | // --------------------------------------------------------- 7 | 8 | // + @Greyscale Colors 9 | // + @Bootstrap Color System 10 | // + @Default Colors 11 | // + @Inverted Colors 12 | // + @Others 13 | // + @Header Themes 14 | // + @Social Networks Colors 15 | 16 | // --------------------------------------------------------- 17 | // @Greyscale Colors 18 | // --------------------------------------------------------- 19 | 20 | // Colors below are ordered from lightest to darkest 21 | 22 | $grey-100 : #f9fafb; 23 | $grey-200 : #f2f3f5; 24 | $grey-300 : #e6eaf0; 25 | $grey-400 : #d3d9e3; 26 | $grey-500 : #b9c2d0; 27 | $grey-600 : #7c8695; 28 | $grey-700 : #72777a; 29 | $grey-800 : #565a5c; 30 | $grey-900 : #313435; 31 | 32 | $grey-colors-alt: ( 33 | grey-100 : #f9fafb, 34 | grey-200 : #f2f3f5, 35 | grey-300 : #e6eaf0, 36 | grey-400 : #d3d9e3, 37 | grey-500 : #b9c2d0, 38 | grey-600 : #7c8695, 39 | grey-700 : #72777a, 40 | grey-800 : #565a5c, 41 | grey-900 : #313435, 42 | ); 43 | 44 | // --------------------------------------------------------- 45 | // @Bootstrap Color System 46 | // --------------------------------------------------------- 47 | 48 | $blue : $md-blue-500; 49 | $indigo : $md-indigo-500; 50 | $purple : $md-purple-500; 51 | $pink : $md-pink-500; 52 | $red : $md-red-500; 53 | $orange : $md-orange-500; 54 | $yellow : $md-yellow-500; 55 | $green : $md-green-500; 56 | $teal : $md-teal-500; 57 | $cyan : $md-cyan-500; 58 | 59 | // --------------------------------------------------------- 60 | // @Default Colors 61 | // --------------------------------------------------------- 62 | 63 | $default-danger : #ff3c7e; 64 | $default-dark : #313435; 65 | $default-grey : #565a5c; 66 | $default-info : #0f9aee; 67 | $default-primary : #7774e7; 68 | $default-success : #37c936; 69 | $default-text-color : #72777a; 70 | $default-warning : #fc0; 71 | $default-white : #fff; 72 | 73 | // --------------------------------------------------------- 74 | // @Inverted Colors 75 | // --------------------------------------------------------- 76 | 77 | $inverse-danger : color.adjust($default-danger, $lightness: 35%); 78 | $inverse-info : color.adjust($default-info, $lightness: 45%); 79 | $inverse-primary : color.adjust($default-primary, $lightness: 30%); 80 | $inverse-success : color.adjust($default-success, $lightness: 45%); 81 | $inverse-warning : color.adjust($default-warning, $lightness: 45%); 82 | 83 | // --------------------------------------------------------- 84 | // @Others 85 | // --------------------------------------------------------- 86 | 87 | $border-color : #e6ecf5; 88 | $collapsed-size : 70px; 89 | $header-height : 65px; 90 | $offscreen-size : 280px; 91 | $side-nav-dark : #313644; 92 | $side-nav-dark-border : rgba(120, 130, 140, 0.3); 93 | $side-nav-dark-font : #99abb4; 94 | 95 | // --------------------------------------------------------- 96 | // @Header Themes 97 | // --------------------------------------------------------- 98 | 99 | $theme-danger : #f53f61; 100 | $theme-dark : color.adjust($side-nav-dark, $lightness: 10%); 101 | $theme-info : $default-info; 102 | $theme-primary : $default-primary; 103 | $theme-success : color.adjust($default-success, $saturation: -5%); 104 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/helpers/positions.scss: -------------------------------------------------------------------------------- 1 | @use '../mixins/generateResponsive' as *; 2 | 3 | // --------------------------------------------------------- 4 | // @TOC 5 | // --------------------------------------------------------- 6 | 7 | // + @Variables 8 | // + @Position (0 > 4 Step 1) 9 | // + @Position (5 > 35 Step 5) 10 | // + @Position (40 > 160 Step 10) 11 | 12 | // --------------------------------------------------------- 13 | // @Variables 14 | // --------------------------------------------------------- 15 | 16 | $responsive: true; 17 | 18 | // --------------------------------------------------------- 19 | // @Position (0 > 4 Step 1) 20 | // --------------------------------------------------------- 21 | 22 | @for $i from 0 through 4 { 23 | .t-#{$i} { top: #{$i}px; } 24 | .r-#{$i} { right: #{$i}px; } 25 | .b-#{$i} { bottom: #{$i}px; } 26 | .l-#{$i} { left: #{$i}px; } 27 | 28 | @if ($responsive == true) { 29 | @include generateResponsive() { 30 | .t-#{$i}\@#{$breakpointAlias} { top: #{$i}px; } 31 | .r-#{$i}\@#{$breakpointAlias} { right: #{$i}px; } 32 | .b-#{$i}\@#{$breakpointAlias} { bottom: #{$i}px; } 33 | .l-#{$i}\@#{$breakpointAlias} { left: #{$i}px; } 34 | } 35 | } 36 | } 37 | 38 | // --------------------------------------------------------- 39 | // @Position (5 > 35 Step 5) 40 | // --------------------------------------------------------- 41 | 42 | @for $i from 5 through 35 { 43 | @if $i % 5 == 0 { 44 | .t-#{$i} { top: #{$i}px; } 45 | .r-#{$i} { right: #{$i}px; } 46 | .b-#{$i} { bottom: #{$i}px; } 47 | .l-#{$i} { left: #{$i}px; } 48 | 49 | @if ($responsive == true) { 50 | @include generateResponsive() { 51 | .t-#{$i}\@#{$breakpointAlias} { top: #{$i}px; } 52 | .r-#{$i}\@#{$breakpointAlias} { right: #{$i}px; } 53 | .b-#{$i}\@#{$breakpointAlias} { bottom: #{$i}px; } 54 | .l-#{$i}\@#{$breakpointAlias} { left: #{$i}px; } 55 | } 56 | } 57 | } 58 | } 59 | 60 | // --------------------------------------------------------- 61 | // @Position (40 > 160 Step 10) 62 | // --------------------------------------------------------- 63 | 64 | @for $i from 40 through 160 { 65 | @if $i % 10 == 0 { 66 | .t-#{$i} { top: #{$i}px; } 67 | .r-#{$i} { right: #{$i}px; } 68 | .b-#{$i} { bottom: #{$i}px; } 69 | .l-#{$i} { left: #{$i}px; } 70 | 71 | @if ($responsive == true) { 72 | @include generateResponsive() { 73 | .t-#{$i}\@#{$breakpointAlias} { top: #{$i}px; } 74 | .r-#{$i}\@#{$breakpointAlias} { right: #{$i}px; } 75 | .b-#{$i}\@#{$breakpointAlias} { bottom: #{$i}px; } 76 | .l-#{$i}\@#{$breakpointAlias} { left: #{$i}px; } 77 | } 78 | } 79 | } 80 | } 81 | 82 | // --------------------------------------------------------- 83 | // @Position (50%) 84 | // --------------------------------------------------------- 85 | 86 | .tl-50p { 87 | top: 50%; 88 | left: 50%; 89 | } 90 | 91 | .tr-50p { 92 | top: 50%; 93 | right: 50%; 94 | } 95 | 96 | .t-50p { top: 50%; } 97 | .r-50p { right: 50%; } 98 | .b-50p { bottom: 50%; } 99 | .l-50p { left: 50%; } 100 | 101 | @if ($responsive == true) { 102 | @include generateResponsive() { 103 | .tl-50p\@#{$breakpointAlias} { 104 | top: 50%; 105 | left: 50%; 106 | } 107 | 108 | .tr-50p\@#{$breakpointAlias} { 109 | top: 50%; 110 | right: 50%; 111 | } 112 | 113 | .t-50p\@#{$breakpointAlias} { top: 50%; } 114 | .r-50p\@#{$breakpointAlias} { right: 50%; } 115 | .b-50p\@#{$breakpointAlias} { bottom: 50%; } 116 | .l-50p\@#{$breakpointAlias} { left: 50%; } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Home 4 | nav_order: 1 5 | description: "Adminator Bootstrap 5 Admin Dashboard with Dark Mode" 6 | permalink: / 7 | --- 8 | 9 | # Adminator Documentation 10 | {: .fs-9 } 11 | 12 | Complete guide for the Bootstrap 5 Admin Dashboard Template with comprehensive Dark Mode system 13 | {: .fs-6 .fw-300 } 14 | 15 | [Get Started Now](getting-started/installation){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 } 16 | [View on GitHub](https://github.com/puikinsh/Adminator-admin-dashboard){: .btn .fs-5 .mb-4 .mb-md-0 } 17 | 18 | --- 19 | 20 | ## ✨ What's New in v2.6.0 21 | 22 | 🌗 **Complete Dark Mode System** - Intelligent theme switching with OS preference detection 23 | ⚡ **Smart Theme Toggle** - Bootstrap-based switch with instant updates 24 | 🎨 **CSS Variables Architecture** - Semantic color system for easy customization 25 | 📊 **Component Integration** - All charts, calendars, and maps are theme-aware 26 | 27 | --- 28 | 29 | ## 🚀 Quick Start 30 | 31 | ```bash 32 | # Clone the repository 33 | git clone https://github.com/puikinsh/Adminator-admin-dashboard.git 34 | 35 | # Install dependencies 36 | npm install 37 | 38 | # Start development server 39 | npm start 40 | ``` 41 | 42 | Visit `http://localhost:4000` to see your dashboard! 43 | 44 | --- 45 | 46 | ## 📚 Documentation Sections 47 | 48 | ### Getting Started 49 | Learn how to install, configure, and deploy Adminator with our comprehensive setup guides. 50 | 51 | [Installation Guide →](getting-started/installation){: .btn .btn-outline } 52 | 53 | ### Dark Mode & Theming 54 | Discover the powerful dark mode system with CSS variables and theme switching capabilities. 55 | 56 | [Theme System →](customization/theme-system){: .btn .btn-outline } 57 | 58 | ### API Reference 59 | Complete JavaScript API documentation for theme management and component integration. 60 | 61 | [API Documentation →](api/theme-api){: .btn .btn-outline } 62 | 63 | ### Examples & Integration 64 | Real-world examples and integration guides for popular frameworks and use cases. 65 | 66 | [View Examples →](examples/theme-integration){: .btn .btn-outline } 67 | 68 | --- 69 | 70 | ## 🎯 Key Features 71 | 72 | | Feature | Description | 73 | |:--------|:------------| 74 | | **Bootstrap 5** | Latest Bootstrap framework with modern components | 75 | | **Dark Mode** | Comprehensive dark theme with intelligent switching | 76 | | **Responsive** | Mobile-first design that works on all devices | 77 | | **Chart Integration** | Chart.js with theme-aware color schemes | 78 | | **Calendar Support** | FullCalendar with dark mode styling | 79 | | **Vector Maps** | Interactive maps with custom color palettes | 80 | | **Clean Code** | Well-organized, documented, and maintainable | 81 | 82 | --- 83 | 84 | ## 🌟 Live Demo 85 | 86 | Experience Adminator's features in action: 87 | 88 | ### Light Mode 89 | ![Adminator Light Mode](assets/images/adminator-light-mode.avif) 90 | 91 | ### Dark Mode 92 | ![Adminator Dark Mode](assets/images/adminator-dark-mode.avif) 93 | 94 | [Try Live Demo](https://colorlib.com/polygon/adminator/index.html){: .btn .btn-green .fs-5 } 95 | 96 | --- 97 | 98 | ## 🤝 Contributing 99 | 100 | We welcome contributions! Please read our [contributing guidelines](contributing/) before submitting pull requests. 101 | 102 | --- 103 | 104 | ## 📄 License 105 | 106 | Adminator is released under the [MIT License](https://github.com/puikinsh/Adminator-admin-dashboard/blob/master/LICENSE). 107 | 108 | --- 109 | 110 | **Ready to build amazing dashboards?** Start with our [installation guide](getting-started/installation) or explore the [dark mode features](customization/theme-system)! -------------------------------------------------------------------------------- /src/assets/styles/spec/screens/chat.scss: -------------------------------------------------------------------------------- 1 | @use '../settings/baseColors' as *; 2 | @use '../settings/breakpoints' as *; 3 | @use '../tools/mixins/mediaQueriesRanges' as *; 4 | 5 | #chat-sidebar { 6 | width: 250px; 7 | height: calc(100vh - #{$header-height} - 60px); 8 | overflow: auto; 9 | 10 | @include to($breakpoint-md) { 11 | transition: all 0.3s ease-in-out; 12 | margin-left: -250px; 13 | 14 | &.open { 15 | margin-left: 0; 16 | } 17 | } 18 | } 19 | 20 | #chat-box { 21 | height: calc(100vh - #{$header-height} - 60px); 22 | overflow: auto; 23 | } 24 | 25 | // Dark mode chat styles 26 | [data-theme="dark"] { 27 | // Chat sidebar search input 28 | input[name="chatSearch"] { 29 | background: var(--c-bkg-card); 30 | color: var(--c-text-base); 31 | border-color: var(--c-border); 32 | 33 | &::placeholder { 34 | color: var(--c-text-muted); 35 | } 36 | 37 | &:focus { 38 | background: var(--c-bkg-card); 39 | border-color: var(--c-primary); 40 | box-shadow: 0 0 0 0.2rem rgba(var(--c-primary-rgb), 0.25); 41 | } 42 | } 43 | 44 | // Contact list items 45 | .peers.bgc-white { 46 | background: var(--c-bkg-card) !important; 47 | border-color: var(--c-border) !important; 48 | 49 | &:hover, 50 | &.bgcH-grey-50:hover { 51 | background: var(--c-bkg-hover) !important; 52 | } 53 | 54 | h6 { 55 | color: var(--c-text-base); 56 | } 57 | 58 | small { 59 | // Status colors remain as they are for visual indication 60 | &.c-grey-500 { 61 | color: var(--c-text-muted) !important; 62 | } 63 | } 64 | } 65 | 66 | // Chat header 67 | .peers.bgc-white { 68 | background: var(--c-bkg-card) !important; 69 | 70 | h6 { 71 | color: var(--c-text-base); 72 | } 73 | 74 | i { 75 | color: var(--c-text-muted); 76 | } 77 | 78 | .c-grey-900 { 79 | color: var(--c-text-base) !important; 80 | 81 | &.cH-blue-500:hover { 82 | color: var(--c-primary) !important; 83 | } 84 | } 85 | } 86 | 87 | // Chat background area 88 | .bgc-grey-200 { 89 | background: var(--c-bkg-body) !important; 90 | } 91 | 92 | .bgc-grey-100 { 93 | background: var(--c-bkg-body) !important; 94 | } 95 | 96 | // Chat messages 97 | .pY-3.pX-10.bgc-white { 98 | background: var(--c-bkg-card) !important; 99 | color: var(--c-text-base); 100 | border: 1px solid var(--c-border); 101 | 102 | small { 103 | color: var(--c-text-muted); 104 | } 105 | 106 | span { 107 | color: var(--c-text-base); 108 | } 109 | } 110 | 111 | // Chat input area 112 | .bdT.bgc-white { 113 | background: var(--c-bkg-card) !important; 114 | border-color: var(--c-border) !important; 115 | 116 | .form-control { 117 | background: var(--c-bkg-body); 118 | border-color: var(--c-border); 119 | color: var(--c-text-base); 120 | 121 | &::placeholder { 122 | color: var(--c-text-muted); 123 | } 124 | 125 | &:focus { 126 | background: var(--c-bkg-body); 127 | border-color: var(--c-primary); 128 | color: var(--c-text-base); 129 | box-shadow: 0 0 0 0.2rem rgba(var(--c-primary-rgb), 0.25); 130 | } 131 | } 132 | 133 | .btn-primary { 134 | background: var(--c-primary); 135 | border-color: var(--c-primary); 136 | 137 | &:hover { 138 | background: var(--c-primary-hover); 139 | border-color: var(--c-primary-hover); 140 | } 141 | 142 | &:focus { 143 | box-shadow: 0 0 0 0.2rem rgba(var(--c-primary-rgb), 0.25); 144 | } 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/signup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sign Up 7 | 55 | 56 | 57 |
58 |
59 |
60 | 61 | 69 |
70 |
71 |
72 |
73 | 74 |
75 |
76 |
77 |
78 |

Register

79 |
80 |
81 | 82 | 83 |
84 |
85 | 86 | 87 |
88 |
89 | 90 | 91 |
92 |
93 | 94 | 95 |
96 |
97 | 98 |
99 |
100 |
101 |
102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/assets/scripts/fullcalendar/index.js: -------------------------------------------------------------------------------- 1 | import { Calendar } from '@fullcalendar/core'; 2 | import interactionPlugin from '@fullcalendar/interaction'; 3 | import dayGridPlugin from '@fullcalendar/daygrid'; 4 | import timeGridPlugin from '@fullcalendar/timegrid'; 5 | import listPlugin from '@fullcalendar/list'; 6 | import DateUtils from '../utils/date'; 7 | 8 | document.addEventListener('DOMContentLoaded', function () { 9 | const calendarEl = document.getElementById('calendar'); 10 | 11 | // element found in dom ? 12 | if (calendarEl == null) { 13 | return; 14 | } 15 | 16 | const calendar = new Calendar(calendarEl, { 17 | plugins: [interactionPlugin, dayGridPlugin, timeGridPlugin, listPlugin], 18 | headerToolbar: { 19 | left: 'prev,next today', 20 | center: 'title', 21 | right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek', 22 | }, 23 | initialDate: DateUtils.format(DateUtils.now(), 'YYYY-MM-DD'), 24 | navLinks: true, // can click day/week names to navigate views 25 | editable: true, 26 | dayMaxEvents: true, // allow "more" link when too many events 27 | events: [ 28 | { 29 | title: 'All Day Event', 30 | start: DateUtils.format(DateUtils.now(), 'YYYY-MM-DD'), 31 | }, 32 | { 33 | title: 'Long Event', 34 | start: DateUtils.format(DateUtils.add(DateUtils.now(), 1, 'day'), 'YYYY-MM-DD'), 35 | end: DateUtils.format(DateUtils.add(DateUtils.now(), 4, 'day'), 'YYYY-MM-DD'), 36 | }, 37 | { 38 | groupId: 999, 39 | title: 'Repeating Event', 40 | start: DateUtils.format(DateUtils.add(DateUtils.now(), 2, 'day'), 'YYYY-MM-DDTHH:mm:ss').replace(/:\d{2}$/, ':00:00').replace(/T\d{2}:\d{2}/, 'T16:00'), 41 | }, 42 | { 43 | groupId: 999, 44 | title: 'Repeating Event', 45 | start: DateUtils.format(DateUtils.add(DateUtils.now(), 9, 'day'), 'YYYY-MM-DDTHH:mm:ss').replace(/:\d{2}$/, ':00:00').replace(/T\d{2}:\d{2}/, 'T16:00'), 46 | }, 47 | { 48 | title: 'Conference', 49 | start: DateUtils.format(DateUtils.add(DateUtils.now(), 5, 'day'), 'YYYY-MM-DD'), 50 | end: DateUtils.format(DateUtils.add(DateUtils.now(), 7, 'day'), 'YYYY-MM-DD'), 51 | }, 52 | { 53 | title: 'Meeting', 54 | start: DateUtils.format(DateUtils.add(DateUtils.now(), 3, 'day'), 'YYYY-MM-DDTHH:mm:ss').replace(/:\d{2}$/, ':00:00').replace(/T\d{2}:\d{2}/, 'T10:30'), 55 | end: DateUtils.format(DateUtils.add(DateUtils.now(), 3, 'day'), 'YYYY-MM-DDTHH:mm:ss').replace(/:\d{2}$/, ':00:00').replace(/T\d{2}:\d{2}/, 'T12:30'), 56 | }, 57 | { 58 | title: 'Lunch', 59 | start: DateUtils.format(DateUtils.add(DateUtils.now(), 3, 'day'), 'YYYY-MM-DDTHH:mm:ss').replace(/:\d{2}$/, ':00:00').replace(/T\d{2}:\d{2}/, 'T12:00'), 60 | }, 61 | { 62 | title: 'Meeting', 63 | start: DateUtils.format(DateUtils.add(DateUtils.now(), 3, 'day'), 'YYYY-MM-DDTHH:mm:ss').replace(/:\d{2}$/, ':00:00').replace(/T\d{2}:\d{2}/, 'T14:30'), 64 | }, 65 | { 66 | title: 'Happy Hour', 67 | start: DateUtils.format(DateUtils.add(DateUtils.now(), 3, 'day'), 'YYYY-MM-DDTHH:mm:ss').replace(/:\d{2}$/, ':00:00').replace(/T\d{2}:\d{2}/, 'T17:30'), 68 | }, 69 | { 70 | title: 'Dinner', 71 | start: DateUtils.format(DateUtils.add(DateUtils.now(), 3, 'day'), 'YYYY-MM-DDTHH:mm:ss').replace(/:\d{2}$/, ':00:00').replace(/T\d{2}:\d{2}/, 'T20:00'), 72 | }, 73 | { 74 | title: 'Birthday Party', 75 | start: DateUtils.format(DateUtils.add(DateUtils.now(), 4, 'day'), 'YYYY-MM-DDTHH:mm:ss').replace(/:\d{2}$/, ':00:00').replace(/T\d{2}:\d{2}/, 'T07:00'), 76 | }, 77 | { 78 | title: 'Click for Google', 79 | url: 'http://google.com/', 80 | start: DateUtils.format(DateUtils.add(DateUtils.now(), 14, 'day'), 'YYYY-MM-DD'), 81 | }, 82 | ], 83 | }); 84 | 85 | calendar.render(); 86 | }); 87 | -------------------------------------------------------------------------------- /src/signin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sign In 7 | 55 | 56 | 57 |
58 |
59 |
60 | 61 | 69 |
70 |
71 |
72 |
73 | 74 |
75 |
76 |
77 |
78 |

Login

79 |
80 |
81 | 82 | 83 |
84 |
85 | 86 | 87 |
88 |
89 |
90 |
91 |
92 | 93 | 96 |
97 |
98 |
99 | 100 |
101 |
102 |
103 |
104 |
105 |
106 | 107 | 108 | -------------------------------------------------------------------------------- /src/assets/scripts/popover/index.js: -------------------------------------------------------------------------------- 1 | // Simple vanilla JS tooltip and popover implementation 2 | export default (function () { 3 | 4 | // Simple tooltip implementation 5 | function initTooltips() { 6 | const tooltipElements = document.querySelectorAll('[data-bs-toggle="tooltip"]'); 7 | 8 | tooltipElements.forEach(element => { 9 | const tooltipText = element.getAttribute('data-bs-title') || element.getAttribute('title'); 10 | 11 | if (tooltipText) { 12 | element.addEventListener('mouseenter', function() { 13 | const tooltip = document.createElement('div'); 14 | tooltip.className = 'custom-tooltip'; 15 | tooltip.textContent = tooltipText; 16 | tooltip.style.cssText = ` 17 | position: absolute; 18 | background: #000; 19 | color: #fff; 20 | padding: 4px 8px; 21 | border-radius: 4px; 22 | font-size: 12px; 23 | z-index: 1050; 24 | pointer-events: none; 25 | white-space: nowrap; 26 | `; 27 | 28 | document.body.appendChild(tooltip); 29 | 30 | const rect = element.getBoundingClientRect(); 31 | tooltip.style.left = `${rect.left + (rect.width / 2) - (tooltip.offsetWidth / 2) }px`; 32 | tooltip.style.top = `${rect.top - tooltip.offsetHeight - 5 }px`; 33 | 34 | element._tooltip = tooltip; 35 | }); 36 | 37 | element.addEventListener('mouseleave', function() { 38 | if (element._tooltip) { 39 | element._tooltip.remove(); 40 | element._tooltip = null; 41 | } 42 | }); 43 | } 44 | }); 45 | } 46 | 47 | // Simple popover implementation 48 | function initPopovers() { 49 | const popoverElements = document.querySelectorAll('[data-bs-toggle="popover"]'); 50 | 51 | popoverElements.forEach(element => { 52 | const popoverContent = element.getAttribute('data-bs-content'); 53 | const popoverTitle = element.getAttribute('data-bs-title'); 54 | 55 | if (popoverContent) { 56 | element.addEventListener('click', function(e) { 57 | e.preventDefault(); 58 | 59 | // Remove existing popover 60 | if (element._popover) { 61 | element._popover.remove(); 62 | element._popover = null; 63 | return; 64 | } 65 | 66 | const popover = document.createElement('div'); 67 | popover.className = 'custom-popover'; 68 | popover.innerHTML = ` 69 | ${popoverTitle ? `
${popoverTitle}
` : ''} 70 |
${popoverContent}
71 | `; 72 | popover.style.cssText = ` 73 | position: absolute; 74 | background: #fff; 75 | border: 1px solid #ccc; 76 | border-radius: 6px; 77 | box-shadow: 0 2px 8px rgba(0,0,0,0.15); 78 | z-index: 1050; 79 | min-width: 200px; 80 | max-width: 300px; 81 | `; 82 | 83 | document.body.appendChild(popover); 84 | 85 | const rect = element.getBoundingClientRect(); 86 | popover.style.left = `${rect.left }px`; 87 | popover.style.top = `${rect.bottom + 5 }px`; 88 | 89 | element._popover = popover; 90 | }); 91 | } 92 | }); 93 | } 94 | 95 | // Initialize both 96 | initTooltips(); 97 | initPopovers(); 98 | 99 | // Close popovers when clicking outside 100 | document.addEventListener('click', function(e) { 101 | const popovers = document.querySelectorAll('.custom-popover'); 102 | popovers.forEach(popover => { 103 | if (!popover.contains(e.target)) { 104 | popover.remove(); 105 | } 106 | }); 107 | }); 108 | 109 | }()); 110 | -------------------------------------------------------------------------------- /src/assets/styles/vendor/jquery.datatables.scss: -------------------------------------------------------------------------------- 1 | @use '../spec/settings/baseColors' as *; 2 | @use '../spec/settings/breakpoints' as *; 3 | @use '../spec/tools/mixins/mediaQueriesRanges' as *; 4 | 5 | table { 6 | &.dataTable { 7 | background: var(--c-bkg-card); 8 | color: var(--c-text-base); 9 | 10 | &.no-footer { 11 | border-bottom: 1px solid var(--c-border); 12 | margin-bottom: 20px; 13 | } 14 | 15 | thead th { 16 | background: var(--c-bkg-card); 17 | color: var(--c-text-base); 18 | border-color: var(--c-border); 19 | } 20 | 21 | tbody td { 22 | background: var(--c-bkg-card); 23 | color: var(--c-text-base); 24 | border-color: var(--c-border); 25 | } 26 | 27 | tbody tr:nth-child(even) td { 28 | background: color-mix(in srgb, var(--c-bkg-card) 95%, var(--c-border)); 29 | } 30 | 31 | tbody tr:hover td { 32 | background: color-mix(in srgb, var(--c-bkg-card) 90%, var(--c-border)) !important; 33 | } 34 | } 35 | } 36 | 37 | .sorting_asc { 38 | &:focus { 39 | outline: none; 40 | } 41 | } 42 | 43 | .dataTables_wrapper { 44 | overflow: hidden; 45 | padding-bottom: 5px; 46 | 47 | .dataTables_length{ 48 | color: var(--c-text-base); 49 | float: left; 50 | 51 | @include to($breakpoint-sm) { 52 | text-align: left; 53 | } 54 | 55 | select { 56 | border: 1px solid var(--c-border); 57 | border-radius: 2px; 58 | box-shadow: none; 59 | height: 35px; 60 | font-size: 14px; 61 | padding: 5px; 62 | margin-left: 5px; 63 | margin-right: 5px; 64 | color: var(--c-text-base); 65 | background: var(--c-bkg-card); 66 | transition: all 0.2s ease-in; 67 | } 68 | } 69 | 70 | .dataTables_filter { 71 | color: var(--c-text-base); 72 | float: right; 73 | 74 | @include to($breakpoint-sm) { 75 | text-align: left; 76 | } 77 | 78 | input { 79 | border: 1px solid var(--c-border); 80 | border-radius: 2px; 81 | box-shadow: none; 82 | height: 35px; 83 | font-size: 14px; 84 | margin-left: 15px; 85 | padding: 5px; 86 | color: var(--c-text-base); 87 | background: var(--c-bkg-card); 88 | transition: all 0.2s ease-in; 89 | 90 | &::placeholder { 91 | color: var(--c-text-muted); 92 | } 93 | } 94 | } 95 | 96 | .dataTables_info { 97 | color: var(--c-text-base); 98 | float: left; 99 | } 100 | 101 | .dataTables_processing { 102 | color: var(--c-text-base); 103 | } 104 | 105 | .dataTables_paginate { 106 | color: var(--c-text-base); 107 | float: right; 108 | 109 | .paginate_button { 110 | color: var(--c-text-base) !important; 111 | padding: 6px 12px; 112 | border-radius: 2px; 113 | margin-right: 10px; 114 | transition: all 0.2s ease-in-out; 115 | text-decoration: none; 116 | background: var(--c-bkg-card); 117 | border: 1px solid var(--c-border); 118 | 119 | &.next, 120 | &.previous, 121 | &.first, 122 | &.last { 123 | border-radius: 2px; 124 | text-decoration: none; 125 | 126 | &:hover, 127 | &:focus { 128 | color: #fff !important; 129 | background: var(--c-primary); 130 | } 131 | 132 | &.disabled { 133 | opacity: 0.4; 134 | pointer-events: none; 135 | } 136 | } 137 | 138 | &:hover { 139 | color: #fff !important; 140 | background: var(--c-primary); 141 | border-color: var(--c-primary); 142 | } 143 | 144 | &.current { 145 | color: #fff !important; 146 | background: var(--c-primary); 147 | border-color: var(--c-primary); 148 | 149 | &:hover { 150 | color: #fff !important; 151 | background: var(--c-primary); 152 | } 153 | } 154 | } 155 | } 156 | 157 | .status { 158 | width: 5px; 159 | height: 5px; 160 | } 161 | } 162 | 163 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adminator-admin-dashboard", 3 | "version": "2.9.0", 4 | "private": false, 5 | "description": "Modern jQuery-free Bootstrap 5 Admin Dashboard Template with Dark Mode", 6 | "main": "dist/index.html", 7 | "keywords": [ 8 | "admin", 9 | "dashboard", 10 | "template", 11 | "bootstrap", 12 | "bootstrap5", 13 | "jquery-free", 14 | "vanilla-js", 15 | "dark-mode", 16 | "responsive", 17 | "mobile-first", 18 | "scss", 19 | "webpack", 20 | "modern", 21 | "ui-kit", 22 | "admin-panel", 23 | "charts", 24 | "datatable" 25 | ], 26 | "author": "Aigars Silkalns (https://colorlib.com)", 27 | "license": "MIT", 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/puikinsh/Adminator-admin-dashboard.git" 31 | }, 32 | "bugs": { 33 | "url": "https://github.com/puikinsh/Adminator-admin-dashboard/issues" 34 | }, 35 | "homepage": "https://puikinsh.github.io/Adminator-admin-dashboard/", 36 | "files": [ 37 | "dist/**/*", 38 | "src/**/*", 39 | "webpack.config.js", 40 | "CLAUDE.md", 41 | "CHANGELOG.md", 42 | "README.md" 43 | ], 44 | "engines": { 45 | "node": ">=14.0.0" 46 | }, 47 | "scripts": { 48 | "start": "webpack server", 49 | "dev": "webpack-dashboard -t 'Project' -- webpack server", 50 | "clean": "shx rm -rf ./dist", 51 | "build": "npm run clean && cross-env webpack", 52 | "release:minified": "npm run clean && NODE_ENV=production MINIFY=true cross-env webpack", 53 | "release:unminified": "npm run clean && NODE_ENV=production MINIFY=false cross-env webpack", 54 | "preview": "cross-env webpack server", 55 | "lint:js": "eslint ./src ./webpack ./*.js -f table --ext .js --ext .jsx", 56 | "lint:scss": "stylelint ./src/**/*.scss", 57 | "lint": "npm run lint:js && npm run lint:scss", 58 | "prepublishOnly": "npm run lint && npm run build", 59 | "postpublish": "echo 'Package published successfully! View at: https://www.npmjs.com/package/adminator-admin-dashboard'" 60 | }, 61 | "devDependencies": { 62 | "@babel/core": "^7.28.5", 63 | "@babel/eslint-parser": "^7.28.5", 64 | "@babel/plugin-transform-runtime": "^7.28.5", 65 | "@babel/preset-env": "^7.28.5", 66 | "@eslint/js": "^9.39.1", 67 | "@typescript-eslint/eslint-plugin": "^8.48.1", 68 | "@typescript-eslint/parser": "^8.48.1", 69 | "babel-loader": "^10.0.0", 70 | "case-sensitive-paths-webpack-plugin": "^2.4.0", 71 | "copy-webpack-plugin": "^13.0.1", 72 | "cross-env": "^10.1.0", 73 | "css-loader": "^7.1.2", 74 | "css-minimizer-webpack-plugin": "^7.0.2", 75 | "eslint": "^9.39.1", 76 | "eslint-formatter-table": "^7.32.1", 77 | "globals": "^16.5.0", 78 | "html-webpack-plugin": "^5.6.5", 79 | "mini-css-extract-plugin": "^2.9.4", 80 | "postcss": "^8.5.6", 81 | "postcss-loader": "^8.2.0", 82 | "postcss-preset-env": "^10.4.0", 83 | "sass": "^1.94.2", 84 | "sass-loader": "^16.0.6", 85 | "shx": "^0.4.0", 86 | "style-loader": "^4.0.0", 87 | "stylelint": "^16.26.1", 88 | "stylelint-config-standard": "^39.0.1", 89 | "stylelint-config-standard-scss": "^16.0.0", 90 | "typescript": "^5.9.3", 91 | "webpack": "^5.103.0", 92 | "webpack-cli": "^6.0.1", 93 | "webpack-dashboard": "^3.3.8", 94 | "webpack-dev-server": "^5.2.2" 95 | }, 96 | "dependencies": { 97 | "@babel/runtime": "^7.28.4", 98 | "@fullcalendar/core": "^6.1.19", 99 | "@fullcalendar/daygrid": "^6.1.19", 100 | "@fullcalendar/interaction": "^6.1.19", 101 | "@fullcalendar/list": "^6.1.19", 102 | "@fullcalendar/timegrid": "^6.1.19", 103 | "@popperjs/core": "^2.11.8", 104 | "bootstrap": "^5.3.8", 105 | "brand-colors": "^2.1.1", 106 | "chart.js": "^4.5.1", 107 | "dayjs": "^1.11.19", 108 | "jsvectormap": "^1.7.0", 109 | "load-google-maps-api": "^2.0.2", 110 | "lodash": "^4.17.21", 111 | "masonry-layout": "^4.2.2", 112 | "perfect-scrollbar": "^1.5.6", 113 | "skycons": "^1.0.0", 114 | "ts-api-utils": "^2.1.0" 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /docs/_sass/custom.scss: -------------------------------------------------------------------------------- 1 | // Custom styles for Adminator Documentation 2 | 3 | // Fix code block formatting 4 | .highlight { 5 | .language-javascript, 6 | .language-css, 7 | .language-html, 8 | .language-bash { 9 | &:before { 10 | content: attr(class); 11 | display: block; 12 | font-size: 0.75rem; 13 | font-weight: 600; 14 | color: #6b7280; 15 | text-transform: uppercase; 16 | letter-spacing: 0.05em; 17 | margin-bottom: 0.5rem; 18 | padding-bottom: 0.25rem; 19 | border-bottom: 1px solid #e5e7eb; 20 | } 21 | } 22 | } 23 | 24 | // Better code block styling 25 | pre.highlight { 26 | background-color: #f8fafc !important; 27 | border: 1px solid #e2e8f0; 28 | border-radius: 6px; 29 | font-size: 0.875rem; 30 | line-height: 1.5; 31 | overflow-x: auto; 32 | 33 | code { 34 | background: transparent !important; 35 | border: none !important; 36 | font-family: 'SFMono-Regular', 'Menlo', 'Monaco', 'Consolas', 'Liberation Mono', 'Courier New', monospace; 37 | } 38 | } 39 | 40 | // Inline code styling 41 | code:not(.highlighter-rouge) { 42 | background-color: #f1f5f9 !important; 43 | color: #475569 !important; 44 | padding: 0.125rem 0.25rem !important; 45 | border-radius: 3px !important; 46 | font-size: 0.875em !important; 47 | border: 1px solid #e2e8f0 !important; 48 | } 49 | 50 | // Feature cards styling 51 | .feature-grid { 52 | display: grid; 53 | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); 54 | gap: 1.5rem; 55 | margin: 2rem 0; 56 | } 57 | 58 | .feature-card { 59 | padding: 1.5rem; 60 | border: 1px solid #e5e7eb; 61 | border-radius: 8px; 62 | background: #ffffff; 63 | 64 | h3 { 65 | margin-top: 0; 66 | color: #1f2937; 67 | display: flex; 68 | align-items: center; 69 | gap: 0.5rem; 70 | 71 | &:before { 72 | content: "✨"; 73 | font-size: 1.25em; 74 | } 75 | } 76 | } 77 | 78 | // Button improvements 79 | .btn { 80 | &.btn-outline { 81 | border: 1px solid #d1d5db; 82 | color: #374151; 83 | text-decoration: none; 84 | 85 | &:hover { 86 | background-color: #f9fafb; 87 | border-color: #9ca3af; 88 | } 89 | } 90 | 91 | &.btn-green { 92 | background-color: #059669; 93 | color: white; 94 | border: 1px solid #059669; 95 | 96 | &:hover { 97 | background-color: #047857; 98 | border-color: #047857; 99 | } 100 | } 101 | } 102 | 103 | // Table styling improvements 104 | table { 105 | border-collapse: collapse; 106 | width: 100%; 107 | margin: 1.5rem 0; 108 | 109 | th, td { 110 | padding: 0.75rem 1rem; 111 | text-align: left; 112 | border-bottom: 1px solid #e5e7eb; 113 | } 114 | 115 | th { 116 | background-color: #f9fafb; 117 | font-weight: 600; 118 | color: #374151; 119 | } 120 | 121 | tr:hover { 122 | background-color: #f9fafb; 123 | } 124 | } 125 | 126 | // Navigation improvements 127 | .site-nav { 128 | .nav-list { 129 | .nav-list-item { 130 | &.active { 131 | > .nav-list-link { 132 | background-color: #eff6ff; 133 | color: #1d4ed8; 134 | } 135 | } 136 | } 137 | } 138 | } 139 | 140 | // Custom alert boxes 141 | .alert { 142 | padding: 1rem 1.25rem; 143 | margin: 1.5rem 0; 144 | border-radius: 6px; 145 | border-left: 4px solid; 146 | 147 | &.alert-info { 148 | background-color: #eff6ff; 149 | border-left-color: #3b82f6; 150 | color: #1e40af; 151 | } 152 | 153 | &.alert-warning { 154 | background-color: #fffbeb; 155 | border-left-color: #f59e0b; 156 | color: #92400e; 157 | } 158 | 159 | &.alert-success { 160 | background-color: #f0fdf4; 161 | border-left-color: #10b981; 162 | color: #065f46; 163 | } 164 | } 165 | 166 | // Dark mode improvements for code 167 | @media (prefers-color-scheme: dark) { 168 | pre.highlight { 169 | background-color: #1f2937 !important; 170 | border-color: #374151; 171 | 172 | code { 173 | color: #e5e7eb !important; 174 | } 175 | } 176 | 177 | code:not(.highlighter-rouge) { 178 | background-color: #374151 !important; 179 | color: #d1d5db !important; 180 | border-color: #4b5563 !important; 181 | } 182 | } -------------------------------------------------------------------------------- /src/assets/scripts/utils/theme.js: -------------------------------------------------------------------------------- 1 | /* global Chart */ 2 | const THEME_KEY = 'adminator-theme'; 3 | 4 | const Theme = { 5 | apply(theme) { 6 | document.documentElement.setAttribute('data-theme', theme); 7 | if (window.Chart && Chart.defaults) { 8 | const isDark = theme === 'dark'; 9 | const textColor = isDark ? '#FFFFFF' : '#212529'; 10 | const mutedColor = isDark ? '#D1D5DB' : '#6C757D'; 11 | const borderColor = isDark ? '#374151' : '#E2E5E8'; 12 | const gridColor = isDark ? 'rgba(209, 213, 219, 0.15)' : 'rgba(0, 0, 0, 0.05)'; 13 | const tooltipBg = isDark ? '#1F2937' : 'rgba(255, 255, 255, 0.95)'; 14 | 15 | // Set global defaults 16 | Chart.defaults.color = textColor; 17 | Chart.defaults.borderColor = borderColor; 18 | Chart.defaults.backgroundColor = tooltipBg; 19 | 20 | // Set plugin defaults 21 | Chart.defaults.plugins.legend.labels.color = textColor; 22 | Chart.defaults.plugins.tooltip.backgroundColor = tooltipBg; 23 | Chart.defaults.plugins.tooltip.titleColor = textColor; 24 | Chart.defaults.plugins.tooltip.bodyColor = textColor; 25 | Chart.defaults.plugins.tooltip.borderColor = borderColor; 26 | 27 | // Set scale defaults 28 | Chart.defaults.scales.category.ticks.color = mutedColor; 29 | Chart.defaults.scales.category.grid.color = gridColor; 30 | Chart.defaults.scales.linear.ticks.color = mutedColor; 31 | Chart.defaults.scales.linear.grid.color = gridColor; 32 | Chart.defaults.scales.logarithmic.ticks.color = mutedColor; 33 | Chart.defaults.scales.logarithmic.grid.color = gridColor; 34 | Chart.defaults.scales.time.ticks.color = mutedColor; 35 | Chart.defaults.scales.time.grid.color = gridColor; 36 | Chart.defaults.scales.radialLinear.ticks.color = mutedColor; 37 | Chart.defaults.scales.radialLinear.grid.color = gridColor; 38 | Chart.defaults.scales.radialLinear.pointLabels.color = mutedColor; 39 | Chart.defaults.scales.radialLinear.angleLines.color = gridColor; 40 | } 41 | try { 42 | localStorage.setItem(THEME_KEY, theme); 43 | } catch { 44 | // Ignore errors 45 | } 46 | window.dispatchEvent(new CustomEvent('adminator:themeChanged', { detail: { theme } })); 47 | }, 48 | toggle() { 49 | const next = this.current() === 'dark' ? 'light' : 'dark'; 50 | this.apply(next); 51 | }, 52 | current() { 53 | try { 54 | return localStorage.getItem(THEME_KEY) || 'light'; 55 | } catch { 56 | return 'light'; 57 | } 58 | }, 59 | init() { 60 | // Detect OS preference first time 61 | if (!localStorage.getItem(THEME_KEY)) { 62 | const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; 63 | this.apply(prefersDark ? 'dark' : 'light'); 64 | } else { 65 | this.apply(this.current()); 66 | } 67 | }, 68 | getCSSVar(varName) { 69 | return getComputedStyle(document.documentElement).getPropertyValue(varName).trim(); 70 | }, 71 | getVectorMapColors() { 72 | return { 73 | backgroundColor: this.getCSSVar('--vmap-bg-color'), 74 | borderColor: this.getCSSVar('--vmap-border-color'), 75 | regionColor: this.getCSSVar('--vmap-region-color'), 76 | markerFill: this.getCSSVar('--vmap-marker-fill'), 77 | markerStroke: this.getCSSVar('--vmap-marker-stroke'), 78 | hoverColor: this.getCSSVar('--vmap-hover-color'), 79 | selectedColor: this.getCSSVar('--vmap-selected-color'), 80 | scaleStart: this.getCSSVar('--vmap-scale-start'), 81 | scaleEnd: this.getCSSVar('--vmap-scale-end'), 82 | scaleLight: this.getCSSVar('--vmap-scale-light'), 83 | scaleDark: this.getCSSVar('--vmap-scale-dark'), 84 | }; 85 | }, 86 | getSparklineColors() { 87 | return { 88 | success: this.getCSSVar('--sparkline-success'), 89 | purple: this.getCSSVar('--sparkline-purple'), 90 | info: this.getCSSVar('--sparkline-info'), 91 | danger: this.getCSSVar('--sparkline-danger'), 92 | light: this.getCSSVar('--sparkline-light'), 93 | }; 94 | }, 95 | getChartColors() { 96 | const isDark = this.current() === 'dark'; 97 | return { 98 | textColor: isDark ? '#FFFFFF' : '#212529', 99 | mutedColor: isDark ? '#D1D5DB' : '#6C757D', 100 | borderColor: isDark ? '#374151' : '#E2E5E8', 101 | gridColor: isDark ? 'rgba(209, 213, 219, 0.15)' : 'rgba(0, 0, 0, 0.05)', 102 | tooltipBg: isDark ? '#1F2937' : 'rgba(255, 255, 255, 0.95)', 103 | }; 104 | }, 105 | }; 106 | 107 | export default Theme; -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/helpers/typography.scss: -------------------------------------------------------------------------------- 1 | @use '../mixins/generateResponsive' as *; 2 | 3 | // --------------------------------------------------------- 4 | // @TOC 5 | // --------------------------------------------------------- 6 | 7 | // + @Variables 8 | // + @Text Align 9 | // + @Text Transform 10 | // + @Font Style 11 | // + @Text Decoration 12 | // + @White Space 13 | // + @Word Break 14 | // + @Word Wrap 15 | // + @Text Overflow 16 | // + @Font Size 17 | // + @Font Weight 18 | // + @Line Height 19 | 20 | // --------------------------------------------------------- 21 | // @Variables 22 | // --------------------------------------------------------- 23 | 24 | $responsive: true; 25 | 26 | // --------------------------------------------------------- 27 | // @Text Align 28 | // --------------------------------------------------------- 29 | 30 | .ta-c { text-align: center !important; } 31 | .ta-l { text-align: left !important; } 32 | .ta-r { text-align: right !important; } 33 | 34 | @if ($responsive == true) { 35 | @include generateResponsive() { 36 | .ta-c\@#{$breakpointAlias} { text-align: center !important; } 37 | .ta-l\@#{$breakpointAlias} { text-align: left !important; } 38 | .ta-r\@#{$breakpointAlias} { text-align: right !important; } 39 | } 40 | } 41 | 42 | // --------------------------------------------------------- 43 | // @Text Transform 44 | // --------------------------------------------------------- 45 | 46 | .tt-n { text-transform: none !important; } 47 | .tt-u { text-transform: uppercase !important; } 48 | .tt-l { text-transform: lowercase !important; } 49 | .tt-c { text-transform: capitalize !important; } 50 | 51 | // --------------------------------------------------------- 52 | // @Font Style 53 | // --------------------------------------------------------- 54 | 55 | .fs-i { font-style: italic !important; } 56 | .fs-o { font-style: oblique !important; } 57 | 58 | // --------------------------------------------------------- 59 | // @Text Decoration 60 | // --------------------------------------------------------- 61 | 62 | .td-n { text-decoration: none !important; } 63 | .td-o { text-decoration: overline !important; } 64 | .td-lt { text-decoration: line-through !important; } 65 | .td-u { text-decoration: underline !important; } 66 | 67 | // --------------------------------------------------------- 68 | // @White Space 69 | // --------------------------------------------------------- 70 | 71 | .whs-nw { white-space: nowrap !important; } 72 | .whs-p { white-space: pre !important; } 73 | .whs-n { white-space: normal !important; } 74 | 75 | // --------------------------------------------------------- 76 | // @Word Break 77 | // --------------------------------------------------------- 78 | 79 | .wob-n { word-break: normal !important; } 80 | .wob-ba { word-break: break-all !important; } 81 | .wob-k { word-break: keep-all !important; } 82 | 83 | // --------------------------------------------------------- 84 | // @Word Wrap 85 | // --------------------------------------------------------- 86 | 87 | .wow-bw { word-wrap: break-word !important; } 88 | .wow-n { word-wrap: normal !important; } 89 | 90 | // --------------------------------------------------------- 91 | // @Text Overflow 92 | // --------------------------------------------------------- 93 | 94 | .tov-e { text-overflow: ellipsis !important; } 95 | 96 | // --------------------------------------------------------- 97 | // @Font Size 98 | // --------------------------------------------------------- 99 | 100 | .fsz-xs { font-size: 0.75rem !important; } 101 | .fsz-sm { font-size: 0.87rem !important; } 102 | .fsz-def { font-size: 1rem !important; } 103 | .fsz-md { font-size: 1.15rem !important; } 104 | .fsz-lg { font-size: 1.4rem !important; } 105 | .fsz-xl { font-size: 1.7rem !important; } 106 | 107 | // --------------------------------------------------------- 108 | // @Font Weight 109 | // --------------------------------------------------------- 110 | 111 | .fw-100 { font-weight: 100 !important; } 112 | .fw-200 { font-weight: 200 !important; } 113 | .fw-300 { font-weight: 300 !important; } 114 | .fw-400 { font-weight: 400 !important; } 115 | .fw-500 { font-weight: 500 !important; } 116 | .fw-600 { font-weight: 600 !important; } 117 | .fw-700 { font-weight: 700 !important; } 118 | .fw-800 { font-weight: 800 !important; } 119 | .fw-900 { font-weight: 900 !important; } 120 | 121 | // --------------------------------------------------------- 122 | // @Line Height 123 | // --------------------------------------------------------- 124 | 125 | .lh-0 { line-height: 0 !important; } 126 | .lh-1 { line-height: 1 !important; } 127 | .lh-3\/2 { line-height: 1.5 !important; } 128 | -------------------------------------------------------------------------------- /src/assets/styles/vendor/datepicker.scss: -------------------------------------------------------------------------------- 1 | @use 'sass:color'; 2 | @use '../spec/settings/baseColors' as *; 3 | @use '../spec/settings/breakpoints' as *; 4 | @use '../spec/tools/mixins/mediaQueriesRanges' as *; 5 | 6 | .datepicker { 7 | border-radius: 0; 8 | padding: 25px; 9 | box-shadow: none; 10 | border: 1px solid $border-color; 11 | 12 | table { 13 | tr { 14 | th, 15 | td { 16 | border-radius: 0; 17 | width: 40px; 18 | height: 35px; 19 | } 20 | 21 | td { 22 | transition: all 0.2s ease-in-out; 23 | 24 | span { 25 | border-radius: 0; 26 | } 27 | } 28 | } 29 | } 30 | } 31 | 32 | .datepicker table tr td span.active:active, 33 | .datepicker table tr td span.active:hover:active, 34 | .datepicker table tr td span.active.disabled:active, 35 | .datepicker table tr td span.active.disabled:hover:active, 36 | .datepicker table tr td span.active.active, 37 | .datepicker table tr td span.active:hover.active, 38 | .datepicker table tr td span.active.disabled.active, 39 | .datepicker table tr td span.active.disabled:hover.active, 40 | .datepicker table tr td span.active.active:hover, 41 | .datepicker table tr td span.active:hover.active:hover, 42 | .datepicker table tr td.active:active, 43 | .datepicker table tr td.active.highlighted:active, 44 | .datepicker table tr td.active.active, 45 | .datepicker table tr td.active.highlighted.active, 46 | .datepicker table tr td.active:active:hover, 47 | .datepicker table tr td.active.highlighted:active:hover, 48 | .datepicker table tr td.active.active:hover, 49 | .datepicker table tr td.active.highlighted.active:hover, 50 | .datepicker table tr td.active:active:focus, 51 | .datepicker table tr td.active.highlighted:active:focus, 52 | .datepicker table tr td.active.active:focus, 53 | .datepicker table tr td.active.highlighted.active:focus, 54 | .datepicker table tr td.active:active.focus, 55 | .datepicker table tr td.active.highlighted:active.focus, 56 | .datepicker table tr td.active.active.focus, 57 | .datepicker table tr td.active.highlighted.active.focus { 58 | color: $default-white; 59 | background-color: $default-primary; 60 | border-color: transparent; 61 | } 62 | 63 | .datepicker table tr td span:hover, 64 | .datepicker table tr td span.focused { 65 | background: $default-primary; 66 | color: #fff; 67 | } 68 | 69 | .datepicker table tr td.day:hover, 70 | .datepicker table tr td.focused { 71 | background: $default-primary; 72 | color: #fff; 73 | cursor: pointer; 74 | } 75 | 76 | .datepicker .datepicker-switch:hover, 77 | .datepicker .prev:hover, 78 | .datepicker .next:hover, 79 | .datepicker tfoot tr th:hover { 80 | background: $default-primary; 81 | color: #fff; 82 | cursor: pointer; 83 | } 84 | 85 | .datepicker-inline { 86 | width: 330px; 87 | } 88 | 89 | .daterangepicker { 90 | border-radius: 0; 91 | padding: 30px; 92 | box-shadow: none; 93 | border: 1px solid $border-color; 94 | 95 | .input-mini { 96 | border-radius: 0; 97 | margin-bottom: 20px; 98 | height: 40px; 99 | padding: 0 6px 0 35px; 100 | 101 | &.active { 102 | border-radius: 0; 103 | border-color: color.adjust($default-info, $lightness: 20%); 104 | } 105 | } 106 | 107 | .daterangepicker_input { 108 | i { 109 | position: absolute; 110 | left: 10px; 111 | top: 13px; 112 | } 113 | } 114 | 115 | td, 116 | th { 117 | border-radius: 0; 118 | width: 40px; 119 | height: 35px; 120 | 121 | &.available{ 122 | &:hover{ 123 | background: $default-primary; 124 | color: #fff; 125 | } 126 | } 127 | } 128 | 129 | td { 130 | &.in-range { 131 | background-color: transparent; 132 | color: $default-primary; 133 | } 134 | 135 | &.active { 136 | background-color: $default-primary; 137 | border-color: transparent; 138 | color: #fff; 139 | 140 | &:hover { 141 | background-color: $default-primary; 142 | border-color: transparent; 143 | color: #fff; 144 | } 145 | } 146 | 147 | &.start-date { 148 | border-radius: 0; 149 | 150 | &.end-date { 151 | border-radius: 0; 152 | } 153 | } 154 | 155 | &.end-date { 156 | border-radius: 0; 157 | } 158 | } 159 | 160 | select { 161 | &.hourselect, 162 | &.minuteselect, 163 | &.secondselect, 164 | &.ampmselect { 165 | border: 1px solid $border-color; 166 | min-height: 30px; 167 | } 168 | } 169 | 170 | .calendar-time { 171 | i { 172 | top: 8px; 173 | left: 35px; 174 | } 175 | } 176 | 177 | @include from($breakpoint-sm) { 178 | .calendar { 179 | margin-right: 20px !important; 180 | } 181 | } 182 | } 183 | 184 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Adminator Documentation 2 | 3 | This directory contains the complete documentation for Adminator Bootstrap 5 Admin Template. 4 | 5 | ## 📁 Documentation Structure 6 | 7 | ``` 8 | docs/ 9 | ├── index.md # Homepage 10 | ├── _config.yml # GitHub Pages configuration 11 | ├── getting-started/ # Installation and setup guides 12 | │ ├── installation.md 13 | │ ├── project-structure.md 14 | │ ├── development.md 15 | │ └── build-deployment.md 16 | ├── components/ # Component documentation 17 | │ ├── charts.md 18 | │ ├── forms.md 19 | │ ├── tables.md 20 | │ ├── navigation.md 21 | │ └── modals.md 22 | ├── customization/ # Theme and styling guides 23 | │ ├── theme-system.md 24 | │ ├── css-variables.md 25 | │ ├── custom-themes.md 26 | │ └── component-theming.md 27 | ├── api/ # API reference 28 | │ ├── theme-api.md 29 | │ ├── component-apis.md 30 | │ ├── utilities.md 31 | │ └── events.md 32 | ├── examples/ # Practical examples 33 | │ ├── basic-setup.md 34 | │ ├── custom-components.md 35 | │ ├── theme-integration.md 36 | │ └── advanced-patterns.md 37 | ├── deployment/ # Production deployment 38 | │ ├── production-build.md 39 | │ ├── static-hosting.md 40 | │ ├── cdn-integration.md 41 | │ └── performance.md 42 | └── contributing/ # Contribution guidelines 43 | ├── development-setup.md 44 | ├── code-standards.md 45 | ├── pull-requests.md 46 | └── issues.md 47 | ``` 48 | 49 | ## 🚀 Hosting Strategy 50 | 51 | ### **GitHub Pages Setup** 52 | 53 | 1. **Main Branch Integration**: Documentation lives in the `docs/` folder of the main branch 54 | 2. **Automatic Deployment**: GitHub Pages automatically builds and deploys on every commit 55 | 3. **Custom Domain Support**: Can be configured with custom domains 56 | 4. **Jekyll Integration**: Uses Jekyll static site generator with the Minima theme 57 | 58 | ### **Benefits of This Approach** 59 | 60 | ✅ **Version Control**: Docs stay in sync with code releases 61 | ✅ **Free Hosting**: GitHub Pages provides free, reliable hosting 62 | ✅ **Easy Discovery**: Users find docs directly in the repository 63 | ✅ **SEO Friendly**: Searchable and indexable documentation 64 | ✅ **Collaboration**: Easy for contributors to update docs 65 | ✅ **Professional URLs**: Clean URLs like `username.github.io/repo/` 66 | 67 | ## 📖 Documentation Sections 68 | 69 | ### **🏁 Getting Started** 70 | Complete setup and installation guides for new users. 71 | 72 | ### **🎨 Components** 73 | Detailed documentation for all UI components and their usage. 74 | 75 | ### **🌙 Dark Mode & Theming** 76 | Comprehensive guide to the theme system and customization options. 77 | 78 | ### **🔧 API Reference** 79 | Complete API documentation for all JavaScript utilities and components. 80 | 81 | ### **💡 Examples** 82 | Practical, copy-paste examples for common use cases. 83 | 84 | ### **🚀 Deployment** 85 | Production deployment guides and performance optimization tips. 86 | 87 | ### **🤝 Contributing** 88 | Guidelines for contributing to the project. 89 | 90 | ## 🔧 Local Development 91 | 92 | To view the documentation locally: 93 | 94 | ```bash 95 | # Install Jekyll (if not already installed) 96 | gem install bundler jekyll 97 | 98 | # Navigate to docs directory 99 | cd docs 100 | 101 | # Install dependencies 102 | bundle install 103 | 104 | # Serve locally 105 | bundle exec jekyll serve 106 | 107 | # Visit http://localhost:4000 108 | ``` 109 | 110 | ## 📝 Writing Documentation 111 | 112 | ### **Markdown Guidelines** 113 | 114 | - Use clear, descriptive headings 115 | - Include code examples for all features 116 | - Add cross-references between related sections 117 | - Use emoji for visual appeal (sparingly) 118 | - Include "Next Steps" sections to guide readers 119 | 120 | ### **Code Examples** 121 | 122 | ```markdown 123 | \```javascript 124 | // Always include working code examples 125 | const example = Theme.current(); 126 | console.log(example); 127 | \``` 128 | ``` 129 | 130 | ### **File Naming** 131 | 132 | - Use lowercase with hyphens: `theme-system.md` 133 | - Be descriptive but concise 134 | - Group related files in subdirectories 135 | 136 | ## 🔗 Quick Links 137 | 138 | - **[Live Documentation](https://puikinsh.github.io/Adminator-admin-dashboard/)** (Coming Soon) 139 | - **[GitHub Repository](https://github.com/puikinsh/Adminator-admin-dashboard)** 140 | - **[Live Demo](https://colorlib.com/polygon/adminator/index.html)** 141 | 142 | ## 📞 Support 143 | 144 | For documentation issues: 145 | - Open an issue with the `documentation` label 146 | - Suggest improvements via pull requests 147 | - Join discussions for larger documentation changes 148 | 149 | --- 150 | 151 | **Happy documenting!** 📚 -------------------------------------------------------------------------------- /src/assets/scripts/charts/chartJS/index.js: -------------------------------------------------------------------------------- 1 | import Chart from 'chart.js/auto'; 2 | import { COLORS } from '../../constants/colors'; 3 | 4 | export default (function () { 5 | // ------------------------------------------------------ 6 | // @Line Charts 7 | // ------------------------------------------------------ 8 | 9 | const lineChartBox = document.getElementById('line-chart'); 10 | 11 | if (lineChartBox) { 12 | const lineCtx = lineChartBox.getContext('2d'); 13 | lineChartBox.height = 80; 14 | 15 | new Chart(lineCtx, { 16 | type: 'line', 17 | data: { 18 | labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], 19 | datasets: [{ 20 | label : 'Series A', 21 | backgroundColor : 'rgba(237, 231, 246, 0.5)', 22 | borderColor : COLORS['deep-purple-500'], 23 | pointBackgroundColor : COLORS['deep-purple-700'], 24 | borderWidth : 2, 25 | data : [60, 50, 70, 60, 50, 70, 60], 26 | }, { 27 | label : 'Series B', 28 | backgroundColor : 'rgba(232, 245, 233, 0.5)', 29 | borderColor : COLORS['blue-500'], 30 | pointBackgroundColor : COLORS['blue-700'], 31 | borderWidth : 2, 32 | data : [70, 75, 85, 70, 75, 85, 70], 33 | }], 34 | }, 35 | 36 | options: { 37 | legend: { 38 | display: false, 39 | }, 40 | }, 41 | 42 | }); 43 | } 44 | 45 | // ------------------------------------------------------ 46 | // @Bar Charts 47 | // ------------------------------------------------------ 48 | 49 | const barChartBox = document.getElementById('bar-chart'); 50 | 51 | if (barChartBox) { 52 | const barCtx = barChartBox.getContext('2d'); 53 | 54 | new Chart(barCtx, { 55 | type: 'bar', 56 | data: { 57 | labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], 58 | datasets: [{ 59 | label : 'Dataset 1', 60 | backgroundColor : COLORS['deep-purple-500'], 61 | borderColor : COLORS['deep-purple-800'], 62 | borderWidth : 1, 63 | data : [10, 50, 20, 40, 60, 30, 70], 64 | }, { 65 | label : 'Dataset 2', 66 | backgroundColor : COLORS['light-blue-500'], 67 | borderColor : COLORS['light-blue-800'], 68 | borderWidth : 1, 69 | data : [10, 50, 20, 40, 60, 30, 70], 70 | }], 71 | }, 72 | 73 | options: { 74 | responsive: true, 75 | legend: { 76 | position: 'bottom', 77 | }, 78 | }, 79 | }); 80 | } 81 | 82 | // ------------------------------------------------------ 83 | // @Area Charts 84 | // ------------------------------------------------------ 85 | 86 | const areaChartBox = document.getElementById('area-chart'); 87 | 88 | if (areaChartBox) { 89 | const areaCtx = areaChartBox.getContext('2d'); 90 | 91 | new Chart(areaCtx, { 92 | type: 'line', 93 | data: { 94 | labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], 95 | datasets: [{ 96 | backgroundColor : 'rgba(3, 169, 244, 0.5)', 97 | borderColor : COLORS['light-blue-800'], 98 | data : [10, 50, 20, 40, 60, 30, 70], 99 | label : 'Dataset', 100 | fill : 'start', 101 | }], 102 | }, 103 | }); 104 | } 105 | 106 | // ------------------------------------------------------ 107 | // @Scatter Charts 108 | // ------------------------------------------------------ 109 | 110 | const scatterChartBox = document.getElementById('scatter-chart'); 111 | 112 | if (scatterChartBox) { 113 | const scatterCtx = scatterChartBox.getContext('2d'); 114 | 115 | new Chart(scatterCtx, { 116 | type: 'scatter', 117 | data: { 118 | datasets: [{ 119 | label : 'My First dataset', 120 | borderColor : COLORS['red-500'], 121 | backgroundColor : COLORS['red-500'], 122 | data: [ 123 | { x: 10, y: 20 }, 124 | { x: 30, y: 40 }, 125 | { x: 50, y: 60 }, 126 | { x: 70, y: 80 }, 127 | { x: 90, y: 100 }, 128 | { x: 110, y: 120 }, 129 | { x: 130, y: 140 }, 130 | ], 131 | }, { 132 | label : 'My Second dataset', 133 | borderColor : COLORS['green-500'], 134 | backgroundColor : COLORS['green-500'], 135 | data: [ 136 | { x: 150, y: 160 }, 137 | { x: 170, y: 180 }, 138 | { x: 190, y: 200 }, 139 | { x: 210, y: 220 }, 140 | { x: 230, y: 240 }, 141 | { x: 250, y: 260 }, 142 | { x: 270, y: 280 }, 143 | ], 144 | }], 145 | }, 146 | }); 147 | } 148 | }()) 149 | -------------------------------------------------------------------------------- /src/assets/scripts/sidebar/index.js: -------------------------------------------------------------------------------- 1 | // Vanilla JS slide animations 2 | function slideUp(element, duration = 200, callback = null) { 3 | element.style.height = `${element.scrollHeight }px`; 4 | element.style.transition = `height ${duration}ms ease`; 5 | element.style.overflow = 'hidden'; 6 | 7 | requestAnimationFrame(() => { 8 | element.style.height = '0'; 9 | element.style.paddingTop = '0'; 10 | element.style.paddingBottom = '0'; 11 | element.style.marginTop = '0'; 12 | element.style.marginBottom = '0'; 13 | }); 14 | 15 | setTimeout(() => { 16 | element.style.display = 'none'; 17 | element.style.removeProperty('height'); 18 | element.style.removeProperty('padding-top'); 19 | element.style.removeProperty('padding-bottom'); 20 | element.style.removeProperty('margin-top'); 21 | element.style.removeProperty('margin-bottom'); 22 | element.style.removeProperty('overflow'); 23 | element.style.removeProperty('transition'); 24 | if (callback) callback(); 25 | }, duration); 26 | } 27 | 28 | function slideDown(element, duration = 200, callback = null) { 29 | element.style.removeProperty('display'); 30 | let display = window.getComputedStyle(element).display; 31 | if (display === 'none') display = 'block'; 32 | 33 | element.style.display = display; 34 | element.style.height = '0'; 35 | element.style.paddingTop = '0'; 36 | element.style.paddingBottom = '0'; 37 | element.style.marginTop = '0'; 38 | element.style.marginBottom = '0'; 39 | element.style.overflow = 'hidden'; 40 | 41 | const height = element.scrollHeight; 42 | 43 | element.style.transition = `height ${duration}ms ease`; 44 | 45 | requestAnimationFrame(() => { 46 | element.style.height = `${height }px`; 47 | element.style.removeProperty('padding-top'); 48 | element.style.removeProperty('padding-bottom'); 49 | element.style.removeProperty('margin-top'); 50 | element.style.removeProperty('margin-bottom'); 51 | }); 52 | 53 | setTimeout(() => { 54 | element.style.removeProperty('height'); 55 | element.style.removeProperty('overflow'); 56 | element.style.removeProperty('transition'); 57 | if (callback) callback(); 58 | }, duration); 59 | } 60 | 61 | export default (function () { 62 | // Sidebar links 63 | const sidebarLinks = document.querySelectorAll('.sidebar .sidebar-menu li a'); 64 | 65 | sidebarLinks.forEach(link => { 66 | link.addEventListener('click', function () { 67 | const parentLi = this.parentElement; 68 | const dropdownMenu = parentLi.querySelector('.dropdown-menu'); 69 | 70 | if (!dropdownMenu) return; 71 | 72 | if (parentLi.classList.contains('open')) { 73 | slideUp(dropdownMenu, 200, () => { 74 | parentLi.classList.remove('open'); 75 | }); 76 | } else { 77 | // Close all other open menus at the same level 78 | const siblingMenus = parentLi.parentElement.querySelectorAll('li.open'); 79 | siblingMenus.forEach(sibling => { 80 | const siblingDropdown = sibling.querySelector('.dropdown-menu'); 81 | const siblingLink = sibling.querySelector('a'); 82 | 83 | if (siblingDropdown) { 84 | slideUp(siblingDropdown, 200); 85 | } 86 | if (siblingLink) { 87 | siblingLink.classList.remove('open'); 88 | } 89 | sibling.classList.remove('open'); 90 | }); 91 | 92 | // Open current menu 93 | slideDown(dropdownMenu, 200, () => { 94 | parentLi.classList.add('open'); 95 | }); 96 | } 97 | }); 98 | }); 99 | 100 | // Sidebar Activity Class 101 | const sidebarLinkElements = document.querySelectorAll('.sidebar .sidebar-link'); 102 | 103 | sidebarLinkElements.forEach(link => { 104 | link.classList.remove('active'); 105 | 106 | const href = link.getAttribute('href'); 107 | if (href) { 108 | const pattern = href[0] === '/' ? href.substr(1) : href; 109 | if (pattern === window.location.pathname.substr(1)) { 110 | link.classList.add('active'); 111 | } 112 | } 113 | }); 114 | 115 | // Sidebar Toggle 116 | const sidebarToggle = document.querySelector('.sidebar-toggle'); 117 | const app = document.querySelector('.app'); 118 | 119 | if (sidebarToggle && app) { 120 | sidebarToggle.addEventListener('click', e => { 121 | app.classList.toggle('is-collapsed'); 122 | e.preventDefault(); 123 | }); 124 | } 125 | 126 | /** 127 | * Wait until sidebar fully toggled (animated in/out) 128 | * then trigger window resize event in order to recalculate 129 | * masonry layout widths and gutters. 130 | */ 131 | const sidebarToggleById = document.getElementById('sidebar-toggle'); 132 | if (sidebarToggleById) { 133 | sidebarToggleById.addEventListener('click', e => { 134 | e.preventDefault(); 135 | setTimeout(() => { 136 | window.dispatchEvent(new Event('resize')); 137 | }, 300); 138 | }); 139 | } 140 | }()); 141 | -------------------------------------------------------------------------------- /docs/getting-started/installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Installation 4 | nav_order: 1 5 | parent: Getting Started 6 | --- 7 | 8 | # Installation Guide 9 | {: .no_toc } 10 | 11 | ## Table of contents 12 | {: .no_toc .text-delta } 13 | 14 | 1. TOC 15 | {:toc} 16 | 17 | --- 18 | 19 | This guide will help you get Adminator up and running on your local machine. 20 | 21 | ## Prerequisites 22 | 23 | Before installing Adminator, ensure you have the following installed: 24 | 25 | ### Required Software 26 | 27 | - **Node.js** (v18.12.0 or higher) 28 | - Download from [nodejs.org](https://nodejs.org/) 29 | - Verify installation: `node --version` 30 | - **npm** (comes with Node.js) or **Yarn** 31 | - Verify npm: `npm --version` 32 | - **Git** for version control 33 | - Download from [git-scm.com](https://git-scm.com/) 34 | 35 | ### System Requirements 36 | 37 | - **Operating System**: Windows 10+, macOS 10.14+, or Linux 38 | - **RAM**: Minimum 4GB (8GB recommended for development) 39 | - **Storage**: 500MB free space for dependencies 40 | 41 | ## Installation Methods 42 | 43 | ### Method 1: Clone from GitHub (Recommended) 44 | 45 | ```bash 46 | # Clone the repository 47 | git clone https://github.com/puikinsh/Adminator-admin-dashboard.git 48 | 49 | # Navigate to the project directory 50 | cd Adminator-admin-dashboard 51 | 52 | # Install dependencies 53 | npm install 54 | 55 | # Start development server 56 | npm start 57 | ``` 58 | 59 | ### Method 2: Download ZIP 60 | 61 | 1. Visit the [GitHub repository](https://github.com/puikinsh/Adminator-admin-dashboard) 62 | 2. Click **"Code"** → **"Download ZIP"** 63 | 3. Extract the downloaded file 64 | 4. Open terminal in the extracted folder 65 | 5. Run `npm install` and `npm start` 66 | 67 | ### Method 3: Use with Existing Project 68 | 69 | ```bash 70 | # Add Adminator to your project 71 | npm install --save adminator 72 | 73 | # Or download specific release 74 | wget https://github.com/puikinsh/Adminator-admin-dashboard/archive/v2.6.0.zip 75 | ``` 76 | 77 | ## Verification 78 | 79 | After installation, verify everything works: 80 | 81 | ### 1. Development Server 82 | ```bash 83 | npm start 84 | ``` 85 | 86 | **Expected Output:** 87 | ``` 88 | > adminator@2.6.0 start 89 | > webpack server 90 | 91 | ✓ Project is running at: http://localhost:4000/ 92 | ✓ webpack compiled successfully 93 | ``` 94 | 95 | ### 2. Build Process 96 | ```bash 97 | npm run build 98 | ``` 99 | 100 | **Expected Output:** 101 | ``` 102 | > adminator@2.6.0 build 103 | > npm run clean && cross-env webpack 104 | 105 | ✓ webpack compiled successfully in [time]ms 106 | ``` 107 | 108 | ### 3. Access the Application 109 | 110 | Open your browser and navigate to: 111 | - **Local**: `http://localhost:4000` 112 | - **Network**: `http://[your-ip]:4000` 113 | 114 | You should see the Adminator dashboard with: 115 | - ✅ Clean interface loading properly 116 | - ✅ Dark/Light mode toggle in the header 117 | - ✅ All components rendering correctly 118 | - ✅ No console errors 119 | 120 | ## Troubleshooting 121 | 122 | ### Common Issues 123 | 124 | #### Port Already in Use 125 | ```bash 126 | # Error: EADDRINUSE: address already in use :::4000 127 | # Solution: Kill the process using port 4000 128 | sudo lsof -ti:4000 | xargs kill -9 129 | 130 | # Or use a different port 131 | PORT=3000 npm start 132 | ``` 133 | 134 | #### Node Version Issues 135 | ```bash 136 | # Check your Node.js version 137 | node --version 138 | 139 | # If version is below 18.12.0, update Node.js 140 | # Use nvm (recommended): 141 | nvm install 18 142 | nvm use 18 143 | ``` 144 | 145 | #### Permission Errors 146 | ```bash 147 | # On macOS/Linux, you might need sudo for global packages 148 | sudo npm install -g npm@latest 149 | 150 | # Better solution: Fix npm permissions 151 | npm config set prefix ~/.npm-global 152 | export PATH=~/.npm-global/bin:$PATH 153 | ``` 154 | 155 | #### Missing Dependencies 156 | ```bash 157 | # Clear npm cache and reinstall 158 | npm cache clean --force 159 | rm -rf node_modules package-lock.json 160 | npm install 161 | ``` 162 | 163 | #### Build Errors 164 | ```bash 165 | # Check for conflicting global packages 166 | npm list -g --depth=0 167 | 168 | # Update npm and dependencies 169 | npm update 170 | npm audit fix 171 | ``` 172 | 173 | ### Getting Help 174 | 175 | If you encounter issues: 176 | 177 | 1. **Check the [GitHub Issues](https://github.com/puikinsh/Adminator-admin-dashboard/issues)** 178 | 2. **Search existing solutions** 179 | 3. **Create a new issue** with: 180 | - Operating system and version 181 | - Node.js and npm versions 182 | - Complete error message 183 | - Steps to reproduce 184 | 185 | ## Next Steps 186 | 187 | After successful installation: 188 | 189 | 1. **[Explore Project Structure](project-structure.md)** - Understand the codebase 190 | 2. **[Development Workflow](development.md)** - Learn the development process 191 | 3. **[Customize Themes](../customization/theme-system.md)** - Set up dark mode and theming 192 | 4. **[Build for Production](build-deployment.md)** - Deploy your application 193 | 194 | --- 195 | 196 | **Installation Complete!** 🎉 You're ready to start building with Adminator. -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import babelParser from "@babel/eslint-parser"; 3 | import tsParser from "@typescript-eslint/parser"; 4 | import tsPlugin from "@typescript-eslint/eslint-plugin"; 5 | import js from "@eslint/js"; 6 | 7 | export default [ 8 | { 9 | files: ["**/*.js", "**/*.mjs", "**/*.jsx"], 10 | languageOptions: { 11 | globals: { 12 | ...globals.browser, 13 | ...globals.node, 14 | }, 15 | parser: babelParser, 16 | ecmaVersion: 2018, 17 | sourceType: "module", 18 | parserOptions: { 19 | requireConfigFile: false, 20 | babelOptions: { 21 | babelrc: false, 22 | configFile: false, 23 | presets: ["@babel/preset-env"], 24 | }, 25 | ecmaFeatures: { 26 | modules: true, 27 | destructuring: true, 28 | classes: true, 29 | forOf: true, 30 | blockBindings: true, 31 | arrowFunctions: true, 32 | }, 33 | }, 34 | }, 35 | 36 | settings: { 37 | ecmascript: 7, 38 | }, 39 | 40 | rules: { 41 | // Start with ESLint recommended rules 42 | ...js.configs.recommended.rules, 43 | 44 | // Apply our custom overrides (keeping original project preferences) 45 | "arrow-body-style": 0, 46 | "prefer-arrow-callback": 0, 47 | "arrow-parens": 0, 48 | "no-param-reassign": 0, 49 | "no-new": 0, 50 | "consistent-return": 0, 51 | "key-spacing": 0, 52 | "no-multi-spaces": 0, 53 | "no-underscore-dangle": 0, 54 | "one-var": 0, 55 | "global-require": 0, 56 | "class-methods-use-this": 0, 57 | "comma-dangle": ["error", { 58 | arrays: "always-multiline", 59 | objects: "always-multiline", 60 | imports: "always-multiline", 61 | exports: "always-multiline", 62 | functions: "never", 63 | }], 64 | "func-names": 0, 65 | "function-paren-newline": 0, 66 | "indent": ["error", 2], 67 | "new-cap": 0, 68 | "no-plusplus": 0, 69 | "no-return-assign": 0, 70 | "quote-props": 0, 71 | "template-curly-spacing": 0, 72 | "no-unused-expressions": 0, 73 | 74 | // Import rules (basic ones that don't require the import plugin) 75 | "no-duplicate-imports": "error", 76 | 77 | // Line ending for Unix/macOS (updated for current platform) 78 | "linebreak-style": ["error", "unix"], 79 | 80 | // Basic ES6+ rules that replace some airbnb functionality 81 | "prefer-const": "warn", 82 | "no-var": "error", 83 | "prefer-template": "warn", 84 | "object-shorthand": "warn", 85 | }, 86 | }, 87 | { 88 | files: ["**/*.ts", "**/*.tsx"], 89 | languageOptions: { 90 | globals: { 91 | ...globals.browser, 92 | ...globals.node, 93 | }, 94 | parser: tsParser, 95 | ecmaVersion: 2020, 96 | sourceType: "module", 97 | parserOptions: { 98 | project: "./tsconfig.json", 99 | ecmaFeatures: { 100 | jsx: true, 101 | }, 102 | }, 103 | }, 104 | plugins: { 105 | "@typescript-eslint": tsPlugin, 106 | }, 107 | rules: { 108 | // TypeScript ESLint recommended rules 109 | ...tsPlugin.configs.recommended.rules, 110 | 111 | // Apply our custom overrides for TypeScript 112 | "@typescript-eslint/no-explicit-any": "warn", 113 | "@typescript-eslint/no-unused-vars": "error", 114 | "@typescript-eslint/explicit-function-return-type": "off", 115 | "@typescript-eslint/explicit-module-boundary-types": "off", 116 | "@typescript-eslint/no-inferrable-types": "off", 117 | "@typescript-eslint/prefer-nullish-coalescing": "warn", 118 | "@typescript-eslint/prefer-optional-chain": "warn", 119 | "@typescript-eslint/no-unnecessary-type-assertion": "warn", 120 | 121 | // Consistent with JS config 122 | "comma-dangle": ["error", { 123 | arrays: "always-multiline", 124 | objects: "always-multiline", 125 | imports: "always-multiline", 126 | exports: "always-multiline", 127 | functions: "never", 128 | }], 129 | "indent": ["error", 2], 130 | "no-unused-expressions": "off", 131 | "@typescript-eslint/no-unused-expressions": "off", 132 | }, 133 | }, 134 | { 135 | // Global ignores 136 | ignores: ["dist/**/*", "node_modules/**/*", "*.min.js"], 137 | }, 138 | ]; -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/helpers/padding.scss: -------------------------------------------------------------------------------- 1 | @use '../mixins/generateResponsive' as *; 2 | 3 | // --------------------------------------------------------- 4 | // @TOC 5 | // --------------------------------------------------------- 6 | 7 | // + @Variables 8 | // + @Padding (0 > 4 Step 1) 9 | // + @Padding (5 > 35 Step 5) 10 | // + @Padding (40 > 160 Step 10) 11 | // + @Padding Auto 12 | 13 | // --------------------------------------------------------- 14 | // @Variables 15 | // --------------------------------------------------------- 16 | 17 | $responsive: true; 18 | 19 | // --------------------------------------------------------- 20 | // @Padding (0 > 4 Step 1) 21 | // --------------------------------------------------------- 22 | 23 | @for $i from 0 through 4 { 24 | .p-#{$i} { padding: #{$i}px !important; } 25 | .pT-#{$i} { padding-top: #{$i}px !important; } 26 | .pR-#{$i} { padding-right: #{$i}px !important; } 27 | .pB-#{$i} { padding-bottom: #{$i}px !important; } 28 | .pL-#{$i} { padding-left: #{$i}px !important; } 29 | 30 | .pY-#{$i} { 31 | padding-top: #{$i}px !important; 32 | padding-bottom: #{$i}px !important; 33 | } 34 | 35 | .pX-#{$i} { 36 | padding-left: #{$i}px !important; 37 | padding-right: #{$i}px !important; 38 | } 39 | 40 | @if ($responsive == true) { 41 | @include generateResponsive() { 42 | .p-#{$i}\@#{$breakpointAlias} { padding: #{$i}px !important; } 43 | .pT-#{$i}\@#{$breakpointAlias} { padding-top: #{$i}px !important; } 44 | .pR-#{$i}\@#{$breakpointAlias} { padding-right: #{$i}px !important; } 45 | .pB-#{$i}\@#{$breakpointAlias} { padding-bottom: #{$i}px !important; } 46 | .pL-#{$i}\@#{$breakpointAlias} { padding-left: #{$i}px !important; } 47 | 48 | .pY-#{$i}\@#{$breakpointAlias} { 49 | padding-top: #{$i}px !important; 50 | padding-bottom: #{$i}px !important; 51 | } 52 | 53 | .pX-#{$i}\@#{$breakpointAlias} { 54 | padding-left: #{$i}px !important; 55 | padding-right: #{$i}px !important; 56 | } 57 | } 58 | } 59 | } 60 | 61 | // --------------------------------------------------------- 62 | // @Padding (5 > 35 Step 5) 63 | // --------------------------------------------------------- 64 | 65 | @for $i from 5 through 35 { 66 | @if $i % 5 == 0 { 67 | .p-#{$i} { padding: #{$i}px !important; } 68 | .pT-#{$i} { padding-top: #{$i}px !important; } 69 | .pR-#{$i} { padding-right: #{$i}px !important; } 70 | .pB-#{$i} { padding-bottom: #{$i}px !important; } 71 | .pL-#{$i} { padding-left: #{$i}px !important; } 72 | 73 | .pY-#{$i} { 74 | padding-top: #{$i}px !important; 75 | padding-bottom: #{$i}px !important; 76 | } 77 | 78 | .pX-#{$i} { 79 | padding-left: #{$i}px !important; 80 | padding-right: #{$i}px !important; 81 | } 82 | 83 | @if ($responsive == true) { 84 | @include generateResponsive() { 85 | .p-#{$i}\@#{$breakpointAlias} { padding: #{$i}px !important; } 86 | .pT-#{$i}\@#{$breakpointAlias} { padding-top: #{$i}px !important; } 87 | .pR-#{$i}\@#{$breakpointAlias} { padding-right: #{$i}px !important; } 88 | .pB-#{$i}\@#{$breakpointAlias} { padding-bottom: #{$i}px !important; } 89 | .pL-#{$i}\@#{$breakpointAlias} { padding-left: #{$i}px !important; } 90 | 91 | .pY-#{$i}\@#{$breakpointAlias} { 92 | padding-top: #{$i}px !important; 93 | padding-bottom: #{$i}px !important; 94 | } 95 | 96 | .pX-#{$i}\@#{$breakpointAlias} { 97 | padding-left: #{$i}px !important; 98 | padding-right: #{$i}px !important; 99 | } 100 | } 101 | } 102 | } 103 | } 104 | 105 | // --------------------------------------------------------- 106 | // @Padding (40 > 160 Step 10) 107 | // --------------------------------------------------------- 108 | 109 | @for $i from 40 through 160 { 110 | @if $i % 10 == 0 { 111 | .p-#{$i} { padding: #{$i}px !important; } 112 | .pT-#{$i} { padding-top: #{$i}px !important; } 113 | .pR-#{$i} { padding-right: #{$i}px !important; } 114 | .pB-#{$i} { padding-bottom: #{$i}px !important; } 115 | .pL-#{$i} { padding-left: #{$i}px !important; } 116 | 117 | .pY-#{$i} { 118 | padding-top: #{$i}px !important; 119 | padding-bottom: #{$i}px !important; 120 | } 121 | 122 | .pX-#{$i} { 123 | padding-left: #{$i}px !important; 124 | padding-right: #{$i}px !important; 125 | } 126 | 127 | @if ($responsive == true) { 128 | @include generateResponsive() { 129 | .p-#{$i}\@#{$breakpointAlias} { padding: #{$i}px !important; } 130 | .pT-#{$i}\@#{$breakpointAlias} { padding-top: #{$i}px !important; } 131 | .pR-#{$i}\@#{$breakpointAlias} { padding-right: #{$i}px !important; } 132 | .pB-#{$i}\@#{$breakpointAlias} { padding-bottom: #{$i}px !important; } 133 | .pL-#{$i}\@#{$breakpointAlias} { padding-left: #{$i}px !important; } 134 | 135 | .pY-#{$i}\@#{$breakpointAlias} { 136 | padding-top: #{$i}px !important; 137 | padding-bottom: #{$i}px !important; 138 | } 139 | 140 | .pX-#{$i}\@#{$breakpointAlias} { 141 | padding-left: #{$i}px !important; 142 | padding-right: #{$i}px !important; 143 | } 144 | } 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/helpers/layout.scss: -------------------------------------------------------------------------------- 1 | @use '../mixins/generateResponsive' as *; 2 | 3 | // --------------------------------------------------------- 4 | // @TOC 5 | // --------------------------------------------------------- 6 | 7 | // + @Variables 8 | // + @Display 9 | // + @Overflow 10 | // + @Float 11 | // + @Vertical Align 12 | // + @Position 13 | // + @Z-Index 14 | 15 | // --------------------------------------------------------- 16 | // @Variables 17 | // --------------------------------------------------------- 18 | 19 | $responsive: true; 20 | 21 | // --------------------------------------------------------- 22 | // @Display 23 | // --------------------------------------------------------- 24 | 25 | .d-b { display: block !important; } 26 | .d-ib { display: inline-block !important; } 27 | .d-i { display: inline !important; } 28 | .d-tb { display: table !important; } 29 | .d-tbc { display: table-cell !important; } 30 | .d-n { display: none !important; } 31 | 32 | @if ($responsive == true) { 33 | @include generateResponsive() { 34 | .d-b\@#{$breakpointAlias} { display: block !important; } 35 | .d-ib\@#{$breakpointAlias} { display: inline-block !important; } 36 | .d-i\@#{$breakpointAlias} { display: inline !important; } 37 | .d-tb\@#{$breakpointAlias} { display: table !important; } 38 | .d-tbc\@#{$breakpointAlias} { display: table-cell !important; } 39 | .d-n\@#{$breakpointAlias} { display: none !important; } 40 | } 41 | } 42 | 43 | // --------------------------------------------------------- 44 | // @Overflow 45 | // --------------------------------------------------------- 46 | 47 | .ov-h { overflow: hidden !important; } 48 | .ov-a { overflow: auto !important; } 49 | .ov-s { overflow: scroll !important; } 50 | 51 | .ovY-h { overflow-y: hidden !important; } 52 | .ovX-h { overflow-x: hidden !important; } 53 | .ovY-a { overflow-y: auto !important; } 54 | .ovX-a { overflow-x: auto !important; } 55 | .ovY-s { overflow-y: scroll !important; } 56 | .ovX-s { overflow-x: scroll !important; } 57 | 58 | @if ($responsive == true) { 59 | @include generateResponsive() { 60 | .ov-h\@#{$breakpointAlias} { overflow: hidden !important; } 61 | .ov-a\@#{$breakpointAlias} { overflow: auto !important; } 62 | .ov-s\@#{$breakpointAlias} { overflow: scroll !important; } 63 | 64 | .ovY-h\@#{$breakpointAlias} { overflow-y: hidden !important; } 65 | .ovX-h\@#{$breakpointAlias} { overflow-x: hidden !important; } 66 | .ovY-a\@#{$breakpointAlias} { overflow-y: auto !important; } 67 | .ovX-a\@#{$breakpointAlias} { overflow-x: auto !important; } 68 | .ovY-s\@#{$breakpointAlias} { overflow-y: scroll !important; } 69 | .ovX-s\@#{$breakpointAlias} { overflow-x: scroll !important; } 70 | } 71 | } 72 | 73 | // --------------------------------------------------------- 74 | // @Float 75 | // --------------------------------------------------------- 76 | 77 | .fl-l { float: left !important; } 78 | .fl-r { float: right !important; } 79 | .fl-n { float: none !important; } 80 | 81 | @if ($responsive == true) { 82 | @include generateResponsive() { 83 | .fl-l\@#{$breakpointAlias} { float: left !important; } 84 | .fl-r\@#{$breakpointAlias} { float: right !important; } 85 | .fl-n\@#{$breakpointAlias} { float: none !important; } 86 | } 87 | } 88 | 89 | // --------------------------------------------------------- 90 | // @Vertical Align 91 | // --------------------------------------------------------- 92 | 93 | .va-t { vertical-align: top !important; } 94 | .va-m { vertical-align: middle !important; } 95 | .va-b { vertical-align: bottom !important; } 96 | 97 | @if ($responsive == true) { 98 | @include generateResponsive() { 99 | .va-t\@#{$breakpointAlias} { vertical-align: top !important; } 100 | .va-m\@#{$breakpointAlias} { vertical-align: middle !important; } 101 | .va-b\@#{$breakpointAlias} { vertical-align: bottom !important; } 102 | } 103 | } 104 | 105 | // --------------------------------------------------------- 106 | // @Position 107 | // --------------------------------------------------------- 108 | 109 | .pos-s { position: static !important; } 110 | .pos-st { position: sticky !important; } 111 | .pos-r { position: relative !important; } 112 | .pos-a { position: absolute !important; } 113 | .pos-f { position: fixed !important; } 114 | 115 | @if ($responsive == true) { 116 | @include generateResponsive() { 117 | .pos-s\@#{$breakpointAlias} { position: static !important; } 118 | .pos-st\@#{$breakpointAlias} { position: sticky !important; } 119 | .pos-r\@#{$breakpointAlias} { position: relative !important; } 120 | .pos-a\@#{$breakpointAlias} { position: absolute !important; } 121 | .pos-f\@#{$breakpointAlias} { position: fixed !important; } 122 | } 123 | } 124 | 125 | // --------------------------------------------------------- 126 | // @Z-Index 127 | // --------------------------------------------------------- 128 | 129 | @for $i from 0 through 9 { 130 | .z-#{$i} { z-index: ($i * 1000) !important; } 131 | 132 | @if ($responsive == true) { 133 | @include generateResponsive() { 134 | .z-#{$i}\@#{$breakpointAlias} { z-index: ($i * 1000) !important; } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/assets/styles/vendor/fullcalendar.scss: -------------------------------------------------------------------------------- 1 | @use '../spec/settings/baseColors' as *; 2 | 3 | .fc { 4 | background-color: var(--c-bkg-card) !important; 5 | border: 1px solid var(--c-border) !important; 6 | color: var(--c-text-base) !important; 7 | 8 | // All table elements within the calendar 9 | table { 10 | background-color: var(--c-bkg-card) !important; 11 | border-color: var(--c-border) !important; 12 | } 13 | 14 | th { 15 | text-align: center; 16 | padding: 15px; 17 | background-color: var(--c-bkg-card) !important; 18 | color: var(--c-text-base) !important; 19 | font-size: 12px; 20 | text-transform: uppercase; 21 | border-right-width: 0; 22 | border-left-width: 0; 23 | border-color: var(--c-border) !important; 24 | } 25 | 26 | td { 27 | background-color: var(--c-bkg-card) !important; 28 | border-color: var(--c-border) !important; 29 | color: var(--c-text-base) !important; 30 | } 31 | 32 | button { 33 | background-color: var(--c-bkg-card) !important; 34 | background-image: none; 35 | height: 37px; 36 | padding: 0 15px; 37 | color: var(--c-text-base) !important; 38 | border-color: var(--c-border) !important; 39 | 40 | &.fc-state-default { 41 | border-color: var(--c-border) !important; 42 | box-shadow: none; 43 | background-color: var(--c-bkg-card) !important; 44 | color: var(--c-text-base) !important; 45 | } 46 | 47 | &.fc-state-active { 48 | box-shadow: none; 49 | background-color: var(--c-primary) !important; 50 | color: white !important; 51 | border-color: var(--c-primary) !important; 52 | } 53 | 54 | &:hover { 55 | background-color: color-mix(in srgb, var(--c-bkg-card) 85%, var(--c-border)) !important; 56 | border-color: var(--c-border) !important; 57 | color: var(--c-text-base) !important; 58 | } 59 | } 60 | 61 | // Calendar header/toolbar 62 | .fc-head { 63 | background-color: var(--c-bkg-card) !important; 64 | border-color: var(--c-border) !important; 65 | } 66 | 67 | .fc-head-container { 68 | background-color: var(--c-bkg-card) !important; 69 | border-color: var(--c-border) !important; 70 | } 71 | 72 | // Calendar body 73 | .fc-body { 74 | background-color: var(--c-bkg-card) !important; 75 | border-color: var(--c-border) !important; 76 | } 77 | 78 | // Individual day cells 79 | .fc-day { 80 | background-color: var(--c-bkg-card) !important; 81 | border-color: var(--c-border) !important; 82 | 83 | &:hover { 84 | background-color: color-mix(in srgb, var(--c-bkg-card) 90%, var(--c-border)) !important; 85 | } 86 | } 87 | 88 | // Week cells 89 | .fc-week { 90 | background-color: var(--c-bkg-card) !important; 91 | border-color: var(--c-border) !important; 92 | } 93 | 94 | // All grid elements 95 | .fc-widget-header, 96 | .fc-widget-content { 97 | border-color: var(--c-border) !important; 98 | background-color: var(--c-bkg-card) !important; 99 | } 100 | } 101 | 102 | .fc-toolbar { 103 | padding: 20px 20px 0; 104 | background-color: var(--c-bkg-card) !important; 105 | } 106 | 107 | .fc-view, 108 | .fc-view > table { 109 | background-color: var(--c-bkg-card) !important; 110 | border-color: var(--c-border) !important; 111 | } 112 | 113 | .fc-basic-view td.fc-day-number, 114 | .fc-basic-view td.fc-week-number span { 115 | padding: 7px 15px; 116 | color: var(--c-text-base) !important; 117 | } 118 | 119 | .fc-unthemed { 120 | background-color: var(--c-bkg-card) !important; 121 | 122 | .fc-content, 123 | .fc-divider, 124 | .fc-popover, 125 | .fc-row, 126 | tbody, 127 | td, 128 | th, 129 | thead { 130 | border-color: var(--c-border) !important; 131 | background-color: var(--c-bkg-card) !important; 132 | } 133 | 134 | .fc-today { 135 | background-color: color-mix(in srgb, var(--c-primary) 10%, var(--c-bkg-card)) !important; 136 | } 137 | 138 | .fc-popover { 139 | background: var(--c-bkg-card) !important; 140 | color: var(--c-text-base) !important; 141 | border-color: var(--c-border) !important; 142 | } 143 | 144 | // Other month dates (faded) 145 | .fc-other-month { 146 | .fc-day-number { 147 | color: var(--c-text-muted) !important; 148 | } 149 | } 150 | 151 | // Weekend styling 152 | .fc-sun, 153 | .fc-sat { 154 | background-color: color-mix(in srgb, var(--c-bkg-card) 95%, var(--c-border)) !important; 155 | } 156 | } 157 | 158 | .fc-basic-view { 159 | background-color: var(--c-bkg-card) !important; 160 | 161 | .fc-day-number { 162 | color: var(--c-text-base) !important; 163 | 164 | &.fc-today { 165 | background-color: var(--c-primary) !important; 166 | color: white !important; 167 | display: inline-block; 168 | float: right; 169 | border-radius: 50%; 170 | padding: 6px 8px; 171 | line-height: 1; 172 | margin: 4px 4px 0 0; 173 | } 174 | } 175 | 176 | .fc-day-header { 177 | background-color: var(--c-bkg-card) !important; 178 | color: var(--c-text-base) !important; 179 | border-color: var(--c-border) !important; 180 | } 181 | } 182 | 183 | .fc-event-container { 184 | .fc-event { 185 | border-radius: 3px; 186 | border: 0; 187 | background-color: var(--c-primary) !important; 188 | color: white !important; 189 | font-size: 12px; 190 | line-height: 2.5; 191 | padding: 0 15px; 192 | 193 | &:hover { 194 | background-color: var(--c-primary-hover) !important; 195 | opacity: 0.9; 196 | } 197 | } 198 | 199 | .fc-day-grid-event { 200 | margin: 1px 5px 5px; 201 | } 202 | } 203 | 204 | // Comprehensive border fix for all calendar elements 205 | .fc * { 206 | border-color: var(--c-border) !important; 207 | } 208 | 209 | // Fix for any remaining border inconsistencies 210 | .fc .fc-grid, 211 | .fc .fc-grid table, 212 | .fc .fc-grid tr, 213 | .fc .fc-grid td, 214 | .fc .fc-grid th { 215 | border-color: var(--c-border) !important; 216 | background-color: var(--c-bkg-card) !important; 217 | } 218 | -------------------------------------------------------------------------------- /src/assets/styles/spec/components/pageContainer.scss: -------------------------------------------------------------------------------- 1 | @use '../settings/baseColors' as *; 2 | @use '../settings/breakpoints' as *; 3 | @use '../tools/mixins/mediaQueriesRanges' as *; 4 | 5 | // --------------------------------------------------------- 6 | // @TOC 7 | // --------------------------------------------------------- 8 | 9 | // + @Page Container 10 | // + @Main Content 11 | // + @Full Container 12 | // + @Collapsed State 13 | // + @Mobile Layout Fixes 14 | 15 | // --------------------------------------------------------- 16 | // @Page Container - MODERN LAYOUT APPROACH 17 | // --------------------------------------------------------- 18 | 19 | .page-container { 20 | min-height: 100vh; 21 | padding-left: $offscreen-size; 22 | transition: all 0.2s ease; 23 | 24 | // Modern flexbox layout to prevent footer overlap 25 | display: flex; 26 | flex-direction: column; 27 | 28 | @include between($breakpoint-md, $breakpoint-xl) { 29 | padding-left: $collapsed-size; 30 | } 31 | 32 | @include to($breakpoint-md) { 33 | padding-left: 0; 34 | } 35 | } 36 | 37 | // --------------------------------------------------------- 38 | // @Main Content - FLEXIBLE LAYOUT 39 | // --------------------------------------------------------- 40 | 41 | .main-content { 42 | padding: 85px 20px 20px; 43 | 44 | // Flex-grow to push footer to bottom 45 | flex: 1 0 auto; 46 | min-height: 0; // Allow flex shrinking 47 | 48 | // Ensure content doesn't overflow 49 | overflow-x: hidden; 50 | 51 | @include to($breakpoint-md) { 52 | padding: 85px 5px 5px; 53 | } 54 | 55 | @include to($breakpoint-sm) { 56 | padding: 85px 10px 30px; // Extra bottom padding on mobile 57 | } 58 | 59 | @include to(400px) { 60 | padding: 85px 5px 40px; // Even more bottom padding on tiny screens 61 | } 62 | } 63 | 64 | .remain-height { 65 | height: calc(100vh - 126px); 66 | } 67 | 68 | // --------------------------------------------------------- 69 | // @Full Container 70 | // --------------------------------------------------------- 71 | 72 | .full-container { 73 | left: $offscreen-size; 74 | min-height: calc(100vh - #{$header-height}); 75 | position: absolute; 76 | right: 0; 77 | top: $header-height; 78 | transition: all 0.2s ease; 79 | 80 | @include between($breakpoint-md, $breakpoint-xl) { 81 | left: 0; 82 | padding-left: $collapsed-size; 83 | } 84 | 85 | @include to($breakpoint-md) { 86 | left: 0; 87 | } 88 | } 89 | 90 | // --------------------------------------------------------- 91 | // @Mobile Layout Fixes - AGGRESSIVE FOOTER SOLUTION 92 | // --------------------------------------------------------- 93 | 94 | // Footer - completely redesigned for mobile 95 | footer { 96 | // Flex-shrink: 0 to prevent compression 97 | flex: 0 0 auto; 98 | 99 | // Positioning 100 | position: relative; 101 | z-index: 1; 102 | 103 | // Styling 104 | margin-top: auto; 105 | padding: 20px; 106 | text-align: center; 107 | border-top: 1px solid var(--c-border); 108 | background: var(--c-bkg-card); 109 | color: var(--c-text-muted); 110 | font-size: 12px; 111 | line-height: 1.4; 112 | 113 | // Prevent any potential overflow 114 | word-wrap: break-word; 115 | overflow-wrap: break-word; 116 | 117 | // Tablet footer adjustments 118 | @include to($breakpoint-md) { 119 | padding: 18px 15px; 120 | font-size: 11px; 121 | } 122 | 123 | // Mobile footer adjustments 124 | @include to($breakpoint-sm) { 125 | padding: 15px 10px; 126 | font-size: 10px; 127 | line-height: 1.3; 128 | 129 | span { 130 | display: inline-block; 131 | max-width: 100%; 132 | 133 | a { 134 | color: var(--c-primary); 135 | text-decoration: none; 136 | word-break: break-word; 137 | 138 | &:hover { 139 | text-decoration: underline; 140 | } 141 | } 142 | } 143 | } 144 | 145 | // Tiny screen footer adjustments 146 | @include to(400px) { 147 | padding: 12px 8px; 148 | font-size: 9px; 149 | line-height: 1.2; 150 | 151 | span { 152 | display: block; 153 | 154 | // Break long text on new lines for readability 155 | &::after { 156 | content: ""; 157 | display: block; 158 | height: 2px; 159 | } 160 | } 161 | } 162 | } 163 | 164 | // Ensure body and html take full height for flex layout 165 | html, body { 166 | height: 100%; 167 | margin: 0; 168 | padding: 0; 169 | } 170 | 171 | // Global mobile overflow prevention 172 | @include to($breakpoint-sm) { 173 | body { 174 | overflow-x: hidden; 175 | } 176 | 177 | // Prevent any element from causing horizontal scroll 178 | * { 179 | max-width: 100%; 180 | box-sizing: border-box; 181 | } 182 | } 183 | 184 | // Additional mobile content spacing 185 | @include to($breakpoint-sm) { 186 | .page-container { 187 | .main-content { 188 | // Extra margin-bottom to ensure footer never overlaps 189 | margin-bottom: 20px; 190 | 191 | // Responsive content adjustments 192 | .row { 193 | margin-left: 0; 194 | margin-right: 0; 195 | } 196 | 197 | .col-md-6, .col-md-3, .col-md-12 { 198 | padding-left: 5px; 199 | padding-right: 5px; 200 | } 201 | } 202 | } 203 | } 204 | 205 | // Emergency footer overlap prevention 206 | @include to(480px) { 207 | .page-container { 208 | // Force minimum height that accounts for content 209 | min-height: calc(100vh - 80px); 210 | 211 | .main-content { 212 | // Ensure there's always space for footer 213 | padding-bottom: 60px !important; 214 | margin-bottom: 20px !important; 215 | } 216 | } 217 | 218 | footer { 219 | // Stick to bottom on very small screens 220 | position: relative; 221 | margin-top: auto; 222 | clear: both; 223 | } 224 | } 225 | 226 | // --------------------------------------------------------- 227 | // @Collapsed State 228 | // --------------------------------------------------------- 229 | 230 | .is-collapsed { 231 | .page-container { 232 | padding-left: $collapsed-size; 233 | 234 | @include to($breakpoint-md) { 235 | padding-left: 0; 236 | } 237 | 238 | @include between($breakpoint-md, $breakpoint-xl) { 239 | padding-left: $offscreen-size; 240 | } 241 | } 242 | 243 | .full-container { 244 | left: $collapsed-size; 245 | 246 | @include to($breakpoint-md) { 247 | left: 0; 248 | } 249 | 250 | @include between($breakpoint-md, $breakpoint-xl) { 251 | left: $offscreen-size; 252 | padding-left: 0; 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/utils/gap.scss: -------------------------------------------------------------------------------- 1 | @use "sass:math"; 2 | @use '../mixins/generateResponsive' as *; 3 | 4 | // --------------------------------------------------------- 5 | // @TOC 6 | // --------------------------------------------------------- 7 | 8 | // + @Variables 9 | // + @Base 10 | // + @Gap (0 > 4 Step 1) 11 | // + @Gap (5 > 35 Step 5) 12 | // + @Gap (40 > 160 Step 10) 13 | 14 | // --------------------------------------------------------- 15 | // @Variables 16 | // --------------------------------------------------------- 17 | 18 | $responsive: true; 19 | 20 | // --------------------------------------------------------- 21 | // @Base 22 | // --------------------------------------------------------- 23 | 24 | [class*='gap'] { 25 | width: auto !important; 26 | overflow: hidden !important; 27 | } 28 | 29 | // --------------------------------------------------------- 30 | // @Gap (0 > 4 Step 1) 31 | // --------------------------------------------------------- 32 | 33 | @for $i from 0 through 4 { 34 | .gapX-#{$i} { 35 | margin-left: #{math.div($i, -2)}px !important; 36 | margin-right: #{math.div($i, -2)}px !important; 37 | 38 | & > * { 39 | padding-left: #{math.div($i, 2)}px !important; 40 | padding-right: #{math.div($i, 2)}px !important; 41 | } 42 | } 43 | 44 | .gapY-#{$i} { 45 | margin-top: #{math.div($i, -2)}px !important; 46 | margin-bottom: #{math.div($i, -2)}px !important; 47 | 48 | & > * { 49 | padding-top: #{math.div($i, 2)}px !important; 50 | padding-bottom: #{math.div($i, 2)}px !important; 51 | } 52 | } 53 | 54 | .gap-#{$i} { 55 | margin: #{math.div($i, -2)}px !important; 56 | 57 | & > * { 58 | padding: #{math.div($i, 2)}px !important; 59 | } 60 | } 61 | 62 | @if ($responsive == true) { 63 | @include generateResponsive() { 64 | .gapX-#{$i}\@#{$breakpointAlias} { 65 | margin-left: #{math.div($i, -2)}px !important; 66 | margin-right: #{math.div($i, -2)}px !important; 67 | 68 | & > * { 69 | padding-left: #{math.div($i, 2)}px !important; 70 | padding-right: #{math.div($i, 2)}px !important; 71 | } 72 | } 73 | 74 | .gapY-#{$i}\@#{$breakpointAlias} { 75 | margin-top: #{math.div($i, -2)}px !important; 76 | margin-bottom: #{math.div($i, -2)}px !important; 77 | 78 | & > * { 79 | padding-top: #{math.div($i, 2)}px !important; 80 | padding-bottom: #{math.div($i, 2)}px !important; 81 | } 82 | } 83 | 84 | .gap-#{$i}\@#{$breakpointAlias} { 85 | margin: #{math.div($i, -2)}px !important; 86 | 87 | & > * { 88 | padding: #{math.div($i, 2)}px !important; 89 | } 90 | } 91 | } 92 | } 93 | } 94 | 95 | // --------------------------------------------------------- 96 | // @Gap (5 > 35 Step 5) 97 | // --------------------------------------------------------- 98 | 99 | @for $i from 5 through 35 { 100 | @if $i % 5 == 0 { 101 | .gapX-#{$i} { 102 | margin-left: #{math.div($i, -2)}px !important; 103 | margin-right: #{math.div($i, -2)}px !important; 104 | 105 | & > * { 106 | padding-left: #{math.div($i, 2)}px !important; 107 | padding-right: #{math.div($i, 2)}px !important; 108 | } 109 | } 110 | 111 | .gapY-#{$i} { 112 | margin-top: #{math.div($i, -2)}px !important; 113 | margin-bottom: #{math.div($i, -2)}px !important; 114 | 115 | & > * { 116 | padding-top: #{math.div($i, 2)}px !important; 117 | padding-bottom: #{math.div($i, 2)}px !important; 118 | } 119 | } 120 | 121 | .gap-#{$i} { 122 | margin: #{math.div($i, -2)}px !important; 123 | 124 | & > * { 125 | padding: #{math.div($i, 2)}px !important; 126 | } 127 | } 128 | 129 | @if ($responsive == true) { 130 | @include generateResponsive() { 131 | .gapX-#{$i}\@#{$breakpointAlias} { 132 | margin-left: #{math.div($i, -2)}px !important; 133 | margin-right: #{math.div($i, -2)}px !important; 134 | 135 | & > * { 136 | padding-left: #{math.div($i, 2)}px !important; 137 | padding-right: #{math.div($i, 2)}px !important; 138 | } 139 | } 140 | 141 | .gapY-#{$i}\@#{$breakpointAlias} { 142 | margin-top: #{math.div($i, -2)}px !important; 143 | margin-bottom: #{math.div($i, -2)}px !important; 144 | 145 | & > * { 146 | padding-top: #{math.div($i, 2)}px !important; 147 | padding-bottom: #{math.div($i, 2)}px !important; 148 | } 149 | } 150 | 151 | .gap-#{$i}\@#{$breakpointAlias} { 152 | margin: #{math.div($i, -2)}px !important; 153 | 154 | & > * { 155 | padding: #{math.div($i, 2)}px !important; 156 | } 157 | } 158 | } 159 | } 160 | } 161 | } 162 | 163 | // --------------------------------------------------------- 164 | // @Gap (40 > 160 Step 10) 165 | // --------------------------------------------------------- 166 | 167 | @for $i from 40 through 160 { 168 | @if $i % 10 == 0 { 169 | .gapX-#{$i} { 170 | margin-left: #{math.div($i, -2)}px !important; 171 | margin-right: #{math.div($i, -2)}px !important; 172 | 173 | & > * { 174 | padding-left: #{math.div($i, 2)}px !important; 175 | padding-right: #{math.div($i, 2)}px !important; 176 | } 177 | } 178 | 179 | .gapY-#{$i} { 180 | margin-top: #{math.div($i, -2)}px !important; 181 | margin-bottom: #{math.div($i, -2)}px !important; 182 | 183 | & > * { 184 | padding-top: #{math.div($i, 2)}px !important; 185 | padding-bottom: #{math.div($i, 2)}px !important; 186 | } 187 | } 188 | 189 | .gap-#{$i} { 190 | margin: #{math.div($i, -2)}px !important; 191 | 192 | & > * { 193 | padding: #{math.div($i, 2)}px !important; 194 | } 195 | } 196 | 197 | @if ($responsive == true) { 198 | @include generateResponsive() { 199 | .gapX-#{$i}\@#{$breakpointAlias} { 200 | margin-left: #{math.div($i, -2)}px !important; 201 | margin-right: #{math.div($i, -2)}px !important; 202 | 203 | & > * { 204 | padding-left: #{math.div($i, 2)}px !important; 205 | padding-right: #{math.div($i, 2)}px !important; 206 | } 207 | } 208 | 209 | .gapY-#{$i}\@#{$breakpointAlias} { 210 | margin-top: #{math.div($i, -2)}px !important; 211 | margin-bottom: #{math.div($i, -2)}px !important; 212 | 213 | & > * { 214 | padding-top: #{math.div($i, 2)}px !important; 215 | padding-bottom: #{math.div($i, 2)}px !important; 216 | } 217 | } 218 | 219 | .gap-#{$i}\@#{$breakpointAlias} { 220 | margin: #{math.div($i, -2)}px !important; 221 | 222 | & > * { 223 | padding: #{math.div($i, 2)}px !important; 224 | } 225 | } 226 | } 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/assets/styles/spec/utils/layout/helpers/sizes.scss: -------------------------------------------------------------------------------- 1 | @use '../mixins/generateResponsive' as *; 2 | 3 | // --------------------------------------------------------- 4 | // @TOC 5 | // --------------------------------------------------------- 6 | 7 | // + @Variables 8 | // + @Fixed Width 9 | // + @Relative Width 10 | // + @Fixed Height 11 | // + @Max Size 12 | 13 | // --------------------------------------------------------- 14 | // @Variables 15 | // --------------------------------------------------------- 16 | 17 | $responsive: true; 18 | 19 | // --------------------------------------------------------- 20 | // @Fixed Width 21 | // --------------------------------------------------------- 22 | 23 | .w-1\/4r, .sz-1\/4r { width: 0.25rem; } 24 | .w-1\/2r, .sz-1\/2r { width: 0.5rem; } 25 | .w-3\/4r, .sz-3\/4r { width: 0.75rem; } 26 | .w-1r, .sz-1r { width: 1rem; } 27 | .w-3\/2r, .sz-3\/2r { width: 1.5rem; } 28 | .w-2r, .sz-2r { width: 2rem; } 29 | .w-5\/2r, .sz-5\/2r { width: 2.5rem; } 30 | .w-3r, .sz-3r { width: 3rem; } 31 | .w-7\/2r, .sz-7\/2r { width: 3.5rem; } 32 | .w-4r, .sz-4r { width: 4rem; } 33 | .w-9\/2r, .sz-9\/2r { width: 4.5rem; } 34 | .w-5r, .sz-5r { width: 5rem; } 35 | .w-11\/2r, .sz-11\/2r { width: 5.5rem; } 36 | .w-6r, .sz-6r { width: 6rem; } 37 | 38 | @if ($responsive == true) { 39 | @include generateResponsive() { 40 | .w-1\/4r\@#{$breakpointAlias}, .sz-1\/4r\@#{$breakpointAlias} { width: 0.25rem; } 41 | .w-1\/2r\@#{$breakpointAlias}, .sz-1\/2r\@#{$breakpointAlias} { width: 0.5rem; } 42 | .w-3\/4r\@#{$breakpointAlias}, .sz-3\/4r\@#{$breakpointAlias} { width: 0.75rem; } 43 | .w-1r\@#{$breakpointAlias}, .sz-1r\@#{$breakpointAlias} { width: 1rem; } 44 | .w-3\/2r\@#{$breakpointAlias}, .sz-3\/2r\@#{$breakpointAlias} { width: 1.5rem; } 45 | .w-2r\@#{$breakpointAlias}, .sz-2r\@#{$breakpointAlias} { width: 2rem; } 46 | .w-5\/2r\@#{$breakpointAlias}, .sz-5\/2r\@#{$breakpointAlias} { width: 2.5rem; } 47 | .w-3r\@#{$breakpointAlias}, .sz-3r\@#{$breakpointAlias} { width: 3rem; } 48 | .w-7\/2r\@#{$breakpointAlias}, .sz-7\/2r\@#{$breakpointAlias} { width: 3.5rem; } 49 | .w-4r\@#{$breakpointAlias}, .sz-4r\@#{$breakpointAlias} { width: 4rem; } 50 | .w-9\/2r\@#{$breakpointAlias}, .sz-9\/2r\@#{$breakpointAlias} { width: 4.5rem; } 51 | .w-5r\@#{$breakpointAlias}, .sz-5r\@#{$breakpointAlias} { width: 5rem; } 52 | .w-11\/2r\@#{$breakpointAlias}, .sz-11\/2r\@#{$breakpointAlias} { width: 5.5rem; } 53 | .w-6r\@#{$breakpointAlias}, .sz-6r\@#{$breakpointAlias} { width: 6rem; } 54 | } 55 | } 56 | 57 | // --------------------------------------------------------- 58 | // @Relative Width 59 | // --------------------------------------------------------- 60 | 61 | .w-0 { width: 0px; } 62 | .w-10p { width: 10%; } 63 | .w-20p { width: 20%; } 64 | .w-30p { width: 30%; } 65 | .w-40p { width: 40%; } 66 | .w-50p { width: 50%; } 67 | .w-60p { width: 60%; } 68 | .w-70p { width: 70%; } 69 | .w-80p { width: 80%; } 70 | .w-90p { width: 90%; } 71 | .w-100p { width: 100%; } 72 | .w-1px { width: 1px; } 73 | .w-a { width: auto; } 74 | 75 | @if ($responsive == true) { 76 | @include generateResponsive() { 77 | .w-0\@#{$breakpointAlias} { width: 0px; } 78 | .w-10p\@#{$breakpointAlias} { width: 10%; } 79 | .w-20p\@#{$breakpointAlias} { width: 20%; } 80 | .w-30p\@#{$breakpointAlias} { width: 30%; } 81 | .w-40p\@#{$breakpointAlias} { width: 40%; } 82 | .w-50p\@#{$breakpointAlias} { width: 50%; } 83 | .w-60p\@#{$breakpointAlias} { width: 60%; } 84 | .w-70p\@#{$breakpointAlias} { width: 70%; } 85 | .w-80p\@#{$breakpointAlias} { width: 80%; } 86 | .w-90p\@#{$breakpointAlias} { width: 90%; } 87 | .w-100p\@#{$breakpointAlias} { width: 100%; } 88 | .w-1px\@#{$breakpointAlias} { width: 1px; } 89 | .w-a\@#{$breakpointAlias} { width: auto; } 90 | } 91 | } 92 | 93 | // --------------------------------------------------------- 94 | // @Fixed Height 95 | // --------------------------------------------------------- 96 | 97 | .h-1\/4r, .sz-1\/4r { height: 0.25rem; } 98 | .h-1\/2r, .sz-1\/2r { height: 0.5rem; } 99 | .h-3\/4r, .sz-3\/4r { height: 0.75rem; } 100 | .h-1r, .sz-1r { height: 1rem; } 101 | .h-3\/2r, .sz-3\/2r { height: 1.5rem; } 102 | .h-2r, .sz-2r { height: 2rem; } 103 | .h-5\/2r, .sz-5\/2r { height: 2.5rem; } 104 | .h-3r, .sz-3r { height: 3rem; } 105 | .h-7\/2r, .sz-7\/2r { height: 3.5rem; } 106 | .h-4r, .sz-4r { height: 4rem; } 107 | .h-9\/2r, .sz-9\/2r { height: 4.5rem; } 108 | .h-5r, .sz-5r { height: 5rem; } 109 | .h-11\/2r, .sz-11\/2r { height: 5.5rem; } 110 | .h-6r, .sz-6r { height: 6rem; } 111 | 112 | @if ($responsive == true) { 113 | @include generateResponsive() { 114 | .h-1\/4r\@#{$breakpointAlias}, .sz-1\/4r\@#{$breakpointAlias} { height: 0.25rem; } 115 | .h-1\/2r\@#{$breakpointAlias}, .sz-1\/2r\@#{$breakpointAlias} { height: 0.5rem; } 116 | .h-3\/4r\@#{$breakpointAlias}, .sz-3\/4r\@#{$breakpointAlias} { height: 0.75rem; } 117 | .h-1r\@#{$breakpointAlias}, .sz-1r\@#{$breakpointAlias} { height: 1rem; } 118 | .h-3\/2r\@#{$breakpointAlias}, .sz-3\/2r\@#{$breakpointAlias} { height: 1.5rem; } 119 | .h-2r\@#{$breakpointAlias}, .sz-2r\@#{$breakpointAlias} { height: 2rem; } 120 | .h-5\/2r\@#{$breakpointAlias}, .sz-5\/2r\@#{$breakpointAlias} { height: 2.5rem; } 121 | .h-3r\@#{$breakpointAlias}, .sz-3r\@#{$breakpointAlias} { height: 3rem; } 122 | .h-7\/2r\@#{$breakpointAlias}, .sz-7\/2r\@#{$breakpointAlias} { height: 3.5rem; } 123 | .h-4r\@#{$breakpointAlias}, .sz-4r\@#{$breakpointAlias} { height: 4rem; } 124 | .h-9\/2r\@#{$breakpointAlias}, .sz-9\/2r\@#{$breakpointAlias} { height: 4.5rem; } 125 | .h-5r\@#{$breakpointAlias}, .sz-5r\@#{$breakpointAlias} { height: 5rem; } 126 | .h-11\/2r\@#{$breakpointAlias}, .sz-11\/2r\@#{$breakpointAlias} { height: 5.5rem; } 127 | .h-6r\@#{$breakpointAlias}, .sz-6r\@#{$breakpointAlias} { height: 6rem; } 128 | } 129 | } 130 | 131 | .h-0 { height: 0; } 132 | .h-auto { height: auto; } 133 | .h-100p { height: 100%; } 134 | .h-100vh { height: 100vh; } 135 | 136 | @if ($responsive == true) { 137 | @include generateResponsive() { 138 | .h-0\@#{$breakpointAlias} { height: 0; } 139 | .h-auto\@#{$breakpointAlias} { height: auto; } 140 | .h-100p\@#{$breakpointAlias} { height: 100%; } 141 | .h-100vh\@#{$breakpointAlias} { height: 100vh; } 142 | } 143 | } 144 | 145 | // --------------------------------------------------------- 146 | // @Max Size 147 | // --------------------------------------------------------- 148 | 149 | .mw-100p { max-width: 100%; } 150 | .mh-100p { max-height: 100%; } 151 | 152 | @if ($responsive == true) { 153 | @include generateResponsive() { 154 | .mw-100p\@#{$breakpointAlias} { max-width: 100%; } 155 | .mh-100p\@#{$breakpointAlias} { max-height: 100%; } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/assets/scripts/charts/easyPieChart/index.js: -------------------------------------------------------------------------------- 1 | import Theme from '../../utils/theme.js'; 2 | 3 | export default (function () { 4 | 5 | // Vanilla JS Pie Chart implementation using SVG 6 | class VanillaPieChart { 7 | constructor(element, options = {}) { 8 | this.element = element; 9 | this.options = { 10 | size: 110, 11 | lineWidth: 3, 12 | lineCap: 'round', 13 | trackColor: '#f2f2f2', 14 | barColor: '#ef1e25', 15 | scaleColor: false, 16 | animate: 1000, 17 | onStep: null, 18 | ...options, 19 | }; 20 | 21 | this.percentage = parseInt(element.dataset.percent || 0); 22 | this.init(); 23 | } 24 | 25 | init() { 26 | this.createSVG(); 27 | this.animate(); 28 | } 29 | 30 | createSVG() { 31 | const size = this.options.size; 32 | const lineWidth = this.options.lineWidth; 33 | const radius = (size - lineWidth) / 2; 34 | const circumference = 2 * Math.PI * radius; 35 | 36 | // Create SVG element 37 | const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 38 | svg.setAttribute('width', size); 39 | svg.setAttribute('height', size); 40 | svg.style.transform = 'rotate(-90deg)'; 41 | 42 | // Create track (background circle) 43 | const track = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); 44 | track.setAttribute('cx', size / 2); 45 | track.setAttribute('cy', size / 2); 46 | track.setAttribute('r', radius); 47 | track.setAttribute('fill', 'none'); 48 | track.setAttribute('stroke', this.options.trackColor); 49 | track.setAttribute('stroke-width', lineWidth); 50 | 51 | // Create bar (progress circle) 52 | const bar = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); 53 | bar.setAttribute('cx', size / 2); 54 | bar.setAttribute('cy', size / 2); 55 | bar.setAttribute('r', radius); 56 | bar.setAttribute('fill', 'none'); 57 | bar.setAttribute('stroke', this.options.barColor); 58 | bar.setAttribute('stroke-width', lineWidth); 59 | bar.setAttribute('stroke-linecap', this.options.lineCap); 60 | bar.setAttribute('stroke-dasharray', circumference); 61 | bar.setAttribute('stroke-dashoffset', circumference); 62 | 63 | // Add elements to SVG 64 | svg.appendChild(track); 65 | svg.appendChild(bar); 66 | 67 | // Clear element and add SVG 68 | this.element.innerHTML = ''; 69 | this.element.appendChild(svg); 70 | 71 | // Add percentage text 72 | const textElement = document.createElement('div'); 73 | textElement.style.position = 'absolute'; 74 | textElement.style.top = '50%'; 75 | textElement.style.left = '50%'; 76 | textElement.style.transform = 'translate(-50%, -50%)'; 77 | textElement.style.fontSize = '14px'; 78 | textElement.style.fontWeight = 'bold'; 79 | textElement.style.color = Theme.getCSSVar('--c-text-base') || '#333'; 80 | textElement.textContent = '0%'; 81 | 82 | this.element.style.position = 'relative'; 83 | this.element.appendChild(textElement); 84 | 85 | // Store references 86 | this.svg = svg; 87 | this.bar = bar; 88 | this.textElement = textElement; 89 | this.circumference = circumference; 90 | } 91 | 92 | animate() { 93 | const targetOffset = this.circumference - (this.percentage / 100) * this.circumference; 94 | const duration = this.options.animate; 95 | const startTime = Date.now(); 96 | const startOffset = this.circumference; 97 | 98 | const animateStep = () => { 99 | const elapsed = Date.now() - startTime; 100 | const progress = Math.min(elapsed / duration, 1); 101 | 102 | // Easing function (easeOutCubic) 103 | const easeProgress = 1 - Math.pow(1 - progress, 3); 104 | 105 | const currentOffset = startOffset - (startOffset - targetOffset) * easeProgress; 106 | const currentPercent = ((this.circumference - currentOffset) / this.circumference) * 100; 107 | 108 | this.bar.setAttribute('stroke-dashoffset', currentOffset); 109 | this.textElement.textContent = `${Math.round(currentPercent)}%`; 110 | 111 | // Call onStep callback if provided 112 | if (this.options.onStep) { 113 | this.options.onStep.call(this, 0, this.percentage, currentPercent); 114 | } 115 | 116 | if (progress < 1) { 117 | requestAnimationFrame(animateStep); 118 | } 119 | }; 120 | 121 | requestAnimationFrame(animateStep); 122 | } 123 | 124 | update(percentage) { 125 | this.percentage = percentage; 126 | this.animate(); 127 | } 128 | 129 | destroy() { 130 | if (this.element) { 131 | this.element.innerHTML = ''; 132 | } 133 | } 134 | } 135 | 136 | // Initialize all pie charts 137 | const initializePieCharts = () => { 138 | const pieChartElements = document.querySelectorAll('.easy-pie-chart'); 139 | 140 | pieChartElements.forEach(element => { 141 | // Skip if already initialized 142 | if (element.pieChartInstance) { 143 | element.pieChartInstance.destroy(); 144 | } 145 | 146 | // Get theme colors 147 | const isDark = Theme.current() === 'dark'; 148 | const barColor = element.dataset.barColor || (isDark ? '#4f46e5' : '#ef4444'); 149 | const trackColor = element.dataset.trackColor || (isDark ? '#374151' : '#f3f4f6'); 150 | 151 | // Create pie chart instance 152 | const pieChart = new VanillaPieChart(element, { 153 | size: parseInt(element.dataset.size || 110), 154 | lineWidth: parseInt(element.dataset.lineWidth || 3), 155 | barColor, 156 | trackColor, 157 | animate: parseInt(element.dataset.animate || 1000), 158 | onStep(from, to, percent) { 159 | // Update the percentage display 160 | const textElement = this.element.querySelector('div'); 161 | if (textElement) { 162 | textElement.innerHTML = `${Math.round(percent)}%`; 163 | } 164 | }, 165 | }); 166 | 167 | // Store instance for cleanup 168 | element.pieChartInstance = pieChart; 169 | }); 170 | }; 171 | 172 | // Initialize on load 173 | initializePieCharts(); 174 | 175 | // Reinitialize on theme change 176 | window.addEventListener('adminator:themeChanged', () => { 177 | setTimeout(initializePieCharts, 100); 178 | }); 179 | 180 | // Reinitialize on window resize 181 | window.addEventListener('resize', () => { 182 | setTimeout(initializePieCharts, 100); 183 | }); 184 | 185 | // Cleanup on page unload 186 | window.addEventListener('beforeunload', () => { 187 | const pieChartElements = document.querySelectorAll('.easy-pie-chart'); 188 | pieChartElements.forEach(element => { 189 | if (element.pieChartInstance) { 190 | element.pieChartInstance.destroy(); 191 | } 192 | }); 193 | }); 194 | 195 | // Return public API 196 | return { 197 | init: initializePieCharts, 198 | VanillaPieChart, 199 | }; 200 | }()); -------------------------------------------------------------------------------- /src/assets/scripts/charts/sparkline/index.js: -------------------------------------------------------------------------------- 1 | import { Chart, registerables } from 'chart.js'; 2 | import { debounce } from 'lodash'; 3 | import { COLORS } from '../../constants/colors'; 4 | import Theme from '../../utils/theme.js'; 5 | 6 | // Register Chart.js components 7 | Chart.register(...registerables); 8 | 9 | export default (function () { 10 | // Store chart instances for cleanup 11 | let chartInstances = []; 12 | 13 | // ------------------------------------------------------ 14 | // @Sparkline Chart Creation Helpers 15 | // ------------------------------------------------------ 16 | 17 | const createSparklineChart = (elementId, data, color, type = 'bar') => { 18 | const element = document.getElementById(elementId); 19 | if (!element) return null; 20 | 21 | // Clear existing chart 22 | const existingChart = chartInstances.find(chart => chart.canvas.id === elementId); 23 | if (existingChart) { 24 | existingChart.destroy(); 25 | chartInstances = chartInstances.filter(chart => chart.canvas.id !== elementId); 26 | } 27 | 28 | // Create canvas if it doesn't exist 29 | let canvas = element.querySelector('canvas'); 30 | if (!canvas) { 31 | canvas = document.createElement('canvas'); 32 | canvas.id = `${elementId }-canvas`; 33 | element.appendChild(canvas); 34 | } 35 | 36 | // Set canvas size 37 | canvas.width = element.offsetWidth || 100; 38 | canvas.height = 20; 39 | 40 | const ctx = canvas.getContext('2d'); 41 | 42 | const chartConfig = { 43 | type, 44 | data: { 45 | labels: data.map((_, index) => index), 46 | datasets: [{ 47 | data, 48 | backgroundColor: color, 49 | borderColor: color, 50 | borderWidth: type === 'line' ? 2 : 0, 51 | barThickness: 3, 52 | categoryPercentage: 1.0, 53 | barPercentage: 0.8, 54 | fill: false, 55 | pointRadius: 0, 56 | pointHoverRadius: 0, 57 | }], 58 | }, 59 | options: { 60 | responsive: true, 61 | maintainAspectRatio: false, 62 | plugins: { 63 | legend: { 64 | display: false, 65 | }, 66 | tooltip: { 67 | enabled: false, 68 | }, 69 | }, 70 | scales: { 71 | x: { 72 | display: false, 73 | grid: { 74 | display: false, 75 | }, 76 | }, 77 | y: { 78 | display: false, 79 | grid: { 80 | display: false, 81 | }, 82 | }, 83 | }, 84 | elements: { 85 | bar: { 86 | borderRadius: 0, 87 | }, 88 | line: { 89 | tension: 0.1, 90 | }, 91 | }, 92 | layout: { 93 | padding: 0, 94 | }, 95 | }, 96 | }; 97 | 98 | const chart = new Chart(ctx, chartConfig); 99 | chartInstances.push(chart); 100 | return chart; 101 | }; 102 | 103 | const createSparklineForElements = (selector, data, color, type = 'bar') => { 104 | const elements = document.querySelectorAll(selector); 105 | elements.forEach((element, index) => { 106 | const elementId = element.id || `sparkline-${selector.replace(/[^a-zA-Z0-9]/g, '')}-${index}`; 107 | if (!element.id) element.id = elementId; 108 | createSparklineChart(elementId, data, color, type); 109 | }); 110 | }; 111 | 112 | // ------------------------------------------------------ 113 | // @Dashboard Sparklines 114 | // ------------------------------------------------------ 115 | 116 | const drawSparklines = () => { 117 | const sparkColors = Theme.getSparklineColors(); 118 | const data = [0, 5, 6, 10, 9, 12, 4, 9]; 119 | 120 | // Dashboard sparklines 121 | createSparklineChart('sparklinedash', data, sparkColors.success); 122 | createSparklineChart('sparklinedash2', data, sparkColors.purple); 123 | createSparklineChart('sparklinedash3', data, sparkColors.info); 124 | createSparklineChart('sparklinedash4', data, sparkColors.danger); 125 | }; 126 | 127 | // ------------------------------------------------------ 128 | // @Other Sparklines 129 | // ------------------------------------------------------ 130 | 131 | const drawOtherSparklines = () => { 132 | const sparkColors = Theme.getSparklineColors(); 133 | 134 | // Line sparklines 135 | createSparklineChart('sparkline', [5, 6, 7, 9, 9, 5, 3, 2, 2, 4, 6, 7], COLORS['red-500'], 'line'); 136 | 137 | // Composite bar - simplified implementation 138 | createSparklineChart('compositebar', [4, 1, 5, 7, 9, 9, 8, 7, 6, 6, 4, 7, 8, 4, 3, 2, 2, 5, 6, 7], sparkColors.light); 139 | 140 | // Normal line 141 | createSparklineChart('normalline', [5, 6, 7, 9, 9, 5, 3, 2, 2, 4, 6, 7], sparkColors.info, 'line'); 142 | 143 | // Various sparkline types for elements with classes 144 | const values = [5, 4, 5, -2, 0, 3, -5, 6, 7, 9, 9, 5, -3, -2, 2, -4]; 145 | const valuesAlt = [1, 1, 0, 1, -1, -1, 1, -1, 0, 0, 1, 1]; 146 | 147 | // Line sparklines 148 | createSparklineForElements('.sparkline', values, COLORS['red-500'], 'line'); 149 | 150 | // Bar sparklines 151 | createSparklineForElements('.sparkbar', values, COLORS['deep-purple-500'], 'bar'); 152 | 153 | // Tristate sparklines (simplified as bar charts) 154 | createSparklineForElements('.sparktri', valuesAlt, COLORS['light-blue-500'], 'bar'); 155 | createSparklineForElements('.sparktristate', valuesAlt, sparkColors.info, 'bar'); 156 | createSparklineForElements('.sparktristatecols', valuesAlt, '#fa7', 'bar'); 157 | 158 | // Discrete sparklines (as line charts) 159 | createSparklineForElements('.sparkdisc', values, '#9f0', 'line'); 160 | 161 | // Bullet sparklines (simplified as bar charts) 162 | createSparklineForElements('.sparkbull', values, COLORS['amber-500'], 'bar'); 163 | 164 | // Box sparklines (simplified as bar charts) 165 | createSparklineForElements('.sparkbox', values, '#9f0', 'bar'); 166 | }; 167 | 168 | // ------------------------------------------------------ 169 | // @Initialization 170 | // ------------------------------------------------------ 171 | 172 | const initializeSparklines = () => { 173 | drawSparklines(); 174 | drawOtherSparklines(); 175 | }; 176 | 177 | // Initial draw 178 | initializeSparklines(); 179 | 180 | // Redraw sparklines on window resize 181 | window.addEventListener('resize', debounce(initializeSparklines, 150)); 182 | 183 | // Listen for theme changes 184 | window.addEventListener('adminator:themeChanged', debounce(initializeSparklines, 150)); 185 | 186 | // Cleanup function for chart instances 187 | window.addEventListener('beforeunload', () => { 188 | chartInstances.forEach(chart => { 189 | if (chart && typeof chart.destroy === 'function') { 190 | chart.destroy(); 191 | } 192 | }); 193 | chartInstances = []; 194 | }); 195 | 196 | // Export for external access 197 | return { 198 | redraw: initializeSparklines, 199 | destroy: () => { 200 | chartInstances.forEach(chart => { 201 | if (chart && typeof chart.destroy === 'function') { 202 | chart.destroy(); 203 | } 204 | }); 205 | chartInstances = []; 206 | }, 207 | }; 208 | }()); --------------------------------------------------------------------------------