@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
@@ -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;
@@ -409,118 +616,19 @@ function useProjectList(options = {}) {
409
616
  };
410
617
  }
411
618
 
412
- // src/ui/SaasProjectList.tsx
413
- import {
414
- StatCard,
415
- StatCardGroup,
416
- StatusChip,
417
- EntityCard,
418
- EmptyState,
419
- LoaderBlock,
420
- ErrorState,
421
- Button
422
- } from "@contractspec/lib.design-system";
423
- import { jsxDEV } from "react/jsx-dev-runtime";
424
- "use client";
425
- function getStatusTone(status) {
426
- switch (status) {
427
- case "ACTIVE":
428
- return "success";
429
- case "DRAFT":
430
- return "neutral";
431
- case "ARCHIVED":
432
- return "danger";
433
- default:
434
- return "neutral";
435
- }
436
- }
437
- function SaasProjectList({
438
- onProjectClick,
439
- onCreateProject
440
- }) {
441
- const { data, loading, error, stats, refetch } = useProjectList();
442
- if (loading && !data) {
443
- return /* @__PURE__ */ jsxDEV(LoaderBlock, {
444
- label: "Loading projects..."
445
- }, undefined, false, undefined, this);
446
- }
447
- if (error) {
448
- return /* @__PURE__ */ jsxDEV(ErrorState, {
449
- title: "Failed to load projects",
450
- description: error.message,
451
- onRetry: refetch,
452
- retryLabel: "Retry"
453
- }, undefined, false, undefined, this);
454
- }
455
- if (!data?.items.length) {
456
- return /* @__PURE__ */ jsxDEV(EmptyState, {
457
- title: "No projects found",
458
- description: "Create your first project to get started.",
459
- primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV(Button, {
460
- onPress: onCreateProject,
461
- children: "Create Project"
462
- }, undefined, false, undefined, this) : undefined
463
- }, undefined, false, undefined, this);
464
- }
465
- return /* @__PURE__ */ jsxDEV("div", {
466
- className: "space-y-6",
467
- children: [
468
- stats && /* @__PURE__ */ jsxDEV(StatCardGroup, {
469
- children: [
470
- /* @__PURE__ */ jsxDEV(StatCard, {
471
- label: "Total Projects",
472
- value: stats.total.toString()
473
- }, undefined, false, undefined, this),
474
- /* @__PURE__ */ jsxDEV(StatCard, {
475
- label: "Active",
476
- value: stats.activeCount.toString()
477
- }, undefined, false, undefined, this),
478
- /* @__PURE__ */ jsxDEV(StatCard, {
479
- label: "Draft",
480
- value: stats.draftCount.toString()
481
- }, undefined, false, undefined, this)
482
- ]
483
- }, undefined, true, undefined, this),
484
- /* @__PURE__ */ jsxDEV("div", {
485
- className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
486
- children: data.items.map((project) => /* @__PURE__ */ jsxDEV(EntityCard, {
487
- cardTitle: project.name,
488
- cardSubtitle: project.tier,
489
- meta: /* @__PURE__ */ jsxDEV("p", {
490
- className: "text-muted-foreground text-sm",
491
- children: project.description
492
- }, undefined, false, undefined, this),
493
- chips: /* @__PURE__ */ jsxDEV(StatusChip, {
494
- tone: getStatusTone(project.status),
495
- label: project.status
496
- }, undefined, false, undefined, this),
497
- footer: /* @__PURE__ */ jsxDEV("span", {
498
- className: "text-muted-foreground text-xs",
499
- children: project.updatedAt.toLocaleDateString()
500
- }, undefined, false, undefined, this),
501
- onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
502
- }, project.id, false, undefined, this))
503
- }, undefined, false, undefined, this)
504
- ]
505
- }, undefined, true, undefined, this);
506
- }
507
-
508
- // src/ui/renderers/project-list.renderer.tsx
509
- import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
510
- var projectListReactRenderer = {
511
- target: "react",
512
- render: async (desc, _ctx) => {
513
- if (desc.source.type !== "component") {
514
- throw new Error("Invalid source type");
515
- }
516
- if (desc.source.componentKey !== "SaasProjectListView") {
517
- throw new Error(`Unknown component: ${desc.source.componentKey}`);
518
- }
519
- return /* @__PURE__ */ jsxDEV2(SaasProjectList, {}, undefined, false, undefined, this);
520
- }
521
- };
522
-
523
619
  // src/ui/renderers/project-list.markdown.ts
620
+ var PROJECT_TIERS = [
621
+ "FREE",
622
+ "PRO",
623
+ "ENTERPRISE"
624
+ ];
625
+ function toVisualizationProject(project, index) {
626
+ return {
627
+ status: project.status === "DELETED" ? "ARCHIVED" : project.status,
628
+ tier: PROJECT_TIERS[index % PROJECT_TIERS.length] ?? "FREE",
629
+ createdAt: project.createdAt
630
+ };
631
+ }
524
632
  var projectListMarkdownRenderer = {
525
633
  target: "markdown",
526
634
  render: async (desc, _ctx) => {
@@ -531,7 +639,7 @@ var projectListMarkdownRenderer = {
531
639
  limit: 20,
532
640
  offset: 0
533
641
  });
534
- const items = data.projects ?? data.items ?? [];
642
+ const items = data.projects ?? [];
535
643
  const lines = [
536
644
  "# Projects",
537
645
  "",
@@ -568,6 +676,7 @@ var saasDashboardMarkdownRenderer = {
568
676
  const projects = projectsData.projects ?? [];
569
677
  const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
570
678
  const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
679
+ const visualizations = createSaasVisualizationItems(projects.map(toVisualizationProject), 10);
571
680
  const lines = [
572
681
  "# SaaS Dashboard",
573
682
  "",
@@ -582,10 +691,16 @@ var saasDashboardMarkdownRenderer = {
582
691
  `| Archived Projects | ${archivedProjects} |`,
583
692
  `| Subscription Plan | ${subscription.planName} |`,
584
693
  `| Subscription Status | ${subscription.status} |`,
585
- "",
586
- "## Projects",
587
694
  ""
588
695
  ];
696
+ lines.push("## Visualization Overview");
697
+ lines.push("");
698
+ for (const item of visualizations) {
699
+ lines.push(`- **${item.title}** via \`${item.spec.meta.key}\``);
700
+ }
701
+ lines.push("");
702
+ lines.push("## Projects");
703
+ lines.push("");
589
704
  if (projects.length === 0) {
590
705
  lines.push("_No projects yet._");
591
706
  } else {
@@ -668,6 +783,117 @@ var saasBillingMarkdownRenderer = {
668
783
  };
669
784
  }
670
785
  };
786
+
787
+ // src/ui/SaasProjectList.tsx
788
+ import {
789
+ Button,
790
+ EmptyState,
791
+ EntityCard,
792
+ ErrorState,
793
+ LoaderBlock,
794
+ StatCard,
795
+ StatCardGroup,
796
+ StatusChip
797
+ } from "@contractspec/lib.design-system";
798
+ import { jsxDEV } from "react/jsx-dev-runtime";
799
+ "use client";
800
+ function getStatusTone(status) {
801
+ switch (status) {
802
+ case "ACTIVE":
803
+ return "success";
804
+ case "DRAFT":
805
+ return "neutral";
806
+ case "ARCHIVED":
807
+ return "danger";
808
+ default:
809
+ return "neutral";
810
+ }
811
+ }
812
+ function SaasProjectList({
813
+ onProjectClick,
814
+ onCreateProject
815
+ }) {
816
+ const { data, loading, error, stats, refetch } = useProjectList();
817
+ if (loading && !data) {
818
+ return /* @__PURE__ */ jsxDEV(LoaderBlock, {
819
+ label: "Loading projects..."
820
+ }, undefined, false, undefined, this);
821
+ }
822
+ if (error) {
823
+ return /* @__PURE__ */ jsxDEV(ErrorState, {
824
+ title: "Failed to load projects",
825
+ description: error.message,
826
+ onRetry: refetch,
827
+ retryLabel: "Retry"
828
+ }, undefined, false, undefined, this);
829
+ }
830
+ if (!data?.items.length) {
831
+ return /* @__PURE__ */ jsxDEV(EmptyState, {
832
+ title: "No projects found",
833
+ description: "Create your first project to get started.",
834
+ primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV(Button, {
835
+ onPress: onCreateProject,
836
+ children: "Create Project"
837
+ }, undefined, false, undefined, this) : undefined
838
+ }, undefined, false, undefined, this);
839
+ }
840
+ return /* @__PURE__ */ jsxDEV("div", {
841
+ className: "space-y-6",
842
+ children: [
843
+ stats && /* @__PURE__ */ jsxDEV(StatCardGroup, {
844
+ children: [
845
+ /* @__PURE__ */ jsxDEV(StatCard, {
846
+ label: "Total Projects",
847
+ value: stats.total.toString()
848
+ }, undefined, false, undefined, this),
849
+ /* @__PURE__ */ jsxDEV(StatCard, {
850
+ label: "Active",
851
+ value: stats.activeCount.toString()
852
+ }, undefined, false, undefined, this),
853
+ /* @__PURE__ */ jsxDEV(StatCard, {
854
+ label: "Draft",
855
+ value: stats.draftCount.toString()
856
+ }, undefined, false, undefined, this)
857
+ ]
858
+ }, undefined, true, undefined, this),
859
+ /* @__PURE__ */ jsxDEV("div", {
860
+ className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
861
+ children: data.items.map((project) => /* @__PURE__ */ jsxDEV(EntityCard, {
862
+ cardTitle: project.name,
863
+ cardSubtitle: project.tier,
864
+ meta: /* @__PURE__ */ jsxDEV("p", {
865
+ className: "text-muted-foreground text-sm",
866
+ children: project.description
867
+ }, undefined, false, undefined, this),
868
+ chips: /* @__PURE__ */ jsxDEV(StatusChip, {
869
+ tone: getStatusTone(project.status),
870
+ label: project.status
871
+ }, undefined, false, undefined, this),
872
+ footer: /* @__PURE__ */ jsxDEV("span", {
873
+ className: "text-muted-foreground text-xs",
874
+ children: project.updatedAt.toLocaleDateString()
875
+ }, undefined, false, undefined, this),
876
+ onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
877
+ }, project.id, false, undefined, this))
878
+ }, undefined, false, undefined, this)
879
+ ]
880
+ }, undefined, true, undefined, this);
881
+ }
882
+
883
+ // src/ui/renderers/project-list.renderer.tsx
884
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
885
+ var projectListReactRenderer = {
886
+ target: "react",
887
+ render: async (desc, _ctx) => {
888
+ if (desc.source.type !== "component") {
889
+ throw new Error("Invalid source type");
890
+ }
891
+ if (desc.source.componentKey !== "SaasProjectListView") {
892
+ throw new Error(`Unknown component: ${desc.source.componentKey}`);
893
+ }
894
+ return /* @__PURE__ */ jsxDEV2(SaasProjectList, {}, undefined, false, undefined, this);
895
+ }
896
+ };
671
897
  export {
672
898
  saasDashboardMarkdownRenderer,
673
899
  saasBillingMarkdownRenderer,