@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,7 +344,226 @@ 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/renderers/project-list.markdown.ts
555
+ var PROJECT_TIERS = [
556
+ "FREE",
557
+ "PRO",
558
+ "ENTERPRISE"
559
+ ];
560
+ function toVisualizationProject(project, index) {
561
+ return {
562
+ status: project.status === "DELETED" ? "ARCHIVED" : project.status,
563
+ tier: PROJECT_TIERS[index % PROJECT_TIERS.length] ?? "FREE",
564
+ createdAt: project.createdAt
565
+ };
566
+ }
348
567
  var projectListMarkdownRenderer = {
349
568
  target: "markdown",
350
569
  render: async (desc, _ctx) => {
@@ -355,7 +574,7 @@ var projectListMarkdownRenderer = {
355
574
  limit: 20,
356
575
  offset: 0
357
576
  });
358
- const items = data.projects ?? data.items ?? [];
577
+ const items = data.projects ?? [];
359
578
  const lines = [
360
579
  "# Projects",
361
580
  "",
@@ -392,6 +611,7 @@ var saasDashboardMarkdownRenderer = {
392
611
  const projects = projectsData.projects ?? [];
393
612
  const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
394
613
  const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
614
+ const visualizations = createSaasVisualizationItems(projects.map(toVisualizationProject), 10);
395
615
  const lines = [
396
616
  "# SaaS Dashboard",
397
617
  "",
@@ -406,10 +626,16 @@ var saasDashboardMarkdownRenderer = {
406
626
  `| Archived Projects | ${archivedProjects} |`,
407
627
  `| Subscription Plan | ${subscription.planName} |`,
408
628
  `| Subscription Status | ${subscription.status} |`,
409
- "",
410
- "## Projects",
411
629
  ""
412
630
  ];
631
+ lines.push("## Visualization Overview");
632
+ lines.push("");
633
+ for (const item of visualizations) {
634
+ lines.push(`- **${item.title}** via \`${item.spec.meta.key}\``);
635
+ }
636
+ lines.push("");
637
+ lines.push("## Projects");
638
+ lines.push("");
413
639
  if (projects.length === 0) {
414
640
  lines.push("_No projects yet._");
415
641
  } else {
@@ -1,6 +1,6 @@
1
1
  // src/ui/hooks/useProjectList.ts
2
- import { useCallback, useEffect, useMemo, useState } from "react";
3
2
  import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
3
+ import { useCallback, useEffect, useMemo, useState } from "react";
4
4
  function useProjectList(options = {}) {
5
5
  const { handlers, projectId } = useTemplateRuntime();
6
6
  const { saas } = handlers;
@@ -64,14 +64,14 @@ function useProjectList(options = {}) {
64
64
 
65
65
  // src/ui/SaasProjectList.tsx
66
66
  import {
67
- StatCard,
68
- StatCardGroup,
69
- StatusChip,
70
- EntityCard,
67
+ Button,
71
68
  EmptyState,
72
- LoaderBlock,
69
+ EntityCard,
73
70
  ErrorState,
74
- Button
71
+ LoaderBlock,
72
+ StatCard,
73
+ StatCardGroup,
74
+ StatusChip
75
75
  } from "@contractspec/lib.design-system";
76
76
  import { jsxDEV } from "react/jsx-dev-runtime";
77
77
  "use client";
@@ -0,0 +1,155 @@
1
+ // src/visualizations/catalog.ts
2
+ import {
3
+ defineVisualization,
4
+ VisualizationRegistry
5
+ } from "@contractspec/lib.contracts-spec/visualizations";
6
+ var PROJECT_LIST_REF = {
7
+ key: "saas.project.list",
8
+ version: "1.0.0"
9
+ };
10
+ var META = {
11
+ version: "1.0.0",
12
+ domain: "saas",
13
+ stability: "experimental",
14
+ owners: ["@example.saas-boilerplate"],
15
+ tags: ["saas", "visualization", "projects"]
16
+ };
17
+ var SaasProjectUsageVisualization = defineVisualization({
18
+ meta: {
19
+ ...META,
20
+ key: "saas-boilerplate.visualization.project-usage",
21
+ title: "Project Capacity",
22
+ description: "Current project count against the current plan limit.",
23
+ goal: "Show usage against the active plan allowance.",
24
+ context: "SaaS account overview."
25
+ },
26
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
27
+ visualization: {
28
+ kind: "metric",
29
+ measure: "totalProjects",
30
+ comparisonMeasure: "projectLimit",
31
+ measures: [
32
+ {
33
+ key: "totalProjects",
34
+ label: "Projects",
35
+ dataPath: "totalProjects",
36
+ format: "number"
37
+ },
38
+ {
39
+ key: "projectLimit",
40
+ label: "Plan Limit",
41
+ dataPath: "projectLimit",
42
+ format: "number"
43
+ }
44
+ ],
45
+ table: { caption: "Current project count and plan limit." }
46
+ }
47
+ });
48
+ var SaasProjectStatusVisualization = defineVisualization({
49
+ meta: {
50
+ ...META,
51
+ key: "saas-boilerplate.visualization.project-status",
52
+ title: "Project Status",
53
+ description: "Distribution of project states.",
54
+ goal: "Show the mix of active, draft, and archived projects.",
55
+ context: "Project portfolio overview."
56
+ },
57
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
58
+ visualization: {
59
+ kind: "pie",
60
+ nameDimension: "status",
61
+ valueMeasure: "projects",
62
+ dimensions: [
63
+ { key: "status", label: "Status", dataPath: "status", type: "category" }
64
+ ],
65
+ measures: [
66
+ {
67
+ key: "projects",
68
+ label: "Projects",
69
+ dataPath: "projects",
70
+ format: "number"
71
+ }
72
+ ],
73
+ table: { caption: "Project counts by status." }
74
+ }
75
+ });
76
+ var SaasProjectTierVisualization = defineVisualization({
77
+ meta: {
78
+ ...META,
79
+ key: "saas-boilerplate.visualization.project-tiers",
80
+ title: "Tier Comparison",
81
+ description: "Distribution of projects across tiers.",
82
+ goal: "Compare how the current portfolio is distributed by tier.",
83
+ context: "Plan and packaging overview."
84
+ },
85
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
86
+ visualization: {
87
+ kind: "cartesian",
88
+ variant: "bar",
89
+ xDimension: "tier",
90
+ yMeasures: ["projects"],
91
+ dimensions: [
92
+ { key: "tier", label: "Tier", dataPath: "tier", type: "category" }
93
+ ],
94
+ measures: [
95
+ {
96
+ key: "projects",
97
+ label: "Projects",
98
+ dataPath: "projects",
99
+ format: "number",
100
+ color: "#1d4ed8"
101
+ }
102
+ ],
103
+ table: { caption: "Project counts by tier." }
104
+ }
105
+ });
106
+ var SaasProjectActivityVisualization = defineVisualization({
107
+ meta: {
108
+ ...META,
109
+ key: "saas-boilerplate.visualization.project-activity",
110
+ title: "Recent Project Activity",
111
+ description: "Daily project creation activity.",
112
+ goal: "Show recent project activity over time.",
113
+ context: "Project portfolio trend view."
114
+ },
115
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
116
+ visualization: {
117
+ kind: "cartesian",
118
+ variant: "line",
119
+ xDimension: "day",
120
+ yMeasures: ["projects"],
121
+ dimensions: [{ key: "day", label: "Day", dataPath: "day", type: "time" }],
122
+ measures: [
123
+ {
124
+ key: "projects",
125
+ label: "Projects",
126
+ dataPath: "projects",
127
+ format: "number",
128
+ color: "#0f766e"
129
+ }
130
+ ],
131
+ table: { caption: "Daily project creation counts." }
132
+ }
133
+ });
134
+ var SaasVisualizationSpecs = [
135
+ SaasProjectUsageVisualization,
136
+ SaasProjectStatusVisualization,
137
+ SaasProjectTierVisualization,
138
+ SaasProjectActivityVisualization
139
+ ];
140
+ var SaasVisualizationRegistry = new VisualizationRegistry([
141
+ ...SaasVisualizationSpecs
142
+ ]);
143
+ var SaasVisualizationRefs = SaasVisualizationSpecs.map((spec) => ({
144
+ key: spec.meta.key,
145
+ version: spec.meta.version
146
+ }));
147
+ export {
148
+ SaasVisualizationSpecs,
149
+ SaasVisualizationRegistry,
150
+ SaasVisualizationRefs,
151
+ SaasProjectUsageVisualization,
152
+ SaasProjectTierVisualization,
153
+ SaasProjectStatusVisualization,
154
+ SaasProjectActivityVisualization
155
+ };
@@ -0,0 +1,217 @@
1
+ // src/visualizations/catalog.ts
2
+ import {
3
+ defineVisualization,
4
+ VisualizationRegistry
5
+ } from "@contractspec/lib.contracts-spec/visualizations";
6
+ var PROJECT_LIST_REF = {
7
+ key: "saas.project.list",
8
+ version: "1.0.0"
9
+ };
10
+ var META = {
11
+ version: "1.0.0",
12
+ domain: "saas",
13
+ stability: "experimental",
14
+ owners: ["@example.saas-boilerplate"],
15
+ tags: ["saas", "visualization", "projects"]
16
+ };
17
+ var SaasProjectUsageVisualization = defineVisualization({
18
+ meta: {
19
+ ...META,
20
+ key: "saas-boilerplate.visualization.project-usage",
21
+ title: "Project Capacity",
22
+ description: "Current project count against the current plan limit.",
23
+ goal: "Show usage against the active plan allowance.",
24
+ context: "SaaS account overview."
25
+ },
26
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
27
+ visualization: {
28
+ kind: "metric",
29
+ measure: "totalProjects",
30
+ comparisonMeasure: "projectLimit",
31
+ measures: [
32
+ {
33
+ key: "totalProjects",
34
+ label: "Projects",
35
+ dataPath: "totalProjects",
36
+ format: "number"
37
+ },
38
+ {
39
+ key: "projectLimit",
40
+ label: "Plan Limit",
41
+ dataPath: "projectLimit",
42
+ format: "number"
43
+ }
44
+ ],
45
+ table: { caption: "Current project count and plan limit." }
46
+ }
47
+ });
48
+ var SaasProjectStatusVisualization = defineVisualization({
49
+ meta: {
50
+ ...META,
51
+ key: "saas-boilerplate.visualization.project-status",
52
+ title: "Project Status",
53
+ description: "Distribution of project states.",
54
+ goal: "Show the mix of active, draft, and archived projects.",
55
+ context: "Project portfolio overview."
56
+ },
57
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
58
+ visualization: {
59
+ kind: "pie",
60
+ nameDimension: "status",
61
+ valueMeasure: "projects",
62
+ dimensions: [
63
+ { key: "status", label: "Status", dataPath: "status", type: "category" }
64
+ ],
65
+ measures: [
66
+ {
67
+ key: "projects",
68
+ label: "Projects",
69
+ dataPath: "projects",
70
+ format: "number"
71
+ }
72
+ ],
73
+ table: { caption: "Project counts by status." }
74
+ }
75
+ });
76
+ var SaasProjectTierVisualization = defineVisualization({
77
+ meta: {
78
+ ...META,
79
+ key: "saas-boilerplate.visualization.project-tiers",
80
+ title: "Tier Comparison",
81
+ description: "Distribution of projects across tiers.",
82
+ goal: "Compare how the current portfolio is distributed by tier.",
83
+ context: "Plan and packaging overview."
84
+ },
85
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
86
+ visualization: {
87
+ kind: "cartesian",
88
+ variant: "bar",
89
+ xDimension: "tier",
90
+ yMeasures: ["projects"],
91
+ dimensions: [
92
+ { key: "tier", label: "Tier", dataPath: "tier", type: "category" }
93
+ ],
94
+ measures: [
95
+ {
96
+ key: "projects",
97
+ label: "Projects",
98
+ dataPath: "projects",
99
+ format: "number",
100
+ color: "#1d4ed8"
101
+ }
102
+ ],
103
+ table: { caption: "Project counts by tier." }
104
+ }
105
+ });
106
+ var SaasProjectActivityVisualization = defineVisualization({
107
+ meta: {
108
+ ...META,
109
+ key: "saas-boilerplate.visualization.project-activity",
110
+ title: "Recent Project Activity",
111
+ description: "Daily project creation activity.",
112
+ goal: "Show recent project activity over time.",
113
+ context: "Project portfolio trend view."
114
+ },
115
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
116
+ visualization: {
117
+ kind: "cartesian",
118
+ variant: "line",
119
+ xDimension: "day",
120
+ yMeasures: ["projects"],
121
+ dimensions: [{ key: "day", label: "Day", dataPath: "day", type: "time" }],
122
+ measures: [
123
+ {
124
+ key: "projects",
125
+ label: "Projects",
126
+ dataPath: "projects",
127
+ format: "number",
128
+ color: "#0f766e"
129
+ }
130
+ ],
131
+ table: { caption: "Daily project creation counts." }
132
+ }
133
+ });
134
+ var SaasVisualizationSpecs = [
135
+ SaasProjectUsageVisualization,
136
+ SaasProjectStatusVisualization,
137
+ SaasProjectTierVisualization,
138
+ SaasProjectActivityVisualization
139
+ ];
140
+ var SaasVisualizationRegistry = new VisualizationRegistry([
141
+ ...SaasVisualizationSpecs
142
+ ]);
143
+ var SaasVisualizationRefs = SaasVisualizationSpecs.map((spec) => ({
144
+ key: spec.meta.key,
145
+ version: spec.meta.version
146
+ }));
147
+
148
+ // src/visualizations/selectors.ts
149
+ function toDayKey(value) {
150
+ const date = value instanceof Date ? value : new Date(value);
151
+ return date.toISOString().slice(0, 10);
152
+ }
153
+ function createSaasVisualizationItems(projects, projectLimit = 10) {
154
+ const statusCounts = new Map;
155
+ const tierCounts = new Map;
156
+ const activityCounts = new Map;
157
+ for (const project of projects) {
158
+ statusCounts.set(project.status, (statusCounts.get(project.status) ?? 0) + 1);
159
+ tierCounts.set(project.tier, (tierCounts.get(project.tier) ?? 0) + 1);
160
+ const day = toDayKey(project.createdAt);
161
+ activityCounts.set(day, (activityCounts.get(day) ?? 0) + 1);
162
+ }
163
+ return [
164
+ {
165
+ key: "saas-capacity",
166
+ spec: SaasProjectUsageVisualization,
167
+ data: { data: [{ totalProjects: projects.length, projectLimit }] },
168
+ title: "Project Capacity",
169
+ description: "Current project count compared to the active limit.",
170
+ height: 220
171
+ },
172
+ {
173
+ key: "saas-status",
174
+ spec: SaasProjectStatusVisualization,
175
+ data: {
176
+ data: Array.from(statusCounts.entries()).map(([status, count]) => ({
177
+ status,
178
+ projects: count
179
+ }))
180
+ },
181
+ title: "Project Status",
182
+ description: "Status mix across the current project portfolio.",
183
+ height: 260
184
+ },
185
+ {
186
+ key: "saas-tier",
187
+ spec: SaasProjectTierVisualization,
188
+ data: {
189
+ data: Array.from(tierCounts.entries()).map(([tier, count]) => ({
190
+ tier,
191
+ projects: count
192
+ }))
193
+ },
194
+ title: "Tier Comparison",
195
+ description: "How projects are distributed across tiers."
196
+ },
197
+ {
198
+ key: "saas-activity",
199
+ spec: SaasProjectActivityVisualization,
200
+ data: {
201
+ data: Array.from(activityCounts.entries()).sort(([left], [right]) => left.localeCompare(right)).map(([day, count]) => ({ day, projects: count }))
202
+ },
203
+ title: "Recent Project Activity",
204
+ description: "Daily project creation activity."
205
+ }
206
+ ];
207
+ }
208
+ export {
209
+ createSaasVisualizationItems,
210
+ SaasVisualizationSpecs,
211
+ SaasVisualizationRegistry,
212
+ SaasVisualizationRefs,
213
+ SaasProjectUsageVisualization,
214
+ SaasProjectTierVisualization,
215
+ SaasProjectStatusVisualization,
216
+ SaasProjectActivityVisualization
217
+ };