├── .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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
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 |
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 | 
90 |
91 | ### Dark Mode
92 | 
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 |
60 |
61 |
69 |
70 |
71 |
72 |
73 |

74 |
75 |
76 |
77 |
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 |
60 |
61 |
69 |
70 |
71 |
72 |
73 |

74 |
75 |
76 |
77 |
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 | }());
--------------------------------------------------------------------------------