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