webspresso 0.0.44 → 0.0.45
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/package.json
CHANGED
|
@@ -125,6 +125,7 @@ const MenuItem = {
|
|
|
125
125
|
: 'text-gray-700 hover:bg-gray-100',
|
|
126
126
|
onclick: (e) => {
|
|
127
127
|
e.preventDefault();
|
|
128
|
+
sidebarOpen = false;
|
|
128
129
|
m.route.set(item.path);
|
|
129
130
|
},
|
|
130
131
|
}, [
|
|
@@ -182,6 +183,9 @@ const MenuGroup = {
|
|
|
182
183
|
},
|
|
183
184
|
};
|
|
184
185
|
|
|
186
|
+
// Global sidebar state (shared between Sidebar, Layout, and MobileHeader)
|
|
187
|
+
var sidebarOpen = false;
|
|
188
|
+
|
|
185
189
|
// Sidebar Component
|
|
186
190
|
const Sidebar = {
|
|
187
191
|
oninit(vnode) {
|
|
@@ -207,53 +211,79 @@ const Sidebar = {
|
|
|
207
211
|
const { menu, settings, loading } = vnode.state;
|
|
208
212
|
const currentPath = m.route.get() || '/';
|
|
209
213
|
|
|
210
|
-
return
|
|
211
|
-
//
|
|
212
|
-
m('div.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
214
|
+
return [
|
|
215
|
+
// Backdrop overlay (mobile only)
|
|
216
|
+
sidebarOpen && m('div.fixed.inset-0.bg-black.bg-opacity-50.z-30.lg:hidden', {
|
|
217
|
+
onclick: () => { sidebarOpen = false; },
|
|
218
|
+
}),
|
|
219
|
+
|
|
220
|
+
m('aside.w-64.bg-white.border-r.border-gray-200.flex.flex-col.h-screen.fixed.left-0.top-0.z-40.transition-transform.duration-200', {
|
|
221
|
+
class: sidebarOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0',
|
|
222
|
+
}, [
|
|
223
|
+
// Logo/Title
|
|
224
|
+
m('div.h-16.flex.items-center.justify-between.px-4.border-b.border-gray-200', [
|
|
225
|
+
m('a.flex.items-center.gap-2.text-lg.font-bold.text-gray-900', {
|
|
226
|
+
href: '/',
|
|
227
|
+
onclick: (e) => { e.preventDefault(); sidebarOpen = false; m.route.set('/'); },
|
|
228
|
+
}, [
|
|
229
|
+
m('div.w-8.h-8.bg-blue-600.rounded-lg.flex.items-center.justify-center', [
|
|
230
|
+
m('span.text-white.font-bold', 'A'),
|
|
231
|
+
]),
|
|
232
|
+
m('span', settings?.title || 'Admin'),
|
|
219
233
|
]),
|
|
220
|
-
|
|
234
|
+
// Close button (mobile only)
|
|
235
|
+
m('button.p-1.text-gray-400.hover:text-gray-600.lg:hidden', {
|
|
236
|
+
onclick: () => { sidebarOpen = false; },
|
|
237
|
+
}, m(Icon, { name: 'x', class: 'w-5 h-5' })),
|
|
221
238
|
]),
|
|
222
|
-
]),
|
|
223
239
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
240
|
+
// Menu
|
|
241
|
+
m('nav.flex-1.overflow-y-auto.p-4.space-y-2', [
|
|
242
|
+
loading
|
|
243
|
+
? m('div.flex.justify-center.py-4', m(Spinner))
|
|
244
|
+
: menu.map(item =>
|
|
245
|
+
item.items
|
|
246
|
+
? m(MenuGroup, { key: item.id, group: item, currentPath })
|
|
247
|
+
: m(MenuItem, { key: item.id, item, active: item.path === currentPath })
|
|
248
|
+
),
|
|
249
|
+
]),
|
|
234
250
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
251
|
+
// User section
|
|
252
|
+
state.user && m('div.p-4.border-t.border-gray-200', [
|
|
253
|
+
m('div.flex.items-center.gap-3', [
|
|
254
|
+
m('div.w-8.h-8.bg-gray-200.rounded-full.flex.items-center.justify-center', [
|
|
255
|
+
m('span.text-sm.font-medium.text-gray-600',
|
|
256
|
+
(state.user.name || state.user.email || 'A').charAt(0).toUpperCase()
|
|
257
|
+
),
|
|
258
|
+
]),
|
|
259
|
+
m('div.flex-1.min-w-0', [
|
|
260
|
+
m('p.text-sm.font-medium.text-gray-900.truncate', state.user.name || 'Admin'),
|
|
261
|
+
m('p.text-xs.text-gray-500.truncate', state.user.email),
|
|
262
|
+
]),
|
|
263
|
+
m('button.p-1.text-gray-400.hover:text-gray-600', {
|
|
264
|
+
title: 'Logout',
|
|
265
|
+
onclick: async () => {
|
|
266
|
+
await api.post('/auth/logout');
|
|
267
|
+
state.user = null;
|
|
268
|
+
sidebarOpen = false;
|
|
269
|
+
m.route.set('/login');
|
|
270
|
+
},
|
|
271
|
+
}, m(Icon, { name: 'logout', class: 'w-5 h-5' })),
|
|
242
272
|
]),
|
|
243
|
-
m('div.flex-1.min-w-0', [
|
|
244
|
-
m('p.text-sm.font-medium.text-gray-900.truncate', state.user.name || 'Admin'),
|
|
245
|
-
m('p.text-xs.text-gray-500.truncate', state.user.email),
|
|
246
|
-
]),
|
|
247
|
-
m('button.p-1.text-gray-400.hover:text-gray-600', {
|
|
248
|
-
title: 'Logout',
|
|
249
|
-
onclick: async () => {
|
|
250
|
-
await api.post('/auth/logout');
|
|
251
|
-
state.user = null;
|
|
252
|
-
m.route.set('/login');
|
|
253
|
-
},
|
|
254
|
-
}, m(Icon, { name: 'logout', class: 'w-5 h-5' })),
|
|
255
273
|
]),
|
|
256
274
|
]),
|
|
275
|
+
];
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// Mobile Header with hamburger button
|
|
280
|
+
const MobileHeader = {
|
|
281
|
+
view(vnode) {
|
|
282
|
+
return m('div.lg:hidden.fixed.top-0.left-0.right-0.h-14.bg-white.border-b.border-gray-200.flex.items-center.px-4.z-20', [
|
|
283
|
+
m('button.p-2.-ml-1.text-gray-600.hover:text-gray-900.rounded-lg.hover:bg-gray-100', {
|
|
284
|
+
onclick: () => { sidebarOpen = true; },
|
|
285
|
+
}, m(Icon, { name: 'menu', class: 'w-6 h-6' })),
|
|
286
|
+
m('span.ml-3.text-lg.font-semibold.text-gray-900', 'Admin'),
|
|
257
287
|
]);
|
|
258
288
|
},
|
|
259
289
|
};
|
|
@@ -262,8 +292,9 @@ const Sidebar = {
|
|
|
262
292
|
const Layout = {
|
|
263
293
|
view(vnode) {
|
|
264
294
|
return m('div.min-h-screen.bg-gray-50', [
|
|
295
|
+
m(MobileHeader),
|
|
265
296
|
m(Sidebar),
|
|
266
|
-
m('main.ml-64.p-6', vnode.children),
|
|
297
|
+
m('main.lg:ml-64.p-6.pt-20.lg:pt-6', vnode.children),
|
|
267
298
|
]);
|
|
268
299
|
},
|
|
269
300
|
};
|
|
@@ -326,11 +326,11 @@ var AnalyticsPage = {
|
|
|
326
326
|
// Top Pages + Recent Activity row
|
|
327
327
|
m('div.grid.grid-cols-1.lg:grid-cols-2.gap-4.mb-6', [
|
|
328
328
|
// Top Pages
|
|
329
|
-
m('div.bg-white.rounded-lg.shadow', [
|
|
330
|
-
m('div.px-5.py-4.border-b.border-gray-100', [
|
|
329
|
+
m('div.bg-white.rounded-lg.shadow.flex.flex-col', { style: 'max-height:480px' }, [
|
|
330
|
+
m('div.px-5.py-4.border-b.border-gray-100.shrink-0', [
|
|
331
331
|
m('h3.text-sm.font-semibold.text-gray-900', 'Top Pages'),
|
|
332
332
|
]),
|
|
333
|
-
m('div.divide-y.divide-gray-50', [
|
|
333
|
+
m('div.divide-y.divide-gray-50.overflow-y-auto.flex-1', [
|
|
334
334
|
s.topPages.length === 0
|
|
335
335
|
? m('p.text-gray-400.text-sm.text-center.py-6', 'No page views yet')
|
|
336
336
|
: (function() {
|
|
@@ -356,12 +356,12 @@ var AnalyticsPage = {
|
|
|
356
356
|
]),
|
|
357
357
|
|
|
358
358
|
// Recent Activity
|
|
359
|
-
m('div.bg-white.rounded-lg.shadow', [
|
|
360
|
-
m('div.px-5.py-4.border-b.border-gray-100.flex.items-center.justify-between', [
|
|
359
|
+
m('div.bg-white.rounded-lg.shadow.flex.flex-col', { style: 'max-height:480px' }, [
|
|
360
|
+
m('div.px-5.py-4.border-b.border-gray-100.flex.items-center.justify-between.shrink-0', [
|
|
361
361
|
m('h3.text-sm.font-semibold.text-gray-900', 'Recent Activity'),
|
|
362
362
|
m('span.text-xs.text-gray-400', 'Live'),
|
|
363
363
|
]),
|
|
364
|
-
m('div.divide-y.divide-gray-50', [
|
|
364
|
+
m('div.divide-y.divide-gray-50.overflow-y-auto.flex-1', [
|
|
365
365
|
s.recent.length === 0
|
|
366
366
|
? m('p.text-gray-400.text-sm.text-center.py-6', 'No recent activity')
|
|
367
367
|
: s.recent.slice(0, 10).map(function(item) {
|
|
@@ -35,6 +35,10 @@ function siteAnalyticsPlugin(options = {}) {
|
|
|
35
35
|
description: 'Self-hosted page view analytics with admin dashboard',
|
|
36
36
|
dependencies: { 'admin-panel': '*' },
|
|
37
37
|
|
|
38
|
+
csp: {
|
|
39
|
+
scriptSrc: ['https://cdn.jsdelivr.net'],
|
|
40
|
+
},
|
|
41
|
+
|
|
38
42
|
register(ctx) {
|
|
39
43
|
const knex = db.knex || db;
|
|
40
44
|
|