create-fullstack-scaffold 0.4.2 → 0.4.4
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/dist/cli/index.js +513 -122
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/template/dist/cli/index.js +237 -94
- package/template/dist/cli/index.js.map +1 -1
- package/template/dist/client/admin.html +15 -10
- package/template/dist/client/assets/App-BpFUIC-5.js +114 -0
- package/template/dist/client/assets/{CartPage-DESSHkaG.js → CartPage-DgDZP-R5.js} +2 -2
- package/template/dist/client/assets/{CategoriesPage-DqLZ_Mom.js → CategoriesPage-fuYNiSmE.js} +2 -2
- package/template/dist/client/assets/ContentDetailPage-BvvFU1Mr.js +1 -0
- package/template/dist/client/assets/ContentListPage-ShPxo6WG.js +1 -0
- package/template/dist/client/assets/DashboardPage-C1rZzZo4.js +11 -0
- package/template/dist/client/assets/{DeveloperDashboardPage-Cum7zFwO.js → DeveloperDashboardPage-_WtmbVoA.js} +2 -2
- package/template/dist/client/assets/{EmptyState-BK7ttK8D.js → EmptyState-DEyOQjbY.js} +1 -1
- package/template/dist/client/assets/LoadingSpinner-BcHjaM1m.js +6 -0
- package/template/dist/client/assets/LoginPage-CjeQqDdO.js +1 -0
- package/template/dist/client/assets/LogoutOutlined-ColFAHdd.js +1 -0
- package/template/dist/client/assets/NotificationPage-DRkEv02s.js +1 -0
- package/template/dist/client/assets/OrdersPage-7I3vPWLP.js +11 -0
- package/template/dist/client/assets/{PluginDetailPage-Br24YS4P.js → PluginDetailPage-CWyxdVCg.js} +3 -13
- package/template/dist/client/assets/PluginsPage-DRgWLeJT.js +6 -0
- package/template/dist/client/assets/ProfilePage-2FFkaii8.js +16 -0
- package/template/dist/client/assets/PublishPage-DW7yvROx.js +1 -0
- package/template/dist/client/assets/{RegisterPage-rPFG4Sry.js → RegisterPage-Dvaf9b0e.js} +2 -2
- package/template/dist/client/assets/SaveOutlined-CLQ2tsSY.js +2 -0
- package/template/dist/client/assets/{SearchPage-DQl1U5Uy.js → SearchPage-DvnEyswS.js} +2 -2
- package/template/dist/client/assets/{SettingsPage-DaYE80wj.js → SettingsPage-IrU-L9Zr.js} +2 -2
- package/template/dist/client/assets/{StatusBadge-BJ3W2Zc7.js → StatusBadge-CG7wVqpu.js} +1 -1
- package/template/dist/client/assets/{TodoPage-zC5wWNBa.js → TodoPage-B7D-bl65.js} +3 -3
- package/template/dist/client/assets/TopicsPage-CYCNr-mn.js +6 -0
- package/template/dist/client/assets/{WebSocketPage-B4NLdz1w.js → WebSocketPage-CGEg6QJE.js} +2 -2
- package/template/dist/client/assets/{activity-B94BUAsD.js → activity-CgTz9Mrd.js} +1 -1
- package/template/dist/client/assets/admin-D2iHWNOg.js +1 -0
- package/template/dist/client/assets/api-request-DIJocPxr.js +356 -0
- package/template/dist/client/assets/{arrow-left-H5JH0iU1.js → arrow-left-DtLQ3tTk.js} +1 -1
- package/template/dist/client/assets/{chevron-down-DwRh_wjD.js → chevron-down-CpIAV_yr.js} +1 -1
- package/template/dist/client/assets/{chevron-right-CpIWaqAL.js → chevron-right-CnU7oPQs.js} +1 -1
- package/template/dist/client/assets/{circle-check-big-CSElLscG.js → circle-check-big-CrZdwS75.js} +1 -1
- package/template/dist/client/assets/{circle-x-CYxr4RAw.js → circle-x-CvN9r3fW.js} +1 -1
- package/template/dist/client/assets/{clock-BseUJ7ex.js → clock-CTeSZBiH.js} +1 -1
- package/template/dist/client/assets/{download-BsGC_Xhq.js → download-OSVfVuNV.js} +1 -1
- package/template/dist/client/assets/{external-link-UTDzdjq8.js → external-link-fBsuHMby.js} +1 -1
- package/template/dist/client/assets/{eye-nR10ui7i.js → eye-CwlZZEsj.js} +1 -1
- package/template/dist/client/assets/index-09IqiOaE.css +1 -0
- package/template/dist/client/assets/index-B3CRby6r.js +51 -0
- package/template/dist/client/assets/index-CC_zy73y.js +17 -0
- package/template/dist/client/assets/main-Cn4IDo2I.js +92 -0
- package/template/dist/client/assets/merchant-DDDN9bMu.js +40 -0
- package/template/dist/client/assets/{message-square-CwWGL-Bh.js → message-square-BRaZfgbJ.js} +1 -1
- package/template/dist/client/assets/pluginStore-DDsWJPe6.js +1 -0
- package/template/dist/client/assets/{plus-pZld_nUL.js → plus-DDZfPiDJ.js} +1 -1
- package/template/dist/client/assets/sse-client-DGQ072hl.js +64 -0
- package/template/dist/client/assets/star-C7tO6w4H.js +11 -0
- package/template/dist/client/assets/{tag-DsiUPF90.js → tag-DBfpQDuJ.js} +1 -1
- package/template/dist/client/assets/tenant-CizbLaLJ.js +1 -0
- package/template/dist/client/assets/{trash-2-DRblNbnA.js → trash-2-BP7fkd5b.js} +1 -1
- package/template/dist/client/assets/{triangle-alert-D1CLh-Vd.js → triangle-alert-CrOQEdJ2.js} +1 -1
- package/template/dist/client/assets/{wifi-BwXjnCqI.js → wifi-CwYSE0J4.js} +1 -1
- package/template/dist/client/assets/{x-BH2rJdFq.js → x-t615pnNR.js} +1 -1
- package/template/dist/client/content/index.html +5 -4
- package/template/dist/client/index.html +4 -3
- package/template/dist/client/merchant.html +18 -0
- package/template/dist/client/notifications/index.html +6 -5
- package/template/dist/client/tenant.html +18 -0
- package/template/dist/client/todos/index.html +6 -5
- package/template/dist/client/websocket/index.html +6 -5
- package/template/dist/cloudflare/chunk-HX7DVRDX.js +1 -0
- package/template/dist/cloudflare/{chunk-QBHAPWIA.js → chunk-IXJL2LE4.js} +1 -1
- package/template/dist/cloudflare/chunk-Q34AN3RF.js +14 -0
- package/template/dist/cloudflare/{chunk-BHPTQ4K2.js → chunk-ZAMVL4EG.js} +2 -2
- package/template/dist/cloudflare/cloudflare.js +35 -29
- package/template/dist/cloudflare/db-4AQOSFAX.js +1 -0
- package/template/dist/cloudflare/{migrator-2ZYYKNI5.js → migrator-TEPBVUFO.js} +1 -1
- package/template/dist/cloudflare/permission-service-impl-RE7NPW3Q.js +1 -0
- package/template/dist/cloudflare/schema-DVEMT3X2.js +1 -0
- package/template/dist/cloudflare/test-setup-Z2RYE5B4.js +265 -0
- package/template/dist/server/chunk-27SIJSPF.js +1 -0
- package/template/dist/server/chunk-37QNGBQU.js +1 -0
- package/template/dist/server/chunk-FIY4CGJJ.js +1 -0
- package/template/dist/server/chunk-HZUDBIT4.js +1 -0
- package/template/dist/server/chunk-I6OBDFDU.js +1 -0
- package/template/dist/server/chunk-JHNYLNG7.js +1 -0
- package/template/dist/server/chunk-MDOVVKKD.js +1 -0
- package/template/dist/server/chunk-NSOCT7E5.js +1 -0
- package/template/dist/server/chunk-OU5IGVVO.js +1 -0
- package/template/dist/server/chunk-OYCBNRMK.js +1 -0
- package/template/dist/server/chunk-P2RJNNGD.js +1 -0
- package/template/dist/server/chunk-UMAQLNPE.js +1 -0
- package/template/dist/server/chunk-XZZTSRSN.js +1 -0
- package/template/dist/server/content-service-5QVHDR5X.js +1 -0
- package/template/dist/server/db-FLI5L5DD.js +1 -0
- package/template/dist/server/dispute-service-E2FCYUEM.js +1 -0
- package/template/dist/server/init-72JXJV3I.js +46 -0
- package/template/dist/server/init-FWYXSKPX.js +46 -0
- package/template/dist/server/node.js +34 -28
- package/template/dist/server/order-service-TKGG5S5K.js +1 -0
- package/template/dist/server/permission-service-impl-M3AKEYNY.js +1 -0
- package/template/dist/server/schema-V4UG7HMU.js +1 -0
- package/template/dist/server/tenant-service-JG6UUWXQ.js +1 -0
- package/template/dist/server/tenant-service-XJWZSEDX.js +1 -0
- package/template/dist/{cloudflare/test-setup-NMZJZLRV.js → server/test-setup-VFOPBGYM.js} +83 -2
- package/template/dist/server/ticket-service-3RSW7V2Z.js +1 -0
- package/template/dist/server/todo-service-S6BGIO7D.js +1 -0
- package/template/package.json +1 -1
- package/template/src/client/App.tsx +3 -1
- package/template/src/client/pages/ContentListPage.tsx +36 -4
- package/template/src/client/pages/PluginsPage.tsx +112 -20
- package/template/src/client/pages/TodoPage.tsx +9 -1
- package/template/src/client/preset-ui-config.ts +79 -2
- package/template/src/merchant/pages/DisputesPage.tsx +11 -11
- package/template/src/merchant/pages/OrdersPage.tsx +4 -3
- package/template/src/merchant/pages/ProductsPage.tsx +2 -2
- package/template/src/merchant/stores/merchantStore.ts +11 -6
- package/template/src/server/db/init.ts +5 -253
- package/template/src/server/module-content/routes/topics-routes.ts +250 -0
- package/template/src/server/module-content/services/content-service.ts +1 -1
- package/template/src/server/module-plugin/services/plugin-seed-service.ts +623 -0
- package/template/src/server/module-tenant/routes/tenant-routes.ts +2 -2
- package/template/src/server/module-tenant/services/tenant-service.ts +63 -17
- package/template/src/server/module-todos/services/todo-service.ts +1 -1
- package/template/src/shared/modules/merchant/index.ts +9 -0
- package/template/src/shared/modules/merchant/schemas.ts +37 -0
- package/template/src/shared/modules/tenant/index.ts +2 -0
- package/template/src/shared/schemas/index.ts +27 -0
- package/template/src/tenant/layouts/Header.tsx +1 -1
- package/template/src/tenant/layouts/Sidebar.tsx +1 -1
- package/template/src/tenant/pages/SettingsPage.tsx +4 -8
- package/template/src/tenant/stores/tenantStore.ts +41 -302
- package/template/tsconfig.json +1 -0
- package/template/tsup.config.ts +11 -3
- package/template/dist/client/assets/ContentDetailPage-DdltMANL.js +0 -1
- package/template/dist/client/assets/ContentListPage-B63sUB6N.js +0 -1
- package/template/dist/client/assets/DashboardPage-tUMilc7Z.js +0 -11
- package/template/dist/client/assets/LoadingSpinner-DVYOLSRM.js +0 -6
- package/template/dist/client/assets/LoginPage-g9mSLIMC.js +0 -6
- package/template/dist/client/assets/NotificationPage-COTzp-HD.js +0 -1
- package/template/dist/client/assets/OrdersPage-BsPh4oEI.js +0 -11
- package/template/dist/client/assets/PluginsPage-BNtAe_yo.js +0 -6
- package/template/dist/client/assets/ProfilePage-dL_iSuZN.js +0 -16
- package/template/dist/client/assets/PublishPage-D96FnbLA.js +0 -1
- package/template/dist/client/assets/TopicsPage-DyLjZa4D.js +0 -6
- package/template/dist/client/assets/admin-Bh1vfz_L.js +0 -486
- package/template/dist/client/assets/index-1bA-ixFv.js +0 -114
- package/template/dist/client/assets/index-CWh3vXC_.css +0 -1
- package/template/dist/client/assets/main-DHnArIp_.js +0 -82
- package/template/dist/client/assets/pluginStore-XQfsm3iR.js +0 -1
- package/template/dist/client/content/content-1/index.html +0 -18
- package/template/dist/client/content/content-13/index.html +0 -18
- package/template/dist/client/content/content-14/index.html +0 -18
- package/template/dist/client/content/content-15/index.html +0 -18
- package/template/dist/client/content/content-19/index.html +0 -18
- package/template/dist/client/content/content-2/index.html +0 -18
- package/template/dist/client/content/content-20/index.html +0 -18
- package/template/dist/cloudflare/chunk-YY6FRMAQ.js +0 -14
- package/template/dist/cloudflare/db-7J42RQ3T.js +0 -1
- package/template/dist/cloudflare/permission-service-impl-TGXVSSSA.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{h as cancelOrder,i as completeOrder,d as createOrder,f as deleteOrder,c as getOrderById,b as getOrders,g as processOrder,a as seedOrdersIfEmpty,e as updateOrder}from'./chunk-HZUDBIT4.js';import'./chunk-I6OBDFDU.js';import'./chunk-FIY4CGJJ.js';import'./chunk-JCPOP4XL.js';import'./chunk-UMAQLNPE.js';import'./chunk-OYCBNRMK.js';import'./chunk-27SIJSPF.js';import'./chunk-5J6HXUF6.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{s as PermissionService,t as permissionService}from'./chunk-37QNGBQU.js';import'./chunk-JCPOP4XL.js';import'./chunk-UMAQLNPE.js';import'./chunk-OYCBNRMK.js';import'./chunk-27SIJSPF.js';import'./chunk-5J6HXUF6.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import {J}from'./chunk-27SIJSPF.js';export{x as contentCategories,w as contentStatuses,y as contents,z as developers,u as disputeStatuses,t as disputeTypes,v as disputes,d as notifications,l as orderStatuses,m as orders,k as permissionAuditLogs,j as permissionRouteMappings,e as permissions,F as pluginCategories,G as pluginCategoryMappings,E as pluginReviews,B as pluginStatus,D as pluginVersions,C as plugins,g as rolePermissions,f as roles,i as routes,H as tenants,p as ticketCategories,o as ticketPriorities,s as ticketReplies,q as ticketReplyAuthorRoles,n as ticketStatuses,r as tickets,c as todoAttachments,a as todoStatus,b as todos,h as userRoles}from'./chunk-27SIJSPF.js';import'./chunk-5J6HXUF6.js';J();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import {b}from'./chunk-MDOVVKKD.js';import {a}from'./chunk-UMAQLNPE.js';import {h,f,i}from'./chunk-OYCBNRMK.js';import {J as J$1,H}from'./chunk-27SIJSPF.js';import'./chunk-5J6HXUF6.js';import {eq,like,desc}from'drizzle-orm';a();J$1();h();var c=f("tenant-service");async function O(){let s=await i();if((await s.select().from(H)).length===0){c.info({},"Seeding tenants...");let e=[{name:"Demo Corp",slug:"demo",status:"active",plan:"pro",maxUsers:50,settings:JSON.stringify({theme:"dark",language:"en"})},{name:"Test Inc",slug:"test",status:"trial",plan:"free",maxUsers:5,settings:null}];await s.insert(H).values(e),c.info({},"Tenants seeding complete!");}}async function J(s=1,r=20,e={}){let t=await i(),o=(s-1)*r,a=t.select().from(H);e.status&&(a=a.where(eq(H.status,e.status))),e.plan&&(a=a.where(eq(H.plan,e.plan))),e.search&&(a=a.where(like(H.name,`%${e.search}%`)));let u=await a.orderBy(desc(H.createdAt)).limit(r).offset(o),g=t.select().from(H);e.status&&(g=g.where(eq(H.status,e.status))),e.plan&&(g=g.where(eq(H.plan,e.plan))),e.search&&(g=g.where(like(H.name,`%${e.search}%`)));let T=(await g).length;return {items:u.map(i=>({id:i.id,name:i.name,slug:i.slug,status:i.status,plan:i.plan,maxUsers:i.maxUsers,settings:i.settings?JSON.parse(i.settings):null,createdAt:i.createdAt,updatedAt:i.updatedAt})),total:T,page:s,pageSize:r}}async function Q(s){let e=await(await i()).select().from(H).where(eq(H.slug,s));if(e.length===0)return null;let t=e[0];return {id:t.id,name:t.name,slug:t.slug,status:t.status,plan:t.plan,maxUsers:t.maxUsers,settings:t.settings?JSON.parse(t.settings):null,createdAt:t.createdAt,updatedAt:t.updatedAt}}async function k(s){let e=await(await i()).select().from(H).where(eq(H.id,s));if(e.length===0)return null;let t=e[0];return {id:t.id,name:t.name,slug:t.slug,status:t.status,plan:t.plan,maxUsers:t.maxUsers,settings:t.settings?JSON.parse(t.settings):null,createdAt:t.createdAt,updatedAt:t.updatedAt}}async function R(s){let r=await i();if((await r.select().from(H).where(eq(H.slug,s.slug))).length>0)throw new b(`Tenant with slug '${s.slug}' already exists`);let t=new Date().toISOString(),a=(await r.insert(H).values({name:s.name,slug:s.slug,status:"trial",plan:s.plan,maxUsers:s.maxUsers,settings:s.settings?JSON.stringify(s.settings):null,createdAt:t,updatedAt:t}).returning())[0];return c.info({tenantId:a.id,slug:a.slug},"Tenant created"),{id:a.id,name:a.name,slug:a.slug,status:a.status,plan:a.plan,maxUsers:a.maxUsers,settings:a.settings?JSON.parse(a.settings):null,createdAt:a.createdAt,updatedAt:a.updatedAt}}async function v(s,r){let e=await i();if((await e.select().from(H).where(eq(H.id,s))).length===0)return null;let o={updatedAt:new Date().toISOString()};r.name!==void 0&&(o.name=r.name),r.status!==void 0&&(o.status=r.status),r.plan!==void 0&&(o.plan=r.plan),r.maxUsers!==void 0&&(o.maxUsers=r.maxUsers),r.settings!==void 0&&(o.settings=r.settings?JSON.stringify(r.settings):null);let a=await e.update(H).set(o).where(eq(H.id,s)).returning();if(a.length===0)return null;let u=a[0];return c.info({tenantId:u.id,slug:u.slug},"Tenant updated"),{id:u.id,name:u.name,slug:u.slug,status:u.status,plan:u.plan,maxUsers:u.maxUsers,settings:u.settings?JSON.parse(u.settings):null,createdAt:u.createdAt,updatedAt:u.updatedAt}}async function D(s){return (await(await i()).delete(H).where(eq(H.id,s)).returning()).length>0?(c.info({tenantId:s},"Tenant deleted"),true):false}export{R as createTenant,D as deleteTenant,k as getTenantById,Q as getTenantBySlug,J as listTenants,O as seedTenantsIfEmpty,v as updateTenant};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import {b}from'./chunk-MDOVVKKD.js';import {a}from'./chunk-UMAQLNPE.js';import {h,f,i}from'./chunk-OYCBNRMK.js';import {J as J$1,H}from'./chunk-27SIJSPF.js';import'./chunk-5J6HXUF6.js';import {eq,like,desc}from'drizzle-orm';a();J$1();h();var g=f("tenant-service");async function N(){let a=await i();if((await a.select().from(H)).length===0){g.info({},"Seeding tenants...");let s=new Date().toISOString(),t=[{name:"Demo Corp",slug:"demo",status:"active",plan:"pro",maxUsers:50,settings:JSON.stringify({theme:"dark",language:"en"}),createdAt:s,updatedAt:s},{name:"Test Inc",slug:"test",status:"trial",plan:"free",maxUsers:5,settings:null,createdAt:s,updatedAt:s}];await a.insert(H).values(t),g.info({},"Tenants seeding complete!");}}async function k(a=1,e=20,s={}){let t=await i(),i$1=(a-1)*e,r=t.select().from(H);s.status&&(r=r.where(eq(H.status,s.status))),s.plan&&(r=r.where(eq(H.plan,s.plan))),s.search&&(r=r.where(like(H.name,`%${s.search}%`)));let l=await r.orderBy(desc(H.createdAt)).limit(e).offset(i$1),c=t.select().from(H);s.status&&(c=c.where(eq(H.status,s.status))),s.plan&&(c=c.where(eq(H.plan,s.plan))),s.search&&(c=c.where(like(H.name,`%${s.search}%`)));let w=(await c).length;return {items:l.map(d=>({id:d.id,name:d.name,slug:d.slug,status:d.status,plan:d.plan,maxUsers:d.maxUsers,settings:d.settings?JSON.parse(d.settings):null,createdAt:d.createdAt,updatedAt:d.updatedAt})),total:w,page:a,pageSize:e}}async function R(a){let s=await(await i()).select().from(H).where(eq(H.slug,a));if(s.length===0)return null;let t=s[0];return {id:t.id,name:t.name,slug:t.slug,status:t.status,plan:t.plan,maxUsers:t.maxUsers,settings:t.settings?JSON.parse(t.settings):null,createdAt:t.createdAt,updatedAt:t.updatedAt}}async function J(a){let s=await(await i()).select().from(H).where(eq(H.id,a));if(s.length===0)return null;let t=s[0];return {id:t.id,name:t.name,slug:t.slug,status:t.status,plan:t.plan,maxUsers:t.maxUsers,settings:t.settings?JSON.parse(t.settings):null,createdAt:t.createdAt,updatedAt:t.updatedAt}}async function Q(a){let e=await i();if((await e.select().from(H).where(eq(H.slug,a.slug))).length>0)throw new b(`Tenant with slug '${a.slug}' already exists`);let t=new Date().toISOString(),r=(await e.insert(H).values({name:a.name,slug:a.slug,status:"trial",plan:a.plan,maxUsers:a.maxUsers,settings:a.settings?JSON.stringify(a.settings):null,createdAt:t,updatedAt:t}).returning())[0];return g.info({tenantId:r.id,slug:r.slug},"Tenant created"),{id:r.id,name:r.name,slug:r.slug,status:r.status,plan:r.plan,maxUsers:r.maxUsers,settings:r.settings?JSON.parse(r.settings):null,createdAt:r.createdAt,updatedAt:r.updatedAt}}async function P(a,e){let s=await i();if((await s.select().from(H).where(eq(H.id,a))).length===0)return null;let i$1={updatedAt:new Date().toISOString()};e.name!==void 0&&(i$1.name=e.name),e.status!==void 0&&e.status!==null&&(i$1.status=e.status),e.plan!==void 0&&e.plan!==null&&(i$1.plan=e.plan),e.maxUsers!==void 0&&e.maxUsers!==null&&(i$1.maxUsers=e.maxUsers),e.settings!==void 0&&(i$1.settings=e.settings?JSON.stringify(e.settings):null),e.name!==void 0&&(i$1.name=e.name),e.status!==void 0&&e.status!==null&&(i$1.status=e.status),e.plan!==void 0&&e.plan!==null&&(i$1.plan=e.plan),e.maxUsers!==void 0&&e.maxUsers!==null&&(i$1.maxUsers=e.maxUsers),e.name!==void 0&&(i$1.name=e.name),e.status!==void 0&&e.status!==null&&(i$1.status=e.status),e.plan!==void 0&&e.plan!==null&&(i$1.plan=e.plan),e.maxUsers!==void 0&&e.maxUsers!==null&&(i$1.maxUsers=e.maxUsers),e.settings!==void 0&&(i$1.settings=e.settings?JSON.stringify(e.settings):null);let r=await s.update(H).set(i$1).where(eq(H.id,a)).returning();if(r.length===0)return null;let l=r[0];return g.info({tenantId:l.id,slug:l.slug},"Tenant updated"),{id:l.id,name:l.name,slug:l.slug,status:l.status,plan:l.plan,maxUsers:l.maxUsers,settings:l.settings?JSON.parse(l.settings):null,createdAt:l.createdAt,updatedAt:l.updatedAt}}async function D(a){return (await(await i()).delete(H).where(eq(H.id,a)).returning()).length>0?(g.info({tenantId:a},"Tenant deleted"),true):false}export{Q as createTenant,D as deleteTenant,J as getTenantById,R as getTenantBySlug,k as listTenants,N as seedTenantsIfEmpty,P as updateTenant};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {a}from'./chunk-UMAQLNPE.js';import {j}from'./chunk-OYCBNRMK.js';import'./chunk-27SIJSPF.js';import'./chunk-5J6HXUF6.js';a();async function O(){let e=await j();if(!e||!("execute"in e))throw new Error("Test database client not available");let t=`
|
|
2
2
|
CREATE TABLE IF NOT EXISTS todos (
|
|
3
3
|
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
4
4
|
title TEXT NOT NULL,
|
|
@@ -181,4 +181,85 @@ import {V,S}from'./chunk-YY6FRMAQ.js';import'./chunk-QBHAPWIA.js';import'./chunk
|
|
|
181
181
|
created_at INTEGER DEFAULT (unixepoch() * 1000) NOT NULL,
|
|
182
182
|
updated_at INTEGER DEFAULT (unixepoch() * 1000) NOT NULL
|
|
183
183
|
);
|
|
184
|
-
|
|
184
|
+
|
|
185
|
+
CREATE TABLE IF NOT EXISTS developers (
|
|
186
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
187
|
+
username TEXT NOT NULL UNIQUE,
|
|
188
|
+
email TEXT NOT NULL UNIQUE,
|
|
189
|
+
password_hash TEXT NOT NULL,
|
|
190
|
+
role TEXT DEFAULT 'developer' NOT NULL,
|
|
191
|
+
api_key TEXT NOT NULL UNIQUE,
|
|
192
|
+
created_at INTEGER DEFAULT (unixepoch() * 1000) NOT NULL,
|
|
193
|
+
updated_at INTEGER DEFAULT (unixepoch() * 1000) NOT NULL
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
CREATE TABLE IF NOT EXISTS plugins (
|
|
197
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
198
|
+
name TEXT NOT NULL,
|
|
199
|
+
slug TEXT NOT NULL UNIQUE,
|
|
200
|
+
description TEXT NOT NULL DEFAULT '',
|
|
201
|
+
readme TEXT,
|
|
202
|
+
author_id TEXT NOT NULL,
|
|
203
|
+
author_name TEXT NOT NULL,
|
|
204
|
+
repository_url TEXT,
|
|
205
|
+
homepage_url TEXT,
|
|
206
|
+
npm_package TEXT,
|
|
207
|
+
license TEXT,
|
|
208
|
+
version TEXT NOT NULL DEFAULT '0.0.1',
|
|
209
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
210
|
+
download_count INTEGER NOT NULL DEFAULT 0,
|
|
211
|
+
view_count INTEGER NOT NULL DEFAULT 0,
|
|
212
|
+
featured INTEGER NOT NULL DEFAULT 0,
|
|
213
|
+
screenshot_url TEXT,
|
|
214
|
+
site_urls TEXT,
|
|
215
|
+
tags TEXT,
|
|
216
|
+
commands TEXT,
|
|
217
|
+
reject_reason TEXT,
|
|
218
|
+
created_at INTEGER DEFAULT (unixepoch() * 1000) NOT NULL,
|
|
219
|
+
updated_at INTEGER DEFAULT (unixepoch() * 1000) NOT NULL
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
CREATE TABLE IF NOT EXISTS plugin_versions (
|
|
223
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
224
|
+
plugin_id TEXT NOT NULL,
|
|
225
|
+
version TEXT NOT NULL,
|
|
226
|
+
changelog TEXT,
|
|
227
|
+
package_url TEXT,
|
|
228
|
+
file_size INTEGER,
|
|
229
|
+
checksum TEXT,
|
|
230
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
231
|
+
published_at INTEGER DEFAULT (unixepoch() * 1000) NOT NULL,
|
|
232
|
+
FOREIGN KEY (plugin_id) REFERENCES plugins(id) ON DELETE CASCADE,
|
|
233
|
+
UNIQUE(plugin_id, version)
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
CREATE TABLE IF NOT EXISTS plugin_reviews (
|
|
237
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
238
|
+
plugin_id TEXT NOT NULL,
|
|
239
|
+
user_id TEXT NOT NULL,
|
|
240
|
+
user_name TEXT NOT NULL,
|
|
241
|
+
rating INTEGER NOT NULL,
|
|
242
|
+
title TEXT,
|
|
243
|
+
content TEXT,
|
|
244
|
+
created_at INTEGER DEFAULT (unixepoch() * 1000) NOT NULL,
|
|
245
|
+
FOREIGN KEY (plugin_id) REFERENCES plugins(id) ON DELETE CASCADE,
|
|
246
|
+
UNIQUE(plugin_id, user_id)
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
CREATE TABLE IF NOT EXISTS plugin_categories (
|
|
250
|
+
id TEXT PRIMARY KEY NOT NULL,
|
|
251
|
+
name TEXT NOT NULL UNIQUE,
|
|
252
|
+
slug TEXT NOT NULL UNIQUE,
|
|
253
|
+
description TEXT,
|
|
254
|
+
icon TEXT,
|
|
255
|
+
sort_order INTEGER NOT NULL DEFAULT 0
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
CREATE TABLE IF NOT EXISTS plugin_category_mappings (
|
|
259
|
+
plugin_id TEXT NOT NULL,
|
|
260
|
+
category_id TEXT NOT NULL,
|
|
261
|
+
FOREIGN KEY (plugin_id) REFERENCES plugins(id) ON DELETE CASCADE,
|
|
262
|
+
FOREIGN KEY (category_id) REFERENCES plugin_categories(id) ON DELETE CASCADE,
|
|
263
|
+
UNIQUE(plugin_id, category_id)
|
|
264
|
+
);
|
|
265
|
+
`.split(";").filter(E=>E.trim());for(let E of t)if(E.trim())try{await e.execute(E);}catch{}await l(e);}async function l(e){let s=[{id:"role_super_admin",code:"super_admin",name:"\u8D85\u7EA7\u7BA1\u7406\u5458",label:"\u8D85\u7EA7\u7BA1\u7406\u5458",is_system:1,sort_order:1},{id:"role_admin",code:"admin",name:"\u7BA1\u7406\u5458",label:"\u7BA1\u7406\u5458",is_system:1,sort_order:2},{id:"role_user",code:"user",name:"\u666E\u901A\u7528\u6237",label:"\u666E\u901A\u7528\u6237",is_system:0,sort_order:3},{id:"role_customer_service",code:"customer_service",name:"\u5BA2\u670D\u4EBA\u5458",label:"\u5BA2\u670D",is_system:0,sort_order:4}];for(let T of s)try{await e.execute({sql:"INSERT OR IGNORE INTO roles (id, code, name, label, is_system, is_active, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",args:[T.id,T.code,T.name,T.label,T.is_system,1,T.sort_order,Date.now(),Date.now()]});}catch{}let t=[{id:"perm_user_view",code:"user:view",name:"\u67E5\u770B\u7528\u6237",label:"\u67E5\u770B\u7528\u6237",category:"user"},{id:"perm_user_create",code:"user:create",name:"\u521B\u5EFA\u7528\u6237",label:"\u521B\u5EFA\u7528\u6237",category:"user"},{id:"perm_user_update",code:"user:update",name:"\u66F4\u65B0\u7528\u6237",label:"\u66F4\u65B0\u7528\u6237",category:"user"},{id:"perm_user_delete",code:"user:delete",name:"\u5220\u9664\u7528\u6237",label:"\u5220\u9664\u7528\u6237",category:"user"}],E=[{id:"perm_content_view",code:"content:view",name:"\u67E5\u770B\u5185\u5BB9",label:"\u67E5\u770B\u5185\u5BB9",category:"content"},{id:"perm_content_create",code:"content:create",name:"\u521B\u5EFA\u5185\u5BB9",label:"\u521B\u5EFA\u5185\u5BB9",category:"content"},{id:"perm_content_update",code:"content:update",name:"\u66F4\u65B0\u5185\u5BB9",label:"\u66F4\u65B0\u5185\u5BB9",category:"content"},{id:"perm_content_delete",code:"content:delete",name:"\u5220\u9664\u5185\u5BB9",label:"\u5220\u9664\u5185\u5BB9",category:"content"}],a=[{id:"perm_system_settings",code:"system:settings",name:"\u7CFB\u7EDF\u8BBE\u7F6E",label:"\u7CFB\u7EDF\u8BBE\u7F6E",category:"system"},{id:"perm_system_logs",code:"system:logs",name:"\u67E5\u770B\u65E5\u5FD7",label:"\u67E5\u770B\u65E5\u5FD7",category:"system"},{id:"perm_system_backup",code:"system:backup",name:"\u7CFB\u7EDF\u5907\u4EFD",label:"\u7CFB\u7EDF\u5907\u4EFD",category:"system"}],o=[{id:"perm_order_view",code:"order:view",name:"\u67E5\u770B\u8BA2\u5355",label:"\u67E5\u770B\u8BA2\u5355",category:"order"},{id:"perm_order_create",code:"order:create",name:"\u521B\u5EFA\u8BA2\u5355",label:"\u521B\u5EFA\u8BA2\u5355",category:"order"},{id:"perm_order_edit",code:"order:edit",name:"\u7F16\u8F91\u8BA2\u5355",label:"\u7F16\u8F91\u8BA2\u5355",category:"order"},{id:"perm_order_delete",code:"order:delete",name:"\u5220\u9664\u8BA2\u5355",label:"\u5220\u9664\u8BA2\u5355",category:"order"},{id:"perm_order_process",code:"order:process",name:"\u5904\u7406\u8BA2\u5355",label:"\u5904\u7406\u8BA2\u5355",category:"order"}],N=[{id:"perm_ticket_view",code:"ticket:view",name:"\u67E5\u770B\u5DE5\u5355",label:"\u67E5\u770B\u5DE5\u5355",category:"ticket"},{id:"perm_ticket_create",code:"ticket:create",name:"\u521B\u5EFA\u5DE5\u5355",label:"\u521B\u5EFA\u5DE5\u5355",category:"ticket"},{id:"perm_ticket_edit",code:"ticket:edit",name:"\u7F16\u8F91\u5DE5\u5355",label:"\u7F16\u8F91\u5DE5\u5355",category:"ticket"},{id:"perm_ticket_delete",code:"ticket:delete",name:"\u5220\u9664\u5DE5\u5355",label:"\u5220\u9664\u5DE5\u5355",category:"ticket"},{id:"perm_ticket_reply",code:"ticket:reply",name:"\u56DE\u590D\u5DE5\u5355",label:"\u56DE\u590D\u5DE5\u5355",category:"ticket"},{id:"perm_ticket_close",code:"ticket:close",name:"\u5173\u95ED\u5DE5\u5355",label:"\u5173\u95ED\u5DE5\u5355",category:"ticket"}],d=[{id:"perm_data_export",code:"data:export",name:"\u5BFC\u51FA\u6570\u636E",label:"\u5BFC\u51FA\u6570\u636E",category:"data"},{id:"perm_data_import",code:"data:import",name:"\u5BFC\u5165\u6570\u636E",label:"\u5BFC\u5165\u6570\u636E",category:"data"}],L=[{id:"perm_dispute_view",code:"dispute:view",name:"\u67E5\u770B\u4E89\u8BAE",label:"\u67E5\u770B\u4E89\u8BAE",category:"dispute"},{id:"perm_dispute_create",code:"dispute:create",name:"\u521B\u5EFA\u4E89\u8BAE",label:"\u521B\u5EFA\u4E89\u8BAE",category:"dispute"},{id:"perm_dispute_edit",code:"dispute:edit",name:"\u7F16\u8F91\u4E89\u8BAE",label:"\u7F16\u8F91\u4E89\u8BAE",category:"dispute"},{id:"perm_dispute_delete",code:"dispute:delete",name:"\u5220\u9664\u4E89\u8BAE",label:"\u5220\u9664\u4E89\u8BAE",category:"dispute"},{id:"perm_dispute_resolve",code:"dispute:resolve",name:"\u89E3\u51B3\u4E89\u8BAE",label:"\u89E3\u51B3\u4E89\u8BAE",category:"dispute"}],i=[...t,...E,...a,...o,...N,...d,...L];for(let T of i)try{await e.execute({sql:"INSERT OR IGNORE INTO permissions (id, code, name, label, category, is_active, sort_order, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",args:[T.id,T.code,T.name,T.label,T.category,1,0,Date.now(),Date.now()]});}catch{}for(let T of i)try{await e.execute({sql:"INSERT OR IGNORE INTO role_permissions (role_id, permission_id, created_at) VALUES (?, ?, ?)",args:["role_super_admin",T.id,Date.now()]});}catch{}for(let T of i)if(T.code!=="system:settings")try{await e.execute({sql:"INSERT OR IGNORE INTO role_permissions (role_id, permission_id, created_at) VALUES (?, ?, ?)",args:["role_admin",T.id,Date.now()]});}catch{}let c=["perm_user_view","perm_content_view","perm_order_view","perm_order_create","perm_order_edit","perm_order_delete","perm_order_process","perm_ticket_view","perm_ticket_create","perm_ticket_edit","perm_ticket_delete","perm_ticket_reply","perm_ticket_close","perm_dispute_view","perm_dispute_create","perm_dispute_edit","perm_dispute_delete","perm_dispute_resolve"];for(let T of c)try{await e.execute({sql:"INSERT OR IGNORE INTO role_permissions (role_id, permission_id, created_at) VALUES (?, ?, ?)",args:["role_customer_service",T,Date.now()]});}catch{}let _=[{userId:"super-admin-1",roleId:"role_super_admin"},{userId:"customer-service-1",roleId:"role_customer_service"},{userId:"user-1",roleId:"role_user"}];for(let T of _)try{await e.execute({sql:"INSERT OR IGNORE INTO user_roles (id, user_id, role_id, is_active, assigned_at) VALUES (?, ?, ?, ?, ?)",args:[`ur_${T.userId}`,T.userId,T.roleId,1,Date.now()]});}catch{}let n=["perm_content_view","perm_order_view"];for(let T of n)try{await e.execute({sql:"INSERT OR IGNORE INTO role_permissions (role_id, permission_id, created_at) VALUES (?, ?, ?)",args:["role_user",T,Date.now()]});}catch{}}async function m(){let e=await j();if(e&&"execute"in e){try{await e.execute("DELETE FROM todo_attachments");}catch{}await e.execute("DELETE FROM todos"),await e.execute("DELETE FROM notifications"),await e.execute("DELETE FROM permission_routes"),await e.execute("DELETE FROM permission_audit_logs"),await e.execute("DELETE FROM role_permissions"),await e.execute("DELETE FROM user_roles"),await e.execute("DELETE FROM permissions"),await e.execute("DELETE FROM roles"),await e.execute("DELETE FROM routes"),await e.execute("DELETE FROM ticket_replies"),await e.execute("DELETE FROM tickets"),await e.execute("DELETE FROM orders"),await e.execute("DELETE FROM disputes"),await e.execute("DELETE FROM contents");try{await e.execute("DELETE FROM plugin_category_mappings"),await e.execute("DELETE FROM plugin_reviews"),await e.execute("DELETE FROM plugin_versions"),await e.execute("DELETE FROM plugins"),await e.execute("DELETE FROM plugin_categories"),await e.execute("DELETE FROM developers");}catch{}}}export{m as cleanupTestDatabase,O as setupTestDatabase};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{h as closeTicket,d as createTicket,f as deleteTicket,c as getTicketById,b as getTickets,g as replyTicket,a as seedTicketsIfEmpty,e as updateTicket}from'./chunk-JHNYLNG7.js';import'./chunk-I6OBDFDU.js';import'./chunk-FIY4CGJJ.js';import'./chunk-JCPOP4XL.js';import'./chunk-UMAQLNPE.js';import'./chunk-OYCBNRMK.js';import'./chunk-27SIJSPF.js';import'./chunk-5J6HXUF6.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{l as createTodo,r as deleteAttachment,n as deleteTodo,k as getTodo,q as getTodoWithAttachments,p as listAttachments,j as listTodos,i as seedTodosIfEmpty,m as updateTodo,o as uploadAttachment}from'./chunk-OU5IGVVO.js';import'./chunk-FIY4CGJJ.js';import'./chunk-JCPOP4XL.js';import'./chunk-UMAQLNPE.js';import'./chunk-OYCBNRMK.js';import'./chunk-27SIJSPF.js';import'./chunk-5J6HXUF6.js';
|
package/template/package.json
CHANGED
|
@@ -19,7 +19,9 @@ export const App: React.FC<{ presetId?: string }> = ({ presetId = 'todo' }) => {
|
|
|
19
19
|
mobileTabs={mobileTabs}
|
|
20
20
|
>
|
|
21
21
|
<Routes>
|
|
22
|
-
|
|
22
|
+
{defaultRoute !== '/' && (
|
|
23
|
+
<Route path="/" element={<Navigate to={defaultRoute} replace />} />
|
|
24
|
+
)}
|
|
23
25
|
{routes
|
|
24
26
|
.filter(
|
|
25
27
|
(r): r is RouteDef & { component: NonNullable<RouteDef['component']> } =>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback } from 'react'
|
|
2
2
|
import { Link } from 'react-router-dom'
|
|
3
3
|
import { Helmet } from 'react-helmet-async'
|
|
4
|
-
import type { Content, ContentCategory } from '@shared/modules/content'
|
|
4
|
+
import type { Content, ContentCategory, ContentStatus } from '@shared/modules/content'
|
|
5
5
|
import { apiClient } from '@client/services/apiClient'
|
|
6
6
|
|
|
7
7
|
const CATEGORIES: { value: ContentCategory | ''; label: string }[] = [
|
|
@@ -66,6 +66,15 @@ export const ContentListPage: React.FC = () => {
|
|
|
66
66
|
})
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
const statusConfig = (status: ContentStatus) => {
|
|
70
|
+
const configs = {
|
|
71
|
+
draft: { color: 'bg-yellow-100 text-yellow-700', label: '草稿' },
|
|
72
|
+
published: { color: 'bg-green-100 text-green-700', label: '已发布' },
|
|
73
|
+
archived: { color: 'bg-gray-100 text-gray-700', label: '已归档' },
|
|
74
|
+
}
|
|
75
|
+
return configs[status]
|
|
76
|
+
}
|
|
77
|
+
|
|
69
78
|
return (
|
|
70
79
|
<>
|
|
71
80
|
<Helmet>
|
|
@@ -137,16 +146,39 @@ export const ContentListPage: React.FC = () => {
|
|
|
137
146
|
>
|
|
138
147
|
<div className="flex items-start justify-between gap-4">
|
|
139
148
|
<div className="flex-1">
|
|
140
|
-
<div className="flex items-center gap-2 mb-2">
|
|
149
|
+
<div className="flex items-center gap-2 mb-2 flex-wrap">
|
|
141
150
|
<span className="px-2 py-1 bg-blue-100 text-blue-700 text-xs font-medium rounded">
|
|
142
151
|
{categoryLabel(content.category)}
|
|
143
152
|
</span>
|
|
153
|
+
<span
|
|
154
|
+
className={`px-2 py-1 text-xs font-medium rounded ${
|
|
155
|
+
statusConfig(content.status).color
|
|
156
|
+
}`}
|
|
157
|
+
>
|
|
158
|
+
{statusConfig(content.status).label}
|
|
159
|
+
</span>
|
|
144
160
|
<span className="text-sm text-gray-500">{content.author}</span>
|
|
145
161
|
</div>
|
|
146
162
|
<h2 className="text-xl font-semibold text-gray-900 mb-2">{content.title}</h2>
|
|
147
163
|
<p className="text-gray-600 line-clamp-2">{content.content.slice(0, 150)}...</p>
|
|
148
|
-
|
|
149
|
-
|
|
164
|
+
{content.tags && content.tags.length > 0 && (
|
|
165
|
+
<div className="flex gap-1.5 flex-wrap mt-3">
|
|
166
|
+
{content.tags.map(tag => (
|
|
167
|
+
<span
|
|
168
|
+
key={tag}
|
|
169
|
+
className="px-2 py-0.5 bg-gray-100 text-gray-600 text-xs rounded"
|
|
170
|
+
>
|
|
171
|
+
{tag}
|
|
172
|
+
</span>
|
|
173
|
+
))}
|
|
174
|
+
</div>
|
|
175
|
+
)}
|
|
176
|
+
<div className="flex items-center gap-4 mt-3 text-sm text-gray-400 flex-wrap">
|
|
177
|
+
{content.createdAt && <span>创建于 {formatDate(content.createdAt)}</span>}
|
|
178
|
+
{content.updatedAt && content.updatedAt !== content.createdAt && (
|
|
179
|
+
<span>更新于 {formatDate(content.updatedAt)}</span>
|
|
180
|
+
)}
|
|
181
|
+
{content.publishedAt && <span>发布于 {formatDate(content.publishedAt)}</span>}
|
|
150
182
|
<span>👁 {content.viewCount}</span>
|
|
151
183
|
<span>❤ {content.likeCount}</span>
|
|
152
184
|
</div>
|
|
@@ -10,10 +10,27 @@ import {
|
|
|
10
10
|
Sparkles,
|
|
11
11
|
TrendingUp,
|
|
12
12
|
Zap,
|
|
13
|
+
Eye,
|
|
14
|
+
ExternalLink,
|
|
15
|
+
Star,
|
|
16
|
+
Shield,
|
|
13
17
|
} from 'lucide-react'
|
|
14
18
|
import { usePluginStore } from '@client/stores/pluginStore'
|
|
15
19
|
import { LoadingSpinner, EmptyState } from '@client/components'
|
|
16
|
-
import { StatusBadge } from '@client/components'
|
|
20
|
+
import { StatusBadge, type ColorScheme } from '@client/components'
|
|
21
|
+
|
|
22
|
+
const getStatusColor = (status: string): ColorScheme => {
|
|
23
|
+
switch (status?.toLowerCase()) {
|
|
24
|
+
case 'approved':
|
|
25
|
+
return 'green'
|
|
26
|
+
case 'pending':
|
|
27
|
+
return 'yellow'
|
|
28
|
+
case 'rejected':
|
|
29
|
+
return 'red'
|
|
30
|
+
default:
|
|
31
|
+
return 'gray'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
17
34
|
|
|
18
35
|
export const PluginsPage: React.FC = () => {
|
|
19
36
|
const plugins = usePluginStore(state => state.plugins)
|
|
@@ -141,37 +158,112 @@ export const PluginsPage: React.FC = () => {
|
|
|
141
158
|
<Link
|
|
142
159
|
key={plugin.id}
|
|
143
160
|
to={`/plugins/${plugin.slug}`}
|
|
144
|
-
className="group relative
|
|
161
|
+
className="group relative bg-white rounded-2xl border border-gray-100 hover:border-violet-200 hover:shadow-xl hover:shadow-violet-500/5 transition-all duration-300 block overflow-hidden"
|
|
145
162
|
>
|
|
146
163
|
<div className="absolute inset-0 bg-gradient-to-br from-violet-500/0 to-fuchsia-500/0 group-hover:from-violet-500/[0.02] group-hover:to-fuchsia-500/[0.04] transition-all duration-300" />
|
|
147
164
|
<div className="relative">
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
165
|
+
{/* Screenshot thumbnail */}
|
|
166
|
+
<div className="relative">
|
|
167
|
+
{plugin.screenshotUrl ? (
|
|
168
|
+
<img
|
|
169
|
+
src={plugin.screenshotUrl}
|
|
170
|
+
alt={plugin.name}
|
|
171
|
+
className="w-full h-32 object-cover"
|
|
172
|
+
/>
|
|
173
|
+
) : (
|
|
174
|
+
<div className="w-full h-20 bg-gradient-to-br from-violet-100 to-fuchsia-100" />
|
|
175
|
+
)}
|
|
176
|
+
{plugin.featured && (
|
|
177
|
+
<div className="absolute top-2 right-2">
|
|
178
|
+
<div className="p-1.5 bg-amber-400 rounded-full shadow-lg">
|
|
179
|
+
<Star className="w-3.5 h-3.5 text-white fill-white" />
|
|
180
|
+
</div>
|
|
152
181
|
</div>
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
182
|
+
)}
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<div className="p-5">
|
|
186
|
+
<div className="flex items-start justify-between gap-3 mb-3">
|
|
187
|
+
<div className="flex items-center gap-3">
|
|
188
|
+
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-violet-100 to-fuchsia-100 flex items-center justify-center text-lg font-bold text-violet-600 shrink-0">
|
|
189
|
+
{plugin.name.charAt(0).toUpperCase()}
|
|
190
|
+
</div>
|
|
191
|
+
<div className="min-w-0">
|
|
192
|
+
<h3 className="text-base font-semibold text-gray-900 truncate group-hover:text-violet-700 transition-colors">
|
|
193
|
+
{plugin.name}
|
|
194
|
+
</h3>
|
|
195
|
+
<p className="text-xs text-gray-400">by {plugin.authorName}</p>
|
|
196
|
+
</div>
|
|
158
197
|
</div>
|
|
198
|
+
<StatusBadge label={`v${plugin.version}`} colorScheme="purple" />
|
|
159
199
|
</div>
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
200
|
+
|
|
201
|
+
{/* Status and License */}
|
|
202
|
+
<div className="flex items-center gap-2 mb-3">
|
|
203
|
+
<StatusBadge
|
|
204
|
+
label={plugin.status || 'Unknown'}
|
|
205
|
+
icon={Shield}
|
|
206
|
+
colorScheme={getStatusColor(plugin.status || '')}
|
|
207
|
+
/>
|
|
208
|
+
{plugin.license && (
|
|
209
|
+
<span className="inline-flex items-center px-2 py-0.5 bg-gray-100 text-gray-600 rounded text-xs font-medium">
|
|
210
|
+
{plugin.license}
|
|
211
|
+
</span>
|
|
212
|
+
)}
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<p className="text-sm text-gray-500 line-clamp-2 mb-4 leading-relaxed">
|
|
216
|
+
{plugin.description}
|
|
217
|
+
</p>
|
|
218
|
+
|
|
219
|
+
{/* Download + View counts */}
|
|
220
|
+
<div className="flex items-center gap-3 text-xs text-gray-400 mb-3">
|
|
167
221
|
<span className="flex items-center gap-1">
|
|
168
222
|
<Download className="w-3.5 h-3.5" />
|
|
169
223
|
{plugin.downloadCount}
|
|
170
224
|
</span>
|
|
225
|
+
{plugin.viewCount !== undefined && (
|
|
226
|
+
<span className="flex items-center gap-1">
|
|
227
|
+
<Eye className="w-3.5 h-3.5" />
|
|
228
|
+
{plugin.viewCount}
|
|
229
|
+
</span>
|
|
230
|
+
)}
|
|
171
231
|
</div>
|
|
232
|
+
|
|
233
|
+
{/* External links */}
|
|
234
|
+
{(plugin.repositoryUrl || plugin.homepageUrl) && (
|
|
235
|
+
<div className="flex items-center gap-1.5 mb-3">
|
|
236
|
+
{plugin.repositoryUrl && (
|
|
237
|
+
<a
|
|
238
|
+
href={plugin.repositoryUrl}
|
|
239
|
+
target="_blank"
|
|
240
|
+
rel="noopener noreferrer"
|
|
241
|
+
onClick={e => e.stopPropagation()}
|
|
242
|
+
className="p-1.5 rounded-lg bg-gray-50 hover:bg-gray-100 text-gray-400 hover:text-violet-600 transition-colors"
|
|
243
|
+
title="Repository"
|
|
244
|
+
>
|
|
245
|
+
<ExternalLink className="w-3.5 h-3.5" />
|
|
246
|
+
</a>
|
|
247
|
+
)}
|
|
248
|
+
{plugin.homepageUrl && (
|
|
249
|
+
<a
|
|
250
|
+
href={plugin.homepageUrl}
|
|
251
|
+
target="_blank"
|
|
252
|
+
rel="noopener noreferrer"
|
|
253
|
+
onClick={e => e.stopPropagation()}
|
|
254
|
+
className="p-1.5 rounded-lg bg-gray-50 hover:bg-gray-100 text-gray-400 hover:text-violet-600 transition-colors"
|
|
255
|
+
title="Homepage"
|
|
256
|
+
>
|
|
257
|
+
<ExternalLink className="w-3.5 h-3.5" />
|
|
258
|
+
</a>
|
|
259
|
+
)}
|
|
260
|
+
</div>
|
|
261
|
+
)}
|
|
262
|
+
|
|
263
|
+
{/* Tags - show all */}
|
|
172
264
|
{plugin.tags && plugin.tags.length > 0 && (
|
|
173
|
-
<div className="flex items-center gap-1">
|
|
174
|
-
{plugin.tags.
|
|
265
|
+
<div className="flex flex-wrap items-center gap-1">
|
|
266
|
+
{plugin.tags.map(tag => (
|
|
175
267
|
<span
|
|
176
268
|
key={tag}
|
|
177
269
|
className="inline-flex items-center px-2 py-0.5 bg-gray-50 text-gray-500 rounded-md text-xs"
|
|
@@ -112,7 +112,10 @@ export const TodoPage: React.FC = () => {
|
|
|
112
112
|
<title>Todo List - Biomimic App</title>
|
|
113
113
|
<meta name="description" content="Manage your todo items with full CRUD operations" />
|
|
114
114
|
<meta property="og:title" content="Todo List - Biomimic App" />
|
|
115
|
-
<meta
|
|
115
|
+
<meta
|
|
116
|
+
property="og:description"
|
|
117
|
+
content="Manage your todo items with full CRUD operations"
|
|
118
|
+
/>
|
|
116
119
|
</Helmet>
|
|
117
120
|
<div className="mb-8">
|
|
118
121
|
<h1
|
|
@@ -275,6 +278,11 @@ export const TodoPage: React.FC = () => {
|
|
|
275
278
|
</select>
|
|
276
279
|
<span className="text-xs text-gray-400">
|
|
277
280
|
{new Date(todo.createdAt).toLocaleString()}
|
|
281
|
+
{todo.updatedAt && todo.updatedAt !== todo.createdAt && (
|
|
282
|
+
<span className="ml-2">
|
|
283
|
+
· Updated {new Date(todo.updatedAt).toLocaleString()}
|
|
284
|
+
</span>
|
|
285
|
+
)}
|
|
278
286
|
</span>
|
|
279
287
|
</div>
|
|
280
288
|
</div>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { lazy, type ComponentType } from 'react'
|
|
2
2
|
|
|
3
|
-
export type PresetType = 'todo' | 'plugin' | 'ecommerce' | 'community' | 'saas'
|
|
3
|
+
export type PresetType = 'todo' | 'plugin' | 'ecommerce' | 'community' | 'forum' | 'saas'
|
|
4
4
|
|
|
5
5
|
export type AppType = 'client' | 'admin'
|
|
6
6
|
export type LayoutType = 'top-nav' | 'minimal'
|
|
@@ -481,10 +481,87 @@ export const PRESET_UI_CONFIGS: Record<PresetType, PresetUIConfig> = {
|
|
|
481
481
|
},
|
|
482
482
|
],
|
|
483
483
|
},
|
|
484
|
+
|
|
485
|
+
// forum is an alias for community — same config, different preset ID
|
|
486
|
+
forum: {
|
|
487
|
+
id: 'community',
|
|
488
|
+
name: 'Community',
|
|
489
|
+
appType: 'client',
|
|
490
|
+
layout: 'top-nav',
|
|
491
|
+
theme: COMMUNITY_THEME,
|
|
492
|
+
navigation: {
|
|
493
|
+
visible: true,
|
|
494
|
+
showLogo: true,
|
|
495
|
+
showSearch: false,
|
|
496
|
+
showCart: false,
|
|
497
|
+
authStyle: 'avatar',
|
|
498
|
+
navItems: 'desktop',
|
|
499
|
+
},
|
|
500
|
+
desktopNav: [
|
|
501
|
+
{ label: 'Topics', icon: 'Hash', path: '/topics' },
|
|
502
|
+
{ label: 'Popular', icon: 'TrendingUp', path: '/popular' },
|
|
503
|
+
{ label: 'Notifications', icon: 'Bell', path: '/notifications' },
|
|
504
|
+
{ label: 'Profile', icon: 'User', path: '/profile' },
|
|
505
|
+
],
|
|
506
|
+
mobileTabs: [
|
|
507
|
+
{ label: 'Topics', icon: 'Hash', path: '/topics' },
|
|
508
|
+
{ label: 'Popular', icon: 'TrendingUp', path: '/popular' },
|
|
509
|
+
{ label: 'Notifications', icon: 'Bell', path: '/notifications' },
|
|
510
|
+
{ label: 'Me', icon: 'User', path: '/profile' },
|
|
511
|
+
],
|
|
512
|
+
defaultRoute: '/topics',
|
|
513
|
+
routes: [
|
|
514
|
+
{
|
|
515
|
+
path: '/topics',
|
|
516
|
+
component: lazy(() => import('./pages/TopicsPage').then(m => ({ default: m.TopicsPage }))),
|
|
517
|
+
label: 'Topics',
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
path: '/popular',
|
|
521
|
+
component: lazy(() => import('./pages/TopicsPage').then(m => ({ default: m.TopicsPage }))),
|
|
522
|
+
label: 'Popular',
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
path: '/notifications',
|
|
526
|
+
component: lazy(() =>
|
|
527
|
+
import('./pages/NotificationPage').then(m => ({ default: m.NotificationPage }))
|
|
528
|
+
),
|
|
529
|
+
label: 'Notifications',
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
path: '/profile',
|
|
533
|
+
component: lazy(() =>
|
|
534
|
+
import('./pages/ProfilePage').then(m => ({ default: m.ProfilePage }))
|
|
535
|
+
),
|
|
536
|
+
label: 'Profile',
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
path: '/login',
|
|
540
|
+
component: lazy(() => import('./pages/LoginPage').then(m => ({ default: m.LoginPage }))),
|
|
541
|
+
label: 'Login',
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
path: '/register',
|
|
545
|
+
component: lazy(() =>
|
|
546
|
+
import('./pages/RegisterPage').then(m => ({ default: m.RegisterPage }))
|
|
547
|
+
),
|
|
548
|
+
label: 'Register',
|
|
549
|
+
},
|
|
550
|
+
],
|
|
551
|
+
},
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Preset ID aliases: deployed preset IDs may differ from internal config keys
|
|
555
|
+
const PRESET_ALIASES: Record<string, PresetType> = {
|
|
556
|
+
forum: 'community',
|
|
557
|
+
'xbrowser-marketplace': 'plugin',
|
|
558
|
+
'fullstack-admin': 'saas',
|
|
559
|
+
'todo-app': 'todo',
|
|
484
560
|
}
|
|
485
561
|
|
|
486
562
|
export function getPresetUIConfig(id: string): PresetUIConfig {
|
|
487
|
-
|
|
563
|
+
const resolvedId = PRESET_ALIASES[id] ?? (id as PresetType)
|
|
564
|
+
return PRESET_UI_CONFIGS[resolvedId] ?? PRESET_UI_CONFIGS.todo
|
|
488
565
|
}
|
|
489
566
|
|
|
490
567
|
export function getPresetUIConfigs(): Record<PresetType, PresetUIConfig> {
|
|
@@ -15,8 +15,8 @@ export const DisputesPage: FC = () => {
|
|
|
15
15
|
setLoading(true)
|
|
16
16
|
try {
|
|
17
17
|
const response = await fetch('/api/merchant/disputes')
|
|
18
|
-
const result = await response.json()
|
|
19
|
-
if (result.success) {
|
|
18
|
+
const result = (await response.json()) as { success?: boolean; data?: Dispute[] }
|
|
19
|
+
if (result.success === true && result.data) {
|
|
20
20
|
setDisputes(result.data)
|
|
21
21
|
}
|
|
22
22
|
} catch (error) {
|
|
@@ -31,7 +31,7 @@ export const DisputesPage: FC = () => {
|
|
|
31
31
|
setShowModal(true)
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const handleResolve = async (disputeId:
|
|
34
|
+
const handleResolve = async (disputeId: string) => {
|
|
35
35
|
try {
|
|
36
36
|
await fetch(`/api/merchant/disputes/${disputeId}/resolve`, {
|
|
37
37
|
method: 'POST',
|
|
@@ -42,7 +42,7 @@ export const DisputesPage: FC = () => {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
const handleClose = async (disputeId:
|
|
45
|
+
const handleClose = async (disputeId: string) => {
|
|
46
46
|
try {
|
|
47
47
|
await fetch(`/api/merchant/disputes/${disputeId}/close`, {
|
|
48
48
|
method: 'POST',
|
|
@@ -59,10 +59,10 @@ export const DisputesPage: FC = () => {
|
|
|
59
59
|
|
|
60
60
|
const getStatusColor = (status: DisputeStatus): string => {
|
|
61
61
|
const colors: Record<DisputeStatus, string> = {
|
|
62
|
-
|
|
62
|
+
pending: 'blue',
|
|
63
63
|
investigating: 'orange',
|
|
64
64
|
resolved: 'green',
|
|
65
|
-
|
|
65
|
+
rejected: 'red',
|
|
66
66
|
}
|
|
67
67
|
return colors[status] || 'default'
|
|
68
68
|
}
|
|
@@ -74,9 +74,9 @@ export const DisputesPage: FC = () => {
|
|
|
74
74
|
key: 'orderId',
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
|
-
title: '
|
|
78
|
-
dataIndex: '
|
|
79
|
-
key: '
|
|
77
|
+
title: 'Type',
|
|
78
|
+
dataIndex: 'type',
|
|
79
|
+
key: 'type',
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
82
|
title: 'Status',
|
|
@@ -98,7 +98,7 @@ export const DisputesPage: FC = () => {
|
|
|
98
98
|
<Button type="link" icon={<EyeOutlined />} onClick={() => handleView(record)}>
|
|
99
99
|
View
|
|
100
100
|
</Button>
|
|
101
|
-
{record.status === '
|
|
101
|
+
{record.status === 'pending' && (
|
|
102
102
|
<>
|
|
103
103
|
<Button type="link" icon={<CheckOutlined />} onClick={() => handleResolve(record.id)}>
|
|
104
104
|
Resolve
|
|
@@ -128,7 +128,7 @@ export const DisputesPage: FC = () => {
|
|
|
128
128
|
<h3>Dispute Details</h3>
|
|
129
129
|
<Descriptions bordered>
|
|
130
130
|
<Descriptions.Item label="Order ID">{selectedDispute.orderId}</Descriptions.Item>
|
|
131
|
-
<Descriptions.Item label="
|
|
131
|
+
<Descriptions.Item label="Type">{selectedDispute.type}</Descriptions.Item>
|
|
132
132
|
<Descriptions.Item label="Status">
|
|
133
133
|
<Tag color={getStatusColor(selectedDispute.status)}>{selectedDispute.status}</Tag>
|
|
134
134
|
</Descriptions.Item>
|
|
@@ -16,8 +16,8 @@ export const OrdersPage: FC = () => {
|
|
|
16
16
|
setLoading(true)
|
|
17
17
|
try {
|
|
18
18
|
const response = await fetch('/api/merchant/orders')
|
|
19
|
-
const result = await response.json()
|
|
20
|
-
if (result.success) {
|
|
19
|
+
const result = (await response.json()) as { success?: boolean; data?: Order[] }
|
|
20
|
+
if (result.success === true && result.data) {
|
|
21
21
|
setOrders(result.data)
|
|
22
22
|
}
|
|
23
23
|
} catch (error) {
|
|
@@ -31,7 +31,8 @@ export const OrdersPage: FC = () => {
|
|
|
31
31
|
setStatusFilter(value)
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Ant Design RangePicker types are complex
|
|
35
|
+
const handleDateRangeChange = (dates: any) => {
|
|
35
36
|
setDateRange(dates)
|
|
36
37
|
}
|
|
37
38
|
|