@contractspec/example.saas-boilerplate 1.46.1 → 1.48.0
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$colon$bundle.log +183 -108
- package/.turbo/turbo-build.log +182 -107
- package/CHANGELOG.md +64 -0
- package/README.md +0 -1
- package/dist/billing/billing.event.d.ts +4 -4
- package/dist/billing/billing.event.js +1 -1
- package/dist/billing/billing.operations.d.ts +5 -5
- package/dist/billing/billing.presentation.d.ts +3 -4
- package/dist/billing/billing.presentation.d.ts.map +1 -1
- package/dist/billing/billing.presentation.js +5 -5
- package/dist/billing/billing.presentation.js.map +1 -1
- package/dist/dashboard/dashboard.presentation.d.ts +3 -4
- package/dist/dashboard/dashboard.presentation.d.ts.map +1 -1
- package/dist/dashboard/dashboard.presentation.js +5 -5
- package/dist/dashboard/dashboard.presentation.js.map +1 -1
- package/dist/example.d.ts +2 -2
- package/dist/example.d.ts.map +1 -1
- package/dist/example.js +4 -2
- package/dist/example.js.map +1 -1
- package/dist/handlers/index.d.ts +2 -1
- package/dist/handlers/index.js +2 -1
- package/dist/handlers/saas.handlers.d.ts +68 -0
- package/dist/handlers/saas.handlers.d.ts.map +1 -0
- package/dist/handlers/saas.handlers.js +148 -0
- package/dist/handlers/saas.handlers.js.map +1 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/dist/project/project.enum.d.ts +3 -3
- package/dist/project/project.event.d.ts +22 -22
- package/dist/project/project.event.d.ts.map +1 -1
- package/dist/project/project.event.js +1 -1
- package/dist/project/project.operations.d.ts +103 -103
- package/dist/project/project.presentation.d.ts +3 -4
- package/dist/project/project.presentation.d.ts.map +1 -1
- package/dist/project/project.presentation.js +5 -5
- package/dist/project/project.presentation.js.map +1 -1
- package/dist/project/project.schema.d.ts +54 -54
- package/dist/saas-boilerplate.feature.d.ts +2 -2
- package/dist/saas-boilerplate.feature.d.ts.map +1 -1
- package/dist/saas-boilerplate.feature.js +9 -2
- package/dist/saas-boilerplate.feature.js.map +1 -1
- package/dist/seeders/index.d.ts +10 -0
- package/dist/seeders/index.d.ts.map +1 -0
- package/dist/seeders/index.js +19 -0
- package/dist/seeders/index.js.map +1 -0
- package/dist/settings/settings.entity.d.ts +24 -24
- package/dist/settings/settings.enum.d.ts +2 -2
- package/dist/shared/overlay-types.d.ts +34 -0
- package/dist/shared/overlay-types.d.ts.map +1 -0
- package/dist/shared/overlay-types.js +0 -0
- package/dist/tests/operations.test-spec.d.ts +10 -0
- package/dist/tests/operations.test-spec.d.ts.map +1 -0
- package/dist/tests/operations.test-spec.js +123 -0
- package/dist/tests/operations.test-spec.js.map +1 -0
- package/dist/ui/SaasDashboard.d.ts +7 -0
- package/dist/ui/SaasDashboard.d.ts.map +1 -0
- package/dist/ui/SaasDashboard.js +298 -0
- package/dist/ui/SaasDashboard.js.map +1 -0
- package/dist/ui/SaasProjectList.d.ts +14 -0
- package/dist/ui/SaasProjectList.d.ts.map +1 -0
- package/dist/ui/SaasProjectList.js +76 -0
- package/dist/ui/SaasProjectList.js.map +1 -0
- package/dist/ui/SaasSettingsPanel.d.ts +7 -0
- package/dist/ui/SaasSettingsPanel.d.ts.map +1 -0
- package/dist/ui/SaasSettingsPanel.js +138 -0
- package/dist/ui/SaasSettingsPanel.js.map +1 -0
- package/dist/ui/hooks/index.d.ts +3 -0
- package/dist/ui/hooks/index.js +6 -0
- package/dist/ui/hooks/useProjectList.d.ts +34 -0
- package/dist/ui/hooks/useProjectList.d.ts.map +1 -0
- package/dist/ui/hooks/useProjectList.js +75 -0
- package/dist/ui/hooks/useProjectList.js.map +1 -0
- package/dist/ui/hooks/useProjectMutations.d.ts +28 -0
- package/dist/ui/hooks/useProjectMutations.d.ts.map +1 -0
- package/dist/ui/hooks/useProjectMutations.js +146 -0
- package/dist/ui/hooks/useProjectMutations.js.map +1 -0
- package/dist/ui/index.d.ts +14 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/modals/CreateProjectModal.d.ts +23 -0
- package/dist/ui/modals/CreateProjectModal.d.ts.map +1 -0
- package/dist/ui/modals/CreateProjectModal.js +139 -0
- package/dist/ui/modals/CreateProjectModal.js.map +1 -0
- package/dist/ui/modals/ProjectActionsModal.d.ts +38 -0
- package/dist/ui/modals/ProjectActionsModal.d.ts.map +1 -0
- package/dist/ui/modals/ProjectActionsModal.js +292 -0
- package/dist/ui/modals/ProjectActionsModal.js.map +1 -0
- package/dist/ui/modals/index.d.ts +3 -0
- package/dist/ui/modals/index.js +4 -0
- package/dist/ui/overlays/demo-overlays.d.ts +19 -0
- package/dist/ui/overlays/demo-overlays.d.ts.map +1 -0
- package/dist/ui/overlays/demo-overlays.js +70 -0
- package/dist/ui/overlays/demo-overlays.js.map +1 -0
- package/dist/ui/overlays/index.d.ts +2 -0
- package/dist/ui/overlays/index.js +3 -0
- package/dist/ui/renderers/index.d.ts +3 -0
- package/dist/ui/renderers/index.js +4 -0
- package/dist/ui/renderers/project-list.markdown.d.ts +31 -0
- package/dist/ui/renderers/project-list.markdown.d.ts.map +1 -0
- package/dist/ui/renderers/project-list.markdown.js +148 -0
- package/dist/ui/renderers/project-list.markdown.js.map +1 -0
- package/dist/ui/renderers/project-list.renderer.d.ts +9 -0
- package/dist/ui/renderers/project-list.renderer.d.ts.map +1 -0
- package/dist/ui/renderers/project-list.renderer.js +17 -0
- package/dist/ui/renderers/project-list.renderer.js.map +1 -0
- package/package.json +38 -14
- package/src/billing/billing.presentation.ts +5 -6
- package/src/dashboard/dashboard.presentation.ts +5 -6
- package/src/example.ts +3 -3
- package/src/handlers/index.ts +3 -0
- package/src/handlers/saas.handlers.ts +300 -0
- package/src/index.ts +5 -0
- package/src/project/project.presentation.ts +5 -6
- package/src/saas-boilerplate.feature.ts +3 -3
- package/src/seeders/index.ts +28 -0
- package/src/shared/overlay-types.ts +39 -0
- package/src/tests/operations.test-spec.ts +109 -0
- package/src/ui/SaasDashboard.tsx +325 -0
- package/src/ui/SaasProjectList.tsx +113 -0
- package/src/ui/SaasSettingsPanel.tsx +96 -0
- package/src/ui/hooks/index.ts +10 -0
- package/src/ui/hooks/useProjectList.ts +95 -0
- package/src/ui/hooks/useProjectMutations.ts +166 -0
- package/src/ui/index.ts +18 -0
- package/src/ui/modals/CreateProjectModal.tsx +176 -0
- package/src/ui/modals/ProjectActionsModal.tsx +346 -0
- package/src/ui/modals/index.ts +2 -0
- package/src/ui/overlays/demo-overlays.ts +74 -0
- package/src/ui/overlays/index.ts +1 -0
- package/src/ui/renderers/index.ts +7 -0
- package/src/ui/renderers/project-list.markdown.ts +239 -0
- package/src/ui/renderers/project-list.renderer.tsx +22 -0
- package/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Project as Project$1, Subscription as Subscription$1 } from "../../handlers/saas.handlers.js";
|
|
2
|
+
|
|
3
|
+
//#region src/ui/hooks/useProjectList.d.ts
|
|
4
|
+
type Project = Project$1;
|
|
5
|
+
type Subscription = Subscription$1;
|
|
6
|
+
interface ListProjectsOutput {
|
|
7
|
+
items: Project[];
|
|
8
|
+
total: number;
|
|
9
|
+
}
|
|
10
|
+
interface UseProjectListOptions {
|
|
11
|
+
status?: 'DRAFT' | 'ACTIVE' | 'ARCHIVED' | 'all';
|
|
12
|
+
search?: string;
|
|
13
|
+
limit?: number;
|
|
14
|
+
}
|
|
15
|
+
declare function useProjectList(options?: UseProjectListOptions): {
|
|
16
|
+
data: ListProjectsOutput | null;
|
|
17
|
+
subscription: Subscription$1 | null;
|
|
18
|
+
loading: boolean;
|
|
19
|
+
error: Error | null;
|
|
20
|
+
stats: {
|
|
21
|
+
total: number;
|
|
22
|
+
activeCount: number;
|
|
23
|
+
draftCount: number;
|
|
24
|
+
projectLimit: number;
|
|
25
|
+
usagePercent: number;
|
|
26
|
+
} | null;
|
|
27
|
+
page: number;
|
|
28
|
+
refetch: () => Promise<void>;
|
|
29
|
+
nextPage: () => void;
|
|
30
|
+
prevPage: () => false | void;
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { ListProjectsOutput, Project, Subscription, UseProjectListOptions, useProjectList };
|
|
34
|
+
//# sourceMappingURL=useProjectList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useProjectList.d.ts","names":[],"sources":["../../../src/ui/hooks/useProjectList.ts"],"sourcesContent":[],"mappings":";;;KAcY,OAAA,GAAU;KACV,YAAA,GAAe;AADf,UAGK,kBAAA,CAHK;EACV,KAAA,EAGH,OAHG,EAAY;EAEP,KAAA,EAAA,MAAA;AAKjB;AAMgB,UANC,qBAAA,CAMa;EAAU,MAAA,CAAA,EAAA,OAAA,GAAA,QAAA,GAAA,UAAA,GAAA,KAAA;;;;iBAAxB,cAAA,WAAwB"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
3
|
+
|
|
4
|
+
//#region src/ui/hooks/useProjectList.ts
|
|
5
|
+
/**
|
|
6
|
+
* Hook for fetching and managing project list data
|
|
7
|
+
*
|
|
8
|
+
* Uses runtime-local database-backed handlers.
|
|
9
|
+
*/
|
|
10
|
+
function useProjectList(options = {}) {
|
|
11
|
+
const { handlers, projectId } = useTemplateRuntime();
|
|
12
|
+
const { saas } = handlers;
|
|
13
|
+
const [data, setData] = useState(null);
|
|
14
|
+
const [subscription, setSubscription] = useState(null);
|
|
15
|
+
const [loading, setLoading] = useState(true);
|
|
16
|
+
const [error, setError] = useState(null);
|
|
17
|
+
const [page, setPage] = useState(1);
|
|
18
|
+
const fetchData = useCallback(async () => {
|
|
19
|
+
setLoading(true);
|
|
20
|
+
setError(null);
|
|
21
|
+
try {
|
|
22
|
+
const [projectsResult, subscriptionResult] = await Promise.all([saas.listProjects({
|
|
23
|
+
projectId,
|
|
24
|
+
status: options.status === "all" ? void 0 : options.status,
|
|
25
|
+
search: options.search,
|
|
26
|
+
limit: options.limit ?? 20,
|
|
27
|
+
offset: (page - 1) * (options.limit ?? 20)
|
|
28
|
+
}), saas.getSubscription({ projectId })]);
|
|
29
|
+
setData({
|
|
30
|
+
items: projectsResult.items,
|
|
31
|
+
total: projectsResult.total
|
|
32
|
+
});
|
|
33
|
+
setSubscription(subscriptionResult);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
setError(err instanceof Error ? err : /* @__PURE__ */ new Error("Unknown error"));
|
|
36
|
+
} finally {
|
|
37
|
+
setLoading(false);
|
|
38
|
+
}
|
|
39
|
+
}, [
|
|
40
|
+
saas,
|
|
41
|
+
projectId,
|
|
42
|
+
options.status,
|
|
43
|
+
options.search,
|
|
44
|
+
options.limit,
|
|
45
|
+
page
|
|
46
|
+
]);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
fetchData();
|
|
49
|
+
}, [fetchData]);
|
|
50
|
+
return {
|
|
51
|
+
data,
|
|
52
|
+
subscription,
|
|
53
|
+
loading,
|
|
54
|
+
error,
|
|
55
|
+
stats: useMemo(() => {
|
|
56
|
+
if (!data) return null;
|
|
57
|
+
const items = data.items;
|
|
58
|
+
return {
|
|
59
|
+
total: data.total,
|
|
60
|
+
activeCount: items.filter((p) => p.status === "ACTIVE").length,
|
|
61
|
+
draftCount: items.filter((p) => p.status === "DRAFT").length,
|
|
62
|
+
projectLimit: 10,
|
|
63
|
+
usagePercent: Math.min(data.total / 10 * 100, 100)
|
|
64
|
+
};
|
|
65
|
+
}, [data]),
|
|
66
|
+
page,
|
|
67
|
+
refetch: fetchData,
|
|
68
|
+
nextPage: () => setPage((p) => p + 1),
|
|
69
|
+
prevPage: () => page > 1 && setPage((p) => p - 1)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
export { useProjectList };
|
|
75
|
+
//# sourceMappingURL=useProjectList.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useProjectList.js","names":[],"sources":["../../../src/ui/hooks/useProjectList.ts"],"sourcesContent":["/**\n * Hook for fetching and managing project list data\n *\n * Uses runtime-local database-backed handlers.\n */\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport { useTemplateRuntime } from '@contractspec/lib.example-shared-ui';\nimport type {\n Project as RuntimeProject,\n Subscription as RuntimeSubscription,\n SaasHandlers,\n} from '../../handlers/saas.handlers';\n\n// Re-export types for convenience\nexport type Project = RuntimeProject;\nexport type Subscription = RuntimeSubscription;\n\nexport interface ListProjectsOutput {\n items: Project[];\n total: number;\n}\n\nexport interface UseProjectListOptions {\n status?: 'DRAFT' | 'ACTIVE' | 'ARCHIVED' | 'all';\n search?: string;\n limit?: number;\n}\n\nexport function useProjectList(options: UseProjectListOptions = {}) {\n const { handlers, projectId } = useTemplateRuntime<{ saas: SaasHandlers }>();\n const { saas } = handlers;\n\n const [data, setData] = useState<ListProjectsOutput | null>(null);\n const [subscription, setSubscription] = useState<Subscription | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [page, setPage] = useState(1);\n\n const fetchData = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n try {\n const [projectsResult, subscriptionResult] = await Promise.all([\n saas.listProjects({\n projectId,\n status: options.status === 'all' ? undefined : options.status,\n search: options.search,\n limit: options.limit ?? 20,\n offset: (page - 1) * (options.limit ?? 20),\n }),\n saas.getSubscription({ projectId }),\n ]);\n setData({\n items: projectsResult.items,\n total: projectsResult.total,\n });\n setSubscription(subscriptionResult);\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Unknown error'));\n } finally {\n setLoading(false);\n }\n }, [saas, projectId, options.status, options.search, options.limit, page]);\n\n useEffect(() => {\n fetchData();\n }, [fetchData]);\n\n // Calculate stats\n const stats = useMemo(() => {\n if (!data) return null;\n const items = data.items;\n return {\n total: data.total,\n activeCount: items.filter((p) => p.status === 'ACTIVE').length,\n draftCount: items.filter((p) => p.status === 'DRAFT').length,\n // Subscription stats are optional since they may not be seeded\n projectLimit: 10, // Default limit for demo\n usagePercent: Math.min((data.total / 10) * 100, 100),\n };\n }, [data]);\n\n return {\n data,\n subscription,\n loading,\n error,\n stats,\n page,\n refetch: fetchData,\n nextPage: () => setPage((p) => p + 1),\n prevPage: () => page > 1 && setPage((p) => p - 1),\n };\n}\n"],"mappings":";;;;;;;;;AA4BA,SAAgB,eAAe,UAAiC,EAAE,EAAE;CAClE,MAAM,EAAE,UAAU,cAAc,oBAA4C;CAC5E,MAAM,EAAE,SAAS;CAEjB,MAAM,CAAC,MAAM,WAAW,SAAoC,KAAK;CACjE,MAAM,CAAC,cAAc,mBAAmB,SAA8B,KAAK;CAC3E,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,OAAO,YAAY,SAAuB,KAAK;CACtD,MAAM,CAAC,MAAM,WAAW,SAAS,EAAE;CAEnC,MAAM,YAAY,YAAY,YAAY;AACxC,aAAW,KAAK;AAChB,WAAS,KAAK;AAEd,MAAI;GACF,MAAM,CAAC,gBAAgB,sBAAsB,MAAM,QAAQ,IAAI,CAC7D,KAAK,aAAa;IAChB;IACA,QAAQ,QAAQ,WAAW,QAAQ,SAAY,QAAQ;IACvD,QAAQ,QAAQ;IAChB,OAAO,QAAQ,SAAS;IACxB,SAAS,OAAO,MAAM,QAAQ,SAAS;IACxC,CAAC,EACF,KAAK,gBAAgB,EAAE,WAAW,CAAC,CACpC,CAAC;AACF,WAAQ;IACN,OAAO,eAAe;IACtB,OAAO,eAAe;IACvB,CAAC;AACF,mBAAgB,mBAAmB;WAC5B,KAAK;AACZ,YAAS,eAAe,QAAQ,sBAAM,IAAI,MAAM,gBAAgB,CAAC;YACzD;AACR,cAAW,MAAM;;IAElB;EAAC;EAAM;EAAW,QAAQ;EAAQ,QAAQ;EAAQ,QAAQ;EAAO;EAAK,CAAC;AAE1E,iBAAgB;AACd,aAAW;IACV,CAAC,UAAU,CAAC;AAgBf,QAAO;EACL;EACA;EACA;EACA;EACA,OAlBY,cAAc;AAC1B,OAAI,CAAC,KAAM,QAAO;GAClB,MAAM,QAAQ,KAAK;AACnB,UAAO;IACL,OAAO,KAAK;IACZ,aAAa,MAAM,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC;IACxD,YAAY,MAAM,QAAQ,MAAM,EAAE,WAAW,QAAQ,CAAC;IAEtD,cAAc;IACd,cAAc,KAAK,IAAK,KAAK,QAAQ,KAAM,KAAK,IAAI;IACrD;KACA,CAAC,KAAK,CAAC;EAQR;EACA,SAAS;EACT,gBAAgB,SAAS,MAAM,IAAI,EAAE;EACrC,gBAAgB,OAAO,KAAK,SAAS,MAAM,IAAI,EAAE;EAClD"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { CreateProjectInput, Project, UpdateProjectInput } from "../../handlers/saas.handlers.js";
|
|
2
|
+
|
|
3
|
+
//#region src/ui/hooks/useProjectMutations.d.ts
|
|
4
|
+
interface MutationState<T> {
|
|
5
|
+
loading: boolean;
|
|
6
|
+
error: Error | null;
|
|
7
|
+
data: T | null;
|
|
8
|
+
}
|
|
9
|
+
interface UseProjectMutationsOptions {
|
|
10
|
+
onSuccess?: () => void;
|
|
11
|
+
onError?: (error: Error) => void;
|
|
12
|
+
}
|
|
13
|
+
declare function useProjectMutations(options?: UseProjectMutationsOptions): {
|
|
14
|
+
createProject: (input: CreateProjectInput) => Promise<Project | null>;
|
|
15
|
+
updateProject: (input: UpdateProjectInput) => Promise<Project | null>;
|
|
16
|
+
deleteProject: (id: string) => Promise<boolean>;
|
|
17
|
+
archiveProject: (id: string) => Promise<Project | null>;
|
|
18
|
+
activateProject: (id: string) => Promise<Project | null>;
|
|
19
|
+
createState: MutationState<Project>;
|
|
20
|
+
updateState: MutationState<Project>;
|
|
21
|
+
deleteState: MutationState<{
|
|
22
|
+
success: boolean;
|
|
23
|
+
}>;
|
|
24
|
+
isLoading: boolean;
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { MutationState, UseProjectMutationsOptions, useProjectMutations };
|
|
28
|
+
//# sourceMappingURL=useProjectMutations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useProjectMutations.d.ts","names":[],"sources":["../../../src/ui/hooks/useProjectMutations.ts"],"sourcesContent":[],"mappings":";;;UAiBiB;;EAAA,KAAA,EAER,KAFQ,GAAA,IAAa;EAMb,IAAA,EAHT,CAGS,GAAA,IAAA;AAKjB;AAA6C,UAL5B,0BAAA,CAK4B;EA4B3B,SAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAA6B,OAAA,CAAA,EAAA,CAAA,KAAA,EA/B3B,KA+B2B,EAAA,GAAA,IAAA;;AAyB7B,iBArDF,mBAAA,CAqDE,OAAA,CAAA,EArD2B,0BAqD3B,CAAA,EAAA;EAA6B,aAAA,EAAA,CAAA,KAAA,EAzB7B,kBAyB6B,EAAA,GAzBR,OAyBQ,CAzBA,OAyBA,GAAA,IAAA,CAAA;EAAR,aAAA,EAAA,CAAA,KAAA,EAArB,kBAAqB,EAAA,GAAA,OAAA,CAAQ,OAAR,GAAA,IAAA,CAAA;EAsBf,aAAA,EAAA,CAAA,EAAA,EAAA,MAAA,EAAA,GAAA,OAAA,CAAA,OAAA,CAAA;EA0BQ,cAAA,EAAA,CAAA,EAAA,EAAA,MAAA,EAAA,GAAR,OAAQ,CAAA,OAAA,GAAA,IAAA,CAAA;EAAR,eAAA,EAAA,CAAA,EAAA,EAAA,MAAA,EAAA,GAUA,OAVA,CAUQ,OAVR,GAAA,IAAA,CAAA;EAUQ,WAAA,eAAA,QAAA,CAAA;EAAR,WAAA,eAAA,QAAA,CAAA"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
3
|
+
|
|
4
|
+
//#region src/ui/hooks/useProjectMutations.ts
|
|
5
|
+
/**
|
|
6
|
+
* Hook for SaaS project mutations (commands)
|
|
7
|
+
*
|
|
8
|
+
* Uses runtime-local database-backed handlers for:
|
|
9
|
+
* - CreateProjectContract
|
|
10
|
+
* - UpdateProjectContract
|
|
11
|
+
* - DeleteProjectContract
|
|
12
|
+
*/
|
|
13
|
+
function useProjectMutations(options = {}) {
|
|
14
|
+
const { handlers, projectId } = useTemplateRuntime();
|
|
15
|
+
const { saas } = handlers;
|
|
16
|
+
const [createState, setCreateState] = useState({
|
|
17
|
+
loading: false,
|
|
18
|
+
error: null,
|
|
19
|
+
data: null
|
|
20
|
+
});
|
|
21
|
+
const [updateState, setUpdateState] = useState({
|
|
22
|
+
loading: false,
|
|
23
|
+
error: null,
|
|
24
|
+
data: null
|
|
25
|
+
});
|
|
26
|
+
const [deleteState, setDeleteState] = useState({
|
|
27
|
+
loading: false,
|
|
28
|
+
error: null,
|
|
29
|
+
data: null
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* Create a new project
|
|
33
|
+
*/
|
|
34
|
+
const createProject = useCallback(async (input) => {
|
|
35
|
+
setCreateState({
|
|
36
|
+
loading: true,
|
|
37
|
+
error: null,
|
|
38
|
+
data: null
|
|
39
|
+
});
|
|
40
|
+
try {
|
|
41
|
+
const result = await saas.createProject(input, {
|
|
42
|
+
projectId,
|
|
43
|
+
organizationId: "demo-org"
|
|
44
|
+
});
|
|
45
|
+
setCreateState({
|
|
46
|
+
loading: false,
|
|
47
|
+
error: null,
|
|
48
|
+
data: result
|
|
49
|
+
});
|
|
50
|
+
options.onSuccess?.();
|
|
51
|
+
return result;
|
|
52
|
+
} catch (err) {
|
|
53
|
+
const error = err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to create project");
|
|
54
|
+
setCreateState({
|
|
55
|
+
loading: false,
|
|
56
|
+
error,
|
|
57
|
+
data: null
|
|
58
|
+
});
|
|
59
|
+
options.onError?.(error);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}, [
|
|
63
|
+
saas,
|
|
64
|
+
projectId,
|
|
65
|
+
options
|
|
66
|
+
]);
|
|
67
|
+
/**
|
|
68
|
+
* Update a project
|
|
69
|
+
*/
|
|
70
|
+
const updateProject = useCallback(async (input) => {
|
|
71
|
+
setUpdateState({
|
|
72
|
+
loading: true,
|
|
73
|
+
error: null,
|
|
74
|
+
data: null
|
|
75
|
+
});
|
|
76
|
+
try {
|
|
77
|
+
const result = await saas.updateProject(input);
|
|
78
|
+
setUpdateState({
|
|
79
|
+
loading: false,
|
|
80
|
+
error: null,
|
|
81
|
+
data: result
|
|
82
|
+
});
|
|
83
|
+
options.onSuccess?.();
|
|
84
|
+
return result;
|
|
85
|
+
} catch (err) {
|
|
86
|
+
const error = err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to update project");
|
|
87
|
+
setUpdateState({
|
|
88
|
+
loading: false,
|
|
89
|
+
error,
|
|
90
|
+
data: null
|
|
91
|
+
});
|
|
92
|
+
options.onError?.(error);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}, [saas, options]);
|
|
96
|
+
return {
|
|
97
|
+
createProject,
|
|
98
|
+
updateProject,
|
|
99
|
+
deleteProject: useCallback(async (id) => {
|
|
100
|
+
setDeleteState({
|
|
101
|
+
loading: true,
|
|
102
|
+
error: null,
|
|
103
|
+
data: null
|
|
104
|
+
});
|
|
105
|
+
try {
|
|
106
|
+
await saas.deleteProject(id);
|
|
107
|
+
setDeleteState({
|
|
108
|
+
loading: false,
|
|
109
|
+
error: null,
|
|
110
|
+
data: { success: true }
|
|
111
|
+
});
|
|
112
|
+
options.onSuccess?.();
|
|
113
|
+
return true;
|
|
114
|
+
} catch (err) {
|
|
115
|
+
const error = err instanceof Error ? err : /* @__PURE__ */ new Error("Failed to delete project");
|
|
116
|
+
setDeleteState({
|
|
117
|
+
loading: false,
|
|
118
|
+
error,
|
|
119
|
+
data: null
|
|
120
|
+
});
|
|
121
|
+
options.onError?.(error);
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}, [saas, options]),
|
|
125
|
+
archiveProject: useCallback(async (id) => {
|
|
126
|
+
return updateProject({
|
|
127
|
+
id,
|
|
128
|
+
status: "ARCHIVED"
|
|
129
|
+
});
|
|
130
|
+
}, [updateProject]),
|
|
131
|
+
activateProject: useCallback(async (id) => {
|
|
132
|
+
return updateProject({
|
|
133
|
+
id,
|
|
134
|
+
status: "ACTIVE"
|
|
135
|
+
});
|
|
136
|
+
}, [updateProject]),
|
|
137
|
+
createState,
|
|
138
|
+
updateState,
|
|
139
|
+
deleteState,
|
|
140
|
+
isLoading: createState.loading || updateState.loading || deleteState.loading
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
//#endregion
|
|
145
|
+
export { useProjectMutations };
|
|
146
|
+
//# sourceMappingURL=useProjectMutations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useProjectMutations.js","names":[],"sources":["../../../src/ui/hooks/useProjectMutations.ts"],"sourcesContent":["/**\n * Hook for SaaS project mutations (commands)\n *\n * Uses runtime-local database-backed handlers for:\n * - CreateProjectContract\n * - UpdateProjectContract\n * - DeleteProjectContract\n */\nimport { useCallback, useState } from 'react';\nimport { useTemplateRuntime } from '@contractspec/lib.example-shared-ui';\nimport type {\n CreateProjectInput,\n Project,\n UpdateProjectInput,\n SaasHandlers,\n} from '../../handlers/saas.handlers';\n\nexport interface MutationState<T> {\n loading: boolean;\n error: Error | null;\n data: T | null;\n}\n\nexport interface UseProjectMutationsOptions {\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n}\n\nexport function useProjectMutations(options: UseProjectMutationsOptions = {}) {\n const { handlers, projectId } = useTemplateRuntime<{ saas: SaasHandlers }>();\n const { saas } = handlers;\n\n const [createState, setCreateState] = useState<MutationState<Project>>({\n loading: false,\n error: null,\n data: null,\n });\n\n const [updateState, setUpdateState] = useState<MutationState<Project>>({\n loading: false,\n error: null,\n data: null,\n });\n\n const [deleteState, setDeleteState] = useState<\n MutationState<{ success: boolean }>\n >({\n loading: false,\n error: null,\n data: null,\n });\n\n /**\n * Create a new project\n */\n const createProject = useCallback(\n async (input: CreateProjectInput): Promise<Project | null> => {\n setCreateState({ loading: true, error: null, data: null });\n try {\n const result = await saas.createProject(input, {\n projectId,\n organizationId: 'demo-org',\n });\n setCreateState({ loading: false, error: null, data: result });\n options.onSuccess?.();\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error('Failed to create project');\n setCreateState({ loading: false, error, data: null });\n options.onError?.(error);\n return null;\n }\n },\n [saas, projectId, options]\n );\n\n /**\n * Update a project\n */\n const updateProject = useCallback(\n async (input: UpdateProjectInput): Promise<Project | null> => {\n setUpdateState({ loading: true, error: null, data: null });\n try {\n const result = await saas.updateProject(input);\n setUpdateState({ loading: false, error: null, data: result });\n options.onSuccess?.();\n return result;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error('Failed to update project');\n setUpdateState({ loading: false, error, data: null });\n options.onError?.(error);\n return null;\n }\n },\n [saas, options]\n );\n\n /**\n * Delete a project (soft delete)\n */\n const deleteProject = useCallback(\n async (id: string): Promise<boolean> => {\n setDeleteState({ loading: true, error: null, data: null });\n try {\n await saas.deleteProject(id);\n setDeleteState({\n loading: false,\n error: null,\n data: { success: true },\n });\n options.onSuccess?.();\n return true;\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error('Failed to delete project');\n setDeleteState({ loading: false, error, data: null });\n options.onError?.(error);\n return false;\n }\n },\n [saas, options]\n );\n\n /**\n * Archive a project (status change)\n */\n const archiveProject = useCallback(\n async (id: string): Promise<Project | null> => {\n return updateProject({ id, status: 'ARCHIVED' });\n },\n [updateProject]\n );\n\n /**\n * Activate a project (status change)\n */\n const activateProject = useCallback(\n async (id: string): Promise<Project | null> => {\n return updateProject({ id, status: 'ACTIVE' });\n },\n [updateProject]\n );\n\n return {\n // Mutations\n createProject,\n updateProject,\n deleteProject,\n archiveProject,\n activateProject,\n\n // State\n createState,\n updateState,\n deleteState,\n\n // Convenience\n isLoading:\n createState.loading || updateState.loading || deleteState.loading,\n };\n}\n\n// Note: Types are re-exported from the handlers package\n// Consumers should import types directly from '@contractspec/example.saas-boilerplate/handlers'\n"],"mappings":";;;;;;;;;;;;AA4BA,SAAgB,oBAAoB,UAAsC,EAAE,EAAE;CAC5E,MAAM,EAAE,UAAU,cAAc,oBAA4C;CAC5E,MAAM,EAAE,SAAS;CAEjB,MAAM,CAAC,aAAa,kBAAkB,SAAiC;EACrE,SAAS;EACT,OAAO;EACP,MAAM;EACP,CAAC;CAEF,MAAM,CAAC,aAAa,kBAAkB,SAAiC;EACrE,SAAS;EACT,OAAO;EACP,MAAM;EACP,CAAC;CAEF,MAAM,CAAC,aAAa,kBAAkB,SAEpC;EACA,SAAS;EACT,OAAO;EACP,MAAM;EACP,CAAC;;;;CAKF,MAAM,gBAAgB,YACpB,OAAO,UAAuD;AAC5D,iBAAe;GAAE,SAAS;GAAM,OAAO;GAAM,MAAM;GAAM,CAAC;AAC1D,MAAI;GACF,MAAM,SAAS,MAAM,KAAK,cAAc,OAAO;IAC7C;IACA,gBAAgB;IACjB,CAAC;AACF,kBAAe;IAAE,SAAS;IAAO,OAAO;IAAM,MAAM;IAAQ,CAAC;AAC7D,WAAQ,aAAa;AACrB,UAAO;WACA,KAAK;GACZ,MAAM,QACJ,eAAe,QAAQ,sBAAM,IAAI,MAAM,2BAA2B;AACpE,kBAAe;IAAE,SAAS;IAAO;IAAO,MAAM;IAAM,CAAC;AACrD,WAAQ,UAAU,MAAM;AACxB,UAAO;;IAGX;EAAC;EAAM;EAAW;EAAQ,CAC3B;;;;CAKD,MAAM,gBAAgB,YACpB,OAAO,UAAuD;AAC5D,iBAAe;GAAE,SAAS;GAAM,OAAO;GAAM,MAAM;GAAM,CAAC;AAC1D,MAAI;GACF,MAAM,SAAS,MAAM,KAAK,cAAc,MAAM;AAC9C,kBAAe;IAAE,SAAS;IAAO,OAAO;IAAM,MAAM;IAAQ,CAAC;AAC7D,WAAQ,aAAa;AACrB,UAAO;WACA,KAAK;GACZ,MAAM,QACJ,eAAe,QAAQ,sBAAM,IAAI,MAAM,2BAA2B;AACpE,kBAAe;IAAE,SAAS;IAAO;IAAO,MAAM;IAAM,CAAC;AACrD,WAAQ,UAAU,MAAM;AACxB,UAAO;;IAGX,CAAC,MAAM,QAAQ,CAChB;AAgDD,QAAO;EAEL;EACA;EACA,eA/CoB,YACpB,OAAO,OAAiC;AACtC,kBAAe;IAAE,SAAS;IAAM,OAAO;IAAM,MAAM;IAAM,CAAC;AAC1D,OAAI;AACF,UAAM,KAAK,cAAc,GAAG;AAC5B,mBAAe;KACb,SAAS;KACT,OAAO;KACP,MAAM,EAAE,SAAS,MAAM;KACxB,CAAC;AACF,YAAQ,aAAa;AACrB,WAAO;YACA,KAAK;IACZ,MAAM,QACJ,eAAe,QAAQ,sBAAM,IAAI,MAAM,2BAA2B;AACpE,mBAAe;KAAE,SAAS;KAAO;KAAO,MAAM;KAAM,CAAC;AACrD,YAAQ,UAAU,MAAM;AACxB,WAAO;;KAGX,CAAC,MAAM,QAAQ,CAChB;EA2BC,gBAtBqB,YACrB,OAAO,OAAwC;AAC7C,UAAO,cAAc;IAAE;IAAI,QAAQ;IAAY,CAAC;KAElD,CAAC,cAAc,CAChB;EAkBC,iBAbsB,YACtB,OAAO,OAAwC;AAC7C,UAAO,cAAc;IAAE;IAAI,QAAQ;IAAU,CAAC;KAEhD,CAAC,cAAc,CAChB;EAWC;EACA;EACA;EAGA,WACE,YAAY,WAAW,YAAY,WAAW,YAAY;EAC7D"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SaasDashboard } from "./SaasDashboard.js";
|
|
2
|
+
import { SaasProjectList } from "./SaasProjectList.js";
|
|
3
|
+
import { SaasSettingsPanel } from "./SaasSettingsPanel.js";
|
|
4
|
+
import { CreateProjectModal } from "./modals/CreateProjectModal.js";
|
|
5
|
+
import { ProjectActionsModal } from "./modals/ProjectActionsModal.js";
|
|
6
|
+
import "./modals/index.js";
|
|
7
|
+
import { UseProjectListOptions, useProjectList } from "./hooks/useProjectList.js";
|
|
8
|
+
import { UseProjectMutationsOptions, useProjectMutations } from "./hooks/useProjectMutations.js";
|
|
9
|
+
import "./hooks/index.js";
|
|
10
|
+
import { projectListReactRenderer } from "./renderers/project-list.renderer.js";
|
|
11
|
+
import { projectListMarkdownRenderer, saasBillingMarkdownRenderer, saasDashboardMarkdownRenderer } from "./renderers/project-list.markdown.js";
|
|
12
|
+
import "./renderers/index.js";
|
|
13
|
+
import { saasDemoOverlay, saasFreeUserOverlay, saasOverlays } from "./overlays/demo-overlays.js";
|
|
14
|
+
export { CreateProjectModal, ProjectActionsModal, SaasDashboard, SaasProjectList, SaasSettingsPanel, UseProjectListOptions, UseProjectMutationsOptions, projectListMarkdownRenderer, projectListReactRenderer, saasBillingMarkdownRenderer, saasDashboardMarkdownRenderer, saasDemoOverlay, saasFreeUserOverlay, saasOverlays, useProjectList, useProjectMutations };
|
package/dist/ui/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useProjectList } from "./hooks/useProjectList.js";
|
|
2
|
+
import { useProjectMutations } from "./hooks/useProjectMutations.js";
|
|
3
|
+
import { CreateProjectModal } from "./modals/CreateProjectModal.js";
|
|
4
|
+
import { ProjectActionsModal } from "./modals/ProjectActionsModal.js";
|
|
5
|
+
import { SaasDashboard } from "./SaasDashboard.js";
|
|
6
|
+
import { SaasProjectList } from "./SaasProjectList.js";
|
|
7
|
+
import { SaasSettingsPanel } from "./SaasSettingsPanel.js";
|
|
8
|
+
import "./modals/index.js";
|
|
9
|
+
import "./hooks/index.js";
|
|
10
|
+
import { projectListReactRenderer } from "./renderers/project-list.renderer.js";
|
|
11
|
+
import { projectListMarkdownRenderer, saasBillingMarkdownRenderer, saasDashboardMarkdownRenderer } from "./renderers/project-list.markdown.js";
|
|
12
|
+
import "./renderers/index.js";
|
|
13
|
+
import { saasDemoOverlay, saasFreeUserOverlay, saasOverlays } from "./overlays/demo-overlays.js";
|
|
14
|
+
|
|
15
|
+
export { CreateProjectModal, ProjectActionsModal, SaasDashboard, SaasProjectList, SaasSettingsPanel, projectListMarkdownRenderer, projectListReactRenderer, saasBillingMarkdownRenderer, saasDashboardMarkdownRenderer, saasDemoOverlay, saasFreeUserOverlay, saasOverlays, useProjectList, useProjectMutations };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as react_jsx_runtime2 from "react/jsx-runtime";
|
|
2
|
+
|
|
3
|
+
//#region src/ui/modals/CreateProjectModal.d.ts
|
|
4
|
+
interface CreateProjectInput {
|
|
5
|
+
name: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
tier: 'FREE' | 'PRO' | 'ENTERPRISE';
|
|
8
|
+
}
|
|
9
|
+
interface CreateProjectModalProps {
|
|
10
|
+
isOpen: boolean;
|
|
11
|
+
onClose: () => void;
|
|
12
|
+
onSubmit: (input: CreateProjectInput) => Promise<void>;
|
|
13
|
+
isLoading?: boolean;
|
|
14
|
+
}
|
|
15
|
+
declare function CreateProjectModal({
|
|
16
|
+
isOpen,
|
|
17
|
+
onClose,
|
|
18
|
+
onSubmit,
|
|
19
|
+
isLoading
|
|
20
|
+
}: CreateProjectModalProps): react_jsx_runtime2.JSX.Element | null;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { CreateProjectInput, CreateProjectModal };
|
|
23
|
+
//# sourceMappingURL=CreateProjectModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateProjectModal.d.ts","names":[],"sources":["../../../src/ui/modals/CreateProjectModal.tsx"],"sourcesContent":[],"mappings":";;;UAWiB,kBAAA;;;EAAA,IAAA,EAAA,MAAA,GAAA,KAAA,GAAkB,YAAA;AAIlC;AAeD,UAbU,uBAAA,CAawB;EAChC,MAAA,EAAA,OAAA;EACA,OAAA,EAAA,GAAA,GAAA,IAAA;EACA,QAAA,EAAA,CAAA,KAAA,EAbkB,kBAalB,EAAA,GAbyC,OAazC,CAAA,IAAA,CAAA;EACA,SAAA,CAAA,EAAA,OAAA;;AACwB,iBALV,kBAAA,CAKU;EAAA,MAAA;EAAA,OAAA;EAAA,QAAA;EAAA;AAAA,CAAA,EAAvB,uBAAuB,CAAA,EAAA,kBAAA,CAAA,GAAA,CAAA,OAAA,GAAA,IAAA"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Button, Input } from "@contractspec/lib.design-system";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
|
|
7
|
+
//#region src/ui/modals/CreateProjectModal.tsx
|
|
8
|
+
/**
|
|
9
|
+
* CreateProjectModal - Form for creating a new project
|
|
10
|
+
*
|
|
11
|
+
* Wires to CreateProjectContract via useProjectMutations hook.
|
|
12
|
+
*/
|
|
13
|
+
const TIERS = [
|
|
14
|
+
{
|
|
15
|
+
value: "FREE",
|
|
16
|
+
label: "Free"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
value: "PRO",
|
|
20
|
+
label: "Pro"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
value: "ENTERPRISE",
|
|
24
|
+
label: "Enterprise"
|
|
25
|
+
}
|
|
26
|
+
];
|
|
27
|
+
function CreateProjectModal({ isOpen, onClose, onSubmit, isLoading = false }) {
|
|
28
|
+
const [name, setName] = useState("");
|
|
29
|
+
const [description, setDescription] = useState("");
|
|
30
|
+
const [tier, setTier] = useState("FREE");
|
|
31
|
+
const [error, setError] = useState(null);
|
|
32
|
+
const handleSubmit = async (e) => {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
setError(null);
|
|
35
|
+
if (!name.trim()) {
|
|
36
|
+
setError("Project name is required");
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
await onSubmit({
|
|
41
|
+
name: name.trim(),
|
|
42
|
+
description: description.trim() || void 0,
|
|
43
|
+
tier
|
|
44
|
+
});
|
|
45
|
+
setName("");
|
|
46
|
+
setDescription("");
|
|
47
|
+
setTier("FREE");
|
|
48
|
+
onClose();
|
|
49
|
+
} catch (err) {
|
|
50
|
+
setError(err instanceof Error ? err.message : "Failed to create project");
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
if (!isOpen) return null;
|
|
54
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
55
|
+
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
56
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
57
|
+
className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
|
|
58
|
+
onClick: onClose,
|
|
59
|
+
role: "button",
|
|
60
|
+
tabIndex: 0,
|
|
61
|
+
onKeyDown: (e) => {
|
|
62
|
+
if (e.key === "Enter" || e.key === " ") onClose();
|
|
63
|
+
},
|
|
64
|
+
"aria-label": "Close modal"
|
|
65
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
66
|
+
className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
|
|
67
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
68
|
+
className: "mb-4 text-xl font-semibold",
|
|
69
|
+
children: "Create New Project"
|
|
70
|
+
}), /* @__PURE__ */ jsxs("form", {
|
|
71
|
+
onSubmit: handleSubmit,
|
|
72
|
+
className: "space-y-4",
|
|
73
|
+
children: [
|
|
74
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
75
|
+
htmlFor: "project-name",
|
|
76
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
77
|
+
children: "Project Name *"
|
|
78
|
+
}), /* @__PURE__ */ jsx(Input, {
|
|
79
|
+
id: "project-name",
|
|
80
|
+
value: name,
|
|
81
|
+
onChange: (e) => setName(e.target.value),
|
|
82
|
+
placeholder: "e.g., My Awesome Project",
|
|
83
|
+
disabled: isLoading
|
|
84
|
+
})] }),
|
|
85
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
86
|
+
htmlFor: "project-description",
|
|
87
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
88
|
+
children: "Description"
|
|
89
|
+
}), /* @__PURE__ */ jsx("textarea", {
|
|
90
|
+
id: "project-description",
|
|
91
|
+
value: description,
|
|
92
|
+
onChange: (e) => setDescription(e.target.value),
|
|
93
|
+
placeholder: "Describe what this project is about...",
|
|
94
|
+
rows: 3,
|
|
95
|
+
disabled: isLoading,
|
|
96
|
+
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"
|
|
97
|
+
})] }),
|
|
98
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
99
|
+
htmlFor: "project-tier",
|
|
100
|
+
className: "text-muted-foreground mb-1 block text-sm font-medium",
|
|
101
|
+
children: "Tier"
|
|
102
|
+
}), /* @__PURE__ */ jsx("select", {
|
|
103
|
+
id: "project-tier",
|
|
104
|
+
value: tier,
|
|
105
|
+
onChange: (e) => setTier(e.target.value),
|
|
106
|
+
disabled: isLoading,
|
|
107
|
+
className: "border-input bg-background focus:ring-ring h-10 w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50",
|
|
108
|
+
children: TIERS.map((t) => /* @__PURE__ */ jsx("option", {
|
|
109
|
+
value: t.value,
|
|
110
|
+
children: t.label
|
|
111
|
+
}, t.value))
|
|
112
|
+
})] }),
|
|
113
|
+
error && /* @__PURE__ */ jsx("div", {
|
|
114
|
+
className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
|
|
115
|
+
children: error
|
|
116
|
+
}),
|
|
117
|
+
/* @__PURE__ */ jsxs("div", {
|
|
118
|
+
className: "flex justify-end gap-3 pt-2",
|
|
119
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
120
|
+
type: "button",
|
|
121
|
+
variant: "ghost",
|
|
122
|
+
onPress: onClose,
|
|
123
|
+
disabled: isLoading,
|
|
124
|
+
children: "Cancel"
|
|
125
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
126
|
+
type: "submit",
|
|
127
|
+
disabled: isLoading,
|
|
128
|
+
children: isLoading ? "Creating..." : "Create Project"
|
|
129
|
+
})]
|
|
130
|
+
})
|
|
131
|
+
]
|
|
132
|
+
})]
|
|
133
|
+
})]
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//#endregion
|
|
138
|
+
export { CreateProjectModal };
|
|
139
|
+
//# sourceMappingURL=CreateProjectModal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CreateProjectModal.js","names":[],"sources":["../../../src/ui/modals/CreateProjectModal.tsx"],"sourcesContent":["'use client';\n\n/**\n * CreateProjectModal - Form for creating a new project\n *\n * Wires to CreateProjectContract via useProjectMutations hook.\n */\nimport { useState } from 'react';\nimport { Button, Input } from '@contractspec/lib.design-system';\n\n// Local type definition for modal props\nexport interface CreateProjectInput {\n name: string;\n description?: string;\n tier: 'FREE' | 'PRO' | 'ENTERPRISE';\n}\n\ninterface CreateProjectModalProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (input: CreateProjectInput) => Promise<void>;\n isLoading?: boolean;\n}\n\nconst TIERS: { value: CreateProjectInput['tier']; label: string }[] = [\n { value: 'FREE', label: 'Free' },\n { value: 'PRO', label: 'Pro' },\n { value: 'ENTERPRISE', label: 'Enterprise' },\n];\n\nexport function CreateProjectModal({\n isOpen,\n onClose,\n onSubmit,\n isLoading = false,\n}: CreateProjectModalProps) {\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [tier, setTier] = useState<CreateProjectInput['tier']>('FREE');\n const [error, setError] = useState<string | null>(null);\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n // Validation\n if (!name.trim()) {\n setError('Project name is required');\n return;\n }\n\n try {\n await onSubmit({\n name: name.trim(),\n description: description.trim() || undefined,\n tier,\n });\n\n // Reset form\n setName('');\n setDescription('');\n setTier('FREE');\n onClose();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create project');\n }\n };\n\n if (!isOpen) return null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <div\n className=\"bg-background/80 absolute inset-0 backdrop-blur-sm\"\n onClick={onClose}\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') onClose();\n }}\n aria-label=\"Close modal\"\n />\n\n {/* Modal */}\n <div className=\"bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl\">\n <h2 className=\"mb-4 text-xl font-semibold\">Create New Project</h2>\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {/* Project Name */}\n <div>\n <label\n htmlFor=\"project-name\"\n className=\"text-muted-foreground mb-1 block text-sm font-medium\"\n >\n Project Name *\n </label>\n <Input\n id=\"project-name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g., My Awesome Project\"\n disabled={isLoading}\n />\n </div>\n\n {/* Description */}\n <div>\n <label\n htmlFor=\"project-description\"\n className=\"text-muted-foreground mb-1 block text-sm font-medium\"\n >\n Description\n </label>\n <textarea\n id=\"project-description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"Describe what this project is about...\"\n rows={3}\n disabled={isLoading}\n 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\"\n />\n </div>\n\n {/* Tier */}\n <div>\n <label\n htmlFor=\"project-tier\"\n className=\"text-muted-foreground mb-1 block text-sm font-medium\"\n >\n Tier\n </label>\n <select\n id=\"project-tier\"\n value={tier}\n onChange={(e) =>\n setTier(e.target.value as CreateProjectInput['tier'])\n }\n disabled={isLoading}\n className=\"border-input bg-background focus:ring-ring h-10 w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50\"\n >\n {TIERS.map((t) => (\n <option key={t.value} value={t.value}>\n {t.label}\n </option>\n ))}\n </select>\n </div>\n\n {/* Error Message */}\n {error && (\n <div className=\"bg-destructive/10 text-destructive rounded-md p-3 text-sm\">\n {error}\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex justify-end gap-3 pt-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n onPress={onClose}\n disabled={isLoading}\n >\n Cancel\n </Button>\n <Button type=\"submit\" disabled={isLoading}>\n {isLoading ? 'Creating...' : 'Create Project'}\n </Button>\n </div>\n </form>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAwBA,MAAM,QAAgE;CACpE;EAAE,OAAO;EAAQ,OAAO;EAAQ;CAChC;EAAE,OAAO;EAAO,OAAO;EAAO;CAC9B;EAAE,OAAO;EAAc,OAAO;EAAc;CAC7C;AAED,SAAgB,mBAAmB,EACjC,QACA,SACA,UACA,YAAY,SACc;CAC1B,MAAM,CAAC,MAAM,WAAW,SAAS,GAAG;CACpC,MAAM,CAAC,aAAa,kBAAkB,SAAS,GAAG;CAClD,MAAM,CAAC,MAAM,WAAW,SAAqC,OAAO;CACpE,MAAM,CAAC,OAAO,YAAY,SAAwB,KAAK;CAEvD,MAAM,eAAe,OAAO,MAAuB;AACjD,IAAE,gBAAgB;AAClB,WAAS,KAAK;AAGd,MAAI,CAAC,KAAK,MAAM,EAAE;AAChB,YAAS,2BAA2B;AACpC;;AAGF,MAAI;AACF,SAAM,SAAS;IACb,MAAM,KAAK,MAAM;IACjB,aAAa,YAAY,MAAM,IAAI;IACnC;IACD,CAAC;AAGF,WAAQ,GAAG;AACX,kBAAe,GAAG;AAClB,WAAQ,OAAO;AACf,YAAS;WACF,KAAK;AACZ,YAAS,eAAe,QAAQ,IAAI,UAAU,2BAA2B;;;AAI7E,KAAI,CAAC,OAAQ,QAAO;AAEpB,QACE,qBAAC;EAAI,WAAU;aAEb,oBAAC;GACC,WAAU;GACV,SAAS;GACT,MAAK;GACL,UAAU;GACV,YAAY,MAAM;AAChB,QAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,IAAK,UAAS;;GAEnD,cAAW;IACX,EAGF,qBAAC;GAAI,WAAU;cACb,oBAAC;IAAG,WAAU;cAA6B;KAAuB,EAElE,qBAAC;IAAK,UAAU;IAAc,WAAU;;KAEtC,qBAAC,oBACC,oBAAC;MACC,SAAQ;MACR,WAAU;gBACX;OAEO,EACR,oBAAC;MACC,IAAG;MACH,OAAO;MACP,WAAW,MAAM,QAAQ,EAAE,OAAO,MAAM;MACxC,aAAY;MACZ,UAAU;OACV,IACE;KAGN,qBAAC,oBACC,oBAAC;MACC,SAAQ;MACR,WAAU;gBACX;OAEO,EACR,oBAAC;MACC,IAAG;MACH,OAAO;MACP,WAAW,MAAM,eAAe,EAAE,OAAO,MAAM;MAC/C,aAAY;MACZ,MAAM;MACN,UAAU;MACV,WAAU;OACV,IACE;KAGN,qBAAC,oBACC,oBAAC;MACC,SAAQ;MACR,WAAU;gBACX;OAEO,EACR,oBAAC;MACC,IAAG;MACH,OAAO;MACP,WAAW,MACT,QAAQ,EAAE,OAAO,MAAoC;MAEvD,UAAU;MACV,WAAU;gBAET,MAAM,KAAK,MACV,oBAAC;OAAqB,OAAO,EAAE;iBAC5B,EAAE;SADQ,EAAE,MAEN,CACT;OACK,IACL;KAGL,SACC,oBAAC;MAAI,WAAU;gBACZ;OACG;KAIR,qBAAC;MAAI,WAAU;iBACb,oBAAC;OACC,MAAK;OACL,SAAQ;OACR,SAAS;OACT,UAAU;iBACX;QAEQ,EACT,oBAAC;OAAO,MAAK;OAAS,UAAU;iBAC7B,YAAY,gBAAgB;QACtB;OACL;;KACD;IACH;GACF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as react_jsx_runtime3 from "react/jsx-runtime";
|
|
2
|
+
|
|
3
|
+
//#region src/ui/modals/ProjectActionsModal.d.ts
|
|
4
|
+
interface Project {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
status: 'DRAFT' | 'ACTIVE' | 'ARCHIVED';
|
|
9
|
+
tier: 'FREE' | 'PRO' | 'ENTERPRISE';
|
|
10
|
+
}
|
|
11
|
+
interface UpdateProjectInput {
|
|
12
|
+
id: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
}
|
|
16
|
+
interface ProjectActionsModalProps {
|
|
17
|
+
isOpen: boolean;
|
|
18
|
+
project: Project | null;
|
|
19
|
+
onClose: () => void;
|
|
20
|
+
onUpdate: (input: UpdateProjectInput) => Promise<void>;
|
|
21
|
+
onArchive: (projectId: string) => Promise<void>;
|
|
22
|
+
onActivate: (projectId: string) => Promise<void>;
|
|
23
|
+
onDelete: (projectId: string) => Promise<void>;
|
|
24
|
+
isLoading?: boolean;
|
|
25
|
+
}
|
|
26
|
+
declare function ProjectActionsModal({
|
|
27
|
+
isOpen,
|
|
28
|
+
project,
|
|
29
|
+
onClose,
|
|
30
|
+
onUpdate,
|
|
31
|
+
onArchive,
|
|
32
|
+
onActivate,
|
|
33
|
+
onDelete,
|
|
34
|
+
isLoading
|
|
35
|
+
}: ProjectActionsModalProps): react_jsx_runtime3.JSX.Element | null;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { Project, ProjectActionsModal, UpdateProjectInput };
|
|
38
|
+
//# sourceMappingURL=ProjectActionsModal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProjectActionsModal.d.ts","names":[],"sources":["../../../src/ui/modals/ProjectActionsModal.tsx"],"sourcesContent":[],"mappings":";;;UAYiB,OAAA;;;EAAA,WAAO,CAAA,EAAA,MAAA;EAQP,MAAA,EAAA,OAAA,GAAA,QAAkB,GAAA,UAAA;EAQzB,IAAA,EAAA,MAAA,GAAA,KAAA,GAAA,YAAwB;;AAId,UAZH,kBAAA,CAYG;EAAuB,EAAA,EAAA,MAAA;EACP,IAAA,CAAA,EAAA,MAAA;EACC,WAAA,CAAA,EAAA,MAAA;;UAN3B,wBAAA,CAOgC;EAI1B,MAAA,EAAA,OAAA;EACd,OAAA,EAVS,OAUT,GAAA,IAAA;EACA,OAAA,EAAA,GAAA,GAAA,IAAA;EACA,QAAA,EAAA,CAAA,KAAA,EAVkB,kBAUlB,EAAA,GAVyC,OAUzC,CAAA,IAAA,CAAA;EACA,SAAA,EAAA,CAAA,SAAA,EAAA,MAAA,EAAA,GAVkC,OAUlC,CAAA,IAAA,CAAA;EACA,UAAA,EAAA,CAAA,SAAA,EAAA,MAAA,EAAA,GAVmC,OAUnC,CAAA,IAAA,CAAA;EACA,QAAA,EAAA,CAAA,SAAA,EAAA,MAAA,EAAA,GAViC,OAUjC,CAAA,IAAA,CAAA;EACA,SAAA,CAAA,EAAA,OAAA;;AAEC,iBATa,mBAAA,CASb;EAAA,MAAA;EAAA,OAAA;EAAA,OAAA;EAAA,QAAA;EAAA,SAAA;EAAA,UAAA;EAAA,QAAA;EAAA;AAAA,CAAA,EAAA,wBAAA,CAAA,EAAwB,kBAAA,CAAA,GAAA,CAAA,OAAA,GAAxB,IAAA"}
|