@contractspec/example.saas-boilerplate 3.7.5 → 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/CHANGELOG.md +16 -0
- 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 +14 -14
- 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
package/dist/browser/ui/index.js
CHANGED
|
@@ -345,8 +345,8 @@ function createSaasHandlers(db) {
|
|
|
345
345
|
};
|
|
346
346
|
}
|
|
347
347
|
// src/ui/hooks/useProjectList.ts
|
|
348
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
349
348
|
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
349
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
350
350
|
function useProjectList(options = {}) {
|
|
351
351
|
const { handlers, projectId } = useTemplateRuntime();
|
|
352
352
|
const { saas: saas2 } = handlers;
|
|
@@ -409,8 +409,8 @@ function useProjectList(options = {}) {
|
|
|
409
409
|
}
|
|
410
410
|
|
|
411
411
|
// src/ui/hooks/useProjectMutations.ts
|
|
412
|
-
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
413
412
|
import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
|
|
413
|
+
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
414
414
|
function useProjectMutations(options = {}) {
|
|
415
415
|
const { handlers, projectId } = useTemplateRuntime2();
|
|
416
416
|
const { saas: saas2 } = handlers;
|
|
@@ -497,9 +497,12 @@ function useProjectMutations(options = {}) {
|
|
|
497
497
|
};
|
|
498
498
|
}
|
|
499
499
|
|
|
500
|
+
// src/ui/hooks/index.ts
|
|
501
|
+
"use client";
|
|
502
|
+
|
|
500
503
|
// src/ui/modals/CreateProjectModal.tsx
|
|
501
|
-
import { useState as useState3 } from "react";
|
|
502
504
|
import { Button, Input } from "@contractspec/lib.design-system";
|
|
505
|
+
import { useState as useState3 } from "react";
|
|
503
506
|
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
504
507
|
"use client";
|
|
505
508
|
var TIERS = [
|
|
@@ -544,7 +547,7 @@ function CreateProjectModal({
|
|
|
544
547
|
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
545
548
|
children: [
|
|
546
549
|
/* @__PURE__ */ jsxDEV("div", {
|
|
547
|
-
className: "bg-background/80
|
|
550
|
+
className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
|
|
548
551
|
onClick: onClose,
|
|
549
552
|
role: "button",
|
|
550
553
|
tabIndex: 0,
|
|
@@ -555,10 +558,10 @@ function CreateProjectModal({
|
|
|
555
558
|
"aria-label": "Close modal"
|
|
556
559
|
}, undefined, false, undefined, this),
|
|
557
560
|
/* @__PURE__ */ jsxDEV("div", {
|
|
558
|
-
className: "
|
|
561
|
+
className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
|
|
559
562
|
children: [
|
|
560
563
|
/* @__PURE__ */ jsxDEV("h2", {
|
|
561
|
-
className: "mb-4 text-xl
|
|
564
|
+
className: "mb-4 font-semibold text-xl",
|
|
562
565
|
children: "Create New Project"
|
|
563
566
|
}, undefined, false, undefined, this),
|
|
564
567
|
/* @__PURE__ */ jsxDEV("form", {
|
|
@@ -569,7 +572,7 @@ function CreateProjectModal({
|
|
|
569
572
|
children: [
|
|
570
573
|
/* @__PURE__ */ jsxDEV("label", {
|
|
571
574
|
htmlFor: "project-name",
|
|
572
|
-
className: "
|
|
575
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
573
576
|
children: "Project Name *"
|
|
574
577
|
}, undefined, false, undefined, this),
|
|
575
578
|
/* @__PURE__ */ jsxDEV(Input, {
|
|
@@ -585,7 +588,7 @@ function CreateProjectModal({
|
|
|
585
588
|
children: [
|
|
586
589
|
/* @__PURE__ */ jsxDEV("label", {
|
|
587
590
|
htmlFor: "project-description",
|
|
588
|
-
className: "
|
|
591
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
589
592
|
children: "Description"
|
|
590
593
|
}, undefined, false, undefined, this),
|
|
591
594
|
/* @__PURE__ */ jsxDEV("textarea", {
|
|
@@ -595,7 +598,7 @@ function CreateProjectModal({
|
|
|
595
598
|
placeholder: "Describe what this project is about...",
|
|
596
599
|
rows: 3,
|
|
597
600
|
disabled: isLoading,
|
|
598
|
-
className: "
|
|
601
|
+
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"
|
|
599
602
|
}, undefined, false, undefined, this)
|
|
600
603
|
]
|
|
601
604
|
}, undefined, true, undefined, this),
|
|
@@ -603,7 +606,7 @@ function CreateProjectModal({
|
|
|
603
606
|
children: [
|
|
604
607
|
/* @__PURE__ */ jsxDEV("label", {
|
|
605
608
|
htmlFor: "project-tier",
|
|
606
|
-
className: "
|
|
609
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
607
610
|
children: "Tier"
|
|
608
611
|
}, undefined, false, undefined, this),
|
|
609
612
|
/* @__PURE__ */ jsxDEV("select", {
|
|
@@ -611,7 +614,7 @@ function CreateProjectModal({
|
|
|
611
614
|
value: tier,
|
|
612
615
|
onChange: (e) => setTier(e.target.value),
|
|
613
616
|
disabled: isLoading,
|
|
614
|
-
className: "
|
|
617
|
+
className: "h-10 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",
|
|
615
618
|
children: TIERS.map((t) => /* @__PURE__ */ jsxDEV("option", {
|
|
616
619
|
value: t.value,
|
|
617
620
|
children: t.label
|
|
@@ -620,7 +623,7 @@ function CreateProjectModal({
|
|
|
620
623
|
]
|
|
621
624
|
}, undefined, true, undefined, this),
|
|
622
625
|
error && /* @__PURE__ */ jsxDEV("div", {
|
|
623
|
-
className: "bg-destructive/10
|
|
626
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
624
627
|
children: error
|
|
625
628
|
}, undefined, false, undefined, this),
|
|
626
629
|
/* @__PURE__ */ jsxDEV("div", {
|
|
@@ -649,8 +652,8 @@ function CreateProjectModal({
|
|
|
649
652
|
}
|
|
650
653
|
|
|
651
654
|
// src/ui/modals/ProjectActionsModal.tsx
|
|
652
|
-
import { useEffect as useEffect2, useState as useState4 } from "react";
|
|
653
655
|
import { Button as Button2, Input as Input2 } from "@contractspec/lib.design-system";
|
|
656
|
+
import { useEffect as useEffect2, useState as useState4 } from "react";
|
|
654
657
|
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
655
658
|
"use client";
|
|
656
659
|
function ProjectActionsModal({
|
|
@@ -743,7 +746,7 @@ function ProjectActionsModal({
|
|
|
743
746
|
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
744
747
|
children: [
|
|
745
748
|
/* @__PURE__ */ jsxDEV2("div", {
|
|
746
|
-
className: "bg-background/80
|
|
749
|
+
className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
|
|
747
750
|
onClick: handleClose,
|
|
748
751
|
role: "button",
|
|
749
752
|
tabIndex: 0,
|
|
@@ -754,13 +757,13 @@ function ProjectActionsModal({
|
|
|
754
757
|
"aria-label": "Close modal"
|
|
755
758
|
}, undefined, false, undefined, this),
|
|
756
759
|
/* @__PURE__ */ jsxDEV2("div", {
|
|
757
|
-
className: "
|
|
760
|
+
className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
|
|
758
761
|
children: [
|
|
759
762
|
/* @__PURE__ */ jsxDEV2("div", {
|
|
760
|
-
className: "
|
|
763
|
+
className: "mb-4 border-border border-b pb-4",
|
|
761
764
|
children: [
|
|
762
765
|
/* @__PURE__ */ jsxDEV2("h2", {
|
|
763
|
-
className: "text-xl
|
|
766
|
+
className: "font-semibold text-xl",
|
|
764
767
|
children: project.name
|
|
765
768
|
}, undefined, false, undefined, this),
|
|
766
769
|
/* @__PURE__ */ jsxDEV2("p", {
|
|
@@ -842,7 +845,7 @@ function ProjectActionsModal({
|
|
|
842
845
|
children: [
|
|
843
846
|
/* @__PURE__ */ jsxDEV2("label", {
|
|
844
847
|
htmlFor: "edit-name",
|
|
845
|
-
className: "
|
|
848
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
846
849
|
children: "Project Name *"
|
|
847
850
|
}, undefined, false, undefined, this),
|
|
848
851
|
/* @__PURE__ */ jsxDEV2(Input2, {
|
|
@@ -857,7 +860,7 @@ function ProjectActionsModal({
|
|
|
857
860
|
children: [
|
|
858
861
|
/* @__PURE__ */ jsxDEV2("label", {
|
|
859
862
|
htmlFor: "edit-description",
|
|
860
|
-
className: "
|
|
863
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
861
864
|
children: "Description"
|
|
862
865
|
}, undefined, false, undefined, this),
|
|
863
866
|
/* @__PURE__ */ jsxDEV2("textarea", {
|
|
@@ -866,12 +869,12 @@ function ProjectActionsModal({
|
|
|
866
869
|
onChange: (e) => setDescription(e.target.value),
|
|
867
870
|
rows: 3,
|
|
868
871
|
disabled: isLoading,
|
|
869
|
-
className: "
|
|
872
|
+
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"
|
|
870
873
|
}, undefined, false, undefined, this)
|
|
871
874
|
]
|
|
872
875
|
}, undefined, true, undefined, this),
|
|
873
876
|
error && /* @__PURE__ */ jsxDEV2("div", {
|
|
874
|
-
className: "bg-destructive/10
|
|
877
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
875
878
|
children: error
|
|
876
879
|
}, undefined, false, undefined, this),
|
|
877
880
|
/* @__PURE__ */ jsxDEV2("div", {
|
|
@@ -901,7 +904,7 @@ function ProjectActionsModal({
|
|
|
901
904
|
"Are you sure you want to archive",
|
|
902
905
|
" ",
|
|
903
906
|
/* @__PURE__ */ jsxDEV2("span", {
|
|
904
|
-
className: "text-foreground
|
|
907
|
+
className: "font-medium text-foreground",
|
|
905
908
|
children: project.name
|
|
906
909
|
}, undefined, false, undefined, this),
|
|
907
910
|
"?"
|
|
@@ -912,7 +915,7 @@ function ProjectActionsModal({
|
|
|
912
915
|
children: "Archived projects can be restored later."
|
|
913
916
|
}, undefined, false, undefined, this),
|
|
914
917
|
error && /* @__PURE__ */ jsxDEV2("div", {
|
|
915
|
-
className: "bg-destructive/10
|
|
918
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
916
919
|
children: error
|
|
917
920
|
}, undefined, false, undefined, this),
|
|
918
921
|
/* @__PURE__ */ jsxDEV2("div", {
|
|
@@ -942,7 +945,7 @@ function ProjectActionsModal({
|
|
|
942
945
|
"Are you sure you want to delete",
|
|
943
946
|
" ",
|
|
944
947
|
/* @__PURE__ */ jsxDEV2("span", {
|
|
945
|
-
className: "text-foreground
|
|
948
|
+
className: "font-medium text-foreground",
|
|
946
949
|
children: project.name
|
|
947
950
|
}, undefined, false, undefined, this),
|
|
948
951
|
"?"
|
|
@@ -953,7 +956,7 @@ function ProjectActionsModal({
|
|
|
953
956
|
children: "This action cannot be undone."
|
|
954
957
|
}, undefined, false, undefined, this),
|
|
955
958
|
error && /* @__PURE__ */ jsxDEV2("div", {
|
|
956
|
-
className: "bg-destructive/10
|
|
959
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
957
960
|
children: error
|
|
958
961
|
}, undefined, false, undefined, this),
|
|
959
962
|
/* @__PURE__ */ jsxDEV2("div", {
|
|
@@ -980,18 +983,221 @@ function ProjectActionsModal({
|
|
|
980
983
|
]
|
|
981
984
|
}, undefined, true, undefined, this);
|
|
982
985
|
}
|
|
986
|
+
// src/ui/overlays/demo-overlays.ts
|
|
987
|
+
var saasFreeUserOverlay = {
|
|
988
|
+
overlayId: "saas-boilerplate.free-tier",
|
|
989
|
+
version: "1.0.0",
|
|
990
|
+
description: "Shows limitations for free tier users",
|
|
991
|
+
appliesTo: {
|
|
992
|
+
feature: "saas-boilerplate",
|
|
993
|
+
tier: "free"
|
|
994
|
+
},
|
|
995
|
+
modifications: [
|
|
996
|
+
{
|
|
997
|
+
type: "setLimit",
|
|
998
|
+
field: "projects",
|
|
999
|
+
max: 3,
|
|
1000
|
+
message: "Upgrade to create more projects"
|
|
1001
|
+
},
|
|
1002
|
+
{ type: "hideField", field: "advancedSettings", reason: "Pro feature" },
|
|
1003
|
+
{
|
|
1004
|
+
type: "addBadge",
|
|
1005
|
+
position: "header",
|
|
1006
|
+
label: "Free Plan",
|
|
1007
|
+
variant: "default"
|
|
1008
|
+
}
|
|
1009
|
+
]
|
|
1010
|
+
};
|
|
1011
|
+
var saasDemoOverlay = {
|
|
1012
|
+
overlayId: "saas-boilerplate.demo-user",
|
|
1013
|
+
version: "1.0.0",
|
|
1014
|
+
description: "Demo mode for SaaS boilerplate",
|
|
1015
|
+
appliesTo: {
|
|
1016
|
+
feature: "saas-boilerplate",
|
|
1017
|
+
role: "demo"
|
|
1018
|
+
},
|
|
1019
|
+
modifications: [
|
|
1020
|
+
{
|
|
1021
|
+
type: "hideField",
|
|
1022
|
+
field: "billingSection",
|
|
1023
|
+
reason: "Demo users cannot access billing"
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
type: "hideField",
|
|
1027
|
+
field: "deleteAccount",
|
|
1028
|
+
reason: "Not available in demo"
|
|
1029
|
+
},
|
|
1030
|
+
{
|
|
1031
|
+
type: "addBadge",
|
|
1032
|
+
position: "header",
|
|
1033
|
+
label: "Demo Mode",
|
|
1034
|
+
variant: "warning"
|
|
1035
|
+
}
|
|
1036
|
+
]
|
|
1037
|
+
};
|
|
1038
|
+
var saasOverlays = [
|
|
1039
|
+
saasFreeUserOverlay,
|
|
1040
|
+
saasDemoOverlay
|
|
1041
|
+
];
|
|
1042
|
+
// src/ui/renderers/project-list.markdown.ts
|
|
1043
|
+
var projectListMarkdownRenderer = {
|
|
1044
|
+
target: "markdown",
|
|
1045
|
+
render: async (desc, _ctx) => {
|
|
1046
|
+
if (desc.source.type !== "component" || desc.source.componentKey !== "ProjectListView") {
|
|
1047
|
+
throw new Error("projectListMarkdownRenderer: not ProjectListView");
|
|
1048
|
+
}
|
|
1049
|
+
const data = await mockListProjectsHandler({
|
|
1050
|
+
limit: 20,
|
|
1051
|
+
offset: 0
|
|
1052
|
+
});
|
|
1053
|
+
const items = data.projects ?? data.items ?? [];
|
|
1054
|
+
const lines = [
|
|
1055
|
+
"# Projects",
|
|
1056
|
+
"",
|
|
1057
|
+
`**Total**: ${data.total} projects`,
|
|
1058
|
+
""
|
|
1059
|
+
];
|
|
1060
|
+
if (items.length === 0) {
|
|
1061
|
+
lines.push("_No projects found._");
|
|
1062
|
+
} else {
|
|
1063
|
+
lines.push("| Status | Project | Description |");
|
|
1064
|
+
lines.push("|--------|---------|-------------|");
|
|
1065
|
+
for (const project of items) {
|
|
1066
|
+
const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
|
|
1067
|
+
lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
return {
|
|
1071
|
+
mimeType: "text/markdown",
|
|
1072
|
+
body: lines.join(`
|
|
1073
|
+
`)
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
var saasDashboardMarkdownRenderer = {
|
|
1078
|
+
target: "markdown",
|
|
1079
|
+
render: async (desc, _ctx) => {
|
|
1080
|
+
if (desc.source.type !== "component" || desc.source.componentKey !== "SaasDashboard") {
|
|
1081
|
+
throw new Error("saasDashboardMarkdownRenderer: not SaasDashboard");
|
|
1082
|
+
}
|
|
1083
|
+
const [projectsData, subscription] = await Promise.all([
|
|
1084
|
+
mockListProjectsHandler({ limit: 50 }),
|
|
1085
|
+
mockGetSubscriptionHandler()
|
|
1086
|
+
]);
|
|
1087
|
+
const projects = projectsData.projects ?? [];
|
|
1088
|
+
const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
|
|
1089
|
+
const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
|
|
1090
|
+
const lines = [
|
|
1091
|
+
"# SaaS Dashboard",
|
|
1092
|
+
"",
|
|
1093
|
+
"> Organization overview and usage summary",
|
|
1094
|
+
"",
|
|
1095
|
+
"## Summary",
|
|
1096
|
+
"",
|
|
1097
|
+
"| Metric | Value |",
|
|
1098
|
+
"|--------|-------|",
|
|
1099
|
+
`| Total Projects | ${projectsData.total} |`,
|
|
1100
|
+
`| Active Projects | ${activeProjects} |`,
|
|
1101
|
+
`| Archived Projects | ${archivedProjects} |`,
|
|
1102
|
+
`| Subscription Plan | ${subscription.planName} |`,
|
|
1103
|
+
`| Subscription Status | ${subscription.status} |`,
|
|
1104
|
+
"",
|
|
1105
|
+
"## Projects",
|
|
1106
|
+
""
|
|
1107
|
+
];
|
|
1108
|
+
if (projects.length === 0) {
|
|
1109
|
+
lines.push("_No projects yet._");
|
|
1110
|
+
} else {
|
|
1111
|
+
lines.push("| Status | Project | Description |");
|
|
1112
|
+
lines.push("|--------|---------|-------------|");
|
|
1113
|
+
for (const project of projects.slice(0, 10)) {
|
|
1114
|
+
const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
|
|
1115
|
+
lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
|
|
1116
|
+
}
|
|
1117
|
+
if (projects.length > 10) {
|
|
1118
|
+
lines.push(`| ... | ... | _${projectsData.total - 10} more projects_ |`);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
lines.push("");
|
|
1122
|
+
lines.push("## Subscription");
|
|
1123
|
+
lines.push("");
|
|
1124
|
+
lines.push(`- **Plan**: ${subscription.planName}`);
|
|
1125
|
+
lines.push(`- **Status**: ${subscription.status}`);
|
|
1126
|
+
if (subscription.currentPeriodEnd) {
|
|
1127
|
+
lines.push(`- **Period End**: ${new Date(subscription.currentPeriodEnd).toLocaleDateString()}`);
|
|
1128
|
+
}
|
|
1129
|
+
return {
|
|
1130
|
+
mimeType: "text/markdown",
|
|
1131
|
+
body: lines.join(`
|
|
1132
|
+
`)
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
};
|
|
1136
|
+
var saasBillingMarkdownRenderer = {
|
|
1137
|
+
target: "markdown",
|
|
1138
|
+
render: async (desc, _ctx) => {
|
|
1139
|
+
if (desc.source.type !== "component" || desc.source.componentKey !== "SubscriptionView") {
|
|
1140
|
+
throw new Error("saasBillingMarkdownRenderer: not SubscriptionView");
|
|
1141
|
+
}
|
|
1142
|
+
const subscription = await mockGetSubscriptionHandler();
|
|
1143
|
+
const lines = [
|
|
1144
|
+
"# Billing & Subscription",
|
|
1145
|
+
"",
|
|
1146
|
+
"> Current subscription details and billing information",
|
|
1147
|
+
"",
|
|
1148
|
+
"## Subscription Details",
|
|
1149
|
+
"",
|
|
1150
|
+
"| Property | Value |",
|
|
1151
|
+
"|----------|-------|",
|
|
1152
|
+
`| Plan | ${subscription.planName} |`,
|
|
1153
|
+
`| Status | ${subscription.status} |`,
|
|
1154
|
+
`| ID | ${subscription.id} |`,
|
|
1155
|
+
`| Period Start | ${new Date(subscription.currentPeriodStart).toLocaleDateString()} |`,
|
|
1156
|
+
`| Period End | ${new Date(subscription.currentPeriodEnd).toLocaleDateString()} |`
|
|
1157
|
+
];
|
|
1158
|
+
lines.push("");
|
|
1159
|
+
lines.push("## Plan Limits");
|
|
1160
|
+
lines.push("");
|
|
1161
|
+
lines.push(`- **Projects**: ${subscription.limits.projects}`);
|
|
1162
|
+
lines.push(`- **Users**: ${subscription.limits.users}`);
|
|
1163
|
+
lines.push("");
|
|
1164
|
+
lines.push("## Plan Features");
|
|
1165
|
+
lines.push("");
|
|
1166
|
+
if (subscription.planName.toLowerCase().includes("free")) {
|
|
1167
|
+
lines.push("- ✅ Up to 3 projects");
|
|
1168
|
+
lines.push("- ✅ Basic support");
|
|
1169
|
+
lines.push("- ❌ Priority support");
|
|
1170
|
+
lines.push("- ❌ Advanced analytics");
|
|
1171
|
+
} else if (subscription.planName.toLowerCase().includes("pro")) {
|
|
1172
|
+
lines.push("- ✅ Unlimited projects");
|
|
1173
|
+
lines.push("- ✅ Priority support");
|
|
1174
|
+
lines.push("- ✅ Advanced analytics");
|
|
1175
|
+
lines.push("- ❌ Custom integrations");
|
|
1176
|
+
} else {
|
|
1177
|
+
lines.push("- ✅ Unlimited projects");
|
|
1178
|
+
lines.push("- ✅ Priority support");
|
|
1179
|
+
lines.push("- ✅ Advanced analytics");
|
|
1180
|
+
lines.push("- ✅ Custom integrations");
|
|
1181
|
+
lines.push("- ✅ Dedicated support");
|
|
1182
|
+
}
|
|
1183
|
+
return {
|
|
1184
|
+
mimeType: "text/markdown",
|
|
1185
|
+
body: lines.join(`
|
|
1186
|
+
`)
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
};
|
|
983
1190
|
|
|
984
|
-
// src/ui/
|
|
985
|
-
import { useState as useState5, useCallback as useCallback3 } from "react";
|
|
1191
|
+
// src/ui/SaasProjectList.tsx
|
|
986
1192
|
import {
|
|
987
|
-
|
|
988
|
-
StatCardGroup,
|
|
989
|
-
StatusChip,
|
|
990
|
-
EntityCard,
|
|
1193
|
+
Button as Button3,
|
|
991
1194
|
EmptyState,
|
|
992
|
-
|
|
1195
|
+
EntityCard,
|
|
993
1196
|
ErrorState,
|
|
994
|
-
|
|
1197
|
+
LoaderBlock,
|
|
1198
|
+
StatCard,
|
|
1199
|
+
StatCardGroup,
|
|
1200
|
+
StatusChip
|
|
995
1201
|
} from "@contractspec/lib.design-system";
|
|
996
1202
|
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
997
1203
|
"use client";
|
|
@@ -1002,58 +1208,169 @@ function getStatusTone(status) {
|
|
|
1002
1208
|
case "DRAFT":
|
|
1003
1209
|
return "neutral";
|
|
1004
1210
|
case "ARCHIVED":
|
|
1005
|
-
return "
|
|
1211
|
+
return "danger";
|
|
1006
1212
|
default:
|
|
1007
1213
|
return "neutral";
|
|
1008
1214
|
}
|
|
1009
1215
|
}
|
|
1010
|
-
function
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
const
|
|
1015
|
-
const { data, subscription, loading, error, stats, refetch } = useProjectList();
|
|
1016
|
-
const mutations = useProjectMutations({
|
|
1017
|
-
onSuccess: () => {
|
|
1018
|
-
refetch();
|
|
1019
|
-
}
|
|
1020
|
-
});
|
|
1021
|
-
const handleProjectClick = useCallback3((project) => {
|
|
1022
|
-
setSelectedProject(project);
|
|
1023
|
-
setIsProjectActionsOpen(true);
|
|
1024
|
-
}, []);
|
|
1025
|
-
const tabs = [
|
|
1026
|
-
{ id: "projects", label: "Projects", icon: "\uD83D\uDCC1" },
|
|
1027
|
-
{ id: "billing", label: "Billing", icon: "\uD83D\uDCB3" },
|
|
1028
|
-
{ id: "settings", label: "Settings", icon: "⚙️" }
|
|
1029
|
-
];
|
|
1216
|
+
function SaasProjectList({
|
|
1217
|
+
onProjectClick,
|
|
1218
|
+
onCreateProject
|
|
1219
|
+
}) {
|
|
1220
|
+
const { data, loading, error, stats, refetch } = useProjectList();
|
|
1030
1221
|
if (loading && !data) {
|
|
1031
1222
|
return /* @__PURE__ */ jsxDEV3(LoaderBlock, {
|
|
1032
|
-
label: "Loading
|
|
1223
|
+
label: "Loading projects..."
|
|
1033
1224
|
}, undefined, false, undefined, this);
|
|
1034
1225
|
}
|
|
1035
1226
|
if (error) {
|
|
1036
1227
|
return /* @__PURE__ */ jsxDEV3(ErrorState, {
|
|
1037
|
-
title: "Failed to load
|
|
1228
|
+
title: "Failed to load projects",
|
|
1038
1229
|
description: error.message,
|
|
1039
1230
|
onRetry: refetch,
|
|
1040
1231
|
retryLabel: "Retry"
|
|
1041
1232
|
}, undefined, false, undefined, this);
|
|
1042
1233
|
}
|
|
1234
|
+
if (!data?.items.length) {
|
|
1235
|
+
return /* @__PURE__ */ jsxDEV3(EmptyState, {
|
|
1236
|
+
title: "No projects found",
|
|
1237
|
+
description: "Create your first project to get started.",
|
|
1238
|
+
primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV3(Button3, {
|
|
1239
|
+
onPress: onCreateProject,
|
|
1240
|
+
children: "Create Project"
|
|
1241
|
+
}, undefined, false, undefined, this) : undefined
|
|
1242
|
+
}, undefined, false, undefined, this);
|
|
1243
|
+
}
|
|
1043
1244
|
return /* @__PURE__ */ jsxDEV3("div", {
|
|
1044
1245
|
className: "space-y-6",
|
|
1045
1246
|
children: [
|
|
1247
|
+
stats && /* @__PURE__ */ jsxDEV3(StatCardGroup, {
|
|
1248
|
+
children: [
|
|
1249
|
+
/* @__PURE__ */ jsxDEV3(StatCard, {
|
|
1250
|
+
label: "Total Projects",
|
|
1251
|
+
value: stats.total.toString()
|
|
1252
|
+
}, undefined, false, undefined, this),
|
|
1253
|
+
/* @__PURE__ */ jsxDEV3(StatCard, {
|
|
1254
|
+
label: "Active",
|
|
1255
|
+
value: stats.activeCount.toString()
|
|
1256
|
+
}, undefined, false, undefined, this),
|
|
1257
|
+
/* @__PURE__ */ jsxDEV3(StatCard, {
|
|
1258
|
+
label: "Draft",
|
|
1259
|
+
value: stats.draftCount.toString()
|
|
1260
|
+
}, undefined, false, undefined, this)
|
|
1261
|
+
]
|
|
1262
|
+
}, undefined, true, undefined, this),
|
|
1046
1263
|
/* @__PURE__ */ jsxDEV3("div", {
|
|
1264
|
+
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
|
|
1265
|
+
children: data.items.map((project) => /* @__PURE__ */ jsxDEV3(EntityCard, {
|
|
1266
|
+
cardTitle: project.name,
|
|
1267
|
+
cardSubtitle: project.tier,
|
|
1268
|
+
meta: /* @__PURE__ */ jsxDEV3("p", {
|
|
1269
|
+
className: "text-muted-foreground text-sm",
|
|
1270
|
+
children: project.description
|
|
1271
|
+
}, undefined, false, undefined, this),
|
|
1272
|
+
chips: /* @__PURE__ */ jsxDEV3(StatusChip, {
|
|
1273
|
+
tone: getStatusTone(project.status),
|
|
1274
|
+
label: project.status
|
|
1275
|
+
}, undefined, false, undefined, this),
|
|
1276
|
+
footer: /* @__PURE__ */ jsxDEV3("span", {
|
|
1277
|
+
className: "text-muted-foreground text-xs",
|
|
1278
|
+
children: project.updatedAt.toLocaleDateString()
|
|
1279
|
+
}, undefined, false, undefined, this),
|
|
1280
|
+
onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
|
|
1281
|
+
}, project.id, false, undefined, this))
|
|
1282
|
+
}, undefined, false, undefined, this)
|
|
1283
|
+
]
|
|
1284
|
+
}, undefined, true, undefined, this);
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
// src/ui/renderers/project-list.renderer.tsx
|
|
1288
|
+
import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
|
|
1289
|
+
var projectListReactRenderer = {
|
|
1290
|
+
target: "react",
|
|
1291
|
+
render: async (desc, _ctx) => {
|
|
1292
|
+
if (desc.source.type !== "component") {
|
|
1293
|
+
throw new Error("Invalid source type");
|
|
1294
|
+
}
|
|
1295
|
+
if (desc.source.componentKey !== "SaasProjectListView") {
|
|
1296
|
+
throw new Error(`Unknown component: ${desc.source.componentKey}`);
|
|
1297
|
+
}
|
|
1298
|
+
return /* @__PURE__ */ jsxDEV4(SaasProjectList, {}, undefined, false, undefined, this);
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
// src/ui/SaasDashboard.tsx
|
|
1302
|
+
import {
|
|
1303
|
+
Button as Button4,
|
|
1304
|
+
EmptyState as EmptyState2,
|
|
1305
|
+
EntityCard as EntityCard2,
|
|
1306
|
+
ErrorState as ErrorState2,
|
|
1307
|
+
LoaderBlock as LoaderBlock2,
|
|
1308
|
+
StatCard as StatCard2,
|
|
1309
|
+
StatCardGroup as StatCardGroup2,
|
|
1310
|
+
StatusChip as StatusChip2
|
|
1311
|
+
} from "@contractspec/lib.design-system";
|
|
1312
|
+
import { useCallback as useCallback3, useState as useState5 } from "react";
|
|
1313
|
+
import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
|
|
1314
|
+
"use client";
|
|
1315
|
+
function getStatusTone2(status) {
|
|
1316
|
+
switch (status) {
|
|
1317
|
+
case "ACTIVE":
|
|
1318
|
+
return "success";
|
|
1319
|
+
case "DRAFT":
|
|
1320
|
+
return "neutral";
|
|
1321
|
+
case "ARCHIVED":
|
|
1322
|
+
return "warning";
|
|
1323
|
+
default:
|
|
1324
|
+
return "neutral";
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
function SaasDashboard() {
|
|
1328
|
+
const [activeTab, setActiveTab] = useState5("projects");
|
|
1329
|
+
const [isCreateModalOpen, setIsCreateModalOpen] = useState5(false);
|
|
1330
|
+
const [selectedProject, setSelectedProject] = useState5(null);
|
|
1331
|
+
const [isProjectActionsOpen, setIsProjectActionsOpen] = useState5(false);
|
|
1332
|
+
const { data, subscription, loading, error, stats, refetch } = useProjectList();
|
|
1333
|
+
const mutations = useProjectMutations({
|
|
1334
|
+
onSuccess: () => {
|
|
1335
|
+
refetch();
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
const handleProjectClick = useCallback3((project) => {
|
|
1339
|
+
setSelectedProject(project);
|
|
1340
|
+
setIsProjectActionsOpen(true);
|
|
1341
|
+
}, []);
|
|
1342
|
+
const tabs = [
|
|
1343
|
+
{ id: "projects", label: "Projects", icon: "\uD83D\uDCC1" },
|
|
1344
|
+
{ id: "billing", label: "Billing", icon: "\uD83D\uDCB3" },
|
|
1345
|
+
{ id: "settings", label: "Settings", icon: "⚙️" }
|
|
1346
|
+
];
|
|
1347
|
+
if (loading && !data) {
|
|
1348
|
+
return /* @__PURE__ */ jsxDEV5(LoaderBlock2, {
|
|
1349
|
+
label: "Loading dashboard..."
|
|
1350
|
+
}, undefined, false, undefined, this);
|
|
1351
|
+
}
|
|
1352
|
+
if (error) {
|
|
1353
|
+
return /* @__PURE__ */ jsxDEV5(ErrorState2, {
|
|
1354
|
+
title: "Failed to load dashboard",
|
|
1355
|
+
description: error.message,
|
|
1356
|
+
onRetry: refetch,
|
|
1357
|
+
retryLabel: "Retry"
|
|
1358
|
+
}, undefined, false, undefined, this);
|
|
1359
|
+
}
|
|
1360
|
+
return /* @__PURE__ */ jsxDEV5("div", {
|
|
1361
|
+
className: "space-y-6",
|
|
1362
|
+
children: [
|
|
1363
|
+
/* @__PURE__ */ jsxDEV5("div", {
|
|
1047
1364
|
className: "flex items-center justify-between",
|
|
1048
1365
|
children: [
|
|
1049
|
-
/* @__PURE__ */
|
|
1050
|
-
className: "text-2xl
|
|
1366
|
+
/* @__PURE__ */ jsxDEV5("h2", {
|
|
1367
|
+
className: "font-bold text-2xl",
|
|
1051
1368
|
children: "SaaS Dashboard"
|
|
1052
1369
|
}, undefined, false, undefined, this),
|
|
1053
|
-
activeTab === "projects" && /* @__PURE__ */
|
|
1370
|
+
activeTab === "projects" && /* @__PURE__ */ jsxDEV5(Button4, {
|
|
1054
1371
|
onPress: () => setIsCreateModalOpen(true),
|
|
1055
1372
|
children: [
|
|
1056
|
-
/* @__PURE__ */
|
|
1373
|
+
/* @__PURE__ */ jsxDEV5("span", {
|
|
1057
1374
|
className: "mr-2",
|
|
1058
1375
|
children: "+"
|
|
1059
1376
|
}, undefined, false, undefined, this),
|
|
@@ -1062,59 +1379,59 @@ function SaasDashboard() {
|
|
|
1062
1379
|
}, undefined, true, undefined, this)
|
|
1063
1380
|
]
|
|
1064
1381
|
}, undefined, true, undefined, this),
|
|
1065
|
-
stats && subscription && /* @__PURE__ */
|
|
1382
|
+
stats && subscription && /* @__PURE__ */ jsxDEV5(StatCardGroup2, {
|
|
1066
1383
|
children: [
|
|
1067
|
-
/* @__PURE__ */
|
|
1384
|
+
/* @__PURE__ */ jsxDEV5(StatCard2, {
|
|
1068
1385
|
label: "Projects",
|
|
1069
1386
|
value: stats.total.toString()
|
|
1070
1387
|
}, undefined, false, undefined, this),
|
|
1071
|
-
/* @__PURE__ */
|
|
1388
|
+
/* @__PURE__ */ jsxDEV5(StatCard2, {
|
|
1072
1389
|
label: "Active",
|
|
1073
1390
|
value: stats.activeCount.toString()
|
|
1074
1391
|
}, undefined, false, undefined, this),
|
|
1075
|
-
/* @__PURE__ */
|
|
1392
|
+
/* @__PURE__ */ jsxDEV5(StatCard2, {
|
|
1076
1393
|
label: "Draft",
|
|
1077
1394
|
value: stats.draftCount.toString()
|
|
1078
1395
|
}, undefined, false, undefined, this),
|
|
1079
|
-
/* @__PURE__ */
|
|
1396
|
+
/* @__PURE__ */ jsxDEV5(StatCard2, {
|
|
1080
1397
|
label: "Plan",
|
|
1081
1398
|
value: subscription.plan,
|
|
1082
1399
|
hint: subscription.status
|
|
1083
1400
|
}, undefined, false, undefined, this)
|
|
1084
1401
|
]
|
|
1085
1402
|
}, undefined, true, undefined, this),
|
|
1086
|
-
/* @__PURE__ */
|
|
1087
|
-
className: "
|
|
1403
|
+
/* @__PURE__ */ jsxDEV5("nav", {
|
|
1404
|
+
className: "flex gap-1 rounded-lg bg-muted p-1",
|
|
1088
1405
|
role: "tablist",
|
|
1089
|
-
children: tabs.map((tab) => /* @__PURE__ */
|
|
1406
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsxDEV5("button", {
|
|
1090
1407
|
type: "button",
|
|
1091
1408
|
role: "tab",
|
|
1092
1409
|
"aria-selected": activeTab === tab.id,
|
|
1093
1410
|
onClick: () => setActiveTab(tab.id),
|
|
1094
|
-
className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 text-sm
|
|
1411
|
+
className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 font-medium text-sm transition-colors ${activeTab === tab.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
|
|
1095
1412
|
children: [
|
|
1096
|
-
/* @__PURE__ */
|
|
1413
|
+
/* @__PURE__ */ jsxDEV5("span", {
|
|
1097
1414
|
children: tab.icon
|
|
1098
1415
|
}, undefined, false, undefined, this),
|
|
1099
1416
|
tab.label
|
|
1100
1417
|
]
|
|
1101
1418
|
}, tab.id, true, undefined, this))
|
|
1102
1419
|
}, undefined, false, undefined, this),
|
|
1103
|
-
/* @__PURE__ */
|
|
1420
|
+
/* @__PURE__ */ jsxDEV5("div", {
|
|
1104
1421
|
className: "min-h-[400px]",
|
|
1105
1422
|
role: "tabpanel",
|
|
1106
1423
|
children: [
|
|
1107
|
-
activeTab === "projects" && /* @__PURE__ */
|
|
1424
|
+
activeTab === "projects" && /* @__PURE__ */ jsxDEV5(ProjectsTab, {
|
|
1108
1425
|
data,
|
|
1109
1426
|
onProjectClick: handleProjectClick
|
|
1110
1427
|
}, undefined, false, undefined, this),
|
|
1111
|
-
activeTab === "billing" && /* @__PURE__ */
|
|
1428
|
+
activeTab === "billing" && /* @__PURE__ */ jsxDEV5(BillingTab, {
|
|
1112
1429
|
subscription
|
|
1113
1430
|
}, undefined, false, undefined, this),
|
|
1114
|
-
activeTab === "settings" && /* @__PURE__ */
|
|
1431
|
+
activeTab === "settings" && /* @__PURE__ */ jsxDEV5(SettingsTab, {}, undefined, false, undefined, this)
|
|
1115
1432
|
]
|
|
1116
1433
|
}, undefined, true, undefined, this),
|
|
1117
|
-
/* @__PURE__ */
|
|
1434
|
+
/* @__PURE__ */ jsxDEV5(CreateProjectModal, {
|
|
1118
1435
|
isOpen: isCreateModalOpen,
|
|
1119
1436
|
onClose: () => setIsCreateModalOpen(false),
|
|
1120
1437
|
onSubmit: async (input) => {
|
|
@@ -1122,7 +1439,7 @@ function SaasDashboard() {
|
|
|
1122
1439
|
},
|
|
1123
1440
|
isLoading: mutations.createState.loading
|
|
1124
1441
|
}, undefined, false, undefined, this),
|
|
1125
|
-
/* @__PURE__ */
|
|
1442
|
+
/* @__PURE__ */ jsxDEV5(ProjectActionsModal, {
|
|
1126
1443
|
isOpen: isProjectActionsOpen,
|
|
1127
1444
|
project: selectedProject,
|
|
1128
1445
|
onClose: () => {
|
|
@@ -1148,34 +1465,34 @@ function SaasDashboard() {
|
|
|
1148
1465
|
}
|
|
1149
1466
|
function ProjectsTab({ data, onProjectClick }) {
|
|
1150
1467
|
if (!data?.items.length) {
|
|
1151
|
-
return /* @__PURE__ */
|
|
1468
|
+
return /* @__PURE__ */ jsxDEV5(EmptyState2, {
|
|
1152
1469
|
title: "No projects yet",
|
|
1153
1470
|
description: "Create your first project to get started."
|
|
1154
1471
|
}, undefined, false, undefined, this);
|
|
1155
1472
|
}
|
|
1156
|
-
return /* @__PURE__ */
|
|
1473
|
+
return /* @__PURE__ */ jsxDEV5("div", {
|
|
1157
1474
|
className: "space-y-4",
|
|
1158
|
-
children: /* @__PURE__ */
|
|
1475
|
+
children: /* @__PURE__ */ jsxDEV5("div", {
|
|
1159
1476
|
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
|
|
1160
|
-
children: data.items.map((project) => /* @__PURE__ */
|
|
1477
|
+
children: data.items.map((project) => /* @__PURE__ */ jsxDEV5(EntityCard2, {
|
|
1161
1478
|
cardTitle: project.name,
|
|
1162
1479
|
cardSubtitle: project.tier,
|
|
1163
|
-
meta: /* @__PURE__ */
|
|
1480
|
+
meta: /* @__PURE__ */ jsxDEV5("p", {
|
|
1164
1481
|
className: "text-muted-foreground text-sm",
|
|
1165
1482
|
children: project.description
|
|
1166
1483
|
}, undefined, false, undefined, this),
|
|
1167
|
-
chips: /* @__PURE__ */
|
|
1168
|
-
tone:
|
|
1484
|
+
chips: /* @__PURE__ */ jsxDEV5(StatusChip2, {
|
|
1485
|
+
tone: getStatusTone2(project.status),
|
|
1169
1486
|
label: project.status
|
|
1170
1487
|
}, undefined, false, undefined, this),
|
|
1171
|
-
footer: /* @__PURE__ */
|
|
1488
|
+
footer: /* @__PURE__ */ jsxDEV5("div", {
|
|
1172
1489
|
className: "flex w-full items-center justify-between",
|
|
1173
1490
|
children: [
|
|
1174
|
-
/* @__PURE__ */
|
|
1491
|
+
/* @__PURE__ */ jsxDEV5("span", {
|
|
1175
1492
|
className: "text-muted-foreground text-xs",
|
|
1176
1493
|
children: project.updatedAt.toLocaleDateString()
|
|
1177
1494
|
}, undefined, false, undefined, this),
|
|
1178
|
-
/* @__PURE__ */
|
|
1495
|
+
/* @__PURE__ */ jsxDEV5(Button4, {
|
|
1179
1496
|
variant: "ghost",
|
|
1180
1497
|
size: "sm",
|
|
1181
1498
|
onPress: () => onProjectClick?.(project),
|
|
@@ -1190,25 +1507,25 @@ function ProjectsTab({ data, onProjectClick }) {
|
|
|
1190
1507
|
function BillingTab({ subscription }) {
|
|
1191
1508
|
if (!subscription)
|
|
1192
1509
|
return null;
|
|
1193
|
-
return /* @__PURE__ */
|
|
1510
|
+
return /* @__PURE__ */ jsxDEV5("div", {
|
|
1194
1511
|
className: "space-y-6",
|
|
1195
1512
|
children: [
|
|
1196
|
-
/* @__PURE__ */
|
|
1197
|
-
className: "border-border bg-card
|
|
1513
|
+
/* @__PURE__ */ jsxDEV5("div", {
|
|
1514
|
+
className: "rounded-xl border border-border bg-card p-6",
|
|
1198
1515
|
children: [
|
|
1199
|
-
/* @__PURE__ */
|
|
1516
|
+
/* @__PURE__ */ jsxDEV5("div", {
|
|
1200
1517
|
className: "flex items-start justify-between",
|
|
1201
1518
|
children: [
|
|
1202
|
-
/* @__PURE__ */
|
|
1519
|
+
/* @__PURE__ */ jsxDEV5("div", {
|
|
1203
1520
|
children: [
|
|
1204
|
-
/* @__PURE__ */
|
|
1205
|
-
className: "text-lg
|
|
1521
|
+
/* @__PURE__ */ jsxDEV5("h3", {
|
|
1522
|
+
className: "font-semibold text-lg",
|
|
1206
1523
|
children: [
|
|
1207
1524
|
subscription.plan,
|
|
1208
1525
|
" Plan"
|
|
1209
1526
|
]
|
|
1210
1527
|
}, undefined, true, undefined, this),
|
|
1211
|
-
/* @__PURE__ */
|
|
1528
|
+
/* @__PURE__ */ jsxDEV5("p", {
|
|
1212
1529
|
className: "text-muted-foreground text-sm",
|
|
1213
1530
|
children: [
|
|
1214
1531
|
"Current period:",
|
|
@@ -1219,7 +1536,7 @@ function BillingTab({ subscription }) {
|
|
|
1219
1536
|
subscription.currentPeriodEnd.toLocaleDateString()
|
|
1220
1537
|
]
|
|
1221
1538
|
}, undefined, true, undefined, this),
|
|
1222
|
-
/* @__PURE__ */
|
|
1539
|
+
/* @__PURE__ */ jsxDEV5("p", {
|
|
1223
1540
|
className: "text-muted-foreground text-sm",
|
|
1224
1541
|
children: [
|
|
1225
1542
|
"Billing cycle: ",
|
|
@@ -1228,21 +1545,21 @@ function BillingTab({ subscription }) {
|
|
|
1228
1545
|
}, undefined, true, undefined, this)
|
|
1229
1546
|
]
|
|
1230
1547
|
}, undefined, true, undefined, this),
|
|
1231
|
-
/* @__PURE__ */
|
|
1548
|
+
/* @__PURE__ */ jsxDEV5(StatusChip2, {
|
|
1232
1549
|
tone: "success",
|
|
1233
1550
|
label: subscription.status
|
|
1234
1551
|
}, undefined, false, undefined, this)
|
|
1235
1552
|
]
|
|
1236
1553
|
}, undefined, true, undefined, this),
|
|
1237
|
-
/* @__PURE__ */
|
|
1554
|
+
/* @__PURE__ */ jsxDEV5("div", {
|
|
1238
1555
|
className: "mt-4 flex gap-3",
|
|
1239
1556
|
children: [
|
|
1240
|
-
/* @__PURE__ */
|
|
1557
|
+
/* @__PURE__ */ jsxDEV5(Button4, {
|
|
1241
1558
|
variant: "outline",
|
|
1242
1559
|
onPress: () => alert("Upgrade clicked!"),
|
|
1243
1560
|
children: "Upgrade Plan"
|
|
1244
1561
|
}, undefined, false, undefined, this),
|
|
1245
|
-
/* @__PURE__ */
|
|
1562
|
+
/* @__PURE__ */ jsxDEV5(Button4, {
|
|
1246
1563
|
variant: "ghost",
|
|
1247
1564
|
onPress: () => alert("Manage Billing clicked!"),
|
|
1248
1565
|
children: "Manage Billing"
|
|
@@ -1251,10 +1568,10 @@ function BillingTab({ subscription }) {
|
|
|
1251
1568
|
}, undefined, true, undefined, this)
|
|
1252
1569
|
]
|
|
1253
1570
|
}, undefined, true, undefined, this),
|
|
1254
|
-
subscription.cancelAtPeriodEnd && /* @__PURE__ */
|
|
1255
|
-
className: "border-border bg-destructive/10 text-destructive
|
|
1256
|
-
children: /* @__PURE__ */
|
|
1257
|
-
className: "text-sm
|
|
1571
|
+
subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsxDEV5("div", {
|
|
1572
|
+
className: "rounded-xl border border-border bg-destructive/10 p-4 text-destructive",
|
|
1573
|
+
children: /* @__PURE__ */ jsxDEV5("p", {
|
|
1574
|
+
className: "font-medium text-sm",
|
|
1258
1575
|
children: "⚠️ Your subscription will be cancelled at the end of the current period."
|
|
1259
1576
|
}, undefined, false, undefined, this)
|
|
1260
1577
|
}, undefined, false, undefined, this)
|
|
@@ -1262,63 +1579,63 @@ function BillingTab({ subscription }) {
|
|
|
1262
1579
|
}, undefined, true, undefined, this);
|
|
1263
1580
|
}
|
|
1264
1581
|
function SettingsTab() {
|
|
1265
|
-
return /* @__PURE__ */
|
|
1582
|
+
return /* @__PURE__ */ jsxDEV5("div", {
|
|
1266
1583
|
className: "space-y-6",
|
|
1267
|
-
children: /* @__PURE__ */
|
|
1268
|
-
className: "border-border bg-card
|
|
1584
|
+
children: /* @__PURE__ */ jsxDEV5("div", {
|
|
1585
|
+
className: "rounded-xl border border-border bg-card p-6",
|
|
1269
1586
|
children: [
|
|
1270
|
-
/* @__PURE__ */
|
|
1271
|
-
className: "mb-4 text-lg
|
|
1587
|
+
/* @__PURE__ */ jsxDEV5("h3", {
|
|
1588
|
+
className: "mb-4 font-semibold text-lg",
|
|
1272
1589
|
children: "Organization Settings"
|
|
1273
1590
|
}, undefined, false, undefined, this),
|
|
1274
|
-
/* @__PURE__ */
|
|
1591
|
+
/* @__PURE__ */ jsxDEV5("div", {
|
|
1275
1592
|
className: "space-y-4",
|
|
1276
1593
|
children: [
|
|
1277
|
-
/* @__PURE__ */
|
|
1594
|
+
/* @__PURE__ */ jsxDEV5("div", {
|
|
1278
1595
|
children: [
|
|
1279
|
-
/* @__PURE__ */
|
|
1596
|
+
/* @__PURE__ */ jsxDEV5("label", {
|
|
1280
1597
|
htmlFor: "org-name",
|
|
1281
|
-
className: "text-sm
|
|
1598
|
+
className: "font-medium text-sm",
|
|
1282
1599
|
children: "Organization Name"
|
|
1283
1600
|
}, undefined, false, undefined, this),
|
|
1284
|
-
/* @__PURE__ */
|
|
1601
|
+
/* @__PURE__ */ jsxDEV5("input", {
|
|
1285
1602
|
id: "org-name",
|
|
1286
1603
|
type: "text",
|
|
1287
1604
|
defaultValue: "Demo Organization",
|
|
1288
|
-
className: "
|
|
1605
|
+
className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2"
|
|
1289
1606
|
}, undefined, false, undefined, this)
|
|
1290
1607
|
]
|
|
1291
1608
|
}, undefined, true, undefined, this),
|
|
1292
|
-
/* @__PURE__ */
|
|
1609
|
+
/* @__PURE__ */ jsxDEV5("div", {
|
|
1293
1610
|
children: [
|
|
1294
|
-
/* @__PURE__ */
|
|
1611
|
+
/* @__PURE__ */ jsxDEV5("label", {
|
|
1295
1612
|
htmlFor: "timezone",
|
|
1296
|
-
className: "text-sm
|
|
1613
|
+
className: "font-medium text-sm",
|
|
1297
1614
|
children: "Default Timezone"
|
|
1298
1615
|
}, undefined, false, undefined, this),
|
|
1299
|
-
/* @__PURE__ */
|
|
1616
|
+
/* @__PURE__ */ jsxDEV5("select", {
|
|
1300
1617
|
id: "timezone",
|
|
1301
|
-
className: "
|
|
1618
|
+
className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2",
|
|
1302
1619
|
children: [
|
|
1303
|
-
/* @__PURE__ */
|
|
1620
|
+
/* @__PURE__ */ jsxDEV5("option", {
|
|
1304
1621
|
children: "UTC"
|
|
1305
1622
|
}, undefined, false, undefined, this),
|
|
1306
|
-
/* @__PURE__ */
|
|
1623
|
+
/* @__PURE__ */ jsxDEV5("option", {
|
|
1307
1624
|
children: "America/New_York"
|
|
1308
1625
|
}, undefined, false, undefined, this),
|
|
1309
|
-
/* @__PURE__ */
|
|
1626
|
+
/* @__PURE__ */ jsxDEV5("option", {
|
|
1310
1627
|
children: "Europe/London"
|
|
1311
1628
|
}, undefined, false, undefined, this),
|
|
1312
|
-
/* @__PURE__ */
|
|
1629
|
+
/* @__PURE__ */ jsxDEV5("option", {
|
|
1313
1630
|
children: "Asia/Tokyo"
|
|
1314
1631
|
}, undefined, false, undefined, this)
|
|
1315
1632
|
]
|
|
1316
1633
|
}, undefined, true, undefined, this)
|
|
1317
1634
|
]
|
|
1318
1635
|
}, undefined, true, undefined, this),
|
|
1319
|
-
/* @__PURE__ */
|
|
1636
|
+
/* @__PURE__ */ jsxDEV5("div", {
|
|
1320
1637
|
className: "pt-2",
|
|
1321
|
-
children: /* @__PURE__ */
|
|
1638
|
+
children: /* @__PURE__ */ jsxDEV5(Button4, {
|
|
1322
1639
|
onPress: () => alert("Settings saved!"),
|
|
1323
1640
|
children: "Save Settings"
|
|
1324
1641
|
}, undefined, false, undefined, this)
|
|
@@ -1330,165 +1647,69 @@ function SettingsTab() {
|
|
|
1330
1647
|
}, undefined, false, undefined, this);
|
|
1331
1648
|
}
|
|
1332
1649
|
|
|
1333
|
-
// src/ui/SaasProjectList.tsx
|
|
1334
|
-
import {
|
|
1335
|
-
StatCard as StatCard2,
|
|
1336
|
-
StatCardGroup as StatCardGroup2,
|
|
1337
|
-
StatusChip as StatusChip2,
|
|
1338
|
-
EntityCard as EntityCard2,
|
|
1339
|
-
EmptyState as EmptyState2,
|
|
1340
|
-
LoaderBlock as LoaderBlock2,
|
|
1341
|
-
ErrorState as ErrorState2,
|
|
1342
|
-
Button as Button4
|
|
1343
|
-
} from "@contractspec/lib.design-system";
|
|
1344
|
-
import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
|
|
1345
|
-
"use client";
|
|
1346
|
-
function getStatusTone2(status) {
|
|
1347
|
-
switch (status) {
|
|
1348
|
-
case "ACTIVE":
|
|
1349
|
-
return "success";
|
|
1350
|
-
case "DRAFT":
|
|
1351
|
-
return "neutral";
|
|
1352
|
-
case "ARCHIVED":
|
|
1353
|
-
return "danger";
|
|
1354
|
-
default:
|
|
1355
|
-
return "neutral";
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
function SaasProjectList({
|
|
1359
|
-
onProjectClick,
|
|
1360
|
-
onCreateProject
|
|
1361
|
-
}) {
|
|
1362
|
-
const { data, loading, error, stats, refetch } = useProjectList();
|
|
1363
|
-
if (loading && !data) {
|
|
1364
|
-
return /* @__PURE__ */ jsxDEV4(LoaderBlock2, {
|
|
1365
|
-
label: "Loading projects..."
|
|
1366
|
-
}, undefined, false, undefined, this);
|
|
1367
|
-
}
|
|
1368
|
-
if (error) {
|
|
1369
|
-
return /* @__PURE__ */ jsxDEV4(ErrorState2, {
|
|
1370
|
-
title: "Failed to load projects",
|
|
1371
|
-
description: error.message,
|
|
1372
|
-
onRetry: refetch,
|
|
1373
|
-
retryLabel: "Retry"
|
|
1374
|
-
}, undefined, false, undefined, this);
|
|
1375
|
-
}
|
|
1376
|
-
if (!data?.items.length) {
|
|
1377
|
-
return /* @__PURE__ */ jsxDEV4(EmptyState2, {
|
|
1378
|
-
title: "No projects found",
|
|
1379
|
-
description: "Create your first project to get started.",
|
|
1380
|
-
primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV4(Button4, {
|
|
1381
|
-
onPress: onCreateProject,
|
|
1382
|
-
children: "Create Project"
|
|
1383
|
-
}, undefined, false, undefined, this) : undefined
|
|
1384
|
-
}, undefined, false, undefined, this);
|
|
1385
|
-
}
|
|
1386
|
-
return /* @__PURE__ */ jsxDEV4("div", {
|
|
1387
|
-
className: "space-y-6",
|
|
1388
|
-
children: [
|
|
1389
|
-
stats && /* @__PURE__ */ jsxDEV4(StatCardGroup2, {
|
|
1390
|
-
children: [
|
|
1391
|
-
/* @__PURE__ */ jsxDEV4(StatCard2, {
|
|
1392
|
-
label: "Total Projects",
|
|
1393
|
-
value: stats.total.toString()
|
|
1394
|
-
}, undefined, false, undefined, this),
|
|
1395
|
-
/* @__PURE__ */ jsxDEV4(StatCard2, {
|
|
1396
|
-
label: "Active",
|
|
1397
|
-
value: stats.activeCount.toString()
|
|
1398
|
-
}, undefined, false, undefined, this),
|
|
1399
|
-
/* @__PURE__ */ jsxDEV4(StatCard2, {
|
|
1400
|
-
label: "Draft",
|
|
1401
|
-
value: stats.draftCount.toString()
|
|
1402
|
-
}, undefined, false, undefined, this)
|
|
1403
|
-
]
|
|
1404
|
-
}, undefined, true, undefined, this),
|
|
1405
|
-
/* @__PURE__ */ jsxDEV4("div", {
|
|
1406
|
-
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
|
|
1407
|
-
children: data.items.map((project) => /* @__PURE__ */ jsxDEV4(EntityCard2, {
|
|
1408
|
-
cardTitle: project.name,
|
|
1409
|
-
cardSubtitle: project.tier,
|
|
1410
|
-
meta: /* @__PURE__ */ jsxDEV4("p", {
|
|
1411
|
-
className: "text-muted-foreground text-sm",
|
|
1412
|
-
children: project.description
|
|
1413
|
-
}, undefined, false, undefined, this),
|
|
1414
|
-
chips: /* @__PURE__ */ jsxDEV4(StatusChip2, {
|
|
1415
|
-
tone: getStatusTone2(project.status),
|
|
1416
|
-
label: project.status
|
|
1417
|
-
}, undefined, false, undefined, this),
|
|
1418
|
-
footer: /* @__PURE__ */ jsxDEV4("span", {
|
|
1419
|
-
className: "text-muted-foreground text-xs",
|
|
1420
|
-
children: project.updatedAt.toLocaleDateString()
|
|
1421
|
-
}, undefined, false, undefined, this),
|
|
1422
|
-
onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
|
|
1423
|
-
}, project.id, false, undefined, this))
|
|
1424
|
-
}, undefined, false, undefined, this)
|
|
1425
|
-
]
|
|
1426
|
-
}, undefined, true, undefined, this);
|
|
1427
|
-
}
|
|
1428
|
-
|
|
1429
1650
|
// src/ui/SaasSettingsPanel.tsx
|
|
1430
|
-
import { useState as useState6 } from "react";
|
|
1431
1651
|
import { Button as Button5 } from "@contractspec/lib.design-system";
|
|
1432
|
-
import {
|
|
1652
|
+
import { useState as useState6 } from "react";
|
|
1653
|
+
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
1433
1654
|
"use client";
|
|
1434
1655
|
function SaasSettingsPanel() {
|
|
1435
1656
|
const [orgName, setOrgName] = useState6("Demo Organization");
|
|
1436
1657
|
const [timezone, setTimezone] = useState6("UTC");
|
|
1437
|
-
return /* @__PURE__ */
|
|
1658
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1438
1659
|
className: "space-y-6",
|
|
1439
1660
|
children: [
|
|
1440
|
-
/* @__PURE__ */
|
|
1441
|
-
className: "border-border bg-card
|
|
1661
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1662
|
+
className: "rounded-xl border border-border bg-card p-6",
|
|
1442
1663
|
children: [
|
|
1443
|
-
/* @__PURE__ */
|
|
1444
|
-
className: "mb-4 text-lg
|
|
1664
|
+
/* @__PURE__ */ jsxDEV6("h3", {
|
|
1665
|
+
className: "mb-4 font-semibold text-lg",
|
|
1445
1666
|
children: "Organization Settings"
|
|
1446
1667
|
}, undefined, false, undefined, this),
|
|
1447
|
-
/* @__PURE__ */
|
|
1668
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1448
1669
|
className: "space-y-4",
|
|
1449
1670
|
children: [
|
|
1450
|
-
/* @__PURE__ */
|
|
1671
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1451
1672
|
children: [
|
|
1452
|
-
/* @__PURE__ */
|
|
1673
|
+
/* @__PURE__ */ jsxDEV6("label", {
|
|
1453
1674
|
htmlFor: "setting-org-name",
|
|
1454
|
-
className: "block text-sm
|
|
1675
|
+
className: "block font-medium text-sm",
|
|
1455
1676
|
children: "Organization Name"
|
|
1456
1677
|
}, undefined, false, undefined, this),
|
|
1457
|
-
/* @__PURE__ */
|
|
1678
|
+
/* @__PURE__ */ jsxDEV6("input", {
|
|
1458
1679
|
id: "setting-org-name",
|
|
1459
1680
|
type: "text",
|
|
1460
1681
|
value: orgName,
|
|
1461
1682
|
onChange: (e) => setOrgName(e.target.value),
|
|
1462
|
-
className: "
|
|
1683
|
+
className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2"
|
|
1463
1684
|
}, undefined, false, undefined, this)
|
|
1464
1685
|
]
|
|
1465
1686
|
}, undefined, true, undefined, this),
|
|
1466
|
-
/* @__PURE__ */
|
|
1687
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1467
1688
|
children: [
|
|
1468
|
-
/* @__PURE__ */
|
|
1689
|
+
/* @__PURE__ */ jsxDEV6("label", {
|
|
1469
1690
|
htmlFor: "setting-timezone",
|
|
1470
|
-
className: "block text-sm
|
|
1691
|
+
className: "block font-medium text-sm",
|
|
1471
1692
|
children: "Default Timezone"
|
|
1472
1693
|
}, undefined, false, undefined, this),
|
|
1473
|
-
/* @__PURE__ */
|
|
1694
|
+
/* @__PURE__ */ jsxDEV6("select", {
|
|
1474
1695
|
id: "setting-timezone",
|
|
1475
1696
|
value: timezone,
|
|
1476
1697
|
onChange: (e) => setTimezone(e.target.value),
|
|
1477
|
-
className: "
|
|
1698
|
+
className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2",
|
|
1478
1699
|
children: [
|
|
1479
|
-
/* @__PURE__ */
|
|
1700
|
+
/* @__PURE__ */ jsxDEV6("option", {
|
|
1480
1701
|
value: "UTC",
|
|
1481
1702
|
children: "UTC"
|
|
1482
1703
|
}, undefined, false, undefined, this),
|
|
1483
|
-
/* @__PURE__ */
|
|
1704
|
+
/* @__PURE__ */ jsxDEV6("option", {
|
|
1484
1705
|
value: "America/New_York",
|
|
1485
1706
|
children: "America/New_York"
|
|
1486
1707
|
}, undefined, false, undefined, this),
|
|
1487
|
-
/* @__PURE__ */
|
|
1708
|
+
/* @__PURE__ */ jsxDEV6("option", {
|
|
1488
1709
|
value: "Europe/London",
|
|
1489
1710
|
children: "Europe/London"
|
|
1490
1711
|
}, undefined, false, undefined, this),
|
|
1491
|
-
/* @__PURE__ */
|
|
1712
|
+
/* @__PURE__ */ jsxDEV6("option", {
|
|
1492
1713
|
value: "Asia/Tokyo",
|
|
1493
1714
|
children: "Asia/Tokyo"
|
|
1494
1715
|
}, undefined, false, undefined, this)
|
|
@@ -1498,37 +1719,37 @@ function SaasSettingsPanel() {
|
|
|
1498
1719
|
}, undefined, true, undefined, this)
|
|
1499
1720
|
]
|
|
1500
1721
|
}, undefined, true, undefined, this),
|
|
1501
|
-
/* @__PURE__ */
|
|
1722
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1502
1723
|
className: "mt-6",
|
|
1503
|
-
children: /* @__PURE__ */
|
|
1724
|
+
children: /* @__PURE__ */ jsxDEV6(Button5, {
|
|
1504
1725
|
variant: "default",
|
|
1505
1726
|
children: "Save Changes"
|
|
1506
1727
|
}, undefined, false, undefined, this)
|
|
1507
1728
|
}, undefined, false, undefined, this)
|
|
1508
1729
|
]
|
|
1509
1730
|
}, undefined, true, undefined, this),
|
|
1510
|
-
/* @__PURE__ */
|
|
1511
|
-
className: "border-border bg-card
|
|
1731
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1732
|
+
className: "rounded-xl border border-border bg-card p-6",
|
|
1512
1733
|
children: [
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
className: "mb-4 text-lg
|
|
1734
|
+
/* @__PURE__ */ jsxDEV6("h3", {
|
|
1735
|
+
className: "mb-4 font-semibold text-lg",
|
|
1515
1736
|
children: "Notifications"
|
|
1516
1737
|
}, undefined, false, undefined, this),
|
|
1517
|
-
/* @__PURE__ */
|
|
1738
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1518
1739
|
className: "space-y-3",
|
|
1519
1740
|
children: [
|
|
1520
1741
|
{ label: "Email notifications", defaultChecked: true },
|
|
1521
1742
|
{ label: "Usage alerts", defaultChecked: true },
|
|
1522
1743
|
{ label: "Weekly digest", defaultChecked: false }
|
|
1523
|
-
].map((item) => /* @__PURE__ */
|
|
1744
|
+
].map((item) => /* @__PURE__ */ jsxDEV6("label", {
|
|
1524
1745
|
className: "flex items-center gap-3",
|
|
1525
1746
|
children: [
|
|
1526
|
-
/* @__PURE__ */
|
|
1747
|
+
/* @__PURE__ */ jsxDEV6("input", {
|
|
1527
1748
|
type: "checkbox",
|
|
1528
1749
|
defaultChecked: item.defaultChecked,
|
|
1529
|
-
className: "
|
|
1750
|
+
className: "h-4 w-4 rounded border-input"
|
|
1530
1751
|
}, undefined, false, undefined, this),
|
|
1531
|
-
/* @__PURE__ */
|
|
1752
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
1532
1753
|
className: "text-sm",
|
|
1533
1754
|
children: item.label
|
|
1534
1755
|
}, undefined, false, undefined, this)
|
|
@@ -1537,26 +1758,26 @@ function SaasSettingsPanel() {
|
|
|
1537
1758
|
}, undefined, false, undefined, this)
|
|
1538
1759
|
]
|
|
1539
1760
|
}, undefined, true, undefined, this),
|
|
1540
|
-
/* @__PURE__ */
|
|
1761
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1541
1762
|
className: "rounded-xl border border-red-200 bg-red-50 p-6 dark:border-red-900 dark:bg-red-950/20",
|
|
1542
1763
|
children: [
|
|
1543
|
-
/* @__PURE__ */
|
|
1544
|
-
className: "mb-2
|
|
1764
|
+
/* @__PURE__ */ jsxDEV6("h3", {
|
|
1765
|
+
className: "mb-2 font-semibold text-lg text-red-700 dark:text-red-400",
|
|
1545
1766
|
children: "Danger Zone"
|
|
1546
1767
|
}, undefined, false, undefined, this),
|
|
1547
|
-
/* @__PURE__ */
|
|
1548
|
-
className: "mb-4 text-
|
|
1768
|
+
/* @__PURE__ */ jsxDEV6("p", {
|
|
1769
|
+
className: "mb-4 text-red-600 text-sm dark:text-red-300",
|
|
1549
1770
|
children: "These actions are irreversible. Please proceed with caution."
|
|
1550
1771
|
}, undefined, false, undefined, this),
|
|
1551
|
-
/* @__PURE__ */
|
|
1772
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1552
1773
|
className: "flex gap-3",
|
|
1553
1774
|
children: [
|
|
1554
|
-
/* @__PURE__ */
|
|
1775
|
+
/* @__PURE__ */ jsxDEV6(Button5, {
|
|
1555
1776
|
variant: "secondary",
|
|
1556
1777
|
size: "sm",
|
|
1557
1778
|
children: "Export Data"
|
|
1558
1779
|
}, undefined, false, undefined, this),
|
|
1559
|
-
/* @__PURE__ */
|
|
1780
|
+
/* @__PURE__ */ jsxDEV6(Button5, {
|
|
1560
1781
|
variant: "secondary",
|
|
1561
1782
|
size: "sm",
|
|
1562
1783
|
children: "Delete Organization"
|
|
@@ -1568,228 +1789,6 @@ function SaasSettingsPanel() {
|
|
|
1568
1789
|
]
|
|
1569
1790
|
}, undefined, true, undefined, this);
|
|
1570
1791
|
}
|
|
1571
|
-
// src/ui/hooks/index.ts
|
|
1572
|
-
"use client";
|
|
1573
|
-
|
|
1574
|
-
// src/ui/renderers/project-list.renderer.tsx
|
|
1575
|
-
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
1576
|
-
var projectListReactRenderer = {
|
|
1577
|
-
target: "react",
|
|
1578
|
-
render: async (desc, _ctx) => {
|
|
1579
|
-
if (desc.source.type !== "component") {
|
|
1580
|
-
throw new Error("Invalid source type");
|
|
1581
|
-
}
|
|
1582
|
-
if (desc.source.componentKey !== "SaasProjectListView") {
|
|
1583
|
-
throw new Error(`Unknown component: ${desc.source.componentKey}`);
|
|
1584
|
-
}
|
|
1585
|
-
return /* @__PURE__ */ jsxDEV6(SaasProjectList, {}, undefined, false, undefined, this);
|
|
1586
|
-
}
|
|
1587
|
-
};
|
|
1588
|
-
|
|
1589
|
-
// src/ui/renderers/project-list.markdown.ts
|
|
1590
|
-
var projectListMarkdownRenderer = {
|
|
1591
|
-
target: "markdown",
|
|
1592
|
-
render: async (desc, _ctx) => {
|
|
1593
|
-
if (desc.source.type !== "component" || desc.source.componentKey !== "ProjectListView") {
|
|
1594
|
-
throw new Error("projectListMarkdownRenderer: not ProjectListView");
|
|
1595
|
-
}
|
|
1596
|
-
const data = await mockListProjectsHandler({
|
|
1597
|
-
limit: 20,
|
|
1598
|
-
offset: 0
|
|
1599
|
-
});
|
|
1600
|
-
const items = data.projects ?? data.items ?? [];
|
|
1601
|
-
const lines = [
|
|
1602
|
-
"# Projects",
|
|
1603
|
-
"",
|
|
1604
|
-
`**Total**: ${data.total} projects`,
|
|
1605
|
-
""
|
|
1606
|
-
];
|
|
1607
|
-
if (items.length === 0) {
|
|
1608
|
-
lines.push("_No projects found._");
|
|
1609
|
-
} else {
|
|
1610
|
-
lines.push("| Status | Project | Description |");
|
|
1611
|
-
lines.push("|--------|---------|-------------|");
|
|
1612
|
-
for (const project of items) {
|
|
1613
|
-
const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
|
|
1614
|
-
lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
return {
|
|
1618
|
-
mimeType: "text/markdown",
|
|
1619
|
-
body: lines.join(`
|
|
1620
|
-
`)
|
|
1621
|
-
};
|
|
1622
|
-
}
|
|
1623
|
-
};
|
|
1624
|
-
var saasDashboardMarkdownRenderer = {
|
|
1625
|
-
target: "markdown",
|
|
1626
|
-
render: async (desc, _ctx) => {
|
|
1627
|
-
if (desc.source.type !== "component" || desc.source.componentKey !== "SaasDashboard") {
|
|
1628
|
-
throw new Error("saasDashboardMarkdownRenderer: not SaasDashboard");
|
|
1629
|
-
}
|
|
1630
|
-
const [projectsData, subscription] = await Promise.all([
|
|
1631
|
-
mockListProjectsHandler({ limit: 50 }),
|
|
1632
|
-
mockGetSubscriptionHandler()
|
|
1633
|
-
]);
|
|
1634
|
-
const projects = projectsData.projects ?? [];
|
|
1635
|
-
const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
|
|
1636
|
-
const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
|
|
1637
|
-
const lines = [
|
|
1638
|
-
"# SaaS Dashboard",
|
|
1639
|
-
"",
|
|
1640
|
-
"> Organization overview and usage summary",
|
|
1641
|
-
"",
|
|
1642
|
-
"## Summary",
|
|
1643
|
-
"",
|
|
1644
|
-
"| Metric | Value |",
|
|
1645
|
-
"|--------|-------|",
|
|
1646
|
-
`| Total Projects | ${projectsData.total} |`,
|
|
1647
|
-
`| Active Projects | ${activeProjects} |`,
|
|
1648
|
-
`| Archived Projects | ${archivedProjects} |`,
|
|
1649
|
-
`| Subscription Plan | ${subscription.planName} |`,
|
|
1650
|
-
`| Subscription Status | ${subscription.status} |`,
|
|
1651
|
-
"",
|
|
1652
|
-
"## Projects",
|
|
1653
|
-
""
|
|
1654
|
-
];
|
|
1655
|
-
if (projects.length === 0) {
|
|
1656
|
-
lines.push("_No projects yet._");
|
|
1657
|
-
} else {
|
|
1658
|
-
lines.push("| Status | Project | Description |");
|
|
1659
|
-
lines.push("|--------|---------|-------------|");
|
|
1660
|
-
for (const project of projects.slice(0, 10)) {
|
|
1661
|
-
const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
|
|
1662
|
-
lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
|
|
1663
|
-
}
|
|
1664
|
-
if (projects.length > 10) {
|
|
1665
|
-
lines.push(`| ... | ... | _${projectsData.total - 10} more projects_ |`);
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
lines.push("");
|
|
1669
|
-
lines.push("## Subscription");
|
|
1670
|
-
lines.push("");
|
|
1671
|
-
lines.push(`- **Plan**: ${subscription.planName}`);
|
|
1672
|
-
lines.push(`- **Status**: ${subscription.status}`);
|
|
1673
|
-
if (subscription.currentPeriodEnd) {
|
|
1674
|
-
lines.push(`- **Period End**: ${new Date(subscription.currentPeriodEnd).toLocaleDateString()}`);
|
|
1675
|
-
}
|
|
1676
|
-
return {
|
|
1677
|
-
mimeType: "text/markdown",
|
|
1678
|
-
body: lines.join(`
|
|
1679
|
-
`)
|
|
1680
|
-
};
|
|
1681
|
-
}
|
|
1682
|
-
};
|
|
1683
|
-
var saasBillingMarkdownRenderer = {
|
|
1684
|
-
target: "markdown",
|
|
1685
|
-
render: async (desc, _ctx) => {
|
|
1686
|
-
if (desc.source.type !== "component" || desc.source.componentKey !== "SubscriptionView") {
|
|
1687
|
-
throw new Error("saasBillingMarkdownRenderer: not SubscriptionView");
|
|
1688
|
-
}
|
|
1689
|
-
const subscription = await mockGetSubscriptionHandler();
|
|
1690
|
-
const lines = [
|
|
1691
|
-
"# Billing & Subscription",
|
|
1692
|
-
"",
|
|
1693
|
-
"> Current subscription details and billing information",
|
|
1694
|
-
"",
|
|
1695
|
-
"## Subscription Details",
|
|
1696
|
-
"",
|
|
1697
|
-
"| Property | Value |",
|
|
1698
|
-
"|----------|-------|",
|
|
1699
|
-
`| Plan | ${subscription.planName} |`,
|
|
1700
|
-
`| Status | ${subscription.status} |`,
|
|
1701
|
-
`| ID | ${subscription.id} |`,
|
|
1702
|
-
`| Period Start | ${new Date(subscription.currentPeriodStart).toLocaleDateString()} |`,
|
|
1703
|
-
`| Period End | ${new Date(subscription.currentPeriodEnd).toLocaleDateString()} |`
|
|
1704
|
-
];
|
|
1705
|
-
lines.push("");
|
|
1706
|
-
lines.push("## Plan Limits");
|
|
1707
|
-
lines.push("");
|
|
1708
|
-
lines.push(`- **Projects**: ${subscription.limits.projects}`);
|
|
1709
|
-
lines.push(`- **Users**: ${subscription.limits.users}`);
|
|
1710
|
-
lines.push("");
|
|
1711
|
-
lines.push("## Plan Features");
|
|
1712
|
-
lines.push("");
|
|
1713
|
-
if (subscription.planName.toLowerCase().includes("free")) {
|
|
1714
|
-
lines.push("- ✅ Up to 3 projects");
|
|
1715
|
-
lines.push("- ✅ Basic support");
|
|
1716
|
-
lines.push("- ❌ Priority support");
|
|
1717
|
-
lines.push("- ❌ Advanced analytics");
|
|
1718
|
-
} else if (subscription.planName.toLowerCase().includes("pro")) {
|
|
1719
|
-
lines.push("- ✅ Unlimited projects");
|
|
1720
|
-
lines.push("- ✅ Priority support");
|
|
1721
|
-
lines.push("- ✅ Advanced analytics");
|
|
1722
|
-
lines.push("- ❌ Custom integrations");
|
|
1723
|
-
} else {
|
|
1724
|
-
lines.push("- ✅ Unlimited projects");
|
|
1725
|
-
lines.push("- ✅ Priority support");
|
|
1726
|
-
lines.push("- ✅ Advanced analytics");
|
|
1727
|
-
lines.push("- ✅ Custom integrations");
|
|
1728
|
-
lines.push("- ✅ Dedicated support");
|
|
1729
|
-
}
|
|
1730
|
-
return {
|
|
1731
|
-
mimeType: "text/markdown",
|
|
1732
|
-
body: lines.join(`
|
|
1733
|
-
`)
|
|
1734
|
-
};
|
|
1735
|
-
}
|
|
1736
|
-
};
|
|
1737
|
-
// src/ui/overlays/demo-overlays.ts
|
|
1738
|
-
var saasFreeUserOverlay = {
|
|
1739
|
-
overlayId: "saas-boilerplate.free-tier",
|
|
1740
|
-
version: "1.0.0",
|
|
1741
|
-
description: "Shows limitations for free tier users",
|
|
1742
|
-
appliesTo: {
|
|
1743
|
-
feature: "saas-boilerplate",
|
|
1744
|
-
tier: "free"
|
|
1745
|
-
},
|
|
1746
|
-
modifications: [
|
|
1747
|
-
{
|
|
1748
|
-
type: "setLimit",
|
|
1749
|
-
field: "projects",
|
|
1750
|
-
max: 3,
|
|
1751
|
-
message: "Upgrade to create more projects"
|
|
1752
|
-
},
|
|
1753
|
-
{ type: "hideField", field: "advancedSettings", reason: "Pro feature" },
|
|
1754
|
-
{
|
|
1755
|
-
type: "addBadge",
|
|
1756
|
-
position: "header",
|
|
1757
|
-
label: "Free Plan",
|
|
1758
|
-
variant: "default"
|
|
1759
|
-
}
|
|
1760
|
-
]
|
|
1761
|
-
};
|
|
1762
|
-
var saasDemoOverlay = {
|
|
1763
|
-
overlayId: "saas-boilerplate.demo-user",
|
|
1764
|
-
version: "1.0.0",
|
|
1765
|
-
description: "Demo mode for SaaS boilerplate",
|
|
1766
|
-
appliesTo: {
|
|
1767
|
-
feature: "saas-boilerplate",
|
|
1768
|
-
role: "demo"
|
|
1769
|
-
},
|
|
1770
|
-
modifications: [
|
|
1771
|
-
{
|
|
1772
|
-
type: "hideField",
|
|
1773
|
-
field: "billingSection",
|
|
1774
|
-
reason: "Demo users cannot access billing"
|
|
1775
|
-
},
|
|
1776
|
-
{
|
|
1777
|
-
type: "hideField",
|
|
1778
|
-
field: "deleteAccount",
|
|
1779
|
-
reason: "Not available in demo"
|
|
1780
|
-
},
|
|
1781
|
-
{
|
|
1782
|
-
type: "addBadge",
|
|
1783
|
-
position: "header",
|
|
1784
|
-
label: "Demo Mode",
|
|
1785
|
-
variant: "warning"
|
|
1786
|
-
}
|
|
1787
|
-
]
|
|
1788
|
-
};
|
|
1789
|
-
var saasOverlays = [
|
|
1790
|
-
saasFreeUserOverlay,
|
|
1791
|
-
saasDemoOverlay
|
|
1792
|
-
];
|
|
1793
1792
|
export {
|
|
1794
1793
|
useProjectMutations,
|
|
1795
1794
|
useProjectList,
|