create-fullstack-scaffold 0.4.10 → 0.4.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/cli/index.js.map +1 -1
  2. package/package.json +1 -1
  3. package/template/src/admin/App.tsx +4 -1
  4. package/template/src/admin/components/NotificationDrawer.tsx +13 -42
  5. package/template/src/admin/components/ThemeToggle.tsx +2 -2
  6. package/template/src/admin/layouts/Header.tsx +12 -9
  7. package/template/src/admin/pages/ContentPage.tsx +8 -10
  8. package/template/src/admin/pages/DashboardPage.tsx +30 -17
  9. package/template/src/admin/pages/DisputesPage.tsx +20 -14
  10. package/template/src/admin/pages/LoginPage.tsx +14 -3
  11. package/template/src/admin/pages/OrdersPage.tsx +10 -6
  12. package/template/src/admin/pages/PermissionsPage.tsx +1 -3
  13. package/template/src/admin/pages/RegisterPage.tsx +6 -4
  14. package/template/src/admin/pages/RolesPage.tsx +10 -3
  15. package/template/src/admin/pages/SystemLogsPage.tsx +12 -4
  16. package/template/src/admin/pages/TicketsPage.tsx +15 -15
  17. package/template/src/admin/pages/UsersPage.tsx +10 -6
  18. package/template/src/admin/stores/themeStore.ts +1 -1
  19. package/template/src/cli/modules/admin/index.ts +192 -0
  20. package/template/src/cli/modules/captcha/index.ts +40 -0
  21. package/template/src/cli/modules/chat/index.ts +21 -0
  22. package/template/src/cli/modules/content/index.ts +152 -0
  23. package/template/src/cli/modules/dispute/index.ts +156 -0
  24. package/template/src/cli/modules/file/index.ts +60 -0
  25. package/template/src/cli/modules/index.ts +30 -5
  26. package/template/src/cli/modules/order/index.ts +183 -0
  27. package/template/src/cli/modules/permission/index.ts +201 -0
  28. package/template/src/cli/modules/tenant/index.ts +142 -0
  29. package/template/src/cli/modules/ticket/index.ts +178 -0
  30. package/template/src/cli/rpc/client.ts +2 -2
  31. package/template/src/client/index.css +22 -24
@@ -0,0 +1,178 @@
1
+ import type { SiteInstance } from '@dyyz1993/xcli-core'
2
+ import { ok, fail } from '@dyyz1993/xcli-core'
3
+ import { z } from 'zod'
4
+ import { getClient } from '@cli/utils/api'
5
+
6
+ export function registerTicketCommands(site: SiteInstance) {
7
+ site.command('list', {
8
+ description: 'List all tickets',
9
+ parameters: z.object({
10
+ limit: z.coerce.number().default(20).describe('Limit results'),
11
+ offset: z.coerce.number().default(0).describe('Offset'),
12
+ }),
13
+ handler: async (params: unknown) => {
14
+ const p = params as { limit: number; offset: number }
15
+ try {
16
+ const client = getClient()
17
+ const res = await client.api.tickets.$get({
18
+ query: { limit: String(p.limit), offset: String(p.offset) },
19
+ })
20
+ const data = await res.json()
21
+ return ok(data)
22
+ } catch (err) {
23
+ return fail(err instanceof Error ? err.message : 'Failed to list tickets')
24
+ }
25
+ },
26
+ })
27
+
28
+ site.command('get', {
29
+ description: 'Get ticket by ID',
30
+ parameters: z.object({
31
+ id: z.string().describe('Ticket ID'),
32
+ }),
33
+ handler: async (params: unknown) => {
34
+ const p = params as { id: string }
35
+ try {
36
+ const client = getClient()
37
+ const res = await client.api.tickets[':id'].$get({ param: { id: p.id } })
38
+ const data = await res.json()
39
+ return ok(data)
40
+ } catch (err) {
41
+ return fail(err instanceof Error ? err.message : 'Failed to get ticket')
42
+ }
43
+ },
44
+ })
45
+
46
+ site.command('create', {
47
+ description: 'Create a new ticket',
48
+ parameters: z.object({
49
+ 'customer-name': z.string().min(1).describe('Customer name'),
50
+ 'customer-email': z.string().email().describe('Customer email'),
51
+ subject: z.string().min(1).describe('Ticket subject'),
52
+ description: z.string().min(1).describe('Ticket description'),
53
+ category: z
54
+ .enum(['technical', 'billing', 'feature_request', 'bug_report', 'general'])
55
+ .default('general')
56
+ .describe('Category'),
57
+ priority: z.enum(['low', 'medium', 'high', 'urgent']).default('medium').describe('Priority'),
58
+ }),
59
+ handler: async (params: unknown) => {
60
+ const p = params as {
61
+ 'customer-name': string
62
+ 'customer-email': string
63
+ subject: string
64
+ description: string
65
+ category: string
66
+ priority: string
67
+ }
68
+ try {
69
+ const client = getClient()
70
+ const res = await client.api.tickets.$post({
71
+ json: {
72
+ customerName: p['customer-name'],
73
+ customerEmail: p['customer-email'],
74
+ subject: p.subject,
75
+ description: p.description,
76
+ category: p.category as
77
+ | 'technical'
78
+ | 'billing'
79
+ | 'feature_request'
80
+ | 'bug_report'
81
+ | 'general',
82
+ priority: p.priority as 'low' | 'medium' | 'high' | 'urgent',
83
+ },
84
+ })
85
+ const data = await res.json()
86
+ return ok(data, ['Ticket created'])
87
+ } catch (err) {
88
+ return fail(err instanceof Error ? err.message : 'Failed to create ticket')
89
+ }
90
+ },
91
+ })
92
+
93
+ site.command('update', {
94
+ description: 'Update ticket status',
95
+ parameters: z.object({
96
+ id: z.string().describe('Ticket ID'),
97
+ status: z
98
+ .enum(['open', 'closed', 'in_progress', 'waiting_customer', 'resolved'])
99
+ .optional()
100
+ .describe('New status'),
101
+ 'assigned-to': z.string().optional().describe('Assign to user'),
102
+ }),
103
+ handler: async (params: unknown) => {
104
+ const p = params as { id: string; status?: string; 'assigned-to'?: string }
105
+ const { id, ...rest } = p
106
+ const body: Record<string, string> = {}
107
+ if (rest.status) body.status = rest.status
108
+ if (rest['assigned-to']) body.assignedTo = rest['assigned-to']
109
+ try {
110
+ const client = getClient()
111
+ const res = await client.api.tickets[':id'].$put({ param: { id }, json: body })
112
+ const data = await res.json()
113
+ return ok(data, ['Ticket updated'])
114
+ } catch (err) {
115
+ return fail(err instanceof Error ? err.message : 'Failed to update ticket')
116
+ }
117
+ },
118
+ })
119
+
120
+ site.command('delete', {
121
+ description: 'Delete a ticket',
122
+ parameters: z.object({
123
+ id: z.string().describe('Ticket ID'),
124
+ }),
125
+ handler: async (params: unknown) => {
126
+ const p = params as { id: string }
127
+ try {
128
+ const client = getClient()
129
+ const res = await client.api.tickets[':id'].$delete({ param: { id: p.id } })
130
+ const data = await res.json()
131
+ return ok(data, ['Ticket deleted'])
132
+ } catch (err) {
133
+ return fail(err instanceof Error ? err.message : 'Failed to delete ticket')
134
+ }
135
+ },
136
+ })
137
+
138
+ site.command('reply', {
139
+ description: 'Reply to a ticket',
140
+ parameters: z.object({
141
+ id: z.string().describe('Ticket ID'),
142
+ content: z.string().min(1).describe('Reply content'),
143
+ author: z.string().min(1).describe('Author name'),
144
+ }),
145
+ handler: async (params: unknown) => {
146
+ const p = params as { id: string; content: string; author: string }
147
+ try {
148
+ const client = getClient()
149
+ const res = await client.api.tickets[':id'].reply.$post({
150
+ param: { id: p.id },
151
+ json: { content: p.content, author: p.author },
152
+ })
153
+ const data = await res.json()
154
+ return ok(data, ['Reply sent'])
155
+ } catch (err) {
156
+ return fail(err instanceof Error ? err.message : 'Failed to reply ticket')
157
+ }
158
+ },
159
+ })
160
+
161
+ site.command('close', {
162
+ description: 'Close a ticket',
163
+ parameters: z.object({
164
+ id: z.string().describe('Ticket ID'),
165
+ }),
166
+ handler: async (params: unknown) => {
167
+ const p = params as { id: string }
168
+ try {
169
+ const client = getClient()
170
+ const res = await client.api.tickets[':id'].close.$put({ param: { id: p.id } })
171
+ const data = await res.json()
172
+ return ok(data, ['Ticket closed'])
173
+ } catch (err) {
174
+ return fail(err instanceof Error ? err.message : 'Failed to close ticket')
175
+ }
176
+ },
177
+ })
178
+ }
@@ -1,5 +1,5 @@
1
1
  import { hc } from 'hono/client'
2
- import type { ClientApiType, AppType } from '@server/index'
2
+ import type { AppType } from '@server/index'
3
3
  import { WSClientImpl } from '@shared/core/ws-client'
4
4
  import { SSEClientImpl } from '@shared/core/sse-client'
5
5
 
@@ -16,7 +16,7 @@ export interface CliFetchExtendOptions {
16
16
  }
17
17
 
18
18
  export function createRPCClient(baseUrl: string) {
19
- return hc<ClientApiType>(baseUrl, {
19
+ return hc<AppType>(baseUrl, {
20
20
  webSocket: url => new WSClientImpl(url) as unknown as WebSocket,
21
21
  sse: url => new SSEClientImpl(url),
22
22
  })
@@ -1,4 +1,4 @@
1
- @import "tailwindcss";
1
+ @import 'tailwindcss';
2
2
 
3
3
  /**
4
4
  * Global styles
@@ -10,91 +10,89 @@
10
10
 
11
11
  body {
12
12
  margin: 0;
13
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
14
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
15
- sans-serif;
13
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
14
+ 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
16
15
  -webkit-font-smoothing: antialiased;
17
16
  -moz-osx-font-smoothing: grayscale;
18
17
  }
19
18
 
20
19
  code {
21
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
22
- monospace;
20
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
23
21
  }
24
22
 
25
23
  #root {
26
24
  min-height: 100vh;
27
25
  }
28
26
 
29
- [data-theme="dark"] {
27
+ [data-theme='dark'] {
30
28
  color-scheme: dark;
31
29
  }
32
30
 
33
- [data-theme="dark"] body {
31
+ [data-theme='dark'] body {
34
32
  background-color: #141414;
35
33
  color: rgba(255, 255, 255, 0.85);
36
34
  }
37
35
 
38
- [data-theme="dark"] .bg-white {
36
+ [data-theme='dark'] .bg-white {
39
37
  background-color: #1f1f1f !important;
40
38
  }
41
39
 
42
- [data-theme="dark"] .bg-gray-50 {
40
+ [data-theme='dark'] .bg-gray-50 {
43
41
  background-color: #1a1a1a !important;
44
42
  }
45
43
 
46
- [data-theme="dark"] .bg-gray-100 {
44
+ [data-theme='dark'] .bg-gray-100 {
47
45
  background-color: #1f1f1f !important;
48
46
  }
49
47
 
50
- [data-theme="dark"] .text-gray-900 {
48
+ [data-theme='dark'] .text-gray-900 {
51
49
  color: rgba(255, 255, 255, 0.95) !important;
52
50
  }
53
51
 
54
- [data-theme="dark"] .text-gray-800 {
52
+ [data-theme='dark'] .text-gray-800 {
55
53
  color: rgba(255, 255, 255, 0.85) !important;
56
54
  }
57
55
 
58
- [data-theme="dark"] .text-gray-700 {
56
+ [data-theme='dark'] .text-gray-700 {
59
57
  color: rgba(255, 255, 255, 0.75) !important;
60
58
  }
61
59
 
62
- [data-theme="dark"] .text-gray-600 {
60
+ [data-theme='dark'] .text-gray-600 {
63
61
  color: rgba(255, 255, 255, 0.65) !important;
64
62
  }
65
63
 
66
- [data-theme="dark"] .text-gray-500 {
64
+ [data-theme='dark'] .text-gray-500 {
67
65
  color: rgba(255, 255, 255, 0.55) !important;
68
66
  }
69
67
 
70
- [data-theme="dark"] .border-gray-200 {
68
+ [data-theme='dark'] .border-gray-200 {
71
69
  border-color: #303030 !important;
72
70
  }
73
71
 
74
- [data-theme="dark"] .border-gray-100 {
72
+ [data-theme='dark'] .border-gray-100 {
75
73
  border-color: #262626 !important;
76
74
  }
77
75
 
78
- [data-theme="dark"] .hover\:bg-gray-100:hover {
76
+ [data-theme='dark'] .hover\:bg-gray-100:hover {
79
77
  background-color: rgba(255, 255, 255, 0.08) !important;
80
78
  }
81
79
 
82
- [data-theme="dark"] .hover\:bg-gray-50:hover {
80
+ [data-theme='dark'] .hover\:bg-gray-50:hover {
83
81
  background-color: rgba(255, 255, 255, 0.06) !important;
84
82
  }
85
83
 
86
- [data-theme="dark"] .text-gray-400 {
84
+ [data-theme='dark'] .text-gray-400 {
87
85
  color: #8c8c8c;
88
86
  }
89
87
 
90
- [data-theme="dark"] .text-gray-300 {
88
+ [data-theme='dark'] .text-gray-300 {
91
89
  color: #a0a0a0;
92
90
  }
93
91
 
94
- [data-theme="dark"] .bg-blue-50 {
92
+ [data-theme='dark'] .bg-blue-50 {
95
93
  background-color: rgba(59, 130, 246, 0.1);
96
94
  }
97
95
 
98
- [data-theme="dark"] .border-gray-300 {
96
+ [data-theme='dark'] .border-gray-300 {
99
97
  border-color: #434343;
100
98
  }