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.
Files changed (179) hide show
  1. package/CHANGELOG.md +499 -0
  2. package/CLAUDE.md +126 -154
  3. package/README.md +321 -370
  4. package/dist/2026.js +8751 -0
  5. package/dist/2026.js.map +1 -0
  6. package/dist/404.html +36 -16
  7. package/dist/500.html +36 -16
  8. package/dist/assets/static/images/logo.svg +3 -3
  9. package/dist/basic-table.html +152 -699
  10. package/dist/blank.html +42 -507
  11. package/dist/buttons.html +152 -448
  12. package/dist/calendar.html +246 -658
  13. package/dist/charts.html +124 -658
  14. package/dist/chat.html +209 -706
  15. package/dist/compose.html +141 -618
  16. package/dist/datatable.html +467 -991
  17. package/dist/email.html +430 -943
  18. package/dist/forms.html +208 -733
  19. package/dist/google-maps.html +123 -513
  20. package/dist/index.html +436 -1041
  21. package/dist/runtime.js +1299 -0
  22. package/dist/runtime.js.map +1 -0
  23. package/dist/signin.html +92 -92
  24. package/dist/signup.html +106 -91
  25. package/dist/ui.html +268 -897
  26. package/dist/vector-maps.html +132 -511
  27. package/dist/vendor-chartjs.js +14593 -0
  28. package/dist/vendor-chartjs.js.map +1 -0
  29. package/dist/vendor-fullcalendar.js +14793 -0
  30. package/dist/vendor-fullcalendar.js.map +1 -0
  31. package/dist/vendors.js +3758 -0
  32. package/dist/vendors.js.map +1 -0
  33. package/package.json +50 -52
  34. package/src/404.html +35 -15
  35. package/src/500.html +35 -15
  36. package/src/assets/scripts/2026/Shell.js +312 -0
  37. package/src/assets/scripts/2026/calendar.js +123 -0
  38. package/src/assets/scripts/2026/charts.js +259 -0
  39. package/src/assets/scripts/2026/index.js +35 -0
  40. package/src/assets/scripts/2026/init.js +207 -0
  41. package/src/assets/scripts/2026/maps.js +78 -0
  42. package/src/assets/scripts/2026/palette.js +266 -0
  43. package/src/assets/static/images/logo.svg +3 -3
  44. package/src/assets/styles/2026/_animations.scss +14 -0
  45. package/src/assets/styles/2026/_auth.scss +215 -0
  46. package/src/assets/styles/2026/_base.scss +37 -0
  47. package/src/assets/styles/2026/_calendar.scss +380 -0
  48. package/src/assets/styles/2026/_charts.scss +44 -0
  49. package/src/assets/styles/2026/_chat.scss +350 -0
  50. package/src/assets/styles/2026/_components.scss +140 -0
  51. package/src/assets/styles/2026/_dashboard.scss +520 -0
  52. package/src/assets/styles/2026/_data.scss +130 -0
  53. package/src/assets/styles/2026/_dropdowns.scss +128 -0
  54. package/src/assets/styles/2026/_email.scss +599 -0
  55. package/src/assets/styles/2026/_error.scss +98 -0
  56. package/src/assets/styles/2026/_forms.scss +215 -0
  57. package/src/assets/styles/2026/_fullcalendar.scss +134 -0
  58. package/src/assets/styles/2026/_palette.scss +173 -0
  59. package/src/assets/styles/2026/_responsive.scss +229 -0
  60. package/src/assets/styles/2026/_shell.scss +290 -0
  61. package/src/assets/styles/2026/_tokens.scss +80 -0
  62. package/src/assets/styles/2026/_ui.scss +365 -0
  63. package/src/assets/styles/2026/index.scss +23 -0
  64. package/src/basic-table.html +153 -710
  65. package/src/blank.html +42 -517
  66. package/src/buttons.html +152 -458
  67. package/src/calendar.html +246 -668
  68. package/src/charts.html +124 -668
  69. package/src/chat.html +209 -716
  70. package/src/compose.html +142 -629
  71. package/src/datatable.html +466 -1000
  72. package/src/email.html +429 -952
  73. package/src/forms.html +207 -742
  74. package/src/google-maps.html +128 -523
  75. package/src/index.html +438 -1050
  76. package/src/signin.html +92 -92
  77. package/src/signup.html +106 -91
  78. package/src/ui.html +267 -906
  79. package/src/vector-maps.html +133 -522
  80. package/dist/1e59d2330b4c6deb84b3.ttf +0 -0
  81. package/dist/20fd1704ea223900efa9.woff2 +0 -0
  82. package/dist/29b39089170885ae2967.woff +0 -0
  83. package/dist/8b43027f47b20503057d.eot +0 -0
  84. package/dist/9bad94440d49256265a5.eot +0 -0
  85. package/dist/assets/fontawesome-webfont.svg +0 -2671
  86. package/dist/assets/themify.svg +0 -362
  87. package/dist/eda8b94308c6f538f04a.ttf +0 -0
  88. package/dist/f691f37e57f04c152e23.woff +0 -0
  89. package/dist/main.js +0 -61323
  90. package/dist/main.js.map +0 -1
  91. package/src/assets/scripts/app 2.js +0 -645
  92. package/src/assets/scripts/app.js +0 -645
  93. package/src/assets/scripts/charts/chartJS/index.js +0 -148
  94. package/src/assets/scripts/charts/easyPieChart/index.js +0 -200
  95. package/src/assets/scripts/charts/index.js +0 -3
  96. package/src/assets/scripts/charts/sparkline/index.js +0 -208
  97. package/src/assets/scripts/chat/index.js +0 -11
  98. package/src/assets/scripts/components/Chart.js +0 -1390
  99. package/src/assets/scripts/components/Sidebar.js +0 -241
  100. package/src/assets/scripts/constants/colors.js +0 -274
  101. package/src/assets/scripts/datatable/index.js +0 -379
  102. package/src/assets/scripts/datepicker/index.js +0 -302
  103. package/src/assets/scripts/email/index.js +0 -25
  104. package/src/assets/scripts/fullcalendar/index.js +0 -86
  105. package/src/assets/scripts/googleMaps/index.js +0 -93
  106. package/src/assets/scripts/index.js +0 -18
  107. package/src/assets/scripts/masonry/index.js +0 -14
  108. package/src/assets/scripts/popover/index.js +0 -109
  109. package/src/assets/scripts/scrollbar/index.js +0 -10
  110. package/src/assets/scripts/search/index.js +0 -15
  111. package/src/assets/scripts/sidebar/index.js +0 -140
  112. package/src/assets/scripts/skycons/index.js +0 -52
  113. package/src/assets/scripts/ui/index.js +0 -412
  114. package/src/assets/scripts/utils/date.js +0 -242
  115. package/src/assets/scripts/utils/dom.js +0 -349
  116. package/src/assets/scripts/utils/index.js +0 -45
  117. package/src/assets/scripts/utils/theme.js +0 -107
  118. package/src/assets/scripts/vectorMaps/index.js +0 -277
  119. package/src/assets/styles/index.scss +0 -801
  120. package/src/assets/styles/spec/components/easyPieChart.scss +0 -11
  121. package/src/assets/styles/spec/components/footer.scss +0 -4
  122. package/src/assets/styles/spec/components/forms.scss +0 -288
  123. package/src/assets/styles/spec/components/index.scss +0 -9
  124. package/src/assets/styles/spec/components/loader.scss +0 -46
  125. package/src/assets/styles/spec/components/masonry.scss +0 -1
  126. package/src/assets/styles/spec/components/pageContainer.scss +0 -255
  127. package/src/assets/styles/spec/components/progressBar.scss +0 -6
  128. package/src/assets/styles/spec/components/sidebar.scss +0 -642
  129. package/src/assets/styles/spec/components/topbar.scss +0 -455
  130. package/src/assets/styles/spec/generic/base.scss +0 -102
  131. package/src/assets/styles/spec/generic/index.scss +0 -1
  132. package/src/assets/styles/spec/index.scss +0 -4
  133. package/src/assets/styles/spec/screens/chat.scss +0 -147
  134. package/src/assets/styles/spec/screens/email.scss +0 -108
  135. package/src/assets/styles/spec/screens/index.scss +0 -2
  136. package/src/assets/styles/spec/settings/baseColors.scss +0 -103
  137. package/src/assets/styles/spec/settings/borders.scss +0 -6
  138. package/src/assets/styles/spec/settings/breakpoints.scss +0 -26
  139. package/src/assets/styles/spec/settings/fonts.scss +0 -4
  140. package/src/assets/styles/spec/settings/index.scss +0 -4
  141. package/src/assets/styles/spec/settings/materialColors.scss +0 -550
  142. package/src/assets/styles/spec/tools/index.scss +0 -1
  143. package/src/assets/styles/spec/tools/mixins/clearfix.scss +0 -15
  144. package/src/assets/styles/spec/tools/mixins/index.scss +0 -3
  145. package/src/assets/styles/spec/tools/mixins/mediaQueriesRanges.scss +0 -58
  146. package/src/assets/styles/spec/tools/mixins/placeholder.scss +0 -10
  147. package/src/assets/styles/spec/utils/colors.scss +0 -33
  148. package/src/assets/styles/spec/utils/index.scss +0 -2
  149. package/src/assets/styles/spec/utils/layout/helpers/border.scss +0 -78
  150. package/src/assets/styles/spec/utils/layout/helpers/flex.scss +0 -220
  151. package/src/assets/styles/spec/utils/layout/helpers/index.scss +0 -11
  152. package/src/assets/styles/spec/utils/layout/helpers/layout.scss +0 -137
  153. package/src/assets/styles/spec/utils/layout/helpers/lists.scss +0 -23
  154. package/src/assets/styles/spec/utils/layout/helpers/margin.scss +0 -266
  155. package/src/assets/styles/spec/utils/layout/helpers/objects.scss +0 -91
  156. package/src/assets/styles/spec/utils/layout/helpers/padding.scss +0 -147
  157. package/src/assets/styles/spec/utils/layout/helpers/positions.scss +0 -118
  158. package/src/assets/styles/spec/utils/layout/helpers/pseudo.scss +0 -6
  159. package/src/assets/styles/spec/utils/layout/helpers/sizes.scss +0 -157
  160. package/src/assets/styles/spec/utils/layout/helpers/typography.scss +0 -127
  161. package/src/assets/styles/spec/utils/layout/index.scss +0 -3
  162. package/src/assets/styles/spec/utils/layout/mixins/generateResponsive.scss +0 -25
  163. package/src/assets/styles/spec/utils/layout/mixins/index.scss +0 -2
  164. package/src/assets/styles/spec/utils/layout/mixins/mediaQueryCondition.scss +0 -28
  165. package/src/assets/styles/spec/utils/layout/utils/center.scss +0 -54
  166. package/src/assets/styles/spec/utils/layout/utils/gap.scss +0 -229
  167. package/src/assets/styles/spec/utils/layout/utils/index.scss +0 -5
  168. package/src/assets/styles/spec/utils/layout/utils/layers.scss +0 -5
  169. package/src/assets/styles/spec/utils/layout/utils/peers.scss +0 -35
  170. package/src/assets/styles/utils/mobile.scss +0 -954
  171. package/src/assets/styles/utils/theme.css +0 -97
  172. package/src/assets/styles/vendor/datepicker.scss +0 -183
  173. package/src/assets/styles/vendor/font-awesome.css +0 -2337
  174. package/src/assets/styles/vendor/fullcalendar.scss +0 -217
  175. package/src/assets/styles/vendor/index.scss +0 -8
  176. package/src/assets/styles/vendor/jquery.datatables.scss +0 -162
  177. package/src/assets/styles/vendor/perfectScrollbar.scss +0 -4
  178. package/src/assets/styles/vendor/sparkline.scss +0 -6
  179. 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
+ }