@contractspec/example.saas-boilerplate 3.7.6 → 3.7.7

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 (114) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/AGENTS.md +50 -27
  3. package/README.md +64 -144
  4. package/dist/billing/billing.event.js +1 -1
  5. package/dist/billing/index.d.ts +6 -6
  6. package/dist/billing/index.js +1 -1
  7. package/dist/browser/billing/billing.event.js +1 -1
  8. package/dist/browser/billing/index.js +1 -1
  9. package/dist/browser/index.js +931 -932
  10. package/dist/browser/project/index.js +209 -209
  11. package/dist/browser/project/project.event.js +1 -1
  12. package/dist/browser/ui/SaasDashboard.js +45 -45
  13. package/dist/browser/ui/SaasProjectList.js +7 -7
  14. package/dist/browser/ui/SaasSettingsPanel.js +12 -12
  15. package/dist/browser/ui/hooks/index.js +2 -2
  16. package/dist/browser/ui/hooks/useProjectList.js +1 -1
  17. package/dist/browser/ui/hooks/useProjectMutations.js +1 -1
  18. package/dist/browser/ui/index.js +483 -484
  19. package/dist/browser/ui/modals/CreateProjectModal.js +10 -10
  20. package/dist/browser/ui/modals/ProjectActionsModal.js +13 -13
  21. package/dist/browser/ui/modals/index.js +23 -23
  22. package/dist/browser/ui/renderers/index.js +112 -112
  23. package/dist/browser/ui/renderers/project-list.renderer.js +7 -7
  24. package/dist/handlers/index.d.ts +2 -2
  25. package/dist/index.d.ts +4 -4
  26. package/dist/index.js +931 -932
  27. package/dist/node/billing/billing.event.js +1 -1
  28. package/dist/node/billing/index.js +1 -1
  29. package/dist/node/index.js +931 -932
  30. package/dist/node/project/index.js +209 -209
  31. package/dist/node/project/project.event.js +1 -1
  32. package/dist/node/ui/SaasDashboard.js +45 -45
  33. package/dist/node/ui/SaasProjectList.js +7 -7
  34. package/dist/node/ui/SaasSettingsPanel.js +12 -12
  35. package/dist/node/ui/hooks/index.js +2 -2
  36. package/dist/node/ui/hooks/useProjectList.js +1 -1
  37. package/dist/node/ui/hooks/useProjectMutations.js +1 -1
  38. package/dist/node/ui/index.js +483 -484
  39. package/dist/node/ui/modals/CreateProjectModal.js +10 -10
  40. package/dist/node/ui/modals/ProjectActionsModal.js +13 -13
  41. package/dist/node/ui/modals/index.js +23 -23
  42. package/dist/node/ui/renderers/index.js +112 -112
  43. package/dist/node/ui/renderers/project-list.renderer.js +7 -7
  44. package/dist/presentations/index.d.ts +1 -1
  45. package/dist/project/index.d.ts +7 -7
  46. package/dist/project/index.js +209 -209
  47. package/dist/project/project.event.js +1 -1
  48. package/dist/settings/index.d.ts +1 -1
  49. package/dist/ui/SaasDashboard.js +45 -45
  50. package/dist/ui/SaasProjectList.js +7 -7
  51. package/dist/ui/SaasSettingsPanel.js +12 -12
  52. package/dist/ui/hooks/index.d.ts +2 -2
  53. package/dist/ui/hooks/index.js +2 -2
  54. package/dist/ui/hooks/useProjectList.d.ts +5 -0
  55. package/dist/ui/hooks/useProjectList.js +1 -1
  56. package/dist/ui/hooks/useProjectMutations.d.ts +8 -0
  57. package/dist/ui/hooks/useProjectMutations.js +1 -1
  58. package/dist/ui/index.d.ts +4 -4
  59. package/dist/ui/index.js +483 -484
  60. package/dist/ui/modals/CreateProjectModal.js +10 -10
  61. package/dist/ui/modals/ProjectActionsModal.js +13 -13
  62. package/dist/ui/modals/index.js +23 -23
  63. package/dist/ui/renderers/index.d.ts +1 -1
  64. package/dist/ui/renderers/index.js +112 -112
  65. package/dist/ui/renderers/project-list.renderer.d.ts +1 -1
  66. package/dist/ui/renderers/project-list.renderer.js +7 -7
  67. package/package.json +10 -10
  68. package/src/billing/billing.entity.ts +132 -132
  69. package/src/billing/billing.enum.ts +9 -9
  70. package/src/billing/billing.event.ts +71 -71
  71. package/src/billing/billing.handler.ts +87 -87
  72. package/src/billing/billing.operations.ts +158 -158
  73. package/src/billing/billing.presentation.ts +45 -45
  74. package/src/billing/billing.schema.ts +76 -76
  75. package/src/billing/index.ts +43 -48
  76. package/src/dashboard/dashboard.presentation.ts +45 -45
  77. package/src/dashboard/index.ts +2 -2
  78. package/src/docs/saas-boilerplate.docblock.ts +43 -43
  79. package/src/example.ts +32 -32
  80. package/src/handlers/index.ts +9 -9
  81. package/src/handlers/saas.handlers.ts +250 -249
  82. package/src/index.ts +40 -41
  83. package/src/presentations/index.ts +18 -20
  84. package/src/project/index.ts +45 -50
  85. package/src/project/project.entity.ts +68 -68
  86. package/src/project/project.enum.ts +8 -8
  87. package/src/project/project.event.ts +79 -79
  88. package/src/project/project.handler.ts +103 -103
  89. package/src/project/project.operations.ts +236 -236
  90. package/src/project/project.presentation.ts +46 -46
  91. package/src/project/project.schema.ts +90 -90
  92. package/src/saas-boilerplate.feature.ts +100 -100
  93. package/src/seeders/index.ts +20 -20
  94. package/src/settings/index.ts +2 -3
  95. package/src/settings/settings.entity.ts +65 -65
  96. package/src/settings/settings.enum.ts +4 -4
  97. package/src/shared/mock-data.ts +92 -92
  98. package/src/shared/overlay-types.ts +23 -23
  99. package/src/tests/operations.test-spec.ts +96 -96
  100. package/src/ui/SaasDashboard.tsx +270 -270
  101. package/src/ui/SaasProjectList.tsx +90 -90
  102. package/src/ui/SaasSettingsPanel.tsx +84 -84
  103. package/src/ui/hooks/index.ts +3 -3
  104. package/src/ui/hooks/useProjectList.ts +69 -68
  105. package/src/ui/hooks/useProjectMutations.ts +144 -143
  106. package/src/ui/index.ts +8 -12
  107. package/src/ui/modals/CreateProjectModal.tsx +154 -154
  108. package/src/ui/modals/ProjectActionsModal.tsx +321 -321
  109. package/src/ui/overlays/demo-overlays.ts +49 -49
  110. package/src/ui/renderers/index.ts +5 -4
  111. package/src/ui/renderers/project-list.markdown.ts +204 -204
  112. package/src/ui/renderers/project-list.renderer.tsx +14 -13
  113. package/tsconfig.json +7 -8
  114. package/tsdown.config.js +7 -3
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { Button, Input } from '@contractspec/lib.design-system';
3
4
  /**
4
5
  * ProjectActionsModal - Actions for a specific project
5
6
  *
@@ -7,340 +8,339 @@
7
8
  * via useProjectMutations hook.
8
9
  */
9
10
  import { useEffect, useState } from 'react';
10
- import { Button, Input } from '@contractspec/lib.design-system';
11
11
 
12
12
  // Local type definitions for modal props
13
13
  export interface Project {
14
- id: string;
15
- name: string;
16
- description?: string;
17
- status: 'DRAFT' | 'ACTIVE' | 'ARCHIVED';
18
- tier: 'FREE' | 'PRO' | 'ENTERPRISE';
14
+ id: string;
15
+ name: string;
16
+ description?: string;
17
+ status: 'DRAFT' | 'ACTIVE' | 'ARCHIVED';
18
+ tier: 'FREE' | 'PRO' | 'ENTERPRISE';
19
19
  }
20
20
 
21
21
  export interface UpdateProjectInput {
22
- id: string;
23
- name?: string;
24
- description?: string;
22
+ id: string;
23
+ name?: string;
24
+ description?: string;
25
25
  }
26
26
 
27
27
  type ActionMode = 'menu' | 'edit' | 'archive' | 'delete';
28
28
 
29
29
  interface ProjectActionsModalProps {
30
- isOpen: boolean;
31
- project: Project | null;
32
- onClose: () => void;
33
- onUpdate: (input: UpdateProjectInput) => Promise<void>;
34
- onArchive: (projectId: string) => Promise<void>;
35
- onActivate: (projectId: string) => Promise<void>;
36
- onDelete: (projectId: string) => Promise<void>;
37
- isLoading?: boolean;
30
+ isOpen: boolean;
31
+ project: Project | null;
32
+ onClose: () => void;
33
+ onUpdate: (input: UpdateProjectInput) => Promise<void>;
34
+ onArchive: (projectId: string) => Promise<void>;
35
+ onActivate: (projectId: string) => Promise<void>;
36
+ onDelete: (projectId: string) => Promise<void>;
37
+ isLoading?: boolean;
38
38
  }
39
39
 
40
40
  export function ProjectActionsModal({
41
- isOpen,
42
- project,
43
- onClose,
44
- onUpdate,
45
- onArchive,
46
- onActivate,
47
- onDelete,
48
- isLoading = false,
41
+ isOpen,
42
+ project,
43
+ onClose,
44
+ onUpdate,
45
+ onArchive,
46
+ onActivate,
47
+ onDelete,
48
+ isLoading = false,
49
49
  }: ProjectActionsModalProps) {
50
- const [mode, setMode] = useState<ActionMode>('menu');
51
- const [name, setName] = useState('');
52
- const [description, setDescription] = useState('');
53
- const [error, setError] = useState<string | null>(null);
54
-
55
- const resetForm = () => {
56
- setMode('menu');
57
- setError(null);
58
- if (project) {
59
- setName(project.name);
60
- setDescription(project.description ?? '');
61
- }
62
- };
63
-
64
- const handleClose = () => {
65
- resetForm();
66
- onClose();
67
- };
68
-
69
- // Initialize form when project changes
70
- useEffect(() => {
71
- if (project) {
72
- setName(project.name);
73
- setDescription(project.description ?? '');
74
- }
75
- }, [project]);
76
-
77
- const handleEdit = async () => {
78
- if (!project) return;
79
- setError(null);
80
-
81
- if (!name.trim()) {
82
- setError('Project name is required');
83
- return;
84
- }
85
-
86
- try {
87
- await onUpdate({
88
- id: project.id,
89
- name: name.trim(),
90
- description: description.trim() || undefined,
91
- });
92
- handleClose();
93
- } catch (err) {
94
- setError(err instanceof Error ? err.message : 'Failed to update project');
95
- }
96
- };
97
-
98
- const handleArchive = async () => {
99
- if (!project) return;
100
- setError(null);
101
-
102
- try {
103
- await onArchive(project.id);
104
- handleClose();
105
- } catch (err) {
106
- setError(
107
- err instanceof Error ? err.message : 'Failed to archive project'
108
- );
109
- }
110
- };
111
-
112
- const handleActivate = async () => {
113
- if (!project) return;
114
- setError(null);
115
-
116
- try {
117
- await onActivate(project.id);
118
- handleClose();
119
- } catch (err) {
120
- setError(
121
- err instanceof Error ? err.message : 'Failed to activate project'
122
- );
123
- }
124
- };
125
-
126
- const handleDelete = async () => {
127
- if (!project) return;
128
- setError(null);
129
-
130
- try {
131
- await onDelete(project.id);
132
- handleClose();
133
- } catch (err) {
134
- setError(err instanceof Error ? err.message : 'Failed to delete project');
135
- }
136
- };
137
-
138
- if (!isOpen || !project) return null;
139
-
140
- return (
141
- <div className="fixed inset-0 z-50 flex items-center justify-center">
142
- {/* Backdrop */}
143
- <div
144
- className="bg-background/80 absolute inset-0 backdrop-blur-sm"
145
- onClick={handleClose}
146
- role="button"
147
- tabIndex={0}
148
- onKeyDown={(e) => {
149
- if (e.key === 'Enter' || e.key === ' ') handleClose();
150
- }}
151
- aria-label="Close modal"
152
- />
153
-
154
- {/* Modal */}
155
- <div className="bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl">
156
- {/* Project Header */}
157
- <div className="border-border mb-4 border-b pb-4">
158
- <h2 className="text-xl font-semibold">{project.name}</h2>
159
- <p className="text-muted-foreground text-sm">
160
- {project.tier} · {project.status}
161
- </p>
162
- </div>
163
-
164
- {/* Main Menu */}
165
- {mode === 'menu' && (
166
- <div className="space-y-3">
167
- <Button
168
- className="w-full justify-start"
169
- variant="ghost"
170
- onPress={() => setMode('edit')}
171
- >
172
- <span className="mr-2">✏️</span> Edit Project
173
- </Button>
174
-
175
- {project.status === 'ACTIVE' || project.status === 'DRAFT' ? (
176
- <Button
177
- className="w-full justify-start"
178
- variant="ghost"
179
- onPress={() => setMode('archive')}
180
- >
181
- <span className="mr-2">📦</span> Archive Project
182
- </Button>
183
- ) : project.status === 'ARCHIVED' ? (
184
- <Button
185
- className="w-full justify-start"
186
- variant="ghost"
187
- onPress={handleActivate}
188
- disabled={isLoading}
189
- >
190
- <span className="mr-2">🔄</span> Restore Project
191
- </Button>
192
- ) : null}
193
-
194
- <Button
195
- className="w-full justify-start text-red-500 hover:text-red-600"
196
- variant="ghost"
197
- onPress={() => setMode('delete')}
198
- >
199
- <span className="mr-2">🗑️</span> Delete Project
200
- </Button>
201
-
202
- <div className="border-border border-t pt-3">
203
- <Button
204
- className="w-full"
205
- variant="outline"
206
- onPress={handleClose}
207
- >
208
- Close
209
- </Button>
210
- </div>
211
- </div>
212
- )}
213
-
214
- {/* Edit Form */}
215
- {mode === 'edit' && (
216
- <div className="space-y-4">
217
- <div>
218
- <label
219
- htmlFor="edit-name"
220
- className="text-muted-foreground mb-1 block text-sm font-medium"
221
- >
222
- Project Name *
223
- </label>
224
- <Input
225
- id="edit-name"
226
- value={name}
227
- onChange={(e) => setName(e.target.value)}
228
- disabled={isLoading}
229
- />
230
- </div>
231
-
232
- <div>
233
- <label
234
- htmlFor="edit-description"
235
- className="text-muted-foreground mb-1 block text-sm font-medium"
236
- >
237
- Description
238
- </label>
239
- <textarea
240
- id="edit-description"
241
- value={description}
242
- onChange={(e) => setDescription(e.target.value)}
243
- rows={3}
244
- disabled={isLoading}
245
- className="border-input bg-background focus:ring-ring w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50"
246
- />
247
- </div>
248
-
249
- {error && (
250
- <div className="bg-destructive/10 text-destructive rounded-md p-3 text-sm">
251
- {error}
252
- </div>
253
- )}
254
-
255
- <div className="flex justify-end gap-3 pt-2">
256
- <Button
257
- variant="ghost"
258
- onPress={() => setMode('menu')}
259
- disabled={isLoading}
260
- >
261
- Back
262
- </Button>
263
- <Button onPress={handleEdit} disabled={isLoading}>
264
- {isLoading ? 'Saving...' : 'Save Changes'}
265
- </Button>
266
- </div>
267
- </div>
268
- )}
269
-
270
- {/* Archive Confirmation */}
271
- {mode === 'archive' && (
272
- <div className="space-y-4">
273
- <p className="text-muted-foreground">
274
- Are you sure you want to archive{' '}
275
- <span className="text-foreground font-medium">
276
- {project.name}
277
- </span>
278
- ?
279
- </p>
280
- <p className="text-muted-foreground text-sm">
281
- Archived projects can be restored later.
282
- </p>
283
-
284
- {error && (
285
- <div className="bg-destructive/10 text-destructive rounded-md p-3 text-sm">
286
- {error}
287
- </div>
288
- )}
289
-
290
- <div className="flex justify-end gap-3 pt-2">
291
- <Button
292
- variant="ghost"
293
- onPress={() => setMode('menu')}
294
- disabled={isLoading}
295
- >
296
- Cancel
297
- </Button>
298
- <Button onPress={handleArchive} disabled={isLoading}>
299
- {isLoading ? 'Archiving...' : '📦 Archive'}
300
- </Button>
301
- </div>
302
- </div>
303
- )}
304
-
305
- {/* Delete Confirmation */}
306
- {mode === 'delete' && (
307
- <div className="space-y-4">
308
- <p className="text-muted-foreground">
309
- Are you sure you want to delete{' '}
310
- <span className="text-foreground font-medium">
311
- {project.name}
312
- </span>
313
- ?
314
- </p>
315
- <p className="text-destructive text-sm">
316
- This action cannot be undone.
317
- </p>
318
-
319
- {error && (
320
- <div className="bg-destructive/10 text-destructive rounded-md p-3 text-sm">
321
- {error}
322
- </div>
323
- )}
324
-
325
- <div className="flex justify-end gap-3 pt-2">
326
- <Button
327
- variant="ghost"
328
- onPress={() => setMode('menu')}
329
- disabled={isLoading}
330
- >
331
- Cancel
332
- </Button>
333
- <Button
334
- variant="destructive"
335
- onPress={handleDelete}
336
- disabled={isLoading}
337
- >
338
- {isLoading ? 'Deleting...' : '🗑️ Delete'}
339
- </Button>
340
- </div>
341
- </div>
342
- )}
343
- </div>
344
- </div>
345
- );
50
+ const [mode, setMode] = useState<ActionMode>('menu');
51
+ const [name, setName] = useState('');
52
+ const [description, setDescription] = useState('');
53
+ const [error, setError] = useState<string | null>(null);
54
+
55
+ const resetForm = () => {
56
+ setMode('menu');
57
+ setError(null);
58
+ if (project) {
59
+ setName(project.name);
60
+ setDescription(project.description ?? '');
61
+ }
62
+ };
63
+
64
+ const handleClose = () => {
65
+ resetForm();
66
+ onClose();
67
+ };
68
+
69
+ // Initialize form when project changes
70
+ useEffect(() => {
71
+ if (project) {
72
+ setName(project.name);
73
+ setDescription(project.description ?? '');
74
+ }
75
+ }, [project]);
76
+
77
+ const handleEdit = async () => {
78
+ if (!project) return;
79
+ setError(null);
80
+
81
+ if (!name.trim()) {
82
+ setError('Project name is required');
83
+ return;
84
+ }
85
+
86
+ try {
87
+ await onUpdate({
88
+ id: project.id,
89
+ name: name.trim(),
90
+ description: description.trim() || undefined,
91
+ });
92
+ handleClose();
93
+ } catch (err) {
94
+ setError(err instanceof Error ? err.message : 'Failed to update project');
95
+ }
96
+ };
97
+
98
+ const handleArchive = async () => {
99
+ if (!project) return;
100
+ setError(null);
101
+
102
+ try {
103
+ await onArchive(project.id);
104
+ handleClose();
105
+ } catch (err) {
106
+ setError(
107
+ err instanceof Error ? err.message : 'Failed to archive project'
108
+ );
109
+ }
110
+ };
111
+
112
+ const handleActivate = async () => {
113
+ if (!project) return;
114
+ setError(null);
115
+
116
+ try {
117
+ await onActivate(project.id);
118
+ handleClose();
119
+ } catch (err) {
120
+ setError(
121
+ err instanceof Error ? err.message : 'Failed to activate project'
122
+ );
123
+ }
124
+ };
125
+
126
+ const handleDelete = async () => {
127
+ if (!project) return;
128
+ setError(null);
129
+
130
+ try {
131
+ await onDelete(project.id);
132
+ handleClose();
133
+ } catch (err) {
134
+ setError(err instanceof Error ? err.message : 'Failed to delete project');
135
+ }
136
+ };
137
+
138
+ if (!isOpen || !project) return null;
139
+
140
+ return (
141
+ <div className="fixed inset-0 z-50 flex items-center justify-center">
142
+ {/* Backdrop */}
143
+ <div
144
+ className="absolute inset-0 bg-background/80 backdrop-blur-sm"
145
+ onClick={handleClose}
146
+ role="button"
147
+ tabIndex={0}
148
+ onKeyDown={(e) => {
149
+ if (e.key === 'Enter' || e.key === ' ') handleClose();
150
+ }}
151
+ aria-label="Close modal"
152
+ />
153
+
154
+ {/* Modal */}
155
+ <div className="relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl">
156
+ {/* Project Header */}
157
+ <div className="mb-4 border-border border-b pb-4">
158
+ <h2 className="font-semibold text-xl">{project.name}</h2>
159
+ <p className="text-muted-foreground text-sm">
160
+ {project.tier} · {project.status}
161
+ </p>
162
+ </div>
163
+
164
+ {/* Main Menu */}
165
+ {mode === 'menu' && (
166
+ <div className="space-y-3">
167
+ <Button
168
+ className="w-full justify-start"
169
+ variant="ghost"
170
+ onPress={() => setMode('edit')}
171
+ >
172
+ <span className="mr-2">✏️</span> Edit Project
173
+ </Button>
174
+
175
+ {project.status === 'ACTIVE' || project.status === 'DRAFT' ? (
176
+ <Button
177
+ className="w-full justify-start"
178
+ variant="ghost"
179
+ onPress={() => setMode('archive')}
180
+ >
181
+ <span className="mr-2">📦</span> Archive Project
182
+ </Button>
183
+ ) : project.status === 'ARCHIVED' ? (
184
+ <Button
185
+ className="w-full justify-start"
186
+ variant="ghost"
187
+ onPress={handleActivate}
188
+ disabled={isLoading}
189
+ >
190
+ <span className="mr-2">🔄</span> Restore Project
191
+ </Button>
192
+ ) : null}
193
+
194
+ <Button
195
+ className="w-full justify-start text-red-500 hover:text-red-600"
196
+ variant="ghost"
197
+ onPress={() => setMode('delete')}
198
+ >
199
+ <span className="mr-2">🗑️</span> Delete Project
200
+ </Button>
201
+
202
+ <div className="border-border border-t pt-3">
203
+ <Button
204
+ className="w-full"
205
+ variant="outline"
206
+ onPress={handleClose}
207
+ >
208
+ Close
209
+ </Button>
210
+ </div>
211
+ </div>
212
+ )}
213
+
214
+ {/* Edit Form */}
215
+ {mode === 'edit' && (
216
+ <div className="space-y-4">
217
+ <div>
218
+ <label
219
+ htmlFor="edit-name"
220
+ className="mb-1 block font-medium text-muted-foreground text-sm"
221
+ >
222
+ Project Name *
223
+ </label>
224
+ <Input
225
+ id="edit-name"
226
+ value={name}
227
+ onChange={(e) => setName(e.target.value)}
228
+ disabled={isLoading}
229
+ />
230
+ </div>
231
+
232
+ <div>
233
+ <label
234
+ htmlFor="edit-description"
235
+ className="mb-1 block font-medium text-muted-foreground text-sm"
236
+ >
237
+ Description
238
+ </label>
239
+ <textarea
240
+ id="edit-description"
241
+ value={description}
242
+ onChange={(e) => setDescription(e.target.value)}
243
+ rows={3}
244
+ disabled={isLoading}
245
+ className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50"
246
+ />
247
+ </div>
248
+
249
+ {error && (
250
+ <div className="rounded-md bg-destructive/10 p-3 text-destructive text-sm">
251
+ {error}
252
+ </div>
253
+ )}
254
+
255
+ <div className="flex justify-end gap-3 pt-2">
256
+ <Button
257
+ variant="ghost"
258
+ onPress={() => setMode('menu')}
259
+ disabled={isLoading}
260
+ >
261
+ Back
262
+ </Button>
263
+ <Button onPress={handleEdit} disabled={isLoading}>
264
+ {isLoading ? 'Saving...' : 'Save Changes'}
265
+ </Button>
266
+ </div>
267
+ </div>
268
+ )}
269
+
270
+ {/* Archive Confirmation */}
271
+ {mode === 'archive' && (
272
+ <div className="space-y-4">
273
+ <p className="text-muted-foreground">
274
+ Are you sure you want to archive{' '}
275
+ <span className="font-medium text-foreground">
276
+ {project.name}
277
+ </span>
278
+ ?
279
+ </p>
280
+ <p className="text-muted-foreground text-sm">
281
+ Archived projects can be restored later.
282
+ </p>
283
+
284
+ {error && (
285
+ <div className="rounded-md bg-destructive/10 p-3 text-destructive text-sm">
286
+ {error}
287
+ </div>
288
+ )}
289
+
290
+ <div className="flex justify-end gap-3 pt-2">
291
+ <Button
292
+ variant="ghost"
293
+ onPress={() => setMode('menu')}
294
+ disabled={isLoading}
295
+ >
296
+ Cancel
297
+ </Button>
298
+ <Button onPress={handleArchive} disabled={isLoading}>
299
+ {isLoading ? 'Archiving...' : '📦 Archive'}
300
+ </Button>
301
+ </div>
302
+ </div>
303
+ )}
304
+
305
+ {/* Delete Confirmation */}
306
+ {mode === 'delete' && (
307
+ <div className="space-y-4">
308
+ <p className="text-muted-foreground">
309
+ Are you sure you want to delete{' '}
310
+ <span className="font-medium text-foreground">
311
+ {project.name}
312
+ </span>
313
+ ?
314
+ </p>
315
+ <p className="text-destructive text-sm">
316
+ This action cannot be undone.
317
+ </p>
318
+
319
+ {error && (
320
+ <div className="rounded-md bg-destructive/10 p-3 text-destructive text-sm">
321
+ {error}
322
+ </div>
323
+ )}
324
+
325
+ <div className="flex justify-end gap-3 pt-2">
326
+ <Button
327
+ variant="ghost"
328
+ onPress={() => setMode('menu')}
329
+ disabled={isLoading}
330
+ >
331
+ Cancel
332
+ </Button>
333
+ <Button
334
+ variant="destructive"
335
+ onPress={handleDelete}
336
+ disabled={isLoading}
337
+ >
338
+ {isLoading ? 'Deleting...' : '🗑️ Delete'}
339
+ </Button>
340
+ </div>
341
+ </div>
342
+ )}
343
+ </div>
344
+ </div>
345
+ );
346
346
  }