webspresso 0.0.96 → 0.0.98
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/.agents/ADMIN.md +153 -5
- package/.agents/ARCHITECTURE.md +10 -0
- package/.agents/CLI.md +195 -1
- package/.agents/PLUGINS.md +153 -9
- package/.agents/QUEUE.md +43 -0
- package/.agents/skills/webspresso-admin-and-plugins/SKILL.md +122 -0
- package/.agents/skills/webspresso-framework/SKILL.md +56 -0
- package/.agents/skills/webspresso-orm-and-database/SKILL.md +119 -0
- package/.agents/skills/webspresso-routing-and-api/SKILL.md +114 -0
- package/.agents/skills/webspresso-services/SKILL.md +101 -0
- package/bin/commands/add-esbuild.js +80 -0
- package/bin/commands/add-tailwind.js +76 -31
- package/bin/commands/admin-password.js +2 -1
- package/bin/commands/admin-setup.js +1 -1
- package/bin/commands/agents-init.js +51 -11
- package/bin/commands/analytics-prune.js +57 -0
- package/bin/commands/api.js +207 -15
- package/bin/commands/audit-prune.js +1 -1
- package/bin/commands/build.js +99 -0
- package/bin/commands/dev.js +64 -35
- package/bin/commands/doctor.js +26 -0
- package/bin/commands/email-prune.js +47 -0
- package/bin/commands/favicon-generate.js +1 -1
- package/bin/commands/make-job.js +86 -0
- package/bin/commands/make-model.js +250 -0
- package/bin/commands/new.js +45 -28
- package/bin/commands/openapi.js +102 -0
- package/bin/commands/orm-map.js +5 -4
- package/bin/commands/page.js +6 -3
- package/bin/commands/polar-migrate.js +1 -1
- package/bin/commands/queue-prune.js +59 -0
- package/bin/commands/routes.js +52 -0
- package/bin/commands/seed.js +1 -1
- package/bin/commands/service.js +6 -3
- package/bin/commands/typegen.js +106 -0
- package/bin/commands/upload-prune.js +79 -0
- package/bin/webspresso.js +22 -0
- package/core/applySchema.js +13 -6
- package/core/auth/hash.js +31 -17
- package/core/compression/middleware.js +23 -17
- package/core/openapi/build-from-api-routes.js +160 -29
- package/core/orm/cache/fingerprint.js +15 -1
- package/core/orm/cache/memory-provider.js +27 -2
- package/core/orm/index.js +12 -7
- package/core/orm/model.js +49 -7
- package/core/queue/adapters/database.js +94 -0
- package/core/queue/adapters/memory.js +62 -0
- package/core/queue/adapters/redis.js +80 -0
- package/core/queue/manager.js +123 -0
- package/core/ssr/stream.js +11 -0
- package/index.d.ts +660 -10
- package/index.js +44 -96
- package/package.json +32 -12
- package/plugins/admin-panel/api.js +279 -17
- package/plugins/admin-panel/app.js +124 -31
- package/plugins/admin-panel/client/parts/01-state-api-breadcrumb.js +314 -9
- package/plugins/admin-panel/client/parts/04-field-renderers.js +264 -67
- package/plugins/admin-panel/client/parts/05-rich-text-file-helpers.js +10 -5
- package/plugins/admin-panel/client/parts/06-login-setup-forms.js +119 -28
- package/plugins/admin-panel/client/parts/07-model-list.js +49 -8
- package/plugins/admin-panel/client/parts/08-record-list.js +243 -23
- package/plugins/admin-panel/client/parts/09-record-form.js +59 -12
- package/plugins/admin-panel/client/parts/10-export-registry.js +23 -0
- package/plugins/admin-panel/core/admin-module.js +13 -7
- package/plugins/admin-panel/core/api-extensions.js +34 -2
- package/plugins/admin-panel/core/registry.js +96 -16
- package/plugins/admin-panel/index.js +96 -0
- package/plugins/admin-panel/lib/login-rate-limiter.js +119 -0
- package/plugins/admin-panel/modules/admin-users.js +16 -1
- package/plugins/admin-panel/modules/bulk-actions.js +13 -5
- package/plugins/admin-panel/modules/custom-pages.js +492 -3
- package/plugins/admin-panel/modules/dashboard.js +283 -0
- package/plugins/admin-panel/modules/menu.js +56 -18
- package/plugins/admin-panel/modules/user-management.js +24 -6
- package/plugins/audit-log/admin-component.js +11 -0
- package/plugins/audit-log/api-handlers.js +34 -0
- package/plugins/audit-log/index.js +129 -3
- package/plugins/audit-log/purge.js +60 -10
- package/plugins/basic-auth/index.js +3 -1
- package/plugins/content/client/inline-edit.js +12 -12
- package/plugins/cors.js +6 -7
- package/plugins/dashboard/app.js +152 -27
- package/plugins/dashboard/index.js +143 -39
- package/plugins/dashboard/styles.js +78 -0
- package/plugins/data-exchange/export-xlsx.js +9 -2
- package/plugins/data-exchange/import.js +9 -2
- package/plugins/data-exchange/index.js +6 -3
- package/plugins/email/index.js +132 -2
- package/plugins/email/purge.js +60 -10
- package/plugins/file-manager/core.js +1 -1
- package/plugins/file-manager/security.js +33 -23
- package/plugins/index.js +49 -87
- package/plugins/mcp/transports/sse.js +4 -2
- package/plugins/orm-cache-admin/index.js +4 -2
- package/plugins/queue/index.js +11 -0
- package/plugins/schema-explorer/admin-component.js +714 -0
- package/plugins/schema-explorer/relations-graph.js +205 -0
- package/plugins/schema-explorer.js +248 -69
- package/plugins/site-analytics/admin-component.js +30 -8
- package/plugins/site-analytics/api-handlers.js +42 -0
- package/plugins/site-analytics/index.js +147 -2
- package/plugins/site-analytics/purge.js +168 -0
- package/plugins/swagger.js +261 -13
- package/plugins/upload/index.js +26 -0
- package/plugins/upload/orphan-pruner.js +292 -0
- package/src/api/api-loader.js +3 -1
- package/src/bundle/index.js +517 -0
- package/src/bundle/manifest.js +165 -0
- package/src/dev/banner.js +296 -0
- package/src/dev/colors.js +70 -0
- package/src/dev/index.js +62 -0
- package/src/dev/profiler.js +354 -0
- package/src/dev/typegen.js +497 -0
- package/src/dev-reload/index.js +611 -0
- package/src/file-router.js +213 -63
- package/src/helpers.js +958 -98
- package/src/njk-frontmatter.js +12 -0
- package/src/pages/page-loader.js +61 -11
- package/src/server.js +629 -63
- package/src/services/discovery.js +6 -1
- package/src/services/index.js +6 -13
|
@@ -22,12 +22,15 @@ function registerDefaultBulkActions(options) {
|
|
|
22
22
|
confirm: true,
|
|
23
23
|
confirmMessage: 'Restore the selected records?',
|
|
24
24
|
handler: async (records, modelName, { db }) => {
|
|
25
|
+
const targetModel = typeof modelName === 'object' && modelName !== null
|
|
26
|
+
? (modelName.name || modelName.modelName || modelName.model || String(modelName))
|
|
27
|
+
: String(modelName);
|
|
25
28
|
const { getModel } = require('../../../core/orm/model');
|
|
26
|
-
const model = db.getModel ? db.getModel(
|
|
29
|
+
const model = db.getModel ? db.getModel(targetModel) : getModel(targetModel);
|
|
27
30
|
if (!model?.scopes?.softDelete) {
|
|
28
31
|
return { message: 'Model does not support restore', error: true };
|
|
29
32
|
}
|
|
30
|
-
const repo = db.getRepository(
|
|
33
|
+
const repo = db.getRepository(targetModel);
|
|
31
34
|
let restored = 0;
|
|
32
35
|
for (const record of records) {
|
|
33
36
|
try {
|
|
@@ -48,9 +51,11 @@ function registerDefaultBulkActions(options) {
|
|
|
48
51
|
color: 'red',
|
|
49
52
|
models: '*',
|
|
50
53
|
confirm: true,
|
|
51
|
-
confirmMessage: 'Are you sure you want to delete the selected records? This action cannot be undone.',
|
|
52
54
|
handler: async (records, model, { db }) => {
|
|
53
|
-
const
|
|
55
|
+
const targetModel = typeof model === 'object' && model !== null
|
|
56
|
+
? (model.name || model.modelName || model.model || String(model))
|
|
57
|
+
: String(model);
|
|
58
|
+
const repo = db.getRepository(targetModel);
|
|
54
59
|
let deleted = 0;
|
|
55
60
|
|
|
56
61
|
for (const record of records) {
|
|
@@ -131,7 +136,10 @@ function createFieldUpdateBulkAction(modelName, field, value, options = {}) {
|
|
|
131
136
|
confirm,
|
|
132
137
|
confirmMessage,
|
|
133
138
|
handler: async (records, model, { db }) => {
|
|
134
|
-
const
|
|
139
|
+
const targetModel = typeof model === 'object' && model !== null
|
|
140
|
+
? (model.name || model.modelName || model.model || String(model))
|
|
141
|
+
: String(model);
|
|
142
|
+
const repo = db.getRepository(targetModel);
|
|
135
143
|
let updated = 0;
|
|
136
144
|
|
|
137
145
|
for (const record of records) {
|
|
@@ -154,6 +154,480 @@ function createCustomPageApiHandlers(options) {
|
|
|
154
154
|
*/
|
|
155
155
|
function generateCustomPageComponent() {
|
|
156
156
|
return `
|
|
157
|
+
// Developer Error Boundary for Custom Pages
|
|
158
|
+
function renderCustomPageError(page, err) {
|
|
159
|
+
var isFullscreen = page && page.layout === false;
|
|
160
|
+
var pageTitle = (page && page.title) || (page && page.id) || 'Custom Page';
|
|
161
|
+
var pageId = (page && page.id) || 'unknown';
|
|
162
|
+
var errorMsg = (err && (err.message || String(err))) || 'Unknown error';
|
|
163
|
+
var stack = (err && err.stack) || '';
|
|
164
|
+
|
|
165
|
+
var errorCard = m('div.max-w-4xl.mx-auto.bg-white.dark:bg-slate-900.rounded-xl.shadow-lg.border.border-red-200.dark:border-red-900/60.overflow-hidden', [
|
|
166
|
+
// Header
|
|
167
|
+
m('div.px-6.py-4.bg-red-50.dark:bg-red-950/60.border-b.border-red-100.dark:border-red-900/40.flex.items-center.justify-between.gap-4', [
|
|
168
|
+
m('div.flex.items-center.gap-3', [
|
|
169
|
+
m('div.w-9.h-9.rounded-lg.bg-red-100.dark:bg-red-900/80.text-red-600.dark:text-red-300.flex.items-center.justify-center.flex-shrink-0', [
|
|
170
|
+
m('svg.w-5.h-5', { fill: 'none', stroke: 'currentColor', viewBox: '0 0 24 24' },
|
|
171
|
+
m('path', { 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2', d: 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z' })
|
|
172
|
+
)
|
|
173
|
+
]),
|
|
174
|
+
m('div', [
|
|
175
|
+
m('h3.text-base.font-bold.text-red-900.dark:text-red-200', 'Custom Page Component Error'),
|
|
176
|
+
m('p.text-xs.text-red-600.dark:text-red-400', 'Page ID: ' + pageId + (pageTitle !== pageId ? ' (' + pageTitle + ')' : ''))
|
|
177
|
+
])
|
|
178
|
+
]),
|
|
179
|
+
m('span.px-2.5.py-1.rounded.text-xs.font-mono.font-semibold.bg-red-200/60.dark:bg-red-900.text-red-800.dark:text-red-200', 'Error Boundary')
|
|
180
|
+
]),
|
|
181
|
+
|
|
182
|
+
// Body
|
|
183
|
+
m('div.p-6.space-y-4', [
|
|
184
|
+
m('div.p-4.rounded-lg.bg-red-50/50.dark:bg-red-950/30.border.border-red-200.dark:border-red-900/40', [
|
|
185
|
+
m('div.text-xs.font-semibold.uppercase.tracking-wider.text-red-700.dark:text-red-400.mb-1', 'Exception Message'),
|
|
186
|
+
m('div.text-sm.font-semibold.text-red-900.dark:text-red-200.break-words', errorMsg)
|
|
187
|
+
]),
|
|
188
|
+
|
|
189
|
+
stack ? m('div.space-y-1.5', [
|
|
190
|
+
m('div.text-xs.font-semibold.uppercase.tracking-wider.text-gray-500.dark:text-slate-400', 'Stack Trace'),
|
|
191
|
+
m('pre.p-4.rounded-lg.bg-slate-900.text-red-300.dark:text-red-400.text-xs.font-mono.overflow-x-auto.leading-relaxed.border.border-slate-800.max-h-80.overflow-y-auto', stack)
|
|
192
|
+
]) : null,
|
|
193
|
+
|
|
194
|
+
// Actions
|
|
195
|
+
m('div.pt-3.flex.items-center.justify-end.gap-3.border-t.border-gray-100.dark:border-slate-800', [
|
|
196
|
+
m('button.px-4.py-2.text-xs.font-medium.text-gray-700.dark:text-slate-300.bg-gray-100.dark:bg-slate-800.hover:bg-gray-200.dark:hover:bg-slate-700.rounded-lg.transition-colors', {
|
|
197
|
+
onclick: function() { m.route.set('/'); }
|
|
198
|
+
}, 'Back to Dashboard'),
|
|
199
|
+
m('button.inline-flex.items-center.gap-1.5.px-4.py-2.text-xs.font-medium.text-white.bg-red-600.hover:bg-red-700.rounded-lg.shadow-sm.transition-colors', {
|
|
200
|
+
onclick: function() { window.location.reload(); }
|
|
201
|
+
}, [
|
|
202
|
+
m('svg.w-3.5.h-3.5', { fill: 'none', stroke: 'currentColor', viewBox: '0 0 24 24' },
|
|
203
|
+
m('path', { 'stroke-linecap': 'round', 'stroke-linejoin': 'round', 'stroke-width': '2', d: 'M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15' })
|
|
204
|
+
),
|
|
205
|
+
'Reload Page'
|
|
206
|
+
])
|
|
207
|
+
])
|
|
208
|
+
])
|
|
209
|
+
]);
|
|
210
|
+
|
|
211
|
+
if (isFullscreen || typeof Layout === 'undefined') {
|
|
212
|
+
return m('div.min-h-screen.bg-slate-950.p-6.flex.items-center.justify-center', errorCard);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
var breadcrumbs = [{ label: pageTitle, href: (page && page.path) || '/' }];
|
|
216
|
+
return m(Layout, [
|
|
217
|
+
typeof Breadcrumb !== 'undefined' ? m(Breadcrumb, { items: breadcrumbs }) : null,
|
|
218
|
+
errorCard
|
|
219
|
+
]);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Admin UI Component Library for Custom Pages & Widgets
|
|
223
|
+
var AdminUI = {
|
|
224
|
+
StatCard: {
|
|
225
|
+
view: function(vnode) {
|
|
226
|
+
var attrs = vnode.attrs || {};
|
|
227
|
+
var color = attrs.color || 'blue';
|
|
228
|
+
var colorMap = {
|
|
229
|
+
blue: { bg: 'bg-blue-50 dark:bg-blue-950/60', text: 'text-blue-600 dark:text-blue-400' },
|
|
230
|
+
green: { bg: 'bg-emerald-50 dark:bg-emerald-950/60', text: 'text-emerald-600 dark:text-emerald-400' },
|
|
231
|
+
emerald: { bg: 'bg-emerald-50 dark:bg-emerald-950/60', text: 'text-emerald-600 dark:text-emerald-400' },
|
|
232
|
+
indigo: { bg: 'bg-indigo-50 dark:bg-indigo-950/60', text: 'text-indigo-600 dark:text-indigo-400' },
|
|
233
|
+
purple: { bg: 'bg-purple-50 dark:bg-purple-950/60', text: 'text-purple-600 dark:text-purple-400' },
|
|
234
|
+
amber: { bg: 'bg-amber-50 dark:bg-amber-950/60', text: 'text-amber-600 dark:text-amber-400' },
|
|
235
|
+
red: { bg: 'bg-red-50 dark:bg-red-950/60', text: 'text-red-600 dark:text-red-400' },
|
|
236
|
+
gray: { bg: 'bg-gray-50 dark:bg-slate-800', text: 'text-gray-600 dark:text-slate-400' },
|
|
237
|
+
};
|
|
238
|
+
var theme = colorMap[color] || colorMap.blue;
|
|
239
|
+
var trend = attrs.trend || (attrs.change && String(attrs.change).startsWith('+') ? 'up' : attrs.change && String(attrs.change).startsWith('-') ? 'down' : 'neutral');
|
|
240
|
+
|
|
241
|
+
return m('div.bg-white.dark:bg-slate-800.rounded-xl.p-5.shadow-sm.border.border-gray-200.dark:border-slate-700.flex.flex-col.justify-between.gap-3', [
|
|
242
|
+
m('div.flex.items-center.justify-between.gap-2', [
|
|
243
|
+
m('span.text-xs.font-medium.text-gray-500.dark:text-slate-400.truncate', attrs.title || ''),
|
|
244
|
+
attrs.icon ? m('div.w-8.h-8.rounded-lg.flex.items-center.justify-center', { class: theme.bg + ' ' + theme.text }, attrs.icon) : null
|
|
245
|
+
]),
|
|
246
|
+
m('div.flex.items-baseline.justify-between.gap-2', [
|
|
247
|
+
m('div.text-2xl.font-bold.text-gray-900.dark:text-slate-100', attrs.value !== undefined ? String(attrs.value) : '0'),
|
|
248
|
+
attrs.change ? m('span.inline-flex.items-center.gap-0.5.text-xs.font-semibold.px-2.py-0.5.rounded-full', {
|
|
249
|
+
class: trend === 'up'
|
|
250
|
+
? 'bg-emerald-100 text-emerald-800 dark:bg-emerald-950/60 dark:text-emerald-300'
|
|
251
|
+
: trend === 'down'
|
|
252
|
+
? 'bg-red-100 text-red-800 dark:bg-red-950/60 dark:text-red-300'
|
|
253
|
+
: 'bg-gray-100 text-gray-800 dark:bg-slate-700 dark:text-slate-300'
|
|
254
|
+
}, [
|
|
255
|
+
trend === 'up' ? '↑ ' : trend === 'down' ? '↓ ' : '',
|
|
256
|
+
attrs.change
|
|
257
|
+
]) : null
|
|
258
|
+
]),
|
|
259
|
+
attrs.subtitle ? m('p.text-xs.text-gray-400.dark:text-slate-500', attrs.subtitle) : null
|
|
260
|
+
]);
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
Card: {
|
|
265
|
+
view: function(vnode) {
|
|
266
|
+
var attrs = vnode.attrs || {};
|
|
267
|
+
var hasHeader = attrs.title || attrs.subtitle || attrs.actions;
|
|
268
|
+
return m('div.bg-white.dark:bg-slate-800.rounded-xl.shadow-sm.border.border-gray-200.dark:border-slate-700.overflow-hidden', { class: attrs.class }, [
|
|
269
|
+
hasHeader ? m('div.px-6.py-4.border-b.border-gray-100.dark:border-slate-700.flex.items-center.justify-between.gap-4.bg-gray-50/50.dark:bg-slate-800/80', [
|
|
270
|
+
m('div', [
|
|
271
|
+
attrs.title ? m('h3.text-base.font-semibold.text-gray-900.dark:text-slate-100', attrs.title) : null,
|
|
272
|
+
attrs.subtitle ? m('p.text-xs.text-gray-500.dark:text-slate-400.mt-0.5', attrs.subtitle) : null
|
|
273
|
+
]),
|
|
274
|
+
attrs.actions ? m('div.flex.items-center.gap-2', attrs.actions) : null
|
|
275
|
+
]) : null,
|
|
276
|
+
m('div.p-6', vnode.children || attrs.children),
|
|
277
|
+
attrs.footer ? m('div.px-6.py-3.bg-gray-50.dark:bg-slate-900/50.border-t.border-gray-100.dark:border-slate-700', attrs.footer) : null
|
|
278
|
+
]);
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
Button: {
|
|
283
|
+
view: function(vnode) {
|
|
284
|
+
var attrs = vnode.attrs || {};
|
|
285
|
+
var variant = attrs.variant || 'primary';
|
|
286
|
+
var size = attrs.size || 'md';
|
|
287
|
+
var variantClasses = {
|
|
288
|
+
primary: 'bg-blue-600 hover:bg-blue-700 text-white shadow-sm disabled:bg-blue-400',
|
|
289
|
+
secondary: 'bg-gray-100 dark:bg-slate-800 hover:bg-gray-200 dark:hover:bg-slate-700 text-gray-700 dark:text-slate-200',
|
|
290
|
+
danger: 'bg-red-600 hover:bg-red-700 text-white shadow-sm disabled:bg-red-400',
|
|
291
|
+
success: 'bg-emerald-600 hover:bg-emerald-700 text-white shadow-sm disabled:bg-emerald-400',
|
|
292
|
+
outline: 'border border-gray-300 dark:border-slate-600 text-gray-700 dark:text-slate-200 hover:bg-gray-50 dark:hover:bg-slate-700/50',
|
|
293
|
+
};
|
|
294
|
+
var sizeClasses = {
|
|
295
|
+
sm: 'px-2.5 py-1.5 text-xs',
|
|
296
|
+
md: 'px-3.5 py-2 text-sm',
|
|
297
|
+
lg: 'px-4.5 py-2.5 text-base',
|
|
298
|
+
};
|
|
299
|
+
var vCls = variantClasses[variant] || variantClasses.primary;
|
|
300
|
+
var sCls = sizeClasses[size] || sizeClasses.md;
|
|
301
|
+
var baseClasses = 'inline-flex items-center justify-center gap-2 font-medium rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed';
|
|
302
|
+
var fullClass = baseClasses + ' ' + vCls + ' ' + sCls + (attrs.class ? ' ' + attrs.class : '');
|
|
303
|
+
|
|
304
|
+
return m('button', {
|
|
305
|
+
type: attrs.type || 'button',
|
|
306
|
+
disabled: attrs.disabled || attrs.loading,
|
|
307
|
+
onclick: attrs.onclick,
|
|
308
|
+
class: fullClass,
|
|
309
|
+
}, [
|
|
310
|
+
attrs.loading ? m('span.inline-block.animate-spin', '⟳') : (attrs.icon || null),
|
|
311
|
+
vnode.children || attrs.children
|
|
312
|
+
]);
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
Badge: {
|
|
317
|
+
view: function(vnode) {
|
|
318
|
+
var attrs = vnode.attrs || {};
|
|
319
|
+
var color = attrs.color || 'blue';
|
|
320
|
+
var size = attrs.size || 'md';
|
|
321
|
+
var colorMap = {
|
|
322
|
+
green: 'bg-emerald-100 text-emerald-800 dark:bg-emerald-950/60 dark:text-emerald-300 border-emerald-200 dark:border-emerald-800',
|
|
323
|
+
emerald: 'bg-emerald-100 text-emerald-800 dark:bg-emerald-950/60 dark:text-emerald-300 border-emerald-200 dark:border-emerald-800',
|
|
324
|
+
red: 'bg-red-100 text-red-800 dark:bg-red-950/60 dark:text-red-300 border-red-200 dark:border-red-800',
|
|
325
|
+
blue: 'bg-blue-100 text-blue-800 dark:bg-blue-950/60 dark:text-blue-300 border-blue-200 dark:border-blue-800',
|
|
326
|
+
amber: 'bg-amber-100 text-amber-800 dark:bg-amber-950/60 dark:text-amber-300 border-amber-200 dark:border-amber-800',
|
|
327
|
+
yellow: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-950/60 dark:text-yellow-300 border-yellow-200 dark:border-yellow-800',
|
|
328
|
+
purple: 'bg-purple-100 text-purple-800 dark:bg-purple-950/60 dark:text-purple-300 border-purple-200 dark:border-purple-800',
|
|
329
|
+
indigo: 'bg-indigo-100 text-indigo-800 dark:bg-indigo-950/60 dark:text-indigo-300 border-indigo-200 dark:border-indigo-800',
|
|
330
|
+
gray: 'bg-gray-100 text-gray-800 dark:bg-slate-700 dark:text-slate-300 border-gray-200 dark:border-slate-600',
|
|
331
|
+
};
|
|
332
|
+
var cCls = colorMap[color] || colorMap.blue;
|
|
333
|
+
var sCls = size === 'sm' ? 'text-[11px] px-2 py-0.5' : 'text-xs px-2.5 py-1';
|
|
334
|
+
var fullClass = 'inline-flex items-center gap-1.5 font-semibold rounded-full border ' + cCls + ' ' + sCls + (attrs.class ? ' ' + attrs.class : '');
|
|
335
|
+
|
|
336
|
+
return m('span', { class: fullClass }, [
|
|
337
|
+
attrs.dot ? m('span.w-1.5.h-1.5.rounded-full.bg-current') : null,
|
|
338
|
+
attrs.text || vnode.children || attrs.children
|
|
339
|
+
]);
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
Modal: {
|
|
344
|
+
view: function(vnode) {
|
|
345
|
+
var attrs = vnode.attrs || {};
|
|
346
|
+
if (!attrs.isOpen) return null;
|
|
347
|
+
var size = attrs.size || 'md';
|
|
348
|
+
var sizeMap = {
|
|
349
|
+
sm: 'max-w-md',
|
|
350
|
+
md: 'max-w-lg',
|
|
351
|
+
lg: 'max-w-2xl',
|
|
352
|
+
xl: 'max-w-4xl',
|
|
353
|
+
};
|
|
354
|
+
var sCls = sizeMap[size] || sizeMap.md;
|
|
355
|
+
|
|
356
|
+
return m('div.fixed.inset-0.z-50.flex.items-center.justify-center.p-4.overflow-y-auto', [
|
|
357
|
+
m('div.fixed.inset-0.bg-black/50.backdrop-blur-sm.transition-opacity', {
|
|
358
|
+
onclick: function() { if (attrs.onClose) attrs.onClose(); }
|
|
359
|
+
}),
|
|
360
|
+
m('div.relative.bg-white.dark:bg-slate-900.rounded-xl.shadow-2xl.border.border-gray-200.dark:border-slate-700.w-full.overflow-hidden.z-10.transform.transition-all', {
|
|
361
|
+
class: sCls
|
|
362
|
+
}, [
|
|
363
|
+
m('div.px-6.py-4.border-b.border-gray-100.dark:border-slate-800.flex.items-center.justify-between', [
|
|
364
|
+
m('h3.text-lg.font-bold.text-gray-900.dark:text-slate-100', attrs.title || 'Modal'),
|
|
365
|
+
m('button.text-gray-400.hover:text-gray-600.dark:hover:text-slate-300.rounded-lg.p-1', {
|
|
366
|
+
onclick: function() { if (attrs.onClose) attrs.onClose(); }
|
|
367
|
+
}, '✕')
|
|
368
|
+
]),
|
|
369
|
+
m('div.p-6', vnode.children || attrs.children),
|
|
370
|
+
(attrs.onConfirm || attrs.onClose) ? m('div.px-6.py-3.bg-gray-50.dark:bg-slate-800/50.border-t.border-gray-100.dark:border-slate-800.flex.items-center.justify-end.gap-3', [
|
|
371
|
+
attrs.onClose ? m('button.px-4.py-2.text-xs.font-medium.text-gray-700.dark:text-slate-300.bg-gray-100.dark:bg-slate-800.hover:bg-gray-200.dark:hover:bg-slate-700.rounded-lg', {
|
|
372
|
+
onclick: function() { attrs.onClose(); }
|
|
373
|
+
}, attrs.cancelText || 'Cancel') : null,
|
|
374
|
+
attrs.onConfirm ? m('button.px-4.py-2.text-xs.font-medium.text-white.rounded-lg.shadow-sm', {
|
|
375
|
+
class: attrs.confirmVariant === 'danger' ? 'bg-red-600 hover:bg-red-700' : 'bg-blue-600 hover:bg-blue-700',
|
|
376
|
+
onclick: function() { attrs.onConfirm(); }
|
|
377
|
+
}, attrs.confirmText || 'Confirm') : null,
|
|
378
|
+
]) : null
|
|
379
|
+
])
|
|
380
|
+
]);
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
|
|
384
|
+
Tabs: {
|
|
385
|
+
view: function(vnode) {
|
|
386
|
+
var attrs = vnode.attrs || {};
|
|
387
|
+
var tabs = attrs.tabs || [];
|
|
388
|
+
var active = attrs.active;
|
|
389
|
+
var onChange = attrs.onChange || function() {};
|
|
390
|
+
|
|
391
|
+
return m('div.border-b.border-gray-200.dark:border-slate-700.mb-6', [
|
|
392
|
+
m('nav.flex.space-x-8', tabs.map(function(tab) {
|
|
393
|
+
var isActive = tab.id === active;
|
|
394
|
+
return m('button.flex.items-center.gap-2.py-3.px-1.border-b-2.text-sm.font-medium.transition-colors', {
|
|
395
|
+
class: isActive
|
|
396
|
+
? 'border-blue-600 text-blue-600 dark:border-blue-400 dark:text-blue-400'
|
|
397
|
+
: 'border-transparent text-gray-500 dark:text-slate-400 hover:text-gray-700 dark:hover:text-slate-200 hover:border-gray-300',
|
|
398
|
+
onclick: function(e) {
|
|
399
|
+
e.preventDefault();
|
|
400
|
+
onChange(tab.id);
|
|
401
|
+
}
|
|
402
|
+
}, [
|
|
403
|
+
tab.icon ? (typeof tab.icon === 'function' ? tab.icon() : tab.icon) : null,
|
|
404
|
+
m('span', tab.label || tab.title || tab.id),
|
|
405
|
+
tab.badge !== undefined ? m('span.ml-1.px-2.py-0.5.text-xs.rounded-full', {
|
|
406
|
+
class: isActive ? 'bg-blue-100 text-blue-800 dark:bg-blue-950 dark:text-blue-300' : 'bg-gray-100 text-gray-700 dark:bg-slate-700 dark:text-slate-300'
|
|
407
|
+
}, String(tab.badge)) : null
|
|
408
|
+
]);
|
|
409
|
+
}))
|
|
410
|
+
]);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
// Toast Notification State & Handlers
|
|
416
|
+
var toastState = {
|
|
417
|
+
items: [],
|
|
418
|
+
add: function(type, message, options) {
|
|
419
|
+
options = options || {};
|
|
420
|
+
if (typeof type === 'object') {
|
|
421
|
+
options = type;
|
|
422
|
+
type = options.type || 'info';
|
|
423
|
+
message = options.message || '';
|
|
424
|
+
}
|
|
425
|
+
var id = 'toast_' + Math.random().toString(36).substring(2, 9);
|
|
426
|
+
var item = {
|
|
427
|
+
id: id,
|
|
428
|
+
type: type || 'info',
|
|
429
|
+
message: message,
|
|
430
|
+
title: options.title || '',
|
|
431
|
+
duration: options.duration !== undefined ? options.duration : 4000
|
|
432
|
+
};
|
|
433
|
+
toastState.items.push(item);
|
|
434
|
+
if (item.duration > 0) {
|
|
435
|
+
setTimeout(function() {
|
|
436
|
+
toastState.remove(id);
|
|
437
|
+
}, item.duration);
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
if (typeof m !== 'undefined' && typeof m.redraw === 'function') {
|
|
441
|
+
m.redraw();
|
|
442
|
+
}
|
|
443
|
+
} catch (e) {}
|
|
444
|
+
return id;
|
|
445
|
+
},
|
|
446
|
+
remove: function(id) {
|
|
447
|
+
toastState.items = toastState.items.filter(function(t) { return t.id !== id; });
|
|
448
|
+
try {
|
|
449
|
+
if (typeof m !== 'undefined' && typeof m.redraw === 'function') {
|
|
450
|
+
m.redraw();
|
|
451
|
+
}
|
|
452
|
+
} catch (e) {}
|
|
453
|
+
},
|
|
454
|
+
clear: function() {
|
|
455
|
+
toastState.items = [];
|
|
456
|
+
try {
|
|
457
|
+
if (typeof m !== 'undefined' && typeof m.redraw === 'function') {
|
|
458
|
+
m.redraw();
|
|
459
|
+
}
|
|
460
|
+
} catch (e) {}
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
function normalizeToastOptions(options) {
|
|
465
|
+
if (typeof options === 'number') {
|
|
466
|
+
return { duration: options };
|
|
467
|
+
}
|
|
468
|
+
return options || {};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
var AdminToast = function(message, type, options) {
|
|
472
|
+
var opts = normalizeToastOptions(options);
|
|
473
|
+
if (typeof type === 'object') {
|
|
474
|
+
opts = type;
|
|
475
|
+
type = opts.type || 'info';
|
|
476
|
+
}
|
|
477
|
+
return toastState.add(type || 'info', message, opts);
|
|
478
|
+
};
|
|
479
|
+
AdminToast.success = function(message, options) { return toastState.add('success', message, normalizeToastOptions(options)); };
|
|
480
|
+
AdminToast.error = function(message, options) { return toastState.add('error', message, normalizeToastOptions(options)); };
|
|
481
|
+
AdminToast.warning = function(message, options) { return toastState.add('warning', message, normalizeToastOptions(options)); };
|
|
482
|
+
AdminToast.info = function(message, options) { return toastState.add('info', message, normalizeToastOptions(options)); };
|
|
483
|
+
AdminToast.remove = function(id) { return toastState.remove(id); };
|
|
484
|
+
AdminToast.clear = function() { return toastState.clear(); };
|
|
485
|
+
|
|
486
|
+
var ToastContainer = {
|
|
487
|
+
view: function() {
|
|
488
|
+
if (!toastState.items || toastState.items.length === 0) return null;
|
|
489
|
+
return m('div.fixed.top-4.right-4.z-50.flex.flex-col.gap-2.5.max-w-sm.w-full.pointer-events-none.px-4.sm:px-0',
|
|
490
|
+
toastState.items.map(function(t) {
|
|
491
|
+
var typeConfig = {
|
|
492
|
+
success: {
|
|
493
|
+
border: 'border-emerald-200 dark:border-emerald-800/80',
|
|
494
|
+
bg: 'bg-white dark:bg-slate-900',
|
|
495
|
+
iconColor: 'text-emerald-600 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-950/60',
|
|
496
|
+
symbol: '✓',
|
|
497
|
+
},
|
|
498
|
+
error: {
|
|
499
|
+
border: 'border-red-200 dark:border-red-800/80',
|
|
500
|
+
bg: 'bg-white dark:bg-slate-900',
|
|
501
|
+
iconColor: 'text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-950/60',
|
|
502
|
+
symbol: '!',
|
|
503
|
+
},
|
|
504
|
+
warning: {
|
|
505
|
+
border: 'border-amber-200 dark:border-amber-800/80',
|
|
506
|
+
bg: 'bg-white dark:bg-slate-900',
|
|
507
|
+
iconColor: 'text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-950/60',
|
|
508
|
+
symbol: '⚠',
|
|
509
|
+
},
|
|
510
|
+
info: {
|
|
511
|
+
border: 'border-blue-200 dark:border-blue-800/80',
|
|
512
|
+
bg: 'bg-white dark:bg-slate-900',
|
|
513
|
+
iconColor: 'text-blue-600 dark:text-blue-400 bg-blue-50 dark:bg-blue-950/60',
|
|
514
|
+
symbol: 'ℹ',
|
|
515
|
+
},
|
|
516
|
+
};
|
|
517
|
+
var cfg = typeConfig[t.type] || typeConfig.info;
|
|
518
|
+
|
|
519
|
+
return m('div.pointer-events-auto.shadow-xl.rounded-xl.border.p-4.flex.items-start.gap-3.transition-all.transform', {
|
|
520
|
+
key: t.id,
|
|
521
|
+
class: cfg.border + ' ' + cfg.bg
|
|
522
|
+
}, [
|
|
523
|
+
m('div.w-7.h-7.rounded-lg.flex.items-center.justify-center.text-xs.font-bold.flex-shrink-0', {
|
|
524
|
+
class: cfg.iconColor
|
|
525
|
+
}, cfg.symbol),
|
|
526
|
+
m('div.flex-1.min-w-0', [
|
|
527
|
+
t.title ? m('h4.text-sm.font-semibold.text-gray-900.dark:text-slate-100', t.title) : null,
|
|
528
|
+
m('p.text-xs.text-gray-600.dark:text-slate-300.break-words', t.message)
|
|
529
|
+
]),
|
|
530
|
+
m('button.text-gray-400.hover:text-gray-600.dark:hover:text-slate-200.p-1.text-xs', {
|
|
531
|
+
onclick: function() { toastState.remove(t.id); }
|
|
532
|
+
}, '✕')
|
|
533
|
+
]);
|
|
534
|
+
})
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
// Confirm Modal State & Handlers
|
|
540
|
+
var confirmState = {
|
|
541
|
+
current: null,
|
|
542
|
+
open: function(options) {
|
|
543
|
+
options = options || {};
|
|
544
|
+
if (typeof options === 'string') {
|
|
545
|
+
options = { message: options };
|
|
546
|
+
}
|
|
547
|
+
return new Promise(function(resolve) {
|
|
548
|
+
confirmState.current = {
|
|
549
|
+
title: options.title || 'Confirmation',
|
|
550
|
+
message: options.message || 'Are you sure you want to proceed?',
|
|
551
|
+
confirmText: options.confirmText || 'Confirm',
|
|
552
|
+
cancelText: options.cancelText || 'Cancel',
|
|
553
|
+
confirmColor: options.confirmColor || (options.danger ? 'red' : 'blue'),
|
|
554
|
+
danger: options.danger || false,
|
|
555
|
+
resolve: resolve
|
|
556
|
+
};
|
|
557
|
+
try {
|
|
558
|
+
if (typeof m !== 'undefined' && typeof m.redraw === 'function') {
|
|
559
|
+
m.redraw();
|
|
560
|
+
}
|
|
561
|
+
} catch (e) {}
|
|
562
|
+
});
|
|
563
|
+
},
|
|
564
|
+
close: function(result) {
|
|
565
|
+
if (confirmState.current && confirmState.current.resolve) {
|
|
566
|
+
confirmState.current.resolve(Boolean(result));
|
|
567
|
+
}
|
|
568
|
+
confirmState.current = null;
|
|
569
|
+
try {
|
|
570
|
+
if (typeof m !== 'undefined' && typeof m.redraw === 'function') {
|
|
571
|
+
m.redraw();
|
|
572
|
+
}
|
|
573
|
+
} catch (e) {}
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
var AdminConfirm = function(options) {
|
|
578
|
+
return confirmState.open(options);
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
var ConfirmModalContainer = {
|
|
582
|
+
view: function() {
|
|
583
|
+
if (!confirmState.current) return null;
|
|
584
|
+
var cur = confirmState.current;
|
|
585
|
+
var isDanger = cur.danger || cur.confirmColor === 'red';
|
|
586
|
+
|
|
587
|
+
return m('div.fixed.inset-0.z-50.flex.items-center.justify-center.p-4', [
|
|
588
|
+
m('div.fixed.inset-0.bg-black/50.backdrop-blur-sm', {
|
|
589
|
+
onclick: function() { confirmState.close(false); }
|
|
590
|
+
}),
|
|
591
|
+
m('div.relative.bg-white.dark:bg-slate-900.rounded-xl.shadow-2xl.border.border-gray-200.dark:border-slate-700.max-w-md.w-full.p-6.z-10', [
|
|
592
|
+
m('div.flex.items-start.gap-4', [
|
|
593
|
+
m('div.w-10.h-10.rounded-full.flex.items-center.justify-center.flex-shrink-0', {
|
|
594
|
+
class: isDanger ? 'bg-red-100 dark:bg-red-950/60 text-red-600 dark:text-red-400' : 'bg-blue-100 dark:bg-blue-950/60 text-blue-600 dark:text-blue-400'
|
|
595
|
+
}, isDanger ? '⚠' : '?'),
|
|
596
|
+
m('div.flex-1', [
|
|
597
|
+
m('h3.text-base.font-bold.text-gray-900.dark:text-slate-100', cur.title),
|
|
598
|
+
m('p.text-xs.text-gray-500.dark:text-slate-400.mt-1.5', cur.message)
|
|
599
|
+
])
|
|
600
|
+
]),
|
|
601
|
+
m('div.mt-6.flex.items-center.justify-end.gap-3', [
|
|
602
|
+
m('button.px-4.py-2.text-xs.font-medium.text-gray-700.dark:text-slate-300.bg-gray-100.dark:bg-slate-800.hover:bg-gray-200.dark:hover:bg-slate-700.rounded-lg', {
|
|
603
|
+
onclick: function() { confirmState.close(false); }
|
|
604
|
+
}, cur.cancelText),
|
|
605
|
+
m('button.px-4.py-2.text-xs.font-medium.text-white.rounded-lg.shadow-sm', {
|
|
606
|
+
class: isDanger ? 'bg-red-600 hover:bg-red-700' : 'bg-blue-600 hover:bg-blue-700',
|
|
607
|
+
onclick: function() { confirmState.close(true); }
|
|
608
|
+
}, cur.confirmText)
|
|
609
|
+
])
|
|
610
|
+
])
|
|
611
|
+
]);
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
// Global Exposure
|
|
616
|
+
if (typeof window !== 'undefined') {
|
|
617
|
+
window.AdminUI = AdminUI;
|
|
618
|
+
window.Admin = window.Admin || {};
|
|
619
|
+
window.Admin.ui = AdminUI;
|
|
620
|
+
window.Admin.toast = AdminToast;
|
|
621
|
+
window.Admin.confirm = AdminConfirm;
|
|
622
|
+
window.notify = AdminToast;
|
|
623
|
+
window.toast = AdminToast;
|
|
624
|
+
window.confirmModal = AdminConfirm;
|
|
625
|
+
window.toastState = toastState;
|
|
626
|
+
window.confirmState = confirmState;
|
|
627
|
+
window.ToastContainer = ToastContainer;
|
|
628
|
+
window.ConfirmModalContainer = ConfirmModalContainer;
|
|
629
|
+
}
|
|
630
|
+
|
|
157
631
|
// Custom Page Component Factory
|
|
158
632
|
function createCustomPage(pageConfig) {
|
|
159
633
|
if (pageConfig && (pageConfig.url || (pageConfig.html && pageConfig.iframe))) {
|
|
@@ -167,7 +641,11 @@ function createCustomPage(pageConfig) {
|
|
|
167
641
|
if (pageConfig.layout === false) {
|
|
168
642
|
return {
|
|
169
643
|
view: function() {
|
|
170
|
-
return m('
|
|
644
|
+
return m('div.relative.w-full.h-full', [
|
|
645
|
+
m('iframe', iframeAttrs),
|
|
646
|
+
m(ToastContainer),
|
|
647
|
+
m(ConfirmModalContainer),
|
|
648
|
+
]);
|
|
171
649
|
}
|
|
172
650
|
};
|
|
173
651
|
}
|
|
@@ -206,7 +684,11 @@ function createCustomPage(pageConfig) {
|
|
|
206
684
|
return {
|
|
207
685
|
oncreate: executeScripts,
|
|
208
686
|
view: function() {
|
|
209
|
-
return m('div.custom-html-container',
|
|
687
|
+
return m('div.custom-html-container', [
|
|
688
|
+
m.trust(pageConfig.html),
|
|
689
|
+
m(ToastContainer),
|
|
690
|
+
m(ConfirmModalContainer),
|
|
691
|
+
]);
|
|
210
692
|
}
|
|
211
693
|
};
|
|
212
694
|
}
|
|
@@ -300,7 +782,14 @@ function createCustomPage(pageConfig) {
|
|
|
300
782
|
: error
|
|
301
783
|
? m('div.bg-red-50.border.border-red-200.rounded.p-4.text-red-700', error)
|
|
302
784
|
: pageConfig.render
|
|
303
|
-
?
|
|
785
|
+
? (function() {
|
|
786
|
+
try {
|
|
787
|
+
return pageConfig.render(data, vnode);
|
|
788
|
+
} catch (renderErr) {
|
|
789
|
+
console.error('[Admin Panel] Error rendering page "' + pageConfig.id + '":', renderErr);
|
|
790
|
+
return renderCustomPageError(pageConfig, renderErr);
|
|
791
|
+
}
|
|
792
|
+
})()
|
|
304
793
|
: m('div.bg-white dark:bg-slate-800.rounded-lg.shadow.p-6', [
|
|
305
794
|
data
|
|
306
795
|
? m('pre.text-sm.overflow-auto', JSON.stringify(data, null, 2))
|