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