@contractspec/example.saas-boilerplate 3.7.6 → 3.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/.turbo/turbo-build.log +39 -27
  2. package/AGENTS.md +50 -27
  3. package/CHANGELOG.md +36 -0
  4. package/README.md +65 -144
  5. package/dist/billing/billing.event.js +1 -1
  6. package/dist/billing/index.d.ts +6 -6
  7. package/dist/billing/index.js +1 -1
  8. package/dist/browser/billing/billing.event.js +1 -1
  9. package/dist/browser/billing/index.js +1 -1
  10. package/dist/browser/index.js +1147 -869
  11. package/dist/browser/project/index.js +209 -209
  12. package/dist/browser/project/project.event.js +1 -1
  13. package/dist/browser/saas-boilerplate.feature.js +208 -0
  14. package/dist/browser/ui/SaasDashboard.js +356 -105
  15. package/dist/browser/ui/SaasDashboard.visualizations.js +249 -0
  16. package/dist/browser/ui/SaasProjectList.js +7 -7
  17. package/dist/browser/ui/SaasSettingsPanel.js +12 -12
  18. package/dist/browser/ui/hooks/index.js +2 -2
  19. package/dist/browser/ui/hooks/useProjectList.js +1 -1
  20. package/dist/browser/ui/hooks/useProjectMutations.js +1 -1
  21. package/dist/browser/ui/index.js +790 -521
  22. package/dist/browser/ui/modals/CreateProjectModal.js +10 -10
  23. package/dist/browser/ui/modals/ProjectActionsModal.js +13 -13
  24. package/dist/browser/ui/modals/index.js +23 -23
  25. package/dist/browser/ui/renderers/index.js +341 -115
  26. package/dist/browser/ui/renderers/project-list.markdown.js +229 -3
  27. package/dist/browser/ui/renderers/project-list.renderer.js +7 -7
  28. package/dist/browser/visualizations/catalog.js +155 -0
  29. package/dist/browser/visualizations/index.js +217 -0
  30. package/dist/browser/visualizations/selectors.js +210 -0
  31. package/dist/handlers/index.d.ts +2 -2
  32. package/dist/index.d.ts +5 -4
  33. package/dist/index.js +1147 -869
  34. package/dist/node/billing/billing.event.js +1 -1
  35. package/dist/node/billing/index.js +1 -1
  36. package/dist/node/index.js +1147 -869
  37. package/dist/node/project/index.js +209 -209
  38. package/dist/node/project/project.event.js +1 -1
  39. package/dist/node/saas-boilerplate.feature.js +208 -0
  40. package/dist/node/ui/SaasDashboard.js +356 -105
  41. package/dist/node/ui/SaasDashboard.visualizations.js +249 -0
  42. package/dist/node/ui/SaasProjectList.js +7 -7
  43. package/dist/node/ui/SaasSettingsPanel.js +12 -12
  44. package/dist/node/ui/hooks/index.js +2 -2
  45. package/dist/node/ui/hooks/useProjectList.js +1 -1
  46. package/dist/node/ui/hooks/useProjectMutations.js +1 -1
  47. package/dist/node/ui/index.js +790 -521
  48. package/dist/node/ui/modals/CreateProjectModal.js +10 -10
  49. package/dist/node/ui/modals/ProjectActionsModal.js +13 -13
  50. package/dist/node/ui/modals/index.js +23 -23
  51. package/dist/node/ui/renderers/index.js +341 -115
  52. package/dist/node/ui/renderers/project-list.markdown.js +229 -3
  53. package/dist/node/ui/renderers/project-list.renderer.js +7 -7
  54. package/dist/node/visualizations/catalog.js +155 -0
  55. package/dist/node/visualizations/index.js +217 -0
  56. package/dist/node/visualizations/selectors.js +210 -0
  57. package/dist/presentations/index.d.ts +1 -1
  58. package/dist/project/index.d.ts +7 -7
  59. package/dist/project/index.js +209 -209
  60. package/dist/project/project.event.js +1 -1
  61. package/dist/saas-boilerplate.feature.js +208 -0
  62. package/dist/settings/index.d.ts +1 -1
  63. package/dist/ui/SaasDashboard.js +356 -105
  64. package/dist/ui/SaasDashboard.visualizations.d.ts +5 -0
  65. package/dist/ui/SaasDashboard.visualizations.js +250 -0
  66. package/dist/ui/SaasProjectList.js +7 -7
  67. package/dist/ui/SaasSettingsPanel.js +12 -12
  68. package/dist/ui/hooks/index.d.ts +2 -2
  69. package/dist/ui/hooks/index.js +2 -2
  70. package/dist/ui/hooks/useProjectList.d.ts +5 -0
  71. package/dist/ui/hooks/useProjectList.js +1 -1
  72. package/dist/ui/hooks/useProjectMutations.d.ts +8 -0
  73. package/dist/ui/hooks/useProjectMutations.js +1 -1
  74. package/dist/ui/index.d.ts +4 -4
  75. package/dist/ui/index.js +790 -521
  76. package/dist/ui/modals/CreateProjectModal.js +10 -10
  77. package/dist/ui/modals/ProjectActionsModal.js +13 -13
  78. package/dist/ui/modals/index.js +23 -23
  79. package/dist/ui/renderers/index.d.ts +1 -1
  80. package/dist/ui/renderers/index.js +341 -115
  81. package/dist/ui/renderers/project-list.markdown.js +229 -3
  82. package/dist/ui/renderers/project-list.renderer.d.ts +1 -1
  83. package/dist/ui/renderers/project-list.renderer.js +7 -7
  84. package/dist/visualizations/catalog.d.ts +11 -0
  85. package/dist/visualizations/catalog.js +156 -0
  86. package/dist/visualizations/index.d.ts +2 -0
  87. package/dist/visualizations/index.js +218 -0
  88. package/dist/visualizations/selectors.d.ts +8 -0
  89. package/dist/visualizations/selectors.js +211 -0
  90. package/dist/visualizations/selectors.test.d.ts +1 -0
  91. package/package.json +70 -14
  92. package/src/billing/billing.entity.ts +132 -132
  93. package/src/billing/billing.enum.ts +9 -9
  94. package/src/billing/billing.event.ts +71 -71
  95. package/src/billing/billing.handler.ts +87 -87
  96. package/src/billing/billing.operations.ts +158 -158
  97. package/src/billing/billing.presentation.ts +45 -45
  98. package/src/billing/billing.schema.ts +76 -76
  99. package/src/billing/index.ts +43 -48
  100. package/src/dashboard/dashboard.presentation.ts +45 -45
  101. package/src/dashboard/index.ts +2 -2
  102. package/src/docs/saas-boilerplate.docblock.ts +43 -43
  103. package/src/example.ts +32 -32
  104. package/src/handlers/index.ts +9 -9
  105. package/src/handlers/saas.handlers.ts +250 -249
  106. package/src/index.ts +41 -41
  107. package/src/presentations/index.ts +18 -20
  108. package/src/project/index.ts +45 -50
  109. package/src/project/project.entity.ts +68 -68
  110. package/src/project/project.enum.ts +8 -8
  111. package/src/project/project.event.ts +79 -79
  112. package/src/project/project.handler.ts +103 -103
  113. package/src/project/project.operations.ts +236 -236
  114. package/src/project/project.presentation.ts +46 -46
  115. package/src/project/project.schema.ts +90 -90
  116. package/src/saas-boilerplate.feature.ts +103 -100
  117. package/src/seeders/index.ts +20 -20
  118. package/src/settings/index.ts +2 -3
  119. package/src/settings/settings.entity.ts +65 -65
  120. package/src/settings/settings.enum.ts +4 -4
  121. package/src/shared/mock-data.ts +92 -92
  122. package/src/shared/overlay-types.ts +23 -23
  123. package/src/tests/operations.test-spec.ts +96 -96
  124. package/src/ui/SaasDashboard.tsx +278 -270
  125. package/src/ui/SaasDashboard.visualizations.tsx +41 -0
  126. package/src/ui/SaasProjectList.tsx +90 -90
  127. package/src/ui/SaasSettingsPanel.tsx +84 -84
  128. package/src/ui/hooks/index.ts +3 -3
  129. package/src/ui/hooks/useProjectList.ts +69 -68
  130. package/src/ui/hooks/useProjectMutations.ts +144 -143
  131. package/src/ui/index.ts +8 -12
  132. package/src/ui/modals/CreateProjectModal.tsx +154 -154
  133. package/src/ui/modals/ProjectActionsModal.tsx +321 -321
  134. package/src/ui/overlays/demo-overlays.ts +49 -49
  135. package/src/ui/renderers/index.ts +5 -4
  136. package/src/ui/renderers/project-list.markdown.ts +229 -205
  137. package/src/ui/renderers/project-list.renderer.tsx +14 -13
  138. package/src/visualizations/catalog.ts +153 -0
  139. package/src/visualizations/index.ts +2 -0
  140. package/src/visualizations/selectors.test.ts +25 -0
  141. package/src/visualizations/selectors.ts +85 -0
  142. package/tsconfig.json +7 -8
  143. package/tsdown.config.js +7 -3
package/dist/ui/index.js CHANGED
@@ -345,9 +345,216 @@ function createSaasHandlers(db) {
345
345
  getSubscription
346
346
  };
347
347
  }
348
+ // src/visualizations/catalog.ts
349
+ import {
350
+ defineVisualization,
351
+ VisualizationRegistry
352
+ } from "@contractspec/lib.contracts-spec/visualizations";
353
+ var PROJECT_LIST_REF = {
354
+ key: "saas.project.list",
355
+ version: "1.0.0"
356
+ };
357
+ var META = {
358
+ version: "1.0.0",
359
+ domain: "saas",
360
+ stability: "experimental",
361
+ owners: ["@example.saas-boilerplate"],
362
+ tags: ["saas", "visualization", "projects"]
363
+ };
364
+ var SaasProjectUsageVisualization = defineVisualization({
365
+ meta: {
366
+ ...META,
367
+ key: "saas-boilerplate.visualization.project-usage",
368
+ title: "Project Capacity",
369
+ description: "Current project count against the current plan limit.",
370
+ goal: "Show usage against the active plan allowance.",
371
+ context: "SaaS account overview."
372
+ },
373
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
374
+ visualization: {
375
+ kind: "metric",
376
+ measure: "totalProjects",
377
+ comparisonMeasure: "projectLimit",
378
+ measures: [
379
+ {
380
+ key: "totalProjects",
381
+ label: "Projects",
382
+ dataPath: "totalProjects",
383
+ format: "number"
384
+ },
385
+ {
386
+ key: "projectLimit",
387
+ label: "Plan Limit",
388
+ dataPath: "projectLimit",
389
+ format: "number"
390
+ }
391
+ ],
392
+ table: { caption: "Current project count and plan limit." }
393
+ }
394
+ });
395
+ var SaasProjectStatusVisualization = defineVisualization({
396
+ meta: {
397
+ ...META,
398
+ key: "saas-boilerplate.visualization.project-status",
399
+ title: "Project Status",
400
+ description: "Distribution of project states.",
401
+ goal: "Show the mix of active, draft, and archived projects.",
402
+ context: "Project portfolio overview."
403
+ },
404
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
405
+ visualization: {
406
+ kind: "pie",
407
+ nameDimension: "status",
408
+ valueMeasure: "projects",
409
+ dimensions: [
410
+ { key: "status", label: "Status", dataPath: "status", type: "category" }
411
+ ],
412
+ measures: [
413
+ {
414
+ key: "projects",
415
+ label: "Projects",
416
+ dataPath: "projects",
417
+ format: "number"
418
+ }
419
+ ],
420
+ table: { caption: "Project counts by status." }
421
+ }
422
+ });
423
+ var SaasProjectTierVisualization = defineVisualization({
424
+ meta: {
425
+ ...META,
426
+ key: "saas-boilerplate.visualization.project-tiers",
427
+ title: "Tier Comparison",
428
+ description: "Distribution of projects across tiers.",
429
+ goal: "Compare how the current portfolio is distributed by tier.",
430
+ context: "Plan and packaging overview."
431
+ },
432
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
433
+ visualization: {
434
+ kind: "cartesian",
435
+ variant: "bar",
436
+ xDimension: "tier",
437
+ yMeasures: ["projects"],
438
+ dimensions: [
439
+ { key: "tier", label: "Tier", dataPath: "tier", type: "category" }
440
+ ],
441
+ measures: [
442
+ {
443
+ key: "projects",
444
+ label: "Projects",
445
+ dataPath: "projects",
446
+ format: "number",
447
+ color: "#1d4ed8"
448
+ }
449
+ ],
450
+ table: { caption: "Project counts by tier." }
451
+ }
452
+ });
453
+ var SaasProjectActivityVisualization = defineVisualization({
454
+ meta: {
455
+ ...META,
456
+ key: "saas-boilerplate.visualization.project-activity",
457
+ title: "Recent Project Activity",
458
+ description: "Daily project creation activity.",
459
+ goal: "Show recent project activity over time.",
460
+ context: "Project portfolio trend view."
461
+ },
462
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
463
+ visualization: {
464
+ kind: "cartesian",
465
+ variant: "line",
466
+ xDimension: "day",
467
+ yMeasures: ["projects"],
468
+ dimensions: [{ key: "day", label: "Day", dataPath: "day", type: "time" }],
469
+ measures: [
470
+ {
471
+ key: "projects",
472
+ label: "Projects",
473
+ dataPath: "projects",
474
+ format: "number",
475
+ color: "#0f766e"
476
+ }
477
+ ],
478
+ table: { caption: "Daily project creation counts." }
479
+ }
480
+ });
481
+ var SaasVisualizationSpecs = [
482
+ SaasProjectUsageVisualization,
483
+ SaasProjectStatusVisualization,
484
+ SaasProjectTierVisualization,
485
+ SaasProjectActivityVisualization
486
+ ];
487
+ var SaasVisualizationRegistry = new VisualizationRegistry([
488
+ ...SaasVisualizationSpecs
489
+ ]);
490
+ var SaasVisualizationRefs = SaasVisualizationSpecs.map((spec) => ({
491
+ key: spec.meta.key,
492
+ version: spec.meta.version
493
+ }));
494
+
495
+ // src/visualizations/selectors.ts
496
+ function toDayKey(value) {
497
+ const date = value instanceof Date ? value : new Date(value);
498
+ return date.toISOString().slice(0, 10);
499
+ }
500
+ function createSaasVisualizationItems(projects, projectLimit = 10) {
501
+ const statusCounts = new Map;
502
+ const tierCounts = new Map;
503
+ const activityCounts = new Map;
504
+ for (const project of projects) {
505
+ statusCounts.set(project.status, (statusCounts.get(project.status) ?? 0) + 1);
506
+ tierCounts.set(project.tier, (tierCounts.get(project.tier) ?? 0) + 1);
507
+ const day = toDayKey(project.createdAt);
508
+ activityCounts.set(day, (activityCounts.get(day) ?? 0) + 1);
509
+ }
510
+ return [
511
+ {
512
+ key: "saas-capacity",
513
+ spec: SaasProjectUsageVisualization,
514
+ data: { data: [{ totalProjects: projects.length, projectLimit }] },
515
+ title: "Project Capacity",
516
+ description: "Current project count compared to the active limit.",
517
+ height: 220
518
+ },
519
+ {
520
+ key: "saas-status",
521
+ spec: SaasProjectStatusVisualization,
522
+ data: {
523
+ data: Array.from(statusCounts.entries()).map(([status, count]) => ({
524
+ status,
525
+ projects: count
526
+ }))
527
+ },
528
+ title: "Project Status",
529
+ description: "Status mix across the current project portfolio.",
530
+ height: 260
531
+ },
532
+ {
533
+ key: "saas-tier",
534
+ spec: SaasProjectTierVisualization,
535
+ data: {
536
+ data: Array.from(tierCounts.entries()).map(([tier, count]) => ({
537
+ tier,
538
+ projects: count
539
+ }))
540
+ },
541
+ title: "Tier Comparison",
542
+ description: "How projects are distributed across tiers."
543
+ },
544
+ {
545
+ key: "saas-activity",
546
+ spec: SaasProjectActivityVisualization,
547
+ data: {
548
+ data: Array.from(activityCounts.entries()).sort(([left], [right]) => left.localeCompare(right)).map(([day, count]) => ({ day, projects: count }))
549
+ },
550
+ title: "Recent Project Activity",
551
+ description: "Daily project creation activity."
552
+ }
553
+ ];
554
+ }
348
555
  // src/ui/hooks/useProjectList.ts
349
- import { useCallback, useEffect, useMemo, useState } from "react";
350
556
  import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
557
+ import { useCallback, useEffect, useMemo, useState } from "react";
351
558
  function useProjectList(options = {}) {
352
559
  const { handlers, projectId } = useTemplateRuntime();
353
560
  const { saas: saas2 } = handlers;
@@ -410,8 +617,8 @@ function useProjectList(options = {}) {
410
617
  }
411
618
 
412
619
  // src/ui/hooks/useProjectMutations.ts
413
- import { useCallback as useCallback2, useState as useState2 } from "react";
414
620
  import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
621
+ import { useCallback as useCallback2, useState as useState2 } from "react";
415
622
  function useProjectMutations(options = {}) {
416
623
  const { handlers, projectId } = useTemplateRuntime2();
417
624
  const { saas: saas2 } = handlers;
@@ -498,9 +705,12 @@ function useProjectMutations(options = {}) {
498
705
  };
499
706
  }
500
707
 
708
+ // src/ui/hooks/index.ts
709
+ "use client";
710
+
501
711
  // src/ui/modals/CreateProjectModal.tsx
502
- import { useState as useState3 } from "react";
503
712
  import { Button, Input } from "@contractspec/lib.design-system";
713
+ import { useState as useState3 } from "react";
504
714
  import { jsxDEV } from "react/jsx-dev-runtime";
505
715
  "use client";
506
716
  var TIERS = [
@@ -545,7 +755,7 @@ function CreateProjectModal({
545
755
  className: "fixed inset-0 z-50 flex items-center justify-center",
546
756
  children: [
547
757
  /* @__PURE__ */ jsxDEV("div", {
548
- className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
758
+ className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
549
759
  onClick: onClose,
550
760
  role: "button",
551
761
  tabIndex: 0,
@@ -556,10 +766,10 @@ function CreateProjectModal({
556
766
  "aria-label": "Close modal"
557
767
  }, undefined, false, undefined, this),
558
768
  /* @__PURE__ */ jsxDEV("div", {
559
- className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
769
+ className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
560
770
  children: [
561
771
  /* @__PURE__ */ jsxDEV("h2", {
562
- className: "mb-4 text-xl font-semibold",
772
+ className: "mb-4 font-semibold text-xl",
563
773
  children: "Create New Project"
564
774
  }, undefined, false, undefined, this),
565
775
  /* @__PURE__ */ jsxDEV("form", {
@@ -570,7 +780,7 @@ function CreateProjectModal({
570
780
  children: [
571
781
  /* @__PURE__ */ jsxDEV("label", {
572
782
  htmlFor: "project-name",
573
- className: "text-muted-foreground mb-1 block text-sm font-medium",
783
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
574
784
  children: "Project Name *"
575
785
  }, undefined, false, undefined, this),
576
786
  /* @__PURE__ */ jsxDEV(Input, {
@@ -586,7 +796,7 @@ function CreateProjectModal({
586
796
  children: [
587
797
  /* @__PURE__ */ jsxDEV("label", {
588
798
  htmlFor: "project-description",
589
- className: "text-muted-foreground mb-1 block text-sm font-medium",
799
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
590
800
  children: "Description"
591
801
  }, undefined, false, undefined, this),
592
802
  /* @__PURE__ */ jsxDEV("textarea", {
@@ -596,7 +806,7 @@ function CreateProjectModal({
596
806
  placeholder: "Describe what this project is about...",
597
807
  rows: 3,
598
808
  disabled: isLoading,
599
- className: "border-input bg-background focus:ring-ring w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50"
809
+ 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
810
  }, undefined, false, undefined, this)
601
811
  ]
602
812
  }, undefined, true, undefined, this),
@@ -604,7 +814,7 @@ function CreateProjectModal({
604
814
  children: [
605
815
  /* @__PURE__ */ jsxDEV("label", {
606
816
  htmlFor: "project-tier",
607
- className: "text-muted-foreground mb-1 block text-sm font-medium",
817
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
608
818
  children: "Tier"
609
819
  }, undefined, false, undefined, this),
610
820
  /* @__PURE__ */ jsxDEV("select", {
@@ -612,7 +822,7 @@ function CreateProjectModal({
612
822
  value: tier,
613
823
  onChange: (e) => setTier(e.target.value),
614
824
  disabled: isLoading,
615
- className: "border-input bg-background focus:ring-ring h-10 w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50",
825
+ 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
826
  children: TIERS.map((t) => /* @__PURE__ */ jsxDEV("option", {
617
827
  value: t.value,
618
828
  children: t.label
@@ -621,7 +831,7 @@ function CreateProjectModal({
621
831
  ]
622
832
  }, undefined, true, undefined, this),
623
833
  error && /* @__PURE__ */ jsxDEV("div", {
624
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
834
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
625
835
  children: error
626
836
  }, undefined, false, undefined, this),
627
837
  /* @__PURE__ */ jsxDEV("div", {
@@ -650,8 +860,8 @@ function CreateProjectModal({
650
860
  }
651
861
 
652
862
  // src/ui/modals/ProjectActionsModal.tsx
653
- import { useEffect as useEffect2, useState as useState4 } from "react";
654
863
  import { Button as Button2, Input as Input2 } from "@contractspec/lib.design-system";
864
+ import { useEffect as useEffect2, useState as useState4 } from "react";
655
865
  import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
656
866
  "use client";
657
867
  function ProjectActionsModal({
@@ -744,7 +954,7 @@ function ProjectActionsModal({
744
954
  className: "fixed inset-0 z-50 flex items-center justify-center",
745
955
  children: [
746
956
  /* @__PURE__ */ jsxDEV2("div", {
747
- className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
957
+ className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
748
958
  onClick: handleClose,
749
959
  role: "button",
750
960
  tabIndex: 0,
@@ -755,13 +965,13 @@ function ProjectActionsModal({
755
965
  "aria-label": "Close modal"
756
966
  }, undefined, false, undefined, this),
757
967
  /* @__PURE__ */ jsxDEV2("div", {
758
- className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
968
+ className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
759
969
  children: [
760
970
  /* @__PURE__ */ jsxDEV2("div", {
761
- className: "border-border mb-4 border-b pb-4",
971
+ className: "mb-4 border-border border-b pb-4",
762
972
  children: [
763
973
  /* @__PURE__ */ jsxDEV2("h2", {
764
- className: "text-xl font-semibold",
974
+ className: "font-semibold text-xl",
765
975
  children: project.name
766
976
  }, undefined, false, undefined, this),
767
977
  /* @__PURE__ */ jsxDEV2("p", {
@@ -843,7 +1053,7 @@ function ProjectActionsModal({
843
1053
  children: [
844
1054
  /* @__PURE__ */ jsxDEV2("label", {
845
1055
  htmlFor: "edit-name",
846
- className: "text-muted-foreground mb-1 block text-sm font-medium",
1056
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
847
1057
  children: "Project Name *"
848
1058
  }, undefined, false, undefined, this),
849
1059
  /* @__PURE__ */ jsxDEV2(Input2, {
@@ -858,7 +1068,7 @@ function ProjectActionsModal({
858
1068
  children: [
859
1069
  /* @__PURE__ */ jsxDEV2("label", {
860
1070
  htmlFor: "edit-description",
861
- className: "text-muted-foreground mb-1 block text-sm font-medium",
1071
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
862
1072
  children: "Description"
863
1073
  }, undefined, false, undefined, this),
864
1074
  /* @__PURE__ */ jsxDEV2("textarea", {
@@ -867,12 +1077,12 @@ function ProjectActionsModal({
867
1077
  onChange: (e) => setDescription(e.target.value),
868
1078
  rows: 3,
869
1079
  disabled: isLoading,
870
- className: "border-input bg-background focus:ring-ring w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50"
1080
+ 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
1081
  }, undefined, false, undefined, this)
872
1082
  ]
873
1083
  }, undefined, true, undefined, this),
874
1084
  error && /* @__PURE__ */ jsxDEV2("div", {
875
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
1085
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
876
1086
  children: error
877
1087
  }, undefined, false, undefined, this),
878
1088
  /* @__PURE__ */ jsxDEV2("div", {
@@ -902,7 +1112,7 @@ function ProjectActionsModal({
902
1112
  "Are you sure you want to archive",
903
1113
  " ",
904
1114
  /* @__PURE__ */ jsxDEV2("span", {
905
- className: "text-foreground font-medium",
1115
+ className: "font-medium text-foreground",
906
1116
  children: project.name
907
1117
  }, undefined, false, undefined, this),
908
1118
  "?"
@@ -913,7 +1123,7 @@ function ProjectActionsModal({
913
1123
  children: "Archived projects can be restored later."
914
1124
  }, undefined, false, undefined, this),
915
1125
  error && /* @__PURE__ */ jsxDEV2("div", {
916
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
1126
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
917
1127
  children: error
918
1128
  }, undefined, false, undefined, this),
919
1129
  /* @__PURE__ */ jsxDEV2("div", {
@@ -943,7 +1153,7 @@ function ProjectActionsModal({
943
1153
  "Are you sure you want to delete",
944
1154
  " ",
945
1155
  /* @__PURE__ */ jsxDEV2("span", {
946
- className: "text-foreground font-medium",
1156
+ className: "font-medium text-foreground",
947
1157
  children: project.name
948
1158
  }, undefined, false, undefined, this),
949
1159
  "?"
@@ -954,7 +1164,7 @@ function ProjectActionsModal({
954
1164
  children: "This action cannot be undone."
955
1165
  }, undefined, false, undefined, this),
956
1166
  error && /* @__PURE__ */ jsxDEV2("div", {
957
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
1167
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
958
1168
  children: error
959
1169
  }, undefined, false, undefined, this),
960
1170
  /* @__PURE__ */ jsxDEV2("div", {
@@ -981,141 +1191,518 @@ function ProjectActionsModal({
981
1191
  ]
982
1192
  }, undefined, true, undefined, this);
983
1193
  }
984
-
985
- // src/ui/SaasDashboard.tsx
986
- import { useState as useState5, useCallback as useCallback3 } from "react";
987
- import {
988
- StatCard,
989
- StatCardGroup,
990
- StatusChip,
991
- EntityCard,
992
- EmptyState,
993
- LoaderBlock,
994
- ErrorState,
995
- Button as Button3
996
- } from "@contractspec/lib.design-system";
997
- import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
998
- "use client";
999
- function getStatusTone(status) {
1000
- switch (status) {
1001
- case "ACTIVE":
1002
- return "success";
1003
- case "DRAFT":
1004
- return "neutral";
1005
- case "ARCHIVED":
1006
- return "warning";
1007
- default:
1008
- return "neutral";
1009
- }
1194
+ // src/ui/overlays/demo-overlays.ts
1195
+ var saasFreeUserOverlay = {
1196
+ overlayId: "saas-boilerplate.free-tier",
1197
+ version: "1.0.0",
1198
+ description: "Shows limitations for free tier users",
1199
+ appliesTo: {
1200
+ feature: "saas-boilerplate",
1201
+ tier: "free"
1202
+ },
1203
+ modifications: [
1204
+ {
1205
+ type: "setLimit",
1206
+ field: "projects",
1207
+ max: 3,
1208
+ message: "Upgrade to create more projects"
1209
+ },
1210
+ { type: "hideField", field: "advancedSettings", reason: "Pro feature" },
1211
+ {
1212
+ type: "addBadge",
1213
+ position: "header",
1214
+ label: "Free Plan",
1215
+ variant: "default"
1216
+ }
1217
+ ]
1218
+ };
1219
+ var saasDemoOverlay = {
1220
+ overlayId: "saas-boilerplate.demo-user",
1221
+ version: "1.0.0",
1222
+ description: "Demo mode for SaaS boilerplate",
1223
+ appliesTo: {
1224
+ feature: "saas-boilerplate",
1225
+ role: "demo"
1226
+ },
1227
+ modifications: [
1228
+ {
1229
+ type: "hideField",
1230
+ field: "billingSection",
1231
+ reason: "Demo users cannot access billing"
1232
+ },
1233
+ {
1234
+ type: "hideField",
1235
+ field: "deleteAccount",
1236
+ reason: "Not available in demo"
1237
+ },
1238
+ {
1239
+ type: "addBadge",
1240
+ position: "header",
1241
+ label: "Demo Mode",
1242
+ variant: "warning"
1243
+ }
1244
+ ]
1245
+ };
1246
+ var saasOverlays = [
1247
+ saasFreeUserOverlay,
1248
+ saasDemoOverlay
1249
+ ];
1250
+ // src/ui/renderers/project-list.markdown.ts
1251
+ var PROJECT_TIERS = [
1252
+ "FREE",
1253
+ "PRO",
1254
+ "ENTERPRISE"
1255
+ ];
1256
+ function toVisualizationProject(project, index) {
1257
+ return {
1258
+ status: project.status === "DELETED" ? "ARCHIVED" : project.status,
1259
+ tier: PROJECT_TIERS[index % PROJECT_TIERS.length] ?? "FREE",
1260
+ createdAt: project.createdAt
1261
+ };
1010
1262
  }
1011
- function SaasDashboard() {
1012
- const [activeTab, setActiveTab] = useState5("projects");
1013
- const [isCreateModalOpen, setIsCreateModalOpen] = useState5(false);
1014
- const [selectedProject, setSelectedProject] = useState5(null);
1015
- const [isProjectActionsOpen, setIsProjectActionsOpen] = useState5(false);
1016
- const { data, subscription, loading, error, stats, refetch } = useProjectList();
1017
- const mutations = useProjectMutations({
1018
- onSuccess: () => {
1019
- refetch();
1263
+ var projectListMarkdownRenderer = {
1264
+ target: "markdown",
1265
+ render: async (desc, _ctx) => {
1266
+ if (desc.source.type !== "component" || desc.source.componentKey !== "ProjectListView") {
1267
+ throw new Error("projectListMarkdownRenderer: not ProjectListView");
1020
1268
  }
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
- ];
1031
- if (loading && !data) {
1032
- return /* @__PURE__ */ jsxDEV3(LoaderBlock, {
1033
- label: "Loading dashboard..."
1034
- }, undefined, false, undefined, this);
1269
+ const data = await mockListProjectsHandler({
1270
+ limit: 20,
1271
+ offset: 0
1272
+ });
1273
+ const items = data.projects ?? [];
1274
+ const lines = [
1275
+ "# Projects",
1276
+ "",
1277
+ `**Total**: ${data.total} projects`,
1278
+ ""
1279
+ ];
1280
+ if (items.length === 0) {
1281
+ lines.push("_No projects found._");
1282
+ } else {
1283
+ lines.push("| Status | Project | Description |");
1284
+ lines.push("|--------|---------|-------------|");
1285
+ for (const project of items) {
1286
+ const status = project.status === "ACTIVE" ? "\u2705" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "\u23F8\uFE0F";
1287
+ lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
1288
+ }
1289
+ }
1290
+ return {
1291
+ mimeType: "text/markdown",
1292
+ body: lines.join(`
1293
+ `)
1294
+ };
1035
1295
  }
1036
- if (error) {
1037
- return /* @__PURE__ */ jsxDEV3(ErrorState, {
1038
- title: "Failed to load dashboard",
1039
- description: error.message,
1040
- onRetry: refetch,
1041
- retryLabel: "Retry"
1042
- }, undefined, false, undefined, this);
1296
+ };
1297
+ var saasDashboardMarkdownRenderer = {
1298
+ target: "markdown",
1299
+ render: async (desc, _ctx) => {
1300
+ if (desc.source.type !== "component" || desc.source.componentKey !== "SaasDashboard") {
1301
+ throw new Error("saasDashboardMarkdownRenderer: not SaasDashboard");
1302
+ }
1303
+ const [projectsData, subscription] = await Promise.all([
1304
+ mockListProjectsHandler({ limit: 50 }),
1305
+ mockGetSubscriptionHandler()
1306
+ ]);
1307
+ const projects = projectsData.projects ?? [];
1308
+ const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
1309
+ const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
1310
+ const visualizations = createSaasVisualizationItems(projects.map(toVisualizationProject), 10);
1311
+ const lines = [
1312
+ "# SaaS Dashboard",
1313
+ "",
1314
+ "> Organization overview and usage summary",
1315
+ "",
1316
+ "## Summary",
1317
+ "",
1318
+ "| Metric | Value |",
1319
+ "|--------|-------|",
1320
+ `| Total Projects | ${projectsData.total} |`,
1321
+ `| Active Projects | ${activeProjects} |`,
1322
+ `| Archived Projects | ${archivedProjects} |`,
1323
+ `| Subscription Plan | ${subscription.planName} |`,
1324
+ `| Subscription Status | ${subscription.status} |`,
1325
+ ""
1326
+ ];
1327
+ lines.push("## Visualization Overview");
1328
+ lines.push("");
1329
+ for (const item of visualizations) {
1330
+ lines.push(`- **${item.title}** via \`${item.spec.meta.key}\``);
1331
+ }
1332
+ lines.push("");
1333
+ lines.push("## Projects");
1334
+ lines.push("");
1335
+ if (projects.length === 0) {
1336
+ lines.push("_No projects yet._");
1337
+ } else {
1338
+ lines.push("| Status | Project | Description |");
1339
+ lines.push("|--------|---------|-------------|");
1340
+ for (const project of projects.slice(0, 10)) {
1341
+ const status = project.status === "ACTIVE" ? "\u2705" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "\u23F8\uFE0F";
1342
+ lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
1343
+ }
1344
+ if (projects.length > 10) {
1345
+ lines.push(`| ... | ... | _${projectsData.total - 10} more projects_ |`);
1346
+ }
1347
+ }
1348
+ lines.push("");
1349
+ lines.push("## Subscription");
1350
+ lines.push("");
1351
+ lines.push(`- **Plan**: ${subscription.planName}`);
1352
+ lines.push(`- **Status**: ${subscription.status}`);
1353
+ if (subscription.currentPeriodEnd) {
1354
+ lines.push(`- **Period End**: ${new Date(subscription.currentPeriodEnd).toLocaleDateString()}`);
1355
+ }
1356
+ return {
1357
+ mimeType: "text/markdown",
1358
+ body: lines.join(`
1359
+ `)
1360
+ };
1043
1361
  }
1044
- return /* @__PURE__ */ jsxDEV3("div", {
1045
- className: "space-y-6",
1046
- children: [
1047
- /* @__PURE__ */ jsxDEV3("div", {
1048
- className: "flex items-center justify-between",
1049
- children: [
1050
- /* @__PURE__ */ jsxDEV3("h2", {
1051
- className: "text-2xl font-bold",
1052
- children: "SaaS Dashboard"
1053
- }, undefined, false, undefined, this),
1054
- activeTab === "projects" && /* @__PURE__ */ jsxDEV3(Button3, {
1055
- onPress: () => setIsCreateModalOpen(true),
1056
- children: [
1057
- /* @__PURE__ */ jsxDEV3("span", {
1058
- className: "mr-2",
1059
- children: "+"
1362
+ };
1363
+ var saasBillingMarkdownRenderer = {
1364
+ target: "markdown",
1365
+ render: async (desc, _ctx) => {
1366
+ if (desc.source.type !== "component" || desc.source.componentKey !== "SubscriptionView") {
1367
+ throw new Error("saasBillingMarkdownRenderer: not SubscriptionView");
1368
+ }
1369
+ const subscription = await mockGetSubscriptionHandler();
1370
+ const lines = [
1371
+ "# Billing & Subscription",
1372
+ "",
1373
+ "> Current subscription details and billing information",
1374
+ "",
1375
+ "## Subscription Details",
1376
+ "",
1377
+ "| Property | Value |",
1378
+ "|----------|-------|",
1379
+ `| Plan | ${subscription.planName} |`,
1380
+ `| Status | ${subscription.status} |`,
1381
+ `| ID | ${subscription.id} |`,
1382
+ `| Period Start | ${new Date(subscription.currentPeriodStart).toLocaleDateString()} |`,
1383
+ `| Period End | ${new Date(subscription.currentPeriodEnd).toLocaleDateString()} |`
1384
+ ];
1385
+ lines.push("");
1386
+ lines.push("## Plan Limits");
1387
+ lines.push("");
1388
+ lines.push(`- **Projects**: ${subscription.limits.projects}`);
1389
+ lines.push(`- **Users**: ${subscription.limits.users}`);
1390
+ lines.push("");
1391
+ lines.push("## Plan Features");
1392
+ lines.push("");
1393
+ if (subscription.planName.toLowerCase().includes("free")) {
1394
+ lines.push("- \u2705 Up to 3 projects");
1395
+ lines.push("- \u2705 Basic support");
1396
+ lines.push("- \u274C Priority support");
1397
+ lines.push("- \u274C Advanced analytics");
1398
+ } else if (subscription.planName.toLowerCase().includes("pro")) {
1399
+ lines.push("- \u2705 Unlimited projects");
1400
+ lines.push("- \u2705 Priority support");
1401
+ lines.push("- \u2705 Advanced analytics");
1402
+ lines.push("- \u274C Custom integrations");
1403
+ } else {
1404
+ lines.push("- \u2705 Unlimited projects");
1405
+ lines.push("- \u2705 Priority support");
1406
+ lines.push("- \u2705 Advanced analytics");
1407
+ lines.push("- \u2705 Custom integrations");
1408
+ lines.push("- \u2705 Dedicated support");
1409
+ }
1410
+ return {
1411
+ mimeType: "text/markdown",
1412
+ body: lines.join(`
1413
+ `)
1414
+ };
1415
+ }
1416
+ };
1417
+
1418
+ // src/ui/SaasProjectList.tsx
1419
+ import {
1420
+ Button as Button3,
1421
+ EmptyState,
1422
+ EntityCard,
1423
+ ErrorState,
1424
+ LoaderBlock,
1425
+ StatCard,
1426
+ StatCardGroup,
1427
+ StatusChip
1428
+ } from "@contractspec/lib.design-system";
1429
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
1430
+ "use client";
1431
+ function getStatusTone(status) {
1432
+ switch (status) {
1433
+ case "ACTIVE":
1434
+ return "success";
1435
+ case "DRAFT":
1436
+ return "neutral";
1437
+ case "ARCHIVED":
1438
+ return "danger";
1439
+ default:
1440
+ return "neutral";
1441
+ }
1442
+ }
1443
+ function SaasProjectList({
1444
+ onProjectClick,
1445
+ onCreateProject
1446
+ }) {
1447
+ const { data, loading, error, stats, refetch } = useProjectList();
1448
+ if (loading && !data) {
1449
+ return /* @__PURE__ */ jsxDEV3(LoaderBlock, {
1450
+ label: "Loading projects..."
1451
+ }, undefined, false, undefined, this);
1452
+ }
1453
+ if (error) {
1454
+ return /* @__PURE__ */ jsxDEV3(ErrorState, {
1455
+ title: "Failed to load projects",
1456
+ description: error.message,
1457
+ onRetry: refetch,
1458
+ retryLabel: "Retry"
1459
+ }, undefined, false, undefined, this);
1460
+ }
1461
+ if (!data?.items.length) {
1462
+ return /* @__PURE__ */ jsxDEV3(EmptyState, {
1463
+ title: "No projects found",
1464
+ description: "Create your first project to get started.",
1465
+ primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV3(Button3, {
1466
+ onPress: onCreateProject,
1467
+ children: "Create Project"
1468
+ }, undefined, false, undefined, this) : undefined
1469
+ }, undefined, false, undefined, this);
1470
+ }
1471
+ return /* @__PURE__ */ jsxDEV3("div", {
1472
+ className: "space-y-6",
1473
+ children: [
1474
+ stats && /* @__PURE__ */ jsxDEV3(StatCardGroup, {
1475
+ children: [
1476
+ /* @__PURE__ */ jsxDEV3(StatCard, {
1477
+ label: "Total Projects",
1478
+ value: stats.total.toString()
1479
+ }, undefined, false, undefined, this),
1480
+ /* @__PURE__ */ jsxDEV3(StatCard, {
1481
+ label: "Active",
1482
+ value: stats.activeCount.toString()
1483
+ }, undefined, false, undefined, this),
1484
+ /* @__PURE__ */ jsxDEV3(StatCard, {
1485
+ label: "Draft",
1486
+ value: stats.draftCount.toString()
1487
+ }, undefined, false, undefined, this)
1488
+ ]
1489
+ }, undefined, true, undefined, this),
1490
+ /* @__PURE__ */ jsxDEV3("div", {
1491
+ className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
1492
+ children: data.items.map((project) => /* @__PURE__ */ jsxDEV3(EntityCard, {
1493
+ cardTitle: project.name,
1494
+ cardSubtitle: project.tier,
1495
+ meta: /* @__PURE__ */ jsxDEV3("p", {
1496
+ className: "text-muted-foreground text-sm",
1497
+ children: project.description
1498
+ }, undefined, false, undefined, this),
1499
+ chips: /* @__PURE__ */ jsxDEV3(StatusChip, {
1500
+ tone: getStatusTone(project.status),
1501
+ label: project.status
1502
+ }, undefined, false, undefined, this),
1503
+ footer: /* @__PURE__ */ jsxDEV3("span", {
1504
+ className: "text-muted-foreground text-xs",
1505
+ children: project.updatedAt.toLocaleDateString()
1506
+ }, undefined, false, undefined, this),
1507
+ onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
1508
+ }, project.id, false, undefined, this))
1509
+ }, undefined, false, undefined, this)
1510
+ ]
1511
+ }, undefined, true, undefined, this);
1512
+ }
1513
+
1514
+ // src/ui/renderers/project-list.renderer.tsx
1515
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
1516
+ var projectListReactRenderer = {
1517
+ target: "react",
1518
+ render: async (desc, _ctx) => {
1519
+ if (desc.source.type !== "component") {
1520
+ throw new Error("Invalid source type");
1521
+ }
1522
+ if (desc.source.componentKey !== "SaasProjectListView") {
1523
+ throw new Error(`Unknown component: ${desc.source.componentKey}`);
1524
+ }
1525
+ return /* @__PURE__ */ jsxDEV4(SaasProjectList, {}, undefined, false, undefined, this);
1526
+ }
1527
+ };
1528
+ // src/ui/SaasDashboard.visualizations.tsx
1529
+ import {
1530
+ VisualizationCard,
1531
+ VisualizationGrid
1532
+ } from "@contractspec/lib.design-system";
1533
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
1534
+ "use client";
1535
+ function SaasVisualizationOverview({
1536
+ projects,
1537
+ projectLimit
1538
+ }) {
1539
+ const items = createSaasVisualizationItems(projects, projectLimit);
1540
+ return /* @__PURE__ */ jsxDEV5("section", {
1541
+ className: "space-y-3",
1542
+ children: [
1543
+ /* @__PURE__ */ jsxDEV5("div", {
1544
+ children: [
1545
+ /* @__PURE__ */ jsxDEV5("h3", {
1546
+ className: "font-semibold text-lg",
1547
+ children: "Portfolio Visualizations"
1548
+ }, undefined, false, undefined, this),
1549
+ /* @__PURE__ */ jsxDEV5("p", {
1550
+ className: "text-muted-foreground text-sm",
1551
+ children: "Contract-backed charts for project mix, capacity, and activity."
1552
+ }, undefined, false, undefined, this)
1553
+ ]
1554
+ }, undefined, true, undefined, this),
1555
+ /* @__PURE__ */ jsxDEV5(VisualizationGrid, {
1556
+ children: items.map((item) => /* @__PURE__ */ jsxDEV5(VisualizationCard, {
1557
+ data: item.data,
1558
+ description: item.description,
1559
+ height: item.height,
1560
+ spec: item.spec,
1561
+ title: item.title
1562
+ }, item.key, false, undefined, this))
1563
+ }, undefined, false, undefined, this)
1564
+ ]
1565
+ }, undefined, true, undefined, this);
1566
+ }
1567
+
1568
+ // src/ui/SaasDashboard.tsx
1569
+ import {
1570
+ Button as Button4,
1571
+ EmptyState as EmptyState2,
1572
+ EntityCard as EntityCard2,
1573
+ ErrorState as ErrorState2,
1574
+ LoaderBlock as LoaderBlock2,
1575
+ StatCard as StatCard2,
1576
+ StatCardGroup as StatCardGroup2,
1577
+ StatusChip as StatusChip2
1578
+ } from "@contractspec/lib.design-system";
1579
+ import { useCallback as useCallback3, useState as useState5 } from "react";
1580
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
1581
+ "use client";
1582
+ function getStatusTone2(status) {
1583
+ switch (status) {
1584
+ case "ACTIVE":
1585
+ return "success";
1586
+ case "DRAFT":
1587
+ return "neutral";
1588
+ case "ARCHIVED":
1589
+ return "warning";
1590
+ default:
1591
+ return "neutral";
1592
+ }
1593
+ }
1594
+ function SaasDashboard() {
1595
+ const [activeTab, setActiveTab] = useState5("projects");
1596
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState5(false);
1597
+ const [selectedProject, setSelectedProject] = useState5(null);
1598
+ const [isProjectActionsOpen, setIsProjectActionsOpen] = useState5(false);
1599
+ const { data, subscription, loading, error, stats, refetch } = useProjectList();
1600
+ const mutations = useProjectMutations({
1601
+ onSuccess: () => {
1602
+ refetch();
1603
+ }
1604
+ });
1605
+ const handleProjectClick = useCallback3((project) => {
1606
+ setSelectedProject(project);
1607
+ setIsProjectActionsOpen(true);
1608
+ }, []);
1609
+ const tabs = [
1610
+ { id: "projects", label: "Projects", icon: "\uD83D\uDCC1" },
1611
+ { id: "billing", label: "Billing", icon: "\uD83D\uDCB3" },
1612
+ { id: "settings", label: "Settings", icon: "\u2699\uFE0F" }
1613
+ ];
1614
+ if (loading && !data) {
1615
+ return /* @__PURE__ */ jsxDEV6(LoaderBlock2, {
1616
+ label: "Loading dashboard..."
1617
+ }, undefined, false, undefined, this);
1618
+ }
1619
+ if (error) {
1620
+ return /* @__PURE__ */ jsxDEV6(ErrorState2, {
1621
+ title: "Failed to load dashboard",
1622
+ description: error.message,
1623
+ onRetry: refetch,
1624
+ retryLabel: "Retry"
1625
+ }, undefined, false, undefined, this);
1626
+ }
1627
+ return /* @__PURE__ */ jsxDEV6("div", {
1628
+ className: "space-y-6",
1629
+ children: [
1630
+ /* @__PURE__ */ jsxDEV6("div", {
1631
+ className: "flex items-center justify-between",
1632
+ children: [
1633
+ /* @__PURE__ */ jsxDEV6("h2", {
1634
+ className: "font-bold text-2xl",
1635
+ children: "SaaS Dashboard"
1636
+ }, undefined, false, undefined, this),
1637
+ activeTab === "projects" && /* @__PURE__ */ jsxDEV6(Button4, {
1638
+ onPress: () => setIsCreateModalOpen(true),
1639
+ children: [
1640
+ /* @__PURE__ */ jsxDEV6("span", {
1641
+ className: "mr-2",
1642
+ children: "+"
1060
1643
  }, undefined, false, undefined, this),
1061
1644
  " New Project"
1062
1645
  ]
1063
1646
  }, undefined, true, undefined, this)
1064
1647
  ]
1065
1648
  }, undefined, true, undefined, this),
1066
- stats && subscription && /* @__PURE__ */ jsxDEV3(StatCardGroup, {
1649
+ stats && subscription && /* @__PURE__ */ jsxDEV6(StatCardGroup2, {
1067
1650
  children: [
1068
- /* @__PURE__ */ jsxDEV3(StatCard, {
1651
+ /* @__PURE__ */ jsxDEV6(StatCard2, {
1069
1652
  label: "Projects",
1070
1653
  value: stats.total.toString()
1071
1654
  }, undefined, false, undefined, this),
1072
- /* @__PURE__ */ jsxDEV3(StatCard, {
1655
+ /* @__PURE__ */ jsxDEV6(StatCard2, {
1073
1656
  label: "Active",
1074
1657
  value: stats.activeCount.toString()
1075
1658
  }, undefined, false, undefined, this),
1076
- /* @__PURE__ */ jsxDEV3(StatCard, {
1659
+ /* @__PURE__ */ jsxDEV6(StatCard2, {
1077
1660
  label: "Draft",
1078
1661
  value: stats.draftCount.toString()
1079
1662
  }, undefined, false, undefined, this),
1080
- /* @__PURE__ */ jsxDEV3(StatCard, {
1663
+ /* @__PURE__ */ jsxDEV6(StatCard2, {
1081
1664
  label: "Plan",
1082
1665
  value: subscription.plan,
1083
1666
  hint: subscription.status
1084
1667
  }, undefined, false, undefined, this)
1085
1668
  ]
1086
1669
  }, undefined, true, undefined, this),
1087
- /* @__PURE__ */ jsxDEV3("nav", {
1088
- className: "bg-muted flex gap-1 rounded-lg p-1",
1670
+ data && stats && /* @__PURE__ */ jsxDEV6(SaasVisualizationOverview, {
1671
+ projectLimit: stats.projectLimit,
1672
+ projects: data.items
1673
+ }, undefined, false, undefined, this),
1674
+ /* @__PURE__ */ jsxDEV6("nav", {
1675
+ className: "flex gap-1 rounded-lg bg-muted p-1",
1089
1676
  role: "tablist",
1090
- children: tabs.map((tab) => /* @__PURE__ */ jsxDEV3("button", {
1677
+ children: tabs.map((tab) => /* @__PURE__ */ jsxDEV6("button", {
1091
1678
  type: "button",
1092
1679
  role: "tab",
1093
1680
  "aria-selected": activeTab === tab.id,
1094
1681
  onClick: () => setActiveTab(tab.id),
1095
- className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors ${activeTab === tab.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
1682
+ 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
1683
  children: [
1097
- /* @__PURE__ */ jsxDEV3("span", {
1684
+ /* @__PURE__ */ jsxDEV6("span", {
1098
1685
  children: tab.icon
1099
1686
  }, undefined, false, undefined, this),
1100
1687
  tab.label
1101
1688
  ]
1102
1689
  }, tab.id, true, undefined, this))
1103
1690
  }, undefined, false, undefined, this),
1104
- /* @__PURE__ */ jsxDEV3("div", {
1691
+ /* @__PURE__ */ jsxDEV6("div", {
1105
1692
  className: "min-h-[400px]",
1106
1693
  role: "tabpanel",
1107
1694
  children: [
1108
- activeTab === "projects" && /* @__PURE__ */ jsxDEV3(ProjectsTab, {
1695
+ activeTab === "projects" && /* @__PURE__ */ jsxDEV6(ProjectsTab, {
1109
1696
  data,
1110
1697
  onProjectClick: handleProjectClick
1111
1698
  }, undefined, false, undefined, this),
1112
- activeTab === "billing" && /* @__PURE__ */ jsxDEV3(BillingTab, {
1699
+ activeTab === "billing" && /* @__PURE__ */ jsxDEV6(BillingTab, {
1113
1700
  subscription
1114
1701
  }, undefined, false, undefined, this),
1115
- activeTab === "settings" && /* @__PURE__ */ jsxDEV3(SettingsTab, {}, undefined, false, undefined, this)
1702
+ activeTab === "settings" && /* @__PURE__ */ jsxDEV6(SettingsTab, {}, undefined, false, undefined, this)
1116
1703
  ]
1117
1704
  }, undefined, true, undefined, this),
1118
- /* @__PURE__ */ jsxDEV3(CreateProjectModal, {
1705
+ /* @__PURE__ */ jsxDEV6(CreateProjectModal, {
1119
1706
  isOpen: isCreateModalOpen,
1120
1707
  onClose: () => setIsCreateModalOpen(false),
1121
1708
  onSubmit: async (input) => {
@@ -1123,7 +1710,7 @@ function SaasDashboard() {
1123
1710
  },
1124
1711
  isLoading: mutations.createState.loading
1125
1712
  }, undefined, false, undefined, this),
1126
- /* @__PURE__ */ jsxDEV3(ProjectActionsModal, {
1713
+ /* @__PURE__ */ jsxDEV6(ProjectActionsModal, {
1127
1714
  isOpen: isProjectActionsOpen,
1128
1715
  project: selectedProject,
1129
1716
  onClose: () => {
@@ -1149,34 +1736,34 @@ function SaasDashboard() {
1149
1736
  }
1150
1737
  function ProjectsTab({ data, onProjectClick }) {
1151
1738
  if (!data?.items.length) {
1152
- return /* @__PURE__ */ jsxDEV3(EmptyState, {
1739
+ return /* @__PURE__ */ jsxDEV6(EmptyState2, {
1153
1740
  title: "No projects yet",
1154
1741
  description: "Create your first project to get started."
1155
1742
  }, undefined, false, undefined, this);
1156
1743
  }
1157
- return /* @__PURE__ */ jsxDEV3("div", {
1744
+ return /* @__PURE__ */ jsxDEV6("div", {
1158
1745
  className: "space-y-4",
1159
- children: /* @__PURE__ */ jsxDEV3("div", {
1746
+ children: /* @__PURE__ */ jsxDEV6("div", {
1160
1747
  className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
1161
- children: data.items.map((project) => /* @__PURE__ */ jsxDEV3(EntityCard, {
1748
+ children: data.items.map((project) => /* @__PURE__ */ jsxDEV6(EntityCard2, {
1162
1749
  cardTitle: project.name,
1163
1750
  cardSubtitle: project.tier,
1164
- meta: /* @__PURE__ */ jsxDEV3("p", {
1751
+ meta: /* @__PURE__ */ jsxDEV6("p", {
1165
1752
  className: "text-muted-foreground text-sm",
1166
1753
  children: project.description
1167
1754
  }, undefined, false, undefined, this),
1168
- chips: /* @__PURE__ */ jsxDEV3(StatusChip, {
1169
- tone: getStatusTone(project.status),
1755
+ chips: /* @__PURE__ */ jsxDEV6(StatusChip2, {
1756
+ tone: getStatusTone2(project.status),
1170
1757
  label: project.status
1171
1758
  }, undefined, false, undefined, this),
1172
- footer: /* @__PURE__ */ jsxDEV3("div", {
1759
+ footer: /* @__PURE__ */ jsxDEV6("div", {
1173
1760
  className: "flex w-full items-center justify-between",
1174
1761
  children: [
1175
- /* @__PURE__ */ jsxDEV3("span", {
1762
+ /* @__PURE__ */ jsxDEV6("span", {
1176
1763
  className: "text-muted-foreground text-xs",
1177
1764
  children: project.updatedAt.toLocaleDateString()
1178
1765
  }, undefined, false, undefined, this),
1179
- /* @__PURE__ */ jsxDEV3(Button3, {
1766
+ /* @__PURE__ */ jsxDEV6(Button4, {
1180
1767
  variant: "ghost",
1181
1768
  size: "sm",
1182
1769
  onPress: () => onProjectClick?.(project),
@@ -1191,25 +1778,25 @@ function ProjectsTab({ data, onProjectClick }) {
1191
1778
  function BillingTab({ subscription }) {
1192
1779
  if (!subscription)
1193
1780
  return null;
1194
- return /* @__PURE__ */ jsxDEV3("div", {
1781
+ return /* @__PURE__ */ jsxDEV6("div", {
1195
1782
  className: "space-y-6",
1196
1783
  children: [
1197
- /* @__PURE__ */ jsxDEV3("div", {
1198
- className: "border-border bg-card rounded-xl border p-6",
1784
+ /* @__PURE__ */ jsxDEV6("div", {
1785
+ className: "rounded-xl border border-border bg-card p-6",
1199
1786
  children: [
1200
- /* @__PURE__ */ jsxDEV3("div", {
1787
+ /* @__PURE__ */ jsxDEV6("div", {
1201
1788
  className: "flex items-start justify-between",
1202
1789
  children: [
1203
- /* @__PURE__ */ jsxDEV3("div", {
1790
+ /* @__PURE__ */ jsxDEV6("div", {
1204
1791
  children: [
1205
- /* @__PURE__ */ jsxDEV3("h3", {
1206
- className: "text-lg font-semibold",
1792
+ /* @__PURE__ */ jsxDEV6("h3", {
1793
+ className: "font-semibold text-lg",
1207
1794
  children: [
1208
1795
  subscription.plan,
1209
1796
  " Plan"
1210
1797
  ]
1211
1798
  }, undefined, true, undefined, this),
1212
- /* @__PURE__ */ jsxDEV3("p", {
1799
+ /* @__PURE__ */ jsxDEV6("p", {
1213
1800
  className: "text-muted-foreground text-sm",
1214
1801
  children: [
1215
1802
  "Current period:",
@@ -1220,7 +1807,7 @@ function BillingTab({ subscription }) {
1220
1807
  subscription.currentPeriodEnd.toLocaleDateString()
1221
1808
  ]
1222
1809
  }, undefined, true, undefined, this),
1223
- /* @__PURE__ */ jsxDEV3("p", {
1810
+ /* @__PURE__ */ jsxDEV6("p", {
1224
1811
  className: "text-muted-foreground text-sm",
1225
1812
  children: [
1226
1813
  "Billing cycle: ",
@@ -1229,21 +1816,21 @@ function BillingTab({ subscription }) {
1229
1816
  }, undefined, true, undefined, this)
1230
1817
  ]
1231
1818
  }, undefined, true, undefined, this),
1232
- /* @__PURE__ */ jsxDEV3(StatusChip, {
1819
+ /* @__PURE__ */ jsxDEV6(StatusChip2, {
1233
1820
  tone: "success",
1234
1821
  label: subscription.status
1235
1822
  }, undefined, false, undefined, this)
1236
1823
  ]
1237
1824
  }, undefined, true, undefined, this),
1238
- /* @__PURE__ */ jsxDEV3("div", {
1825
+ /* @__PURE__ */ jsxDEV6("div", {
1239
1826
  className: "mt-4 flex gap-3",
1240
1827
  children: [
1241
- /* @__PURE__ */ jsxDEV3(Button3, {
1828
+ /* @__PURE__ */ jsxDEV6(Button4, {
1242
1829
  variant: "outline",
1243
1830
  onPress: () => alert("Upgrade clicked!"),
1244
1831
  children: "Upgrade Plan"
1245
1832
  }, undefined, false, undefined, this),
1246
- /* @__PURE__ */ jsxDEV3(Button3, {
1833
+ /* @__PURE__ */ jsxDEV6(Button4, {
1247
1834
  variant: "ghost",
1248
1835
  onPress: () => alert("Manage Billing clicked!"),
1249
1836
  children: "Manage Billing"
@@ -1252,10 +1839,10 @@ function BillingTab({ subscription }) {
1252
1839
  }, undefined, true, undefined, this)
1253
1840
  ]
1254
1841
  }, undefined, true, undefined, this),
1255
- subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsxDEV3("div", {
1256
- className: "border-border bg-destructive/10 text-destructive rounded-xl border p-4",
1257
- children: /* @__PURE__ */ jsxDEV3("p", {
1258
- className: "text-sm font-medium",
1842
+ subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsxDEV6("div", {
1843
+ className: "rounded-xl border border-border bg-destructive/10 p-4 text-destructive",
1844
+ children: /* @__PURE__ */ jsxDEV6("p", {
1845
+ className: "font-medium text-sm",
1259
1846
  children: "\u26A0\uFE0F Your subscription will be cancelled at the end of the current period."
1260
1847
  }, undefined, false, undefined, this)
1261
1848
  }, undefined, false, undefined, this)
@@ -1263,63 +1850,63 @@ function BillingTab({ subscription }) {
1263
1850
  }, undefined, true, undefined, this);
1264
1851
  }
1265
1852
  function SettingsTab() {
1266
- return /* @__PURE__ */ jsxDEV3("div", {
1853
+ return /* @__PURE__ */ jsxDEV6("div", {
1267
1854
  className: "space-y-6",
1268
- children: /* @__PURE__ */ jsxDEV3("div", {
1269
- className: "border-border bg-card rounded-xl border p-6",
1855
+ children: /* @__PURE__ */ jsxDEV6("div", {
1856
+ className: "rounded-xl border border-border bg-card p-6",
1270
1857
  children: [
1271
- /* @__PURE__ */ jsxDEV3("h3", {
1272
- className: "mb-4 text-lg font-semibold",
1858
+ /* @__PURE__ */ jsxDEV6("h3", {
1859
+ className: "mb-4 font-semibold text-lg",
1273
1860
  children: "Organization Settings"
1274
1861
  }, undefined, false, undefined, this),
1275
- /* @__PURE__ */ jsxDEV3("div", {
1862
+ /* @__PURE__ */ jsxDEV6("div", {
1276
1863
  className: "space-y-4",
1277
1864
  children: [
1278
- /* @__PURE__ */ jsxDEV3("div", {
1865
+ /* @__PURE__ */ jsxDEV6("div", {
1279
1866
  children: [
1280
- /* @__PURE__ */ jsxDEV3("label", {
1867
+ /* @__PURE__ */ jsxDEV6("label", {
1281
1868
  htmlFor: "org-name",
1282
- className: "text-sm font-medium",
1869
+ className: "font-medium text-sm",
1283
1870
  children: "Organization Name"
1284
1871
  }, undefined, false, undefined, this),
1285
- /* @__PURE__ */ jsxDEV3("input", {
1872
+ /* @__PURE__ */ jsxDEV6("input", {
1286
1873
  id: "org-name",
1287
1874
  type: "text",
1288
1875
  defaultValue: "Demo Organization",
1289
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2"
1876
+ className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2"
1290
1877
  }, undefined, false, undefined, this)
1291
1878
  ]
1292
1879
  }, undefined, true, undefined, this),
1293
- /* @__PURE__ */ jsxDEV3("div", {
1880
+ /* @__PURE__ */ jsxDEV6("div", {
1294
1881
  children: [
1295
- /* @__PURE__ */ jsxDEV3("label", {
1882
+ /* @__PURE__ */ jsxDEV6("label", {
1296
1883
  htmlFor: "timezone",
1297
- className: "text-sm font-medium",
1884
+ className: "font-medium text-sm",
1298
1885
  children: "Default Timezone"
1299
1886
  }, undefined, false, undefined, this),
1300
- /* @__PURE__ */ jsxDEV3("select", {
1887
+ /* @__PURE__ */ jsxDEV6("select", {
1301
1888
  id: "timezone",
1302
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2",
1889
+ className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2",
1303
1890
  children: [
1304
- /* @__PURE__ */ jsxDEV3("option", {
1891
+ /* @__PURE__ */ jsxDEV6("option", {
1305
1892
  children: "UTC"
1306
1893
  }, undefined, false, undefined, this),
1307
- /* @__PURE__ */ jsxDEV3("option", {
1894
+ /* @__PURE__ */ jsxDEV6("option", {
1308
1895
  children: "America/New_York"
1309
1896
  }, undefined, false, undefined, this),
1310
- /* @__PURE__ */ jsxDEV3("option", {
1897
+ /* @__PURE__ */ jsxDEV6("option", {
1311
1898
  children: "Europe/London"
1312
1899
  }, undefined, false, undefined, this),
1313
- /* @__PURE__ */ jsxDEV3("option", {
1900
+ /* @__PURE__ */ jsxDEV6("option", {
1314
1901
  children: "Asia/Tokyo"
1315
1902
  }, undefined, false, undefined, this)
1316
1903
  ]
1317
1904
  }, undefined, true, undefined, this)
1318
1905
  ]
1319
1906
  }, undefined, true, undefined, this),
1320
- /* @__PURE__ */ jsxDEV3("div", {
1907
+ /* @__PURE__ */ jsxDEV6("div", {
1321
1908
  className: "pt-2",
1322
- children: /* @__PURE__ */ jsxDEV3(Button3, {
1909
+ children: /* @__PURE__ */ jsxDEV6(Button4, {
1323
1910
  onPress: () => alert("Settings saved!"),
1324
1911
  children: "Save Settings"
1325
1912
  }, undefined, false, undefined, this)
@@ -1331,165 +1918,69 @@ function SettingsTab() {
1331
1918
  }, undefined, false, undefined, this);
1332
1919
  }
1333
1920
 
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
1921
  // src/ui/SaasSettingsPanel.tsx
1431
- import { useState as useState6 } from "react";
1432
1922
  import { Button as Button5 } from "@contractspec/lib.design-system";
1433
- import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
1923
+ import { useState as useState6 } from "react";
1924
+ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
1434
1925
  "use client";
1435
1926
  function SaasSettingsPanel() {
1436
1927
  const [orgName, setOrgName] = useState6("Demo Organization");
1437
1928
  const [timezone, setTimezone] = useState6("UTC");
1438
- return /* @__PURE__ */ jsxDEV5("div", {
1929
+ return /* @__PURE__ */ jsxDEV7("div", {
1439
1930
  className: "space-y-6",
1440
1931
  children: [
1441
- /* @__PURE__ */ jsxDEV5("div", {
1442
- className: "border-border bg-card rounded-xl border p-6",
1932
+ /* @__PURE__ */ jsxDEV7("div", {
1933
+ className: "rounded-xl border border-border bg-card p-6",
1443
1934
  children: [
1444
- /* @__PURE__ */ jsxDEV5("h3", {
1445
- className: "mb-4 text-lg font-semibold",
1935
+ /* @__PURE__ */ jsxDEV7("h3", {
1936
+ className: "mb-4 font-semibold text-lg",
1446
1937
  children: "Organization Settings"
1447
1938
  }, undefined, false, undefined, this),
1448
- /* @__PURE__ */ jsxDEV5("div", {
1939
+ /* @__PURE__ */ jsxDEV7("div", {
1449
1940
  className: "space-y-4",
1450
1941
  children: [
1451
- /* @__PURE__ */ jsxDEV5("div", {
1942
+ /* @__PURE__ */ jsxDEV7("div", {
1452
1943
  children: [
1453
- /* @__PURE__ */ jsxDEV5("label", {
1944
+ /* @__PURE__ */ jsxDEV7("label", {
1454
1945
  htmlFor: "setting-org-name",
1455
- className: "block text-sm font-medium",
1946
+ className: "block font-medium text-sm",
1456
1947
  children: "Organization Name"
1457
1948
  }, undefined, false, undefined, this),
1458
- /* @__PURE__ */ jsxDEV5("input", {
1949
+ /* @__PURE__ */ jsxDEV7("input", {
1459
1950
  id: "setting-org-name",
1460
1951
  type: "text",
1461
1952
  value: orgName,
1462
1953
  onChange: (e) => setOrgName(e.target.value),
1463
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2"
1954
+ className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2"
1464
1955
  }, undefined, false, undefined, this)
1465
1956
  ]
1466
1957
  }, undefined, true, undefined, this),
1467
- /* @__PURE__ */ jsxDEV5("div", {
1958
+ /* @__PURE__ */ jsxDEV7("div", {
1468
1959
  children: [
1469
- /* @__PURE__ */ jsxDEV5("label", {
1960
+ /* @__PURE__ */ jsxDEV7("label", {
1470
1961
  htmlFor: "setting-timezone",
1471
- className: "block text-sm font-medium",
1962
+ className: "block font-medium text-sm",
1472
1963
  children: "Default Timezone"
1473
1964
  }, undefined, false, undefined, this),
1474
- /* @__PURE__ */ jsxDEV5("select", {
1965
+ /* @__PURE__ */ jsxDEV7("select", {
1475
1966
  id: "setting-timezone",
1476
1967
  value: timezone,
1477
1968
  onChange: (e) => setTimezone(e.target.value),
1478
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2",
1969
+ className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2",
1479
1970
  children: [
1480
- /* @__PURE__ */ jsxDEV5("option", {
1971
+ /* @__PURE__ */ jsxDEV7("option", {
1481
1972
  value: "UTC",
1482
1973
  children: "UTC"
1483
1974
  }, undefined, false, undefined, this),
1484
- /* @__PURE__ */ jsxDEV5("option", {
1975
+ /* @__PURE__ */ jsxDEV7("option", {
1485
1976
  value: "America/New_York",
1486
1977
  children: "America/New_York"
1487
1978
  }, undefined, false, undefined, this),
1488
- /* @__PURE__ */ jsxDEV5("option", {
1979
+ /* @__PURE__ */ jsxDEV7("option", {
1489
1980
  value: "Europe/London",
1490
1981
  children: "Europe/London"
1491
1982
  }, undefined, false, undefined, this),
1492
- /* @__PURE__ */ jsxDEV5("option", {
1983
+ /* @__PURE__ */ jsxDEV7("option", {
1493
1984
  value: "Asia/Tokyo",
1494
1985
  children: "Asia/Tokyo"
1495
1986
  }, undefined, false, undefined, this)
@@ -1499,37 +1990,37 @@ function SaasSettingsPanel() {
1499
1990
  }, undefined, true, undefined, this)
1500
1991
  ]
1501
1992
  }, undefined, true, undefined, this),
1502
- /* @__PURE__ */ jsxDEV5("div", {
1993
+ /* @__PURE__ */ jsxDEV7("div", {
1503
1994
  className: "mt-6",
1504
- children: /* @__PURE__ */ jsxDEV5(Button5, {
1995
+ children: /* @__PURE__ */ jsxDEV7(Button5, {
1505
1996
  variant: "default",
1506
1997
  children: "Save Changes"
1507
1998
  }, undefined, false, undefined, this)
1508
1999
  }, undefined, false, undefined, this)
1509
2000
  ]
1510
2001
  }, undefined, true, undefined, this),
1511
- /* @__PURE__ */ jsxDEV5("div", {
1512
- className: "border-border bg-card rounded-xl border p-6",
2002
+ /* @__PURE__ */ jsxDEV7("div", {
2003
+ className: "rounded-xl border border-border bg-card p-6",
1513
2004
  children: [
1514
- /* @__PURE__ */ jsxDEV5("h3", {
1515
- className: "mb-4 text-lg font-semibold",
2005
+ /* @__PURE__ */ jsxDEV7("h3", {
2006
+ className: "mb-4 font-semibold text-lg",
1516
2007
  children: "Notifications"
1517
2008
  }, undefined, false, undefined, this),
1518
- /* @__PURE__ */ jsxDEV5("div", {
2009
+ /* @__PURE__ */ jsxDEV7("div", {
1519
2010
  className: "space-y-3",
1520
2011
  children: [
1521
2012
  { label: "Email notifications", defaultChecked: true },
1522
2013
  { label: "Usage alerts", defaultChecked: true },
1523
2014
  { label: "Weekly digest", defaultChecked: false }
1524
- ].map((item) => /* @__PURE__ */ jsxDEV5("label", {
2015
+ ].map((item) => /* @__PURE__ */ jsxDEV7("label", {
1525
2016
  className: "flex items-center gap-3",
1526
2017
  children: [
1527
- /* @__PURE__ */ jsxDEV5("input", {
2018
+ /* @__PURE__ */ jsxDEV7("input", {
1528
2019
  type: "checkbox",
1529
2020
  defaultChecked: item.defaultChecked,
1530
- className: "border-input h-4 w-4 rounded"
2021
+ className: "h-4 w-4 rounded border-input"
1531
2022
  }, undefined, false, undefined, this),
1532
- /* @__PURE__ */ jsxDEV5("span", {
2023
+ /* @__PURE__ */ jsxDEV7("span", {
1533
2024
  className: "text-sm",
1534
2025
  children: item.label
1535
2026
  }, undefined, false, undefined, this)
@@ -1538,26 +2029,26 @@ function SaasSettingsPanel() {
1538
2029
  }, undefined, false, undefined, this)
1539
2030
  ]
1540
2031
  }, undefined, true, undefined, this),
1541
- /* @__PURE__ */ jsxDEV5("div", {
2032
+ /* @__PURE__ */ jsxDEV7("div", {
1542
2033
  className: "rounded-xl border border-red-200 bg-red-50 p-6 dark:border-red-900 dark:bg-red-950/20",
1543
2034
  children: [
1544
- /* @__PURE__ */ jsxDEV5("h3", {
1545
- className: "mb-2 text-lg font-semibold text-red-700 dark:text-red-400",
2035
+ /* @__PURE__ */ jsxDEV7("h3", {
2036
+ className: "mb-2 font-semibold text-lg text-red-700 dark:text-red-400",
1546
2037
  children: "Danger Zone"
1547
2038
  }, undefined, false, undefined, this),
1548
- /* @__PURE__ */ jsxDEV5("p", {
1549
- className: "mb-4 text-sm text-red-600 dark:text-red-300",
2039
+ /* @__PURE__ */ jsxDEV7("p", {
2040
+ className: "mb-4 text-red-600 text-sm dark:text-red-300",
1550
2041
  children: "These actions are irreversible. Please proceed with caution."
1551
2042
  }, undefined, false, undefined, this),
1552
- /* @__PURE__ */ jsxDEV5("div", {
2043
+ /* @__PURE__ */ jsxDEV7("div", {
1553
2044
  className: "flex gap-3",
1554
2045
  children: [
1555
- /* @__PURE__ */ jsxDEV5(Button5, {
2046
+ /* @__PURE__ */ jsxDEV7(Button5, {
1556
2047
  variant: "secondary",
1557
2048
  size: "sm",
1558
2049
  children: "Export Data"
1559
2050
  }, undefined, false, undefined, this),
1560
- /* @__PURE__ */ jsxDEV5(Button5, {
2051
+ /* @__PURE__ */ jsxDEV7(Button5, {
1561
2052
  variant: "secondary",
1562
2053
  size: "sm",
1563
2054
  children: "Delete Organization"
@@ -1569,228 +2060,6 @@ function SaasSettingsPanel() {
1569
2060
  ]
1570
2061
  }, undefined, true, undefined, this);
1571
2062
  }
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
2063
  export {
1795
2064
  useProjectMutations,
1796
2065
  useProjectList,