adminator-admin-dashboard 2.8.1 → 4.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +499 -0
- package/CLAUDE.md +126 -154
- package/README.md +321 -370
- package/dist/2026.js +8751 -0
- package/dist/2026.js.map +1 -0
- package/dist/404.html +36 -16
- package/dist/500.html +36 -16
- package/dist/assets/static/images/logo.svg +3 -3
- package/dist/basic-table.html +152 -699
- package/dist/blank.html +42 -507
- package/dist/buttons.html +152 -448
- package/dist/calendar.html +246 -658
- package/dist/charts.html +124 -658
- package/dist/chat.html +209 -706
- package/dist/compose.html +141 -618
- package/dist/datatable.html +467 -991
- package/dist/email.html +430 -943
- package/dist/forms.html +208 -733
- package/dist/google-maps.html +123 -513
- package/dist/index.html +436 -1041
- package/dist/runtime.js +1299 -0
- package/dist/runtime.js.map +1 -0
- package/dist/signin.html +92 -92
- package/dist/signup.html +106 -91
- package/dist/ui.html +268 -897
- package/dist/vector-maps.html +132 -511
- package/dist/vendor-chartjs.js +14593 -0
- package/dist/vendor-chartjs.js.map +1 -0
- package/dist/vendor-fullcalendar.js +14793 -0
- package/dist/vendor-fullcalendar.js.map +1 -0
- package/dist/vendors.js +3758 -0
- package/dist/vendors.js.map +1 -0
- package/package.json +50 -52
- package/src/404.html +35 -15
- package/src/500.html +35 -15
- package/src/assets/scripts/2026/Shell.js +312 -0
- package/src/assets/scripts/2026/calendar.js +123 -0
- package/src/assets/scripts/2026/charts.js +259 -0
- package/src/assets/scripts/2026/index.js +35 -0
- package/src/assets/scripts/2026/init.js +207 -0
- package/src/assets/scripts/2026/maps.js +78 -0
- package/src/assets/scripts/2026/palette.js +266 -0
- package/src/assets/static/images/logo.svg +3 -3
- package/src/assets/styles/2026/_animations.scss +14 -0
- package/src/assets/styles/2026/_auth.scss +215 -0
- package/src/assets/styles/2026/_base.scss +37 -0
- package/src/assets/styles/2026/_calendar.scss +380 -0
- package/src/assets/styles/2026/_charts.scss +44 -0
- package/src/assets/styles/2026/_chat.scss +350 -0
- package/src/assets/styles/2026/_components.scss +140 -0
- package/src/assets/styles/2026/_dashboard.scss +520 -0
- package/src/assets/styles/2026/_data.scss +130 -0
- package/src/assets/styles/2026/_dropdowns.scss +128 -0
- package/src/assets/styles/2026/_email.scss +599 -0
- package/src/assets/styles/2026/_error.scss +98 -0
- package/src/assets/styles/2026/_forms.scss +215 -0
- package/src/assets/styles/2026/_fullcalendar.scss +134 -0
- package/src/assets/styles/2026/_palette.scss +173 -0
- package/src/assets/styles/2026/_responsive.scss +229 -0
- package/src/assets/styles/2026/_shell.scss +290 -0
- package/src/assets/styles/2026/_tokens.scss +80 -0
- package/src/assets/styles/2026/_ui.scss +365 -0
- package/src/assets/styles/2026/index.scss +23 -0
- package/src/basic-table.html +153 -710
- package/src/blank.html +42 -517
- package/src/buttons.html +152 -458
- package/src/calendar.html +246 -668
- package/src/charts.html +124 -668
- package/src/chat.html +209 -716
- package/src/compose.html +142 -629
- package/src/datatable.html +466 -1000
- package/src/email.html +429 -952
- package/src/forms.html +207 -742
- package/src/google-maps.html +128 -523
- package/src/index.html +438 -1050
- package/src/signin.html +92 -92
- package/src/signup.html +106 -91
- package/src/ui.html +267 -906
- package/src/vector-maps.html +133 -522
- package/dist/1e59d2330b4c6deb84b3.ttf +0 -0
- package/dist/20fd1704ea223900efa9.woff2 +0 -0
- package/dist/29b39089170885ae2967.woff +0 -0
- package/dist/8b43027f47b20503057d.eot +0 -0
- package/dist/9bad94440d49256265a5.eot +0 -0
- package/dist/assets/fontawesome-webfont.svg +0 -2671
- package/dist/assets/themify.svg +0 -362
- package/dist/eda8b94308c6f538f04a.ttf +0 -0
- package/dist/f691f37e57f04c152e23.woff +0 -0
- package/dist/main.js +0 -61323
- package/dist/main.js.map +0 -1
- package/src/assets/scripts/app 2.js +0 -645
- package/src/assets/scripts/app.js +0 -645
- package/src/assets/scripts/charts/chartJS/index.js +0 -148
- package/src/assets/scripts/charts/easyPieChart/index.js +0 -200
- package/src/assets/scripts/charts/index.js +0 -3
- package/src/assets/scripts/charts/sparkline/index.js +0 -208
- package/src/assets/scripts/chat/index.js +0 -11
- package/src/assets/scripts/components/Chart.js +0 -1390
- package/src/assets/scripts/components/Sidebar.js +0 -241
- package/src/assets/scripts/constants/colors.js +0 -274
- package/src/assets/scripts/datatable/index.js +0 -379
- package/src/assets/scripts/datepicker/index.js +0 -302
- package/src/assets/scripts/email/index.js +0 -25
- package/src/assets/scripts/fullcalendar/index.js +0 -86
- package/src/assets/scripts/googleMaps/index.js +0 -93
- package/src/assets/scripts/index.js +0 -18
- package/src/assets/scripts/masonry/index.js +0 -14
- package/src/assets/scripts/popover/index.js +0 -109
- package/src/assets/scripts/scrollbar/index.js +0 -10
- package/src/assets/scripts/search/index.js +0 -15
- package/src/assets/scripts/sidebar/index.js +0 -140
- package/src/assets/scripts/skycons/index.js +0 -52
- package/src/assets/scripts/ui/index.js +0 -412
- package/src/assets/scripts/utils/date.js +0 -242
- package/src/assets/scripts/utils/dom.js +0 -349
- package/src/assets/scripts/utils/index.js +0 -45
- package/src/assets/scripts/utils/theme.js +0 -107
- package/src/assets/scripts/vectorMaps/index.js +0 -277
- package/src/assets/styles/index.scss +0 -801
- package/src/assets/styles/spec/components/easyPieChart.scss +0 -11
- package/src/assets/styles/spec/components/footer.scss +0 -4
- package/src/assets/styles/spec/components/forms.scss +0 -288
- package/src/assets/styles/spec/components/index.scss +0 -9
- package/src/assets/styles/spec/components/loader.scss +0 -46
- package/src/assets/styles/spec/components/masonry.scss +0 -1
- package/src/assets/styles/spec/components/pageContainer.scss +0 -255
- package/src/assets/styles/spec/components/progressBar.scss +0 -6
- package/src/assets/styles/spec/components/sidebar.scss +0 -642
- package/src/assets/styles/spec/components/topbar.scss +0 -455
- package/src/assets/styles/spec/generic/base.scss +0 -102
- package/src/assets/styles/spec/generic/index.scss +0 -1
- package/src/assets/styles/spec/index.scss +0 -4
- package/src/assets/styles/spec/screens/chat.scss +0 -147
- package/src/assets/styles/spec/screens/email.scss +0 -108
- package/src/assets/styles/spec/screens/index.scss +0 -2
- package/src/assets/styles/spec/settings/baseColors.scss +0 -103
- package/src/assets/styles/spec/settings/borders.scss +0 -6
- package/src/assets/styles/spec/settings/breakpoints.scss +0 -26
- package/src/assets/styles/spec/settings/fonts.scss +0 -4
- package/src/assets/styles/spec/settings/index.scss +0 -4
- package/src/assets/styles/spec/settings/materialColors.scss +0 -550
- package/src/assets/styles/spec/tools/index.scss +0 -1
- package/src/assets/styles/spec/tools/mixins/clearfix.scss +0 -15
- package/src/assets/styles/spec/tools/mixins/index.scss +0 -3
- package/src/assets/styles/spec/tools/mixins/mediaQueriesRanges.scss +0 -58
- package/src/assets/styles/spec/tools/mixins/placeholder.scss +0 -10
- package/src/assets/styles/spec/utils/colors.scss +0 -33
- package/src/assets/styles/spec/utils/index.scss +0 -2
- package/src/assets/styles/spec/utils/layout/helpers/border.scss +0 -78
- package/src/assets/styles/spec/utils/layout/helpers/flex.scss +0 -220
- package/src/assets/styles/spec/utils/layout/helpers/index.scss +0 -11
- package/src/assets/styles/spec/utils/layout/helpers/layout.scss +0 -137
- package/src/assets/styles/spec/utils/layout/helpers/lists.scss +0 -23
- package/src/assets/styles/spec/utils/layout/helpers/margin.scss +0 -266
- package/src/assets/styles/spec/utils/layout/helpers/objects.scss +0 -91
- package/src/assets/styles/spec/utils/layout/helpers/padding.scss +0 -147
- package/src/assets/styles/spec/utils/layout/helpers/positions.scss +0 -118
- package/src/assets/styles/spec/utils/layout/helpers/pseudo.scss +0 -6
- package/src/assets/styles/spec/utils/layout/helpers/sizes.scss +0 -157
- package/src/assets/styles/spec/utils/layout/helpers/typography.scss +0 -127
- package/src/assets/styles/spec/utils/layout/index.scss +0 -3
- package/src/assets/styles/spec/utils/layout/mixins/generateResponsive.scss +0 -25
- package/src/assets/styles/spec/utils/layout/mixins/index.scss +0 -2
- package/src/assets/styles/spec/utils/layout/mixins/mediaQueryCondition.scss +0 -28
- package/src/assets/styles/spec/utils/layout/utils/center.scss +0 -54
- package/src/assets/styles/spec/utils/layout/utils/gap.scss +0 -229
- package/src/assets/styles/spec/utils/layout/utils/index.scss +0 -5
- package/src/assets/styles/spec/utils/layout/utils/layers.scss +0 -5
- package/src/assets/styles/spec/utils/layout/utils/peers.scss +0 -35
- package/src/assets/styles/utils/mobile.scss +0 -954
- package/src/assets/styles/utils/theme.css +0 -97
- package/src/assets/styles/vendor/datepicker.scss +0 -183
- package/src/assets/styles/vendor/font-awesome.css +0 -2337
- package/src/assets/styles/vendor/fullcalendar.scss +0 -217
- package/src/assets/styles/vendor/index.scss +0 -8
- package/src/assets/styles/vendor/jquery.datatables.scss +0 -162
- package/src/assets/styles/vendor/perfectScrollbar.scss +0 -4
- package/src/assets/styles/vendor/sparkline.scss +0 -6
- package/src/assets/styles/vendor/themify-icons.css +0 -1081
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FullCalendar wiring for the 2026 design system.
|
|
3
|
+
*
|
|
4
|
+
* A page declares the calendar mount as <div data-fc></div>; this module
|
|
5
|
+
* initializes FullCalendar with the seed events below, themes it via the
|
|
6
|
+
* 2026 toolbar (we hide FC's built-in toolbar and let our own buttons drive
|
|
7
|
+
* .next() / .prev() / .today() / .changeView()), and re-renders on theme
|
|
8
|
+
* change so colors stay in sync.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { Calendar } from '@fullcalendar/core';
|
|
12
|
+
import dayGridPlugin from '@fullcalendar/daygrid';
|
|
13
|
+
import timeGridPlugin from '@fullcalendar/timegrid';
|
|
14
|
+
import listPlugin from '@fullcalendar/list';
|
|
15
|
+
import interactionPlugin from '@fullcalendar/interaction';
|
|
16
|
+
|
|
17
|
+
const SEED_EVENTS = [
|
|
18
|
+
{ title: 'Q2 kickoff', start: '2026-04-01T09:00', classNames: ['fc-cat-work'] },
|
|
19
|
+
{ title: 'Design review', start: '2026-04-02T11:00', classNames: ['fc-cat-team'] },
|
|
20
|
+
{ title: 'Lunch w/ Marcus', start: '2026-04-03T13:00', classNames: ['fc-cat-personal'] },
|
|
21
|
+
{ title: '🎂 Sara birthday', start: '2026-04-05', allDay: true, classNames: ['fc-cat-birthday'] },
|
|
22
|
+
{ title: 'Standup', start: '2026-04-07T10:00', classNames: ['fc-cat-work'] },
|
|
23
|
+
{ title: 'Brand workshop', start: '2026-04-07T14:00', classNames: ['fc-cat-team'] },
|
|
24
|
+
{ title: 'All-hands', start: '2026-04-08T15:00', classNames: ['fc-cat-work'] },
|
|
25
|
+
{ title: '✈ Lisbon trip', start: '2026-04-09', end: '2026-04-13', allDay: true, classNames: ['fc-cat-travel'] },
|
|
26
|
+
{ title: 'Investor sync', start: '2026-04-14T16:00', classNames: ['fc-cat-work'] },
|
|
27
|
+
{ title: '📑 Tax deadline', start: '2026-04-15', allDay: true, classNames: ['fc-cat-finance'] },
|
|
28
|
+
{ title: 'Invoice approval', start: '2026-04-17T12:00', classNames: ['fc-cat-finance'] },
|
|
29
|
+
{ title: 'Run with Mira', start: '2026-04-20T07:00', classNames: ['fc-cat-personal'] },
|
|
30
|
+
{ title: 'Earth day talk', start: '2026-04-22T14:00', classNames: ['fc-cat-team'] },
|
|
31
|
+
{ title: '✓ Dependency merge', start: '2026-04-23', allDay: true, classNames: ['fc-cat-work'] },
|
|
32
|
+
{ title: 'Coffee w/ Rita', start: '2026-04-24T10:00', classNames: ['fc-cat-personal'] },
|
|
33
|
+
{ title: 'PR reviews', start: '2026-04-24T15:00', classNames: ['fc-cat-work'] },
|
|
34
|
+
{ title: 'Run · 5K', start: '2026-04-25T07:00', classNames: ['fc-cat-personal'] },
|
|
35
|
+
{ title: "Dinner @ Carla's", start: '2026-04-25T20:00', classNames: ['fc-cat-personal'] },
|
|
36
|
+
{ title: 'Sprint planning', start: '2026-04-27T10:00', classNames: ['fc-cat-work'] },
|
|
37
|
+
{ title: 'Board review', start: '2026-04-28T14:00', classNames: ['fc-cat-work'] },
|
|
38
|
+
{ title: 'Eng review', start: '2026-04-28T17:00', classNames: ['fc-cat-team'] },
|
|
39
|
+
{ title: 'Anya 1:1', start: '2026-04-29T11:30', classNames: ['fc-cat-team'] },
|
|
40
|
+
{ title: 'Newsletter goes out',start: '2026-04-30T09:00', classNames: ['fc-cat-team'] },
|
|
41
|
+
{ title: 'Yoga', start: '2026-04-30T19:00', classNames: ['fc-cat-personal'] },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const VIEW_MAP = { Day: 'timeGridDay', Week: 'timeGridWeek', Month: 'dayGridMonth', Agenda: 'listWeek' };
|
|
45
|
+
|
|
46
|
+
let calendar = null;
|
|
47
|
+
|
|
48
|
+
function bindToolbar(host) {
|
|
49
|
+
// Wire the page's existing toolbar buttons to the FC instance.
|
|
50
|
+
const root = host.closest('.cal-main') || document;
|
|
51
|
+
const monthEl = root.querySelector('.cal-month');
|
|
52
|
+
|
|
53
|
+
const updateTitle = () => {
|
|
54
|
+
if (!monthEl || !calendar) return;
|
|
55
|
+
const d = calendar.getDate();
|
|
56
|
+
const month = d.toLocaleString('en-US', { month: 'long' });
|
|
57
|
+
const year = d.getFullYear();
|
|
58
|
+
monthEl.innerHTML = `${month} <span class="yr">${year}</span>`;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
root.querySelectorAll('.cal-nav-btn').forEach((btn, idx) => {
|
|
62
|
+
btn.addEventListener('click', () => {
|
|
63
|
+
if (!calendar) return;
|
|
64
|
+
if (idx === 0) calendar.prev();
|
|
65
|
+
if (idx === 1) calendar.next();
|
|
66
|
+
updateTitle();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const today = root.querySelector('.cal-today-btn');
|
|
71
|
+
if (today) today.addEventListener('click', () => { calendar.today(); updateTitle(); });
|
|
72
|
+
|
|
73
|
+
root.querySelectorAll('.cal-view-tab').forEach((tab) => {
|
|
74
|
+
tab.addEventListener('click', () => {
|
|
75
|
+
const label = tab.textContent.trim();
|
|
76
|
+
const view = VIEW_MAP[label] || 'dayGridMonth';
|
|
77
|
+
root.querySelectorAll('.cal-view-tab').forEach((t) => t.classList.toggle('is-active', t === tab));
|
|
78
|
+
calendar.changeView(view);
|
|
79
|
+
updateTitle();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Title isn't filled in until the calendar is rendered.
|
|
84
|
+
setTimeout(updateTitle, 0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function build(host) {
|
|
88
|
+
if (calendar) {
|
|
89
|
+
try { calendar.destroy(); } catch { /* re-build below */ }
|
|
90
|
+
}
|
|
91
|
+
calendar = new Calendar(host, {
|
|
92
|
+
plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin],
|
|
93
|
+
initialView: 'dayGridMonth',
|
|
94
|
+
initialDate: '2026-04-25',
|
|
95
|
+
headerToolbar: false,
|
|
96
|
+
height: '100%',
|
|
97
|
+
expandRows: true,
|
|
98
|
+
dayMaxEvents: 3,
|
|
99
|
+
fixedWeekCount: false,
|
|
100
|
+
firstDay: 0,
|
|
101
|
+
nowIndicator: true,
|
|
102
|
+
selectable: true,
|
|
103
|
+
editable: true,
|
|
104
|
+
events: SEED_EVENTS,
|
|
105
|
+
dayHeaderFormat: { weekday: 'short' },
|
|
106
|
+
});
|
|
107
|
+
calendar.render();
|
|
108
|
+
bindToolbar(host);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function initCalendarPage() {
|
|
112
|
+
const host = document.querySelector('[data-fc]');
|
|
113
|
+
if (!host) return;
|
|
114
|
+
build(host);
|
|
115
|
+
// Re-render on theme change so any token-driven colors update.
|
|
116
|
+
const observer = new MutationObserver((records) => {
|
|
117
|
+
if (records.some((r) => r.attributeName === 'data-theme')) {
|
|
118
|
+
// Just trigger a redraw — colors come from CSS, not from JS.
|
|
119
|
+
if (calendar) calendar.render();
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
observer.observe(document.documentElement, { attributes: true });
|
|
123
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chart.js wiring for the 2026 design system.
|
|
3
|
+
*
|
|
4
|
+
* Pages declare charts as <canvas data-chart="<type>" data-key="<seed>"></canvas>.
|
|
5
|
+
* This module reads CSS variables for theme colors so charts match the active
|
|
6
|
+
* theme, instantiates a Chart.js instance, and re-renders on theme toggle.
|
|
7
|
+
*
|
|
8
|
+
* To keep the chart-page demo visually rich, the seeds are baked-in samples
|
|
9
|
+
* keyed by data-key.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Chart, registerables } from 'chart.js';
|
|
13
|
+
Chart.register(...registerables);
|
|
14
|
+
|
|
15
|
+
function tokens() {
|
|
16
|
+
const cs = getComputedStyle(document.documentElement);
|
|
17
|
+
return {
|
|
18
|
+
primary: cs.getPropertyValue('--primary').trim(),
|
|
19
|
+
success: cs.getPropertyValue('--success').trim(),
|
|
20
|
+
danger: cs.getPropertyValue('--danger').trim(),
|
|
21
|
+
warning: cs.getPropertyValue('--warning').trim(),
|
|
22
|
+
info: cs.getPropertyValue('--info').trim(),
|
|
23
|
+
purple: cs.getPropertyValue('--purple').trim(),
|
|
24
|
+
pink: cs.getPropertyValue('--pink').trim(),
|
|
25
|
+
orange: cs.getPropertyValue('--orange').trim(),
|
|
26
|
+
teal: cs.getPropertyValue('--teal').trim(),
|
|
27
|
+
text: cs.getPropertyValue('--t-base').trim(),
|
|
28
|
+
muted: cs.getPropertyValue('--t-muted').trim(),
|
|
29
|
+
light: cs.getPropertyValue('--t-light').trim(),
|
|
30
|
+
border: cs.getPropertyValue('--border').trim(),
|
|
31
|
+
soft: cs.getPropertyValue('--border-soft').trim(),
|
|
32
|
+
bg: cs.getPropertyValue('--bg-card').trim(),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function applyDefaults(t) {
|
|
37
|
+
Chart.defaults.font.family = "'Inter', system-ui, sans-serif";
|
|
38
|
+
Chart.defaults.font.size = 12;
|
|
39
|
+
Chart.defaults.color = t.muted;
|
|
40
|
+
Chart.defaults.borderColor = t.soft;
|
|
41
|
+
Chart.defaults.plugins.legend.position = 'bottom';
|
|
42
|
+
Chart.defaults.plugins.legend.labels.usePointStyle = true;
|
|
43
|
+
Chart.defaults.plugins.legend.labels.padding = 16;
|
|
44
|
+
Chart.defaults.plugins.legend.labels.boxWidth = 8;
|
|
45
|
+
Chart.defaults.plugins.legend.labels.boxHeight = 8;
|
|
46
|
+
Chart.defaults.plugins.tooltip.backgroundColor = t.text;
|
|
47
|
+
Chart.defaults.plugins.tooltip.titleColor = t.bg;
|
|
48
|
+
Chart.defaults.plugins.tooltip.bodyColor = t.bg;
|
|
49
|
+
Chart.defaults.plugins.tooltip.padding = 10;
|
|
50
|
+
Chart.defaults.plugins.tooltip.cornerRadius = 6;
|
|
51
|
+
Chart.defaults.plugins.tooltip.displayColors = false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const SEEDS = {
|
|
55
|
+
'revenue-line': (t) => ({
|
|
56
|
+
type: 'line',
|
|
57
|
+
data: {
|
|
58
|
+
labels: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
|
|
59
|
+
datasets: [
|
|
60
|
+
{
|
|
61
|
+
label: '2026',
|
|
62
|
+
data: [42, 56, 50, 78, 88, 96, 110, 124, 118, 142, 158, 184],
|
|
63
|
+
borderColor: t.primary,
|
|
64
|
+
backgroundColor: `${t.primary}20`,
|
|
65
|
+
tension: 0.35,
|
|
66
|
+
fill: true,
|
|
67
|
+
pointRadius: 0,
|
|
68
|
+
pointHoverRadius: 5,
|
|
69
|
+
borderWidth: 2.5,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
label: '2025',
|
|
73
|
+
data: [38, 44, 46, 60, 70, 74, 82, 90, 92, 102, 110, 118],
|
|
74
|
+
borderColor: t.muted,
|
|
75
|
+
backgroundColor: 'transparent',
|
|
76
|
+
tension: 0.35,
|
|
77
|
+
fill: false,
|
|
78
|
+
pointRadius: 0,
|
|
79
|
+
pointHoverRadius: 5,
|
|
80
|
+
borderWidth: 2,
|
|
81
|
+
borderDash: [4, 4],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
options: {
|
|
86
|
+
responsive: true, maintainAspectRatio: false,
|
|
87
|
+
scales: {
|
|
88
|
+
y: { grid: { color: t.soft, drawBorder: false }, ticks: { color: t.light } },
|
|
89
|
+
x: { grid: { display: false }, ticks: { color: t.light } },
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
}),
|
|
93
|
+
|
|
94
|
+
'channels-bar': (t) => ({
|
|
95
|
+
type: 'bar',
|
|
96
|
+
data: {
|
|
97
|
+
labels: ['Direct','Search','Social','Email','Affiliate','Display','Other'],
|
|
98
|
+
datasets: [{
|
|
99
|
+
label: 'Visitors',
|
|
100
|
+
data: [124, 88, 72, 54, 36, 28, 18],
|
|
101
|
+
backgroundColor: [t.primary, t.success, t.purple, t.info, t.warning, t.pink, t.muted],
|
|
102
|
+
borderRadius: 6, borderSkipped: false,
|
|
103
|
+
}],
|
|
104
|
+
},
|
|
105
|
+
options: {
|
|
106
|
+
responsive: true, maintainAspectRatio: false,
|
|
107
|
+
plugins: { legend: { display: false } },
|
|
108
|
+
scales: {
|
|
109
|
+
y: { grid: { color: t.soft, drawBorder: false }, ticks: { color: t.light } },
|
|
110
|
+
x: { grid: { display: false }, ticks: { color: t.muted } },
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
}),
|
|
114
|
+
|
|
115
|
+
'devices-doughnut': (t) => ({
|
|
116
|
+
type: 'doughnut',
|
|
117
|
+
data: {
|
|
118
|
+
labels: ['Desktop','Mobile','Tablet'],
|
|
119
|
+
datasets: [{
|
|
120
|
+
data: [62, 30, 8],
|
|
121
|
+
backgroundColor: [t.primary, t.purple, t.info],
|
|
122
|
+
borderColor: t.bg,
|
|
123
|
+
borderWidth: 3,
|
|
124
|
+
}],
|
|
125
|
+
},
|
|
126
|
+
options: {
|
|
127
|
+
responsive: true, maintainAspectRatio: false,
|
|
128
|
+
cutout: '68%',
|
|
129
|
+
plugins: { legend: { position: 'right' } },
|
|
130
|
+
},
|
|
131
|
+
}),
|
|
132
|
+
|
|
133
|
+
'sources-radar': (t) => ({
|
|
134
|
+
type: 'radar',
|
|
135
|
+
data: {
|
|
136
|
+
labels: ['Speed','UX','Reliability','Pricing','Support','Features'],
|
|
137
|
+
datasets: [
|
|
138
|
+
{
|
|
139
|
+
label: 'Adminator',
|
|
140
|
+
data: [85, 92, 88, 76, 80, 95],
|
|
141
|
+
borderColor: t.primary,
|
|
142
|
+
backgroundColor: `${t.primary}30`,
|
|
143
|
+
pointBackgroundColor: t.primary,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
label: 'Competitor',
|
|
147
|
+
data: [70, 65, 75, 82, 60, 70],
|
|
148
|
+
borderColor: t.muted,
|
|
149
|
+
backgroundColor: `${t.muted}20`,
|
|
150
|
+
pointBackgroundColor: t.muted,
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
options: {
|
|
155
|
+
responsive: true, maintainAspectRatio: false,
|
|
156
|
+
scales: {
|
|
157
|
+
r: { angleLines: { color: t.soft }, grid: { color: t.soft }, pointLabels: { color: t.muted, font: { size: 11 } }, ticks: { display: false } },
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
}),
|
|
161
|
+
|
|
162
|
+
'mrr-stacked': (t) => ({
|
|
163
|
+
type: 'bar',
|
|
164
|
+
data: {
|
|
165
|
+
labels: ['Q1','Q2','Q3','Q4'],
|
|
166
|
+
datasets: [
|
|
167
|
+
{ label: 'Starter', data: [12, 18, 22, 28], backgroundColor: t.info, borderRadius: 4, stack: 'a' },
|
|
168
|
+
{ label: 'Pro', data: [38, 48, 56, 64], backgroundColor: t.primary, borderRadius: 4, stack: 'a' },
|
|
169
|
+
{ label: 'Team', data: [22, 28, 36, 44], backgroundColor: t.purple, borderRadius: 4, stack: 'a' },
|
|
170
|
+
],
|
|
171
|
+
},
|
|
172
|
+
options: {
|
|
173
|
+
responsive: true, maintainAspectRatio: false,
|
|
174
|
+
scales: {
|
|
175
|
+
y: { stacked: true, grid: { color: t.soft, drawBorder: false }, ticks: { color: t.light } },
|
|
176
|
+
x: { stacked: true, grid: { display: false }, ticks: { color: t.muted } },
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
}),
|
|
180
|
+
|
|
181
|
+
'dashboard-monthly': (t) => ({
|
|
182
|
+
type: 'line',
|
|
183
|
+
data: {
|
|
184
|
+
labels: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
|
|
185
|
+
datasets: [{
|
|
186
|
+
label: 'Revenue',
|
|
187
|
+
data: [42, 38, 56, 50, 78, 70, 96, 88, 118, 102, 144, 168],
|
|
188
|
+
borderColor: t.primary,
|
|
189
|
+
backgroundColor: `${t.primary}24`,
|
|
190
|
+
tension: 0.4,
|
|
191
|
+
fill: true,
|
|
192
|
+
pointRadius: 0,
|
|
193
|
+
pointHoverRadius: 5,
|
|
194
|
+
pointHoverBackgroundColor: t.primary,
|
|
195
|
+
pointHoverBorderColor: t.bg,
|
|
196
|
+
pointHoverBorderWidth: 3,
|
|
197
|
+
borderWidth: 2.5,
|
|
198
|
+
}],
|
|
199
|
+
},
|
|
200
|
+
options: {
|
|
201
|
+
responsive: true, maintainAspectRatio: false,
|
|
202
|
+
plugins: { legend: { display: false } },
|
|
203
|
+
scales: {
|
|
204
|
+
y: { grid: { color: t.soft, drawBorder: false }, ticks: { color: t.light, maxTicksLimit: 4 } },
|
|
205
|
+
x: { grid: { display: false }, ticks: { color: t.light, font: { size: 10 } } },
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
|
|
210
|
+
'sessions-area': (t) => ({
|
|
211
|
+
type: 'line',
|
|
212
|
+
data: {
|
|
213
|
+
labels: Array.from({length: 30}, (_, i) => `${i + 1}`),
|
|
214
|
+
datasets: [{
|
|
215
|
+
label: 'Sessions',
|
|
216
|
+
data: [120, 132, 110, 145, 162, 158, 175, 188, 172, 195, 210, 224, 218, 240, 256, 248, 272, 290, 282, 308, 322, 318, 340, 358, 352, 376, 392, 388, 410, 432],
|
|
217
|
+
borderColor: t.success,
|
|
218
|
+
backgroundColor: `${t.success}24`,
|
|
219
|
+
tension: 0.4,
|
|
220
|
+
fill: true,
|
|
221
|
+
pointRadius: 0,
|
|
222
|
+
borderWidth: 2,
|
|
223
|
+
}],
|
|
224
|
+
},
|
|
225
|
+
options: {
|
|
226
|
+
responsive: true, maintainAspectRatio: false,
|
|
227
|
+
plugins: { legend: { display: false } },
|
|
228
|
+
scales: {
|
|
229
|
+
y: { grid: { color: t.soft, drawBorder: false }, ticks: { color: t.light } },
|
|
230
|
+
x: { grid: { display: false }, ticks: { color: t.light, maxTicksLimit: 6 } },
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
}),
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const instances = new Map();
|
|
237
|
+
|
|
238
|
+
function buildAll() {
|
|
239
|
+
const t = tokens();
|
|
240
|
+
applyDefaults(t);
|
|
241
|
+
document.querySelectorAll('canvas[data-chart-key]').forEach((canvas) => {
|
|
242
|
+
const key = canvas.getAttribute('data-chart-key');
|
|
243
|
+
const seed = SEEDS[key];
|
|
244
|
+
if (!seed) return;
|
|
245
|
+
const existing = instances.get(canvas);
|
|
246
|
+
if (existing) existing.destroy();
|
|
247
|
+
instances.set(canvas, new Chart(canvas, seed(t)));
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function initCharts() {
|
|
252
|
+
if (!document.querySelector('canvas[data-chart-key]')) return;
|
|
253
|
+
buildAll();
|
|
254
|
+
// Re-render charts whenever the theme changes.
|
|
255
|
+
const observer = new MutationObserver((records) => {
|
|
256
|
+
if (records.some((r) => r.attributeName === 'data-theme')) buildAll();
|
|
257
|
+
});
|
|
258
|
+
observer.observe(document.documentElement, { attributes: true });
|
|
259
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 2026 entry point.
|
|
3
|
+
*
|
|
4
|
+
* Order of operations:
|
|
5
|
+
* 1. Import the SCSS bundle (extracted to its own CSS file in production).
|
|
6
|
+
* 2. Mount the shell (sidebar/topbar/footer) into the page placeholders.
|
|
7
|
+
* 3. Wire shell behaviors (theme toggle, dropdowns, nav groups, todos).
|
|
8
|
+
*
|
|
9
|
+
* Each *-2026.html page has data-active + data-crumbs on <body> and three
|
|
10
|
+
* placeholder divs ([data-shell-sidebar], [data-shell-topbar],
|
|
11
|
+
* [data-shell-footer]) inside the .shell wrapper.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import '../../styles/2026/index.scss';
|
|
15
|
+
import { mountShell } from './Shell.js';
|
|
16
|
+
import { initShellBehaviors } from './init.js';
|
|
17
|
+
import { initCharts } from './charts.js';
|
|
18
|
+
import { initVectorMaps } from './maps.js';
|
|
19
|
+
import { initCalendarPage } from './calendar.js';
|
|
20
|
+
import { initPalette } from './palette.js';
|
|
21
|
+
|
|
22
|
+
function start() {
|
|
23
|
+
mountShell();
|
|
24
|
+
initShellBehaviors();
|
|
25
|
+
initPalette();
|
|
26
|
+
initCharts();
|
|
27
|
+
initVectorMaps();
|
|
28
|
+
initCalendarPage();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (document.readyState === 'loading') {
|
|
32
|
+
document.addEventListener('DOMContentLoaded', start);
|
|
33
|
+
} else {
|
|
34
|
+
start();
|
|
35
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 2026 Shell behaviors:
|
|
3
|
+
* - theme toggle (icon swap + persistence)
|
|
4
|
+
* - dropdown open/close
|
|
5
|
+
* - sidebar nav-group expand/collapse
|
|
6
|
+
* - hero date population
|
|
7
|
+
* - todo checkbox state
|
|
8
|
+
*
|
|
9
|
+
* The early-paint <script> in each page body sets the initial data-theme
|
|
10
|
+
* attribute to avoid a flash; this module only handles runtime toggles.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const STORE_KEY = 'dash26-theme';
|
|
14
|
+
|
|
15
|
+
const SUN_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>';
|
|
16
|
+
const MOON_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>';
|
|
17
|
+
|
|
18
|
+
function initThemeToggle() {
|
|
19
|
+
const root = document.documentElement;
|
|
20
|
+
const toggle = document.getElementById('themeToggle');
|
|
21
|
+
if (!toggle) return;
|
|
22
|
+
|
|
23
|
+
const update = () => {
|
|
24
|
+
toggle.innerHTML = root.getAttribute('data-theme') === 'dark' ? SUN_ICON : MOON_ICON;
|
|
25
|
+
};
|
|
26
|
+
update();
|
|
27
|
+
|
|
28
|
+
toggle.addEventListener('click', () => {
|
|
29
|
+
const next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
|
|
30
|
+
root.setAttribute('data-theme', next);
|
|
31
|
+
try { localStorage.setItem(STORE_KEY, next); } catch { /* localStorage may be unavailable */ }
|
|
32
|
+
update();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function initHeroDate() {
|
|
37
|
+
const el = document.getElementById('heroDate');
|
|
38
|
+
if (!el) return;
|
|
39
|
+
const fmt = new Intl.DateTimeFormat('en-US', {
|
|
40
|
+
weekday: 'long', month: 'long', day: 'numeric', year: 'numeric',
|
|
41
|
+
}).format(new Date());
|
|
42
|
+
const parts = fmt.replace(/,/g, '').split(' ');
|
|
43
|
+
el.textContent = `${parts[0]} · ${parts[1]} ${parts[2]} · ${parts[3]}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function initNavGroups() {
|
|
47
|
+
// Rail mode (sidebar collapsed to 72px) renders the submenu as a flyout,
|
|
48
|
+
// so only one should be open at a time and outside-clicks should dismiss it.
|
|
49
|
+
// The flyout is position:fixed (see _responsive.scss) — we position it here
|
|
50
|
+
// via getBoundingClientRect so it survives a scrolling sidebar and clamps to
|
|
51
|
+
// the viewport bottom on short screens (e.g. 750×590).
|
|
52
|
+
const railMQ = window.matchMedia('(min-width: 721px) and (max-width: 1100px)');
|
|
53
|
+
|
|
54
|
+
const positionFlyout = (group) => {
|
|
55
|
+
const trigger = group.querySelector('[data-nav-toggle]');
|
|
56
|
+
const submenu = group.querySelector('.nav-submenu');
|
|
57
|
+
if (!trigger || !submenu) return;
|
|
58
|
+
const r = trigger.getBoundingClientRect();
|
|
59
|
+
const h = submenu.offsetHeight; // natural content height (max-height: 400 clamp)
|
|
60
|
+
let top = Math.round(r.top);
|
|
61
|
+
const maxTop = window.innerHeight - h - 8;
|
|
62
|
+
if (top > maxTop) top = Math.max(8, maxTop);
|
|
63
|
+
submenu.style.left = `${Math.round(r.right + 10)}px`;
|
|
64
|
+
submenu.style.top = `${top}px`;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
document.querySelectorAll('[data-nav-toggle]').forEach((a) => {
|
|
68
|
+
a.addEventListener('click', (e) => {
|
|
69
|
+
const group = a.closest('[data-nav-group]');
|
|
70
|
+
if (!group) return;
|
|
71
|
+
e.stopPropagation();
|
|
72
|
+
const willOpen = !group.classList.contains('is-open');
|
|
73
|
+
if (railMQ.matches) {
|
|
74
|
+
document.querySelectorAll('[data-nav-group].is-open').forEach((g) => {
|
|
75
|
+
if (g !== group) g.classList.remove('is-open');
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
group.classList.toggle('is-open', willOpen);
|
|
79
|
+
if (willOpen && railMQ.matches) positionFlyout(group);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
document.addEventListener('click', (e) => {
|
|
84
|
+
if (!railMQ.matches) return;
|
|
85
|
+
if (e.target.closest('[data-nav-group]')) return;
|
|
86
|
+
document.querySelectorAll('[data-nav-group].is-open').forEach((g) => g.classList.remove('is-open'));
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Keep the open flyout pinned to its trigger when the sidebar scrolls or the viewport resizes.
|
|
90
|
+
const reposition = () => {
|
|
91
|
+
if (!railMQ.matches) return;
|
|
92
|
+
document.querySelectorAll('[data-nav-group].is-open').forEach(positionFlyout);
|
|
93
|
+
};
|
|
94
|
+
const sidebar = document.querySelector('.d-sidebar');
|
|
95
|
+
if (sidebar) sidebar.addEventListener('scroll', reposition, { passive: true });
|
|
96
|
+
window.addEventListener('resize', reposition);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function initDropdowns() {
|
|
100
|
+
const closeAll = (except) => {
|
|
101
|
+
document.querySelectorAll('.dd-wrap.is-open').forEach((w) => {
|
|
102
|
+
if (w !== except) w.classList.remove('is-open');
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
document.querySelectorAll('[data-dropdown]').forEach((trigger) => {
|
|
107
|
+
const wrap = trigger.closest('.dd-wrap');
|
|
108
|
+
if (!wrap) return;
|
|
109
|
+
trigger.addEventListener('click', (e) => {
|
|
110
|
+
e.stopPropagation();
|
|
111
|
+
const willOpen = !wrap.classList.contains('is-open');
|
|
112
|
+
closeAll(wrap);
|
|
113
|
+
wrap.classList.toggle('is-open', willOpen);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
document.addEventListener('click', (e) => {
|
|
118
|
+
if (!e.target.closest('.dd-wrap')) closeAll();
|
|
119
|
+
});
|
|
120
|
+
document.addEventListener('keydown', (e) => {
|
|
121
|
+
if (e.key === 'Escape') closeAll();
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function initTodos() {
|
|
126
|
+
document.querySelectorAll('.todo-check').forEach((cb) => {
|
|
127
|
+
cb.addEventListener('change', () => {
|
|
128
|
+
const item = cb.closest('.todo-item');
|
|
129
|
+
if (item) item.classList.toggle('is-done', cb.checked);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function initAccordions() {
|
|
135
|
+
document.querySelectorAll('[data-accordion-trigger]').forEach((trigger) => {
|
|
136
|
+
trigger.addEventListener('click', () => {
|
|
137
|
+
const item = trigger.closest('[data-accordion]');
|
|
138
|
+
if (item) item.classList.toggle('is-open');
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function initTabGroups() {
|
|
144
|
+
// Tabs that opt in by sharing a [data-tab-group] container.
|
|
145
|
+
// Each .tab inside gets click → toggles is-active on siblings and
|
|
146
|
+
// matches a sibling .tab-panel by data-tab-target.
|
|
147
|
+
document.querySelectorAll('[data-tab-group]').forEach((group) => {
|
|
148
|
+
const tabs = group.querySelectorAll('.tab');
|
|
149
|
+
const panels = group.querySelectorAll('.tab-panel');
|
|
150
|
+
tabs.forEach((tab) => {
|
|
151
|
+
tab.addEventListener('click', (e) => {
|
|
152
|
+
e.preventDefault();
|
|
153
|
+
const target = tab.getAttribute('data-tab-target');
|
|
154
|
+
tabs.forEach((t) => t.classList.toggle('is-active', t === tab));
|
|
155
|
+
panels.forEach((p) => p.classList.toggle('is-active', p.getAttribute('data-tab-id') === target));
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function initMobileDrawer() {
|
|
162
|
+
// Mobile: hamburger button (rendered in topbar at ≤720px) opens an off-canvas
|
|
163
|
+
// sidebar drawer. Closes on backdrop click, Esc, or selecting a nav link.
|
|
164
|
+
const body = document.body;
|
|
165
|
+
if (!body) return;
|
|
166
|
+
|
|
167
|
+
// Inject a backdrop element once (used by CSS via body.has-drawer-open).
|
|
168
|
+
if (!document.querySelector('.drawer-backdrop')) {
|
|
169
|
+
const backdrop = document.createElement('div');
|
|
170
|
+
backdrop.className = 'drawer-backdrop';
|
|
171
|
+
backdrop.setAttribute('aria-hidden', 'true');
|
|
172
|
+
body.appendChild(backdrop);
|
|
173
|
+
backdrop.addEventListener('click', closeDrawer);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function openDrawer() {
|
|
177
|
+
body.classList.add('has-drawer-open');
|
|
178
|
+
}
|
|
179
|
+
function closeDrawer() {
|
|
180
|
+
body.classList.remove('has-drawer-open');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
document.addEventListener('click', (e) => {
|
|
184
|
+
const trigger = e.target.closest('[data-drawer-open]');
|
|
185
|
+
if (trigger) { e.preventDefault(); openDrawer(); return; }
|
|
186
|
+
// Auto-close when a sidebar nav link is selected (so the next page opens cleanly).
|
|
187
|
+
const linkInDrawer = e.target.closest('.d-sidebar a[href]:not([href^="#"]):not([href="javascript:void(0)"])');
|
|
188
|
+
if (body.classList.contains('has-drawer-open') && linkInDrawer) {
|
|
189
|
+
closeDrawer();
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
document.addEventListener('keydown', (e) => {
|
|
194
|
+
if (e.key === 'Escape' && body.classList.contains('has-drawer-open')) closeDrawer();
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function initShellBehaviors() {
|
|
199
|
+
initThemeToggle();
|
|
200
|
+
initHeroDate();
|
|
201
|
+
initNavGroups();
|
|
202
|
+
initDropdowns();
|
|
203
|
+
initTodos();
|
|
204
|
+
initAccordions();
|
|
205
|
+
initTabGroups();
|
|
206
|
+
initMobileDrawer();
|
|
207
|
+
}
|