@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.
- package/.turbo/turbo-build.log +8 -8
- package/AGENTS.md +50 -27
- package/README.md +64 -144
- package/dist/billing/billing.event.js +1 -1
- package/dist/billing/index.d.ts +6 -6
- package/dist/billing/index.js +1 -1
- package/dist/browser/billing/billing.event.js +1 -1
- package/dist/browser/billing/index.js +1 -1
- package/dist/browser/index.js +931 -932
- package/dist/browser/project/index.js +209 -209
- package/dist/browser/project/project.event.js +1 -1
- package/dist/browser/ui/SaasDashboard.js +45 -45
- package/dist/browser/ui/SaasProjectList.js +7 -7
- package/dist/browser/ui/SaasSettingsPanel.js +12 -12
- package/dist/browser/ui/hooks/index.js +2 -2
- package/dist/browser/ui/hooks/useProjectList.js +1 -1
- package/dist/browser/ui/hooks/useProjectMutations.js +1 -1
- package/dist/browser/ui/index.js +483 -484
- package/dist/browser/ui/modals/CreateProjectModal.js +10 -10
- package/dist/browser/ui/modals/ProjectActionsModal.js +13 -13
- package/dist/browser/ui/modals/index.js +23 -23
- package/dist/browser/ui/renderers/index.js +112 -112
- package/dist/browser/ui/renderers/project-list.renderer.js +7 -7
- package/dist/handlers/index.d.ts +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +931 -932
- package/dist/node/billing/billing.event.js +1 -1
- package/dist/node/billing/index.js +1 -1
- package/dist/node/index.js +931 -932
- package/dist/node/project/index.js +209 -209
- package/dist/node/project/project.event.js +1 -1
- package/dist/node/ui/SaasDashboard.js +45 -45
- package/dist/node/ui/SaasProjectList.js +7 -7
- package/dist/node/ui/SaasSettingsPanel.js +12 -12
- package/dist/node/ui/hooks/index.js +2 -2
- package/dist/node/ui/hooks/useProjectList.js +1 -1
- package/dist/node/ui/hooks/useProjectMutations.js +1 -1
- package/dist/node/ui/index.js +483 -484
- package/dist/node/ui/modals/CreateProjectModal.js +10 -10
- package/dist/node/ui/modals/ProjectActionsModal.js +13 -13
- package/dist/node/ui/modals/index.js +23 -23
- package/dist/node/ui/renderers/index.js +112 -112
- package/dist/node/ui/renderers/project-list.renderer.js +7 -7
- package/dist/presentations/index.d.ts +1 -1
- package/dist/project/index.d.ts +7 -7
- package/dist/project/index.js +209 -209
- package/dist/project/project.event.js +1 -1
- package/dist/settings/index.d.ts +1 -1
- package/dist/ui/SaasDashboard.js +45 -45
- package/dist/ui/SaasProjectList.js +7 -7
- package/dist/ui/SaasSettingsPanel.js +12 -12
- package/dist/ui/hooks/index.d.ts +2 -2
- package/dist/ui/hooks/index.js +2 -2
- package/dist/ui/hooks/useProjectList.d.ts +5 -0
- package/dist/ui/hooks/useProjectList.js +1 -1
- package/dist/ui/hooks/useProjectMutations.d.ts +8 -0
- package/dist/ui/hooks/useProjectMutations.js +1 -1
- package/dist/ui/index.d.ts +4 -4
- package/dist/ui/index.js +483 -484
- package/dist/ui/modals/CreateProjectModal.js +10 -10
- package/dist/ui/modals/ProjectActionsModal.js +13 -13
- package/dist/ui/modals/index.js +23 -23
- package/dist/ui/renderers/index.d.ts +1 -1
- package/dist/ui/renderers/index.js +112 -112
- package/dist/ui/renderers/project-list.renderer.d.ts +1 -1
- package/dist/ui/renderers/project-list.renderer.js +7 -7
- package/package.json +10 -10
- package/src/billing/billing.entity.ts +132 -132
- package/src/billing/billing.enum.ts +9 -9
- package/src/billing/billing.event.ts +71 -71
- package/src/billing/billing.handler.ts +87 -87
- package/src/billing/billing.operations.ts +158 -158
- package/src/billing/billing.presentation.ts +45 -45
- package/src/billing/billing.schema.ts +76 -76
- package/src/billing/index.ts +43 -48
- package/src/dashboard/dashboard.presentation.ts +45 -45
- package/src/dashboard/index.ts +2 -2
- package/src/docs/saas-boilerplate.docblock.ts +43 -43
- package/src/example.ts +32 -32
- package/src/handlers/index.ts +9 -9
- package/src/handlers/saas.handlers.ts +250 -249
- package/src/index.ts +40 -41
- package/src/presentations/index.ts +18 -20
- package/src/project/index.ts +45 -50
- package/src/project/project.entity.ts +68 -68
- package/src/project/project.enum.ts +8 -8
- package/src/project/project.event.ts +79 -79
- package/src/project/project.handler.ts +103 -103
- package/src/project/project.operations.ts +236 -236
- package/src/project/project.presentation.ts +46 -46
- package/src/project/project.schema.ts +90 -90
- package/src/saas-boilerplate.feature.ts +100 -100
- package/src/seeders/index.ts +20 -20
- package/src/settings/index.ts +2 -3
- package/src/settings/settings.entity.ts +65 -65
- package/src/settings/settings.enum.ts +4 -4
- package/src/shared/mock-data.ts +92 -92
- package/src/shared/overlay-types.ts +23 -23
- package/src/tests/operations.test-spec.ts +96 -96
- package/src/ui/SaasDashboard.tsx +270 -270
- package/src/ui/SaasProjectList.tsx +90 -90
- package/src/ui/SaasSettingsPanel.tsx +84 -84
- package/src/ui/hooks/index.ts +3 -3
- package/src/ui/hooks/useProjectList.ts +69 -68
- package/src/ui/hooks/useProjectMutations.ts +144 -143
- package/src/ui/index.ts +8 -12
- package/src/ui/modals/CreateProjectModal.tsx +154 -154
- package/src/ui/modals/ProjectActionsModal.tsx +321 -321
- package/src/ui/overlays/demo-overlays.ts +49 -49
- package/src/ui/renderers/index.ts +5 -4
- package/src/ui/renderers/project-list.markdown.ts +204 -204
- package/src/ui/renderers/project-list.renderer.tsx +14 -13
- package/tsconfig.json +7 -8
- 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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
isOpen,
|
|
42
|
+
project,
|
|
43
|
+
onClose,
|
|
44
|
+
onUpdate,
|
|
45
|
+
onArchive,
|
|
46
|
+
onActivate,
|
|
47
|
+
onDelete,
|
|
48
|
+
isLoading = false,
|
|
49
49
|
}: ProjectActionsModalProps) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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
|
}
|