create-fullstack-scaffold 0.4.11 → 0.4.13

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 (29) 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 +15 -40
  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 +28 -30
  20. package/template/src/cli/modules/content/index.ts +10 -2
  21. package/template/src/cli/modules/dispute/index.ts +8 -2
  22. package/template/src/cli/modules/file/index.ts +6 -1
  23. package/template/src/cli/modules/index.ts +3 -0
  24. package/template/src/cli/modules/merchant/index.ts +123 -0
  25. package/template/src/cli/modules/order/index.ts +16 -3
  26. package/template/src/cli/modules/permission/index.ts +7 -1
  27. package/template/src/cli/modules/ticket/index.ts +14 -3
  28. package/template/src/client/index.css +22 -24
  29. package/template/vite.config.ts +5 -0
@@ -1,4 +1,4 @@
1
- import { useState, useEffect } from 'react'
1
+ import { useState, useEffect, useCallback } from 'react'
2
2
  import {
3
3
  Table,
4
4
  Card,
@@ -37,18 +37,20 @@ export const UsersPage: React.FC = () => {
37
37
  const [form] = Form.useForm<UserFormData>()
38
38
  const { roleLabels } = useRoleLabels()
39
39
 
40
- const fetchUsers = async () => {
40
+ const fetchUsers = useCallback(async () => {
41
41
  try {
42
- const data = await api(apiClient.api.admin.users.$get()).withLoading(t('users.loading')).json()
42
+ const data = await api(apiClient.api.admin.users.$get())
43
+ .withLoading(t('users.loading'))
44
+ .json()
43
45
  setUsers(data)
44
46
  } catch {
45
47
  // handled by api-request
46
48
  }
47
- }
49
+ }, [t])
48
50
 
49
51
  useEffect(() => {
50
52
  fetchUsers()
51
- }, [])
53
+ }, [fetchUsers])
52
54
 
53
55
  const handleCreate = () => {
54
56
  setEditingUser(null)
@@ -271,7 +273,9 @@ export const UsersPage: React.FC = () => {
271
273
  >
272
274
  <Select placeholder={t('users.rolePlaceholder')}>
273
275
  <Select.Option value={Role.SUPER_ADMIN}>{t('users.superAdmin')}</Select.Option>
274
- <Select.Option value={Role.CUSTOMER_SERVICE}>{t('users.customerService')}</Select.Option>
276
+ <Select.Option value={Role.CUSTOMER_SERVICE}>
277
+ {t('users.customerService')}
278
+ </Select.Option>
275
279
  <Select.Option value={Role.USER}>{t('users.normalUser')}</Select.Option>
276
280
  </Select>
277
281
  </Form.Item>
@@ -21,7 +21,7 @@ export const useThemeStore = create<ThemeState>()(
21
21
  {
22
22
  name: 'admin-theme',
23
23
  onRehydrateStorage: () => {
24
- return (state) => {
24
+ return state => {
25
25
  if (state) {
26
26
  document.documentElement.setAttribute('data-theme', state.mode)
27
27
  }
@@ -2,6 +2,21 @@ import type { SiteInstance } from '@dyyz1993/xcli-core'
2
2
  import { ok, fail } from '@dyyz1993/xcli-core'
3
3
  import { z } from 'zod'
4
4
  import { getClient } from '@cli/utils/api'
5
+ import type { Role } from '@shared/modules/permission'
6
+
7
+ const createUserParams = z.object({
8
+ username: z.string().min(1).describe('Username'),
9
+ email: z.string().email().describe('Email'),
10
+ password: z.string().min(6).describe('Password'),
11
+ role: z.enum(['super_admin', 'customer_service', 'user']).default('user').describe('Role'),
12
+ })
13
+
14
+ const updateUserParams = z.object({
15
+ id: z.string().describe('User ID'),
16
+ username: z.string().optional().describe('New username'),
17
+ email: z.string().email().optional().describe('New email'),
18
+ role: z.enum(['super_admin', 'customer_service', 'user']).optional().describe('New role'),
19
+ })
5
20
 
6
21
  export function registerAdminCommands(site: SiteInstance) {
7
22
  site.command('dashboard', {
@@ -84,29 +99,21 @@ export function registerAdminCommands(site: SiteInstance) {
84
99
 
85
100
  site.command('create-user', {
86
101
  description: 'Create a new user',
87
- parameters: z.object({
88
- username: z.string().min(1).describe('Username'),
89
- email: z.string().email().describe('Email'),
90
- password: z.string().min(6).describe('Password'),
91
- role: z.enum(['super_admin', 'customer_service', 'user']).default('user').describe('Role'),
92
- }),
93
- handler: async (params: unknown) => {
94
- const p = params as {
95
- username: string
96
- email: string
97
- password: string
98
- role: 'super_admin' | 'customer_service' | 'user'
99
- }
102
+ parameters: createUserParams,
103
+ handler: async params => {
104
+ // @ts-expect-error -- auto-command passes unknown, we parse from zod schema
105
+ const p: z.infer<typeof createUserParams> = params
100
106
  try {
101
107
  const client = getClient()
102
108
  const res = await client.api.admin.users.$post({
103
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
104
109
  json: {
105
110
  username: p.username,
106
111
  email: p.email,
107
112
  password: p.password,
108
- role: p.role,
109
- } as any,
113
+ // Role enum values overlap with z.enum string literals — safe cast for RPC compatibility
114
+ // eslint-disable-next-line local-rules/no-type-assertion-on-shared-types
115
+ role: p.role as Role,
116
+ },
110
117
  })
111
118
  const data = await res.json()
112
119
  return ok(data, ['User created'])
@@ -118,19 +125,10 @@ export function registerAdminCommands(site: SiteInstance) {
118
125
 
119
126
  site.command('update-user', {
120
127
  description: 'Update a user',
121
- parameters: z.object({
122
- id: z.string().describe('User ID'),
123
- username: z.string().optional().describe('New username'),
124
- email: z.string().email().optional().describe('New email'),
125
- role: z.enum(['super_admin', 'customer_service', 'user']).optional().describe('New role'),
126
- }),
127
- handler: async (params: unknown) => {
128
- const p = params as {
129
- id: string
130
- username?: string
131
- email?: string
132
- role?: 'super_admin' | 'customer_service' | 'user'
133
- }
128
+ parameters: updateUserParams,
129
+ handler: async params => {
130
+ // @ts-expect-error -- auto-command passes unknown, we parse from zod schema
131
+ const p: z.infer<typeof updateUserParams> = params
134
132
  const { id, ...rest } = p
135
133
  const body: Record<string, string | undefined> = {}
136
134
  if (rest.username) body.username = rest.username
@@ -138,7 +136,7 @@ export function registerAdminCommands(site: SiteInstance) {
138
136
  if (rest.role) body.role = rest.role
139
137
  try {
140
138
  const client = getClient()
141
- const res = await client.api.admin.users[':id'].$put({ param: { id }, json: body as Record<string, string | null | undefined> })
139
+ const res = await client.api.admin.users[':id'].$put({ param: { id }, json: body })
142
140
  const data = await res.json()
143
141
  return ok(data, ['User updated'])
144
142
  } catch (err) {
@@ -48,7 +48,10 @@ export function registerContentCommands(site: SiteInstance) {
48
48
  parameters: z.object({
49
49
  title: z.string().min(1).describe('Content title'),
50
50
  content: z.string().min(1).describe('Content body'),
51
- category: z.enum(['article', 'announcement', 'tutorial', 'news', 'policy']).default('article').describe('Category'),
51
+ category: z
52
+ .enum(['article', 'announcement', 'tutorial', 'news', 'policy'])
53
+ .default('article')
54
+ .describe('Category'),
52
55
  tags: z.string().optional().describe('Comma-separated tags'),
53
56
  }),
54
57
  handler: async (params: unknown) => {
@@ -57,7 +60,12 @@ export function registerContentCommands(site: SiteInstance) {
57
60
  const client = getClient()
58
61
  const tags = p.tags ? p.tags.split(',') : undefined
59
62
  const res = await client.api.contents.$post({
60
- json: { title: p.title, content: p.content, category: p.category as 'article' | 'announcement' | 'tutorial' | 'news' | 'policy', tags },
63
+ json: {
64
+ title: p.title,
65
+ content: p.content,
66
+ category: p.category as 'article' | 'announcement' | 'tutorial' | 'news' | 'policy',
67
+ tags,
68
+ },
61
69
  })
62
70
  const data = await res.json()
63
71
  return ok(data, ['Content created'])
@@ -50,7 +50,10 @@ export function registerDisputeCommands(site: SiteInstance) {
50
50
  'order-no': z.string().describe('Order number'),
51
51
  'customer-name': z.string().describe('Customer name'),
52
52
  'customer-email': z.string().email().describe('Customer email'),
53
- type: z.enum(['refund', 'product_quality', 'service_quality', 'delivery', 'other']).default('other').describe('Type'),
53
+ type: z
54
+ .enum(['refund', 'product_quality', 'service_quality', 'delivery', 'other'])
55
+ .default('other')
56
+ .describe('Type'),
54
57
  description: z.string().min(1).describe('Description'),
55
58
  amount: z.coerce.number().positive().describe('Amount'),
56
59
  }),
@@ -89,7 +92,10 @@ export function registerDisputeCommands(site: SiteInstance) {
89
92
  description: 'Update a dispute status',
90
93
  parameters: z.object({
91
94
  id: z.string().describe('Dispute ID'),
92
- status: z.enum(['pending', 'resolved', 'investigating', 'rejected']).optional().describe('New status'),
95
+ status: z
96
+ .enum(['pending', 'resolved', 'investigating', 'rejected'])
97
+ .optional()
98
+ .describe('New status'),
93
99
  }),
94
100
  handler: async (params: unknown) => {
95
101
  const p = params as { id: string; status?: string }
@@ -13,7 +13,12 @@ export function registerFileCommands(site: SiteInstance) {
13
13
  'expiry-seconds': z.coerce.number().default(3600).describe('URL expiry in seconds'),
14
14
  }),
15
15
  handler: async (params: unknown) => {
16
- const p = params as { namespace: string; filename: string; private: boolean; 'expiry-seconds': number }
16
+ const p = params as {
17
+ namespace: string
18
+ filename: string
19
+ private: boolean
20
+ 'expiry-seconds': number
21
+ }
17
22
  try {
18
23
  const client = getClient()
19
24
  const res = await client.api['generate-url'].$post({
@@ -14,6 +14,7 @@ import { registerOrderCommands } from './order'
14
14
  import { registerPermissionCommands } from './permission'
15
15
  import { registerTenantCommands } from './tenant'
16
16
  import { registerTicketCommands } from './ticket'
17
+ import { registerMerchantCommands } from './merchant'
17
18
 
18
19
  export function registerBuiltinCommands(app: Core) {
19
20
  const api = app.loader.getAPI()
@@ -38,6 +39,7 @@ export function registerBuiltinCommands(app: Core) {
38
39
  registerPermissionCommands(site)
39
40
  registerTenantCommands(site)
40
41
  registerTicketCommands(site)
42
+ registerMerchantCommands(site)
41
43
  }
42
44
 
43
45
  export {
@@ -56,4 +58,5 @@ export {
56
58
  registerPermissionCommands,
57
59
  registerTenantCommands,
58
60
  registerTicketCommands,
61
+ registerMerchantCommands,
59
62
  }
@@ -0,0 +1,123 @@
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
+ const createProductParams = z.object({
7
+ name: z.string().min(1).describe('Product name'),
8
+ description: z.string().max(1000).describe('Product description'),
9
+ price: z.coerce.number().positive().describe('Product price'),
10
+ stock: z.coerce.number().int().min(0).default(0).describe('Stock quantity'),
11
+ status: z
12
+ .enum(['active', 'inactive', 'out_of_stock'])
13
+ .default('active')
14
+ .describe('Product status'),
15
+ 'image-url': z.string().url().nullable().default(null).describe('Image URL'),
16
+ })
17
+
18
+ export function registerMerchantCommands(site: SiteInstance) {
19
+ site.command('login', {
20
+ description: 'Login as merchant',
21
+ parameters: z.object({
22
+ username: z.string().min(1).describe('Merchant username'),
23
+ password: z.string().min(6).describe('Merchant password'),
24
+ }),
25
+ handler: async (params: unknown) => {
26
+ const p = params as { username: string; password: string }
27
+ try {
28
+ const client = getClient()
29
+ const res = await client.api.merchant.login.$post({
30
+ json: { username: p.username, password: p.password },
31
+ })
32
+ const data = await res.json()
33
+ return ok(data)
34
+ } catch (err) {
35
+ return fail(err instanceof Error ? err.message : 'Failed to login')
36
+ }
37
+ },
38
+ })
39
+
40
+ site.command('me', {
41
+ description: 'Get current merchant profile',
42
+ parameters: z.object({}),
43
+ handler: async () => {
44
+ try {
45
+ const client = getClient()
46
+ const res = await client.api.merchant.me.$get()
47
+ const data = await res.json()
48
+ return ok(data)
49
+ } catch (err) {
50
+ return fail(err instanceof Error ? err.message : 'Failed to get merchant profile')
51
+ }
52
+ },
53
+ })
54
+
55
+ site.command('stats', {
56
+ description: 'Get merchant statistics',
57
+ parameters: z.object({}),
58
+ handler: async () => {
59
+ try {
60
+ const client = getClient()
61
+ const res = await client.api.merchant.stats.$get()
62
+ const data = await res.json()
63
+ return ok(data)
64
+ } catch (err) {
65
+ return fail(err instanceof Error ? err.message : 'Failed to get merchant stats')
66
+ }
67
+ },
68
+ })
69
+
70
+ site.command('list-products', {
71
+ description: 'List merchant products',
72
+ parameters: z.object({
73
+ page: z.coerce.number().int().positive().default(1).describe('Page number'),
74
+ 'page-size': z.coerce.number().int().positive().max(100).default(20).describe('Page size'),
75
+ status: z
76
+ .enum(['active', 'inactive', 'out_of_stock'])
77
+ .optional()
78
+ .describe('Filter by status'),
79
+ }),
80
+ handler: async (params: unknown) => {
81
+ const p = params as { page: number; 'page-size': number; status?: string }
82
+ try {
83
+ const client = getClient()
84
+ const query: Record<string, string> = {
85
+ page: String(p.page),
86
+ pageSize: String(p['page-size']),
87
+ }
88
+ if (p.status) query.status = p.status
89
+ const res = await client.api.merchant.products.$get({ query })
90
+ const data = await res.json()
91
+ return ok(data)
92
+ } catch (err) {
93
+ return fail(err instanceof Error ? err.message : 'Failed to list products')
94
+ }
95
+ },
96
+ })
97
+
98
+ site.command('create-product', {
99
+ description: 'Create a new product',
100
+ parameters: createProductParams,
101
+ handler: async params => {
102
+ // @ts-expect-error -- auto-command passes unknown, we parse from zod schema
103
+ const p: z.infer<typeof createProductParams> = params
104
+ try {
105
+ const client = getClient()
106
+ const res = await client.api.merchant.products.$post({
107
+ json: {
108
+ name: p.name,
109
+ description: p.description,
110
+ price: p.price,
111
+ stock: p.stock,
112
+ status: p.status,
113
+ imageUrl: p['image-url'],
114
+ },
115
+ })
116
+ const data = await res.json()
117
+ return ok(data, ['Product created'])
118
+ } catch (err) {
119
+ return fail(err instanceof Error ? err.message : 'Failed to create product')
120
+ }
121
+ },
122
+ })
123
+ }
@@ -13,7 +13,12 @@ export function registerOrderCommands(site: SiteInstance) {
13
13
  'customer-name': z.string().optional().describe('Filter by customer name'),
14
14
  }),
15
15
  handler: async (params: unknown) => {
16
- const p = params as { limit: number; offset: number; status?: string; 'customer-name'?: string }
16
+ const p = params as {
17
+ limit: number
18
+ offset: number
19
+ status?: string
20
+ 'customer-name'?: string
21
+ }
17
22
  try {
18
23
  const client = getClient()
19
24
  const query: Record<string, string> = {
@@ -58,7 +63,12 @@ export function registerOrderCommands(site: SiteInstance) {
58
63
  amount: z.coerce.number().positive().describe('Order amount'),
59
64
  }),
60
65
  handler: async (params: unknown) => {
61
- const p = params as { 'customer-name': string; 'customer-email': string; 'product-name': string; amount: number }
66
+ const p = params as {
67
+ 'customer-name': string
68
+ 'customer-email': string
69
+ 'product-name': string
70
+ amount: number
71
+ }
62
72
  try {
63
73
  const client = getClient()
64
74
  const res = await client.api.orders.$post({
@@ -81,7 +91,10 @@ export function registerOrderCommands(site: SiteInstance) {
81
91
  description: 'Update order status',
82
92
  parameters: z.object({
83
93
  id: z.string().describe('Order ID'),
84
- status: z.enum(['pending', 'processing', 'completed', 'cancelled', 'disputed']).optional().describe('New status'),
94
+ status: z
95
+ .enum(['pending', 'processing', 'completed', 'cancelled', 'disputed'])
96
+ .optional()
97
+ .describe('New status'),
85
98
  }),
86
99
  handler: async (params: unknown) => {
87
100
  const p = params as { id: string; status?: string }
@@ -152,7 +152,13 @@ export function registerPermissionCommands(site: SiteInstance) {
152
152
  'resource-type': z.string().optional().describe('Filter by resource type'),
153
153
  }),
154
154
  handler: async (params: unknown) => {
155
- const p = params as { limit: number; offset: number; 'user-id'?: string; action?: string; 'resource-type'?: string }
155
+ const p = params as {
156
+ limit: number
157
+ offset: number
158
+ 'user-id'?: string
159
+ action?: string
160
+ 'resource-type'?: string
161
+ }
156
162
  try {
157
163
  const client = getClient()
158
164
  const query: Record<string, string> = {
@@ -50,7 +50,10 @@ export function registerTicketCommands(site: SiteInstance) {
50
50
  'customer-email': z.string().email().describe('Customer email'),
51
51
  subject: z.string().min(1).describe('Ticket subject'),
52
52
  description: z.string().min(1).describe('Ticket description'),
53
- category: z.enum(['technical', 'billing', 'feature_request', 'bug_report', 'general']).default('general').describe('Category'),
53
+ category: z
54
+ .enum(['technical', 'billing', 'feature_request', 'bug_report', 'general'])
55
+ .default('general')
56
+ .describe('Category'),
54
57
  priority: z.enum(['low', 'medium', 'high', 'urgent']).default('medium').describe('Priority'),
55
58
  }),
56
59
  handler: async (params: unknown) => {
@@ -70,7 +73,12 @@ export function registerTicketCommands(site: SiteInstance) {
70
73
  customerEmail: p['customer-email'],
71
74
  subject: p.subject,
72
75
  description: p.description,
73
- category: p.category as 'technical' | 'billing' | 'feature_request' | 'bug_report' | 'general',
76
+ category: p.category as
77
+ | 'technical'
78
+ | 'billing'
79
+ | 'feature_request'
80
+ | 'bug_report'
81
+ | 'general',
74
82
  priority: p.priority as 'low' | 'medium' | 'high' | 'urgent',
75
83
  },
76
84
  })
@@ -86,7 +94,10 @@ export function registerTicketCommands(site: SiteInstance) {
86
94
  description: 'Update ticket status',
87
95
  parameters: z.object({
88
96
  id: z.string().describe('Ticket ID'),
89
- status: z.enum(['open', 'closed', 'in_progress', 'waiting_customer', 'resolved']).optional().describe('New status'),
97
+ status: z
98
+ .enum(['open', 'closed', 'in_progress', 'waiting_customer', 'resolved'])
99
+ .optional()
100
+ .describe('New status'),
90
101
  'assigned-to': z.string().optional().describe('Assign to user'),
91
102
  }),
92
103
  handler: async (params: unknown) => {
@@ -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
  }
@@ -85,6 +85,11 @@ export default defineConfig({
85
85
  }),
86
86
  ],
87
87
  },
88
+ onwarn(warning, defaultHandler) {
89
+ // Suppress antd "use client" directive warnings (React Server Components marker)
90
+ if (warning.code === 'MODULE_LEVEL_DIRECTIVE') return
91
+ defaultHandler(warning)
92
+ },
88
93
  },
89
94
  },
90
95
  resolve: {