@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.
- package/.turbo/turbo-build.log +39 -27
- package/AGENTS.md +50 -27
- package/CHANGELOG.md +36 -0
- package/README.md +65 -144
- package/dist/billing/billing.event.js +1 -1
- package/dist/billing/index.d.ts +6 -6
- package/dist/billing/index.js +1 -1
- package/dist/browser/billing/billing.event.js +1 -1
- package/dist/browser/billing/index.js +1 -1
- package/dist/browser/index.js +1147 -869
- package/dist/browser/project/index.js +209 -209
- package/dist/browser/project/project.event.js +1 -1
- package/dist/browser/saas-boilerplate.feature.js +208 -0
- package/dist/browser/ui/SaasDashboard.js +356 -105
- package/dist/browser/ui/SaasDashboard.visualizations.js +249 -0
- package/dist/browser/ui/SaasProjectList.js +7 -7
- package/dist/browser/ui/SaasSettingsPanel.js +12 -12
- package/dist/browser/ui/hooks/index.js +2 -2
- package/dist/browser/ui/hooks/useProjectList.js +1 -1
- package/dist/browser/ui/hooks/useProjectMutations.js +1 -1
- package/dist/browser/ui/index.js +790 -521
- package/dist/browser/ui/modals/CreateProjectModal.js +10 -10
- package/dist/browser/ui/modals/ProjectActionsModal.js +13 -13
- package/dist/browser/ui/modals/index.js +23 -23
- package/dist/browser/ui/renderers/index.js +341 -115
- package/dist/browser/ui/renderers/project-list.markdown.js +229 -3
- package/dist/browser/ui/renderers/project-list.renderer.js +7 -7
- package/dist/browser/visualizations/catalog.js +155 -0
- package/dist/browser/visualizations/index.js +217 -0
- package/dist/browser/visualizations/selectors.js +210 -0
- package/dist/handlers/index.d.ts +2 -2
- package/dist/index.d.ts +5 -4
- package/dist/index.js +1147 -869
- package/dist/node/billing/billing.event.js +1 -1
- package/dist/node/billing/index.js +1 -1
- package/dist/node/index.js +1147 -869
- package/dist/node/project/index.js +209 -209
- package/dist/node/project/project.event.js +1 -1
- package/dist/node/saas-boilerplate.feature.js +208 -0
- package/dist/node/ui/SaasDashboard.js +356 -105
- package/dist/node/ui/SaasDashboard.visualizations.js +249 -0
- package/dist/node/ui/SaasProjectList.js +7 -7
- package/dist/node/ui/SaasSettingsPanel.js +12 -12
- package/dist/node/ui/hooks/index.js +2 -2
- package/dist/node/ui/hooks/useProjectList.js +1 -1
- package/dist/node/ui/hooks/useProjectMutations.js +1 -1
- package/dist/node/ui/index.js +790 -521
- package/dist/node/ui/modals/CreateProjectModal.js +10 -10
- package/dist/node/ui/modals/ProjectActionsModal.js +13 -13
- package/dist/node/ui/modals/index.js +23 -23
- package/dist/node/ui/renderers/index.js +341 -115
- package/dist/node/ui/renderers/project-list.markdown.js +229 -3
- package/dist/node/ui/renderers/project-list.renderer.js +7 -7
- package/dist/node/visualizations/catalog.js +155 -0
- package/dist/node/visualizations/index.js +217 -0
- package/dist/node/visualizations/selectors.js +210 -0
- package/dist/presentations/index.d.ts +1 -1
- package/dist/project/index.d.ts +7 -7
- package/dist/project/index.js +209 -209
- package/dist/project/project.event.js +1 -1
- package/dist/saas-boilerplate.feature.js +208 -0
- package/dist/settings/index.d.ts +1 -1
- package/dist/ui/SaasDashboard.js +356 -105
- package/dist/ui/SaasDashboard.visualizations.d.ts +5 -0
- package/dist/ui/SaasDashboard.visualizations.js +250 -0
- package/dist/ui/SaasProjectList.js +7 -7
- package/dist/ui/SaasSettingsPanel.js +12 -12
- package/dist/ui/hooks/index.d.ts +2 -2
- package/dist/ui/hooks/index.js +2 -2
- package/dist/ui/hooks/useProjectList.d.ts +5 -0
- package/dist/ui/hooks/useProjectList.js +1 -1
- package/dist/ui/hooks/useProjectMutations.d.ts +8 -0
- package/dist/ui/hooks/useProjectMutations.js +1 -1
- package/dist/ui/index.d.ts +4 -4
- package/dist/ui/index.js +790 -521
- package/dist/ui/modals/CreateProjectModal.js +10 -10
- package/dist/ui/modals/ProjectActionsModal.js +13 -13
- package/dist/ui/modals/index.js +23 -23
- package/dist/ui/renderers/index.d.ts +1 -1
- package/dist/ui/renderers/index.js +341 -115
- package/dist/ui/renderers/project-list.markdown.js +229 -3
- package/dist/ui/renderers/project-list.renderer.d.ts +1 -1
- package/dist/ui/renderers/project-list.renderer.js +7 -7
- package/dist/visualizations/catalog.d.ts +11 -0
- package/dist/visualizations/catalog.js +156 -0
- package/dist/visualizations/index.d.ts +2 -0
- package/dist/visualizations/index.js +218 -0
- package/dist/visualizations/selectors.d.ts +8 -0
- package/dist/visualizations/selectors.js +211 -0
- package/dist/visualizations/selectors.test.d.ts +1 -0
- package/package.json +70 -14
- package/src/billing/billing.entity.ts +132 -132
- package/src/billing/billing.enum.ts +9 -9
- package/src/billing/billing.event.ts +71 -71
- package/src/billing/billing.handler.ts +87 -87
- package/src/billing/billing.operations.ts +158 -158
- package/src/billing/billing.presentation.ts +45 -45
- package/src/billing/billing.schema.ts +76 -76
- package/src/billing/index.ts +43 -48
- package/src/dashboard/dashboard.presentation.ts +45 -45
- package/src/dashboard/index.ts +2 -2
- package/src/docs/saas-boilerplate.docblock.ts +43 -43
- package/src/example.ts +32 -32
- package/src/handlers/index.ts +9 -9
- package/src/handlers/saas.handlers.ts +250 -249
- package/src/index.ts +41 -41
- package/src/presentations/index.ts +18 -20
- package/src/project/index.ts +45 -50
- package/src/project/project.entity.ts +68 -68
- package/src/project/project.enum.ts +8 -8
- package/src/project/project.event.ts +79 -79
- package/src/project/project.handler.ts +103 -103
- package/src/project/project.operations.ts +236 -236
- package/src/project/project.presentation.ts +46 -46
- package/src/project/project.schema.ts +90 -90
- package/src/saas-boilerplate.feature.ts +103 -100
- package/src/seeders/index.ts +20 -20
- package/src/settings/index.ts +2 -3
- package/src/settings/settings.entity.ts +65 -65
- package/src/settings/settings.enum.ts +4 -4
- package/src/shared/mock-data.ts +92 -92
- package/src/shared/overlay-types.ts +23 -23
- package/src/tests/operations.test-spec.ts +96 -96
- package/src/ui/SaasDashboard.tsx +278 -270
- package/src/ui/SaasDashboard.visualizations.tsx +41 -0
- package/src/ui/SaasProjectList.tsx +90 -90
- package/src/ui/SaasSettingsPanel.tsx +84 -84
- package/src/ui/hooks/index.ts +3 -3
- package/src/ui/hooks/useProjectList.ts +69 -68
- package/src/ui/hooks/useProjectMutations.ts +144 -143
- package/src/ui/index.ts +8 -12
- package/src/ui/modals/CreateProjectModal.tsx +154 -154
- package/src/ui/modals/ProjectActionsModal.tsx +321 -321
- package/src/ui/overlays/demo-overlays.ts +49 -49
- package/src/ui/renderers/index.ts +5 -4
- package/src/ui/renderers/project-list.markdown.ts +229 -205
- package/src/ui/renderers/project-list.renderer.tsx +14 -13
- package/src/visualizations/catalog.ts +153 -0
- package/src/visualizations/index.ts +2 -0
- package/src/visualizations/selectors.test.ts +25 -0
- package/src/visualizations/selectors.ts +85 -0
- package/tsconfig.json +7 -8
- package/tsdown.config.js +7 -3
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/visualizations/catalog.ts
|
|
3
|
+
import {
|
|
4
|
+
defineVisualization,
|
|
5
|
+
VisualizationRegistry
|
|
6
|
+
} from "@contractspec/lib.contracts-spec/visualizations";
|
|
7
|
+
var PROJECT_LIST_REF = {
|
|
8
|
+
key: "saas.project.list",
|
|
9
|
+
version: "1.0.0"
|
|
10
|
+
};
|
|
11
|
+
var META = {
|
|
12
|
+
version: "1.0.0",
|
|
13
|
+
domain: "saas",
|
|
14
|
+
stability: "experimental",
|
|
15
|
+
owners: ["@example.saas-boilerplate"],
|
|
16
|
+
tags: ["saas", "visualization", "projects"]
|
|
17
|
+
};
|
|
18
|
+
var SaasProjectUsageVisualization = defineVisualization({
|
|
19
|
+
meta: {
|
|
20
|
+
...META,
|
|
21
|
+
key: "saas-boilerplate.visualization.project-usage",
|
|
22
|
+
title: "Project Capacity",
|
|
23
|
+
description: "Current project count against the current plan limit.",
|
|
24
|
+
goal: "Show usage against the active plan allowance.",
|
|
25
|
+
context: "SaaS account overview."
|
|
26
|
+
},
|
|
27
|
+
source: { primary: PROJECT_LIST_REF, resultPath: "data" },
|
|
28
|
+
visualization: {
|
|
29
|
+
kind: "metric",
|
|
30
|
+
measure: "totalProjects",
|
|
31
|
+
comparisonMeasure: "projectLimit",
|
|
32
|
+
measures: [
|
|
33
|
+
{
|
|
34
|
+
key: "totalProjects",
|
|
35
|
+
label: "Projects",
|
|
36
|
+
dataPath: "totalProjects",
|
|
37
|
+
format: "number"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: "projectLimit",
|
|
41
|
+
label: "Plan Limit",
|
|
42
|
+
dataPath: "projectLimit",
|
|
43
|
+
format: "number"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
table: { caption: "Current project count and plan limit." }
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
var SaasProjectStatusVisualization = defineVisualization({
|
|
50
|
+
meta: {
|
|
51
|
+
...META,
|
|
52
|
+
key: "saas-boilerplate.visualization.project-status",
|
|
53
|
+
title: "Project Status",
|
|
54
|
+
description: "Distribution of project states.",
|
|
55
|
+
goal: "Show the mix of active, draft, and archived projects.",
|
|
56
|
+
context: "Project portfolio overview."
|
|
57
|
+
},
|
|
58
|
+
source: { primary: PROJECT_LIST_REF, resultPath: "data" },
|
|
59
|
+
visualization: {
|
|
60
|
+
kind: "pie",
|
|
61
|
+
nameDimension: "status",
|
|
62
|
+
valueMeasure: "projects",
|
|
63
|
+
dimensions: [
|
|
64
|
+
{ key: "status", label: "Status", dataPath: "status", type: "category" }
|
|
65
|
+
],
|
|
66
|
+
measures: [
|
|
67
|
+
{
|
|
68
|
+
key: "projects",
|
|
69
|
+
label: "Projects",
|
|
70
|
+
dataPath: "projects",
|
|
71
|
+
format: "number"
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
table: { caption: "Project counts by status." }
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
var SaasProjectTierVisualization = defineVisualization({
|
|
78
|
+
meta: {
|
|
79
|
+
...META,
|
|
80
|
+
key: "saas-boilerplate.visualization.project-tiers",
|
|
81
|
+
title: "Tier Comparison",
|
|
82
|
+
description: "Distribution of projects across tiers.",
|
|
83
|
+
goal: "Compare how the current portfolio is distributed by tier.",
|
|
84
|
+
context: "Plan and packaging overview."
|
|
85
|
+
},
|
|
86
|
+
source: { primary: PROJECT_LIST_REF, resultPath: "data" },
|
|
87
|
+
visualization: {
|
|
88
|
+
kind: "cartesian",
|
|
89
|
+
variant: "bar",
|
|
90
|
+
xDimension: "tier",
|
|
91
|
+
yMeasures: ["projects"],
|
|
92
|
+
dimensions: [
|
|
93
|
+
{ key: "tier", label: "Tier", dataPath: "tier", type: "category" }
|
|
94
|
+
],
|
|
95
|
+
measures: [
|
|
96
|
+
{
|
|
97
|
+
key: "projects",
|
|
98
|
+
label: "Projects",
|
|
99
|
+
dataPath: "projects",
|
|
100
|
+
format: "number",
|
|
101
|
+
color: "#1d4ed8"
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
table: { caption: "Project counts by tier." }
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
var SaasProjectActivityVisualization = defineVisualization({
|
|
108
|
+
meta: {
|
|
109
|
+
...META,
|
|
110
|
+
key: "saas-boilerplate.visualization.project-activity",
|
|
111
|
+
title: "Recent Project Activity",
|
|
112
|
+
description: "Daily project creation activity.",
|
|
113
|
+
goal: "Show recent project activity over time.",
|
|
114
|
+
context: "Project portfolio trend view."
|
|
115
|
+
},
|
|
116
|
+
source: { primary: PROJECT_LIST_REF, resultPath: "data" },
|
|
117
|
+
visualization: {
|
|
118
|
+
kind: "cartesian",
|
|
119
|
+
variant: "line",
|
|
120
|
+
xDimension: "day",
|
|
121
|
+
yMeasures: ["projects"],
|
|
122
|
+
dimensions: [{ key: "day", label: "Day", dataPath: "day", type: "time" }],
|
|
123
|
+
measures: [
|
|
124
|
+
{
|
|
125
|
+
key: "projects",
|
|
126
|
+
label: "Projects",
|
|
127
|
+
dataPath: "projects",
|
|
128
|
+
format: "number",
|
|
129
|
+
color: "#0f766e"
|
|
130
|
+
}
|
|
131
|
+
],
|
|
132
|
+
table: { caption: "Daily project creation counts." }
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
var SaasVisualizationSpecs = [
|
|
136
|
+
SaasProjectUsageVisualization,
|
|
137
|
+
SaasProjectStatusVisualization,
|
|
138
|
+
SaasProjectTierVisualization,
|
|
139
|
+
SaasProjectActivityVisualization
|
|
140
|
+
];
|
|
141
|
+
var SaasVisualizationRegistry = new VisualizationRegistry([
|
|
142
|
+
...SaasVisualizationSpecs
|
|
143
|
+
]);
|
|
144
|
+
var SaasVisualizationRefs = SaasVisualizationSpecs.map((spec) => ({
|
|
145
|
+
key: spec.meta.key,
|
|
146
|
+
version: spec.meta.version
|
|
147
|
+
}));
|
|
148
|
+
|
|
149
|
+
// src/visualizations/selectors.ts
|
|
150
|
+
function toDayKey(value) {
|
|
151
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
152
|
+
return date.toISOString().slice(0, 10);
|
|
153
|
+
}
|
|
154
|
+
function createSaasVisualizationItems(projects, projectLimit = 10) {
|
|
155
|
+
const statusCounts = new Map;
|
|
156
|
+
const tierCounts = new Map;
|
|
157
|
+
const activityCounts = new Map;
|
|
158
|
+
for (const project of projects) {
|
|
159
|
+
statusCounts.set(project.status, (statusCounts.get(project.status) ?? 0) + 1);
|
|
160
|
+
tierCounts.set(project.tier, (tierCounts.get(project.tier) ?? 0) + 1);
|
|
161
|
+
const day = toDayKey(project.createdAt);
|
|
162
|
+
activityCounts.set(day, (activityCounts.get(day) ?? 0) + 1);
|
|
163
|
+
}
|
|
164
|
+
return [
|
|
165
|
+
{
|
|
166
|
+
key: "saas-capacity",
|
|
167
|
+
spec: SaasProjectUsageVisualization,
|
|
168
|
+
data: { data: [{ totalProjects: projects.length, projectLimit }] },
|
|
169
|
+
title: "Project Capacity",
|
|
170
|
+
description: "Current project count compared to the active limit.",
|
|
171
|
+
height: 220
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
key: "saas-status",
|
|
175
|
+
spec: SaasProjectStatusVisualization,
|
|
176
|
+
data: {
|
|
177
|
+
data: Array.from(statusCounts.entries()).map(([status, count]) => ({
|
|
178
|
+
status,
|
|
179
|
+
projects: count
|
|
180
|
+
}))
|
|
181
|
+
},
|
|
182
|
+
title: "Project Status",
|
|
183
|
+
description: "Status mix across the current project portfolio.",
|
|
184
|
+
height: 260
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
key: "saas-tier",
|
|
188
|
+
spec: SaasProjectTierVisualization,
|
|
189
|
+
data: {
|
|
190
|
+
data: Array.from(tierCounts.entries()).map(([tier, count]) => ({
|
|
191
|
+
tier,
|
|
192
|
+
projects: count
|
|
193
|
+
}))
|
|
194
|
+
},
|
|
195
|
+
title: "Tier Comparison",
|
|
196
|
+
description: "How projects are distributed across tiers."
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
key: "saas-activity",
|
|
200
|
+
spec: SaasProjectActivityVisualization,
|
|
201
|
+
data: {
|
|
202
|
+
data: Array.from(activityCounts.entries()).sort(([left], [right]) => left.localeCompare(right)).map(([day, count]) => ({ day, projects: count }))
|
|
203
|
+
},
|
|
204
|
+
title: "Recent Project Activity",
|
|
205
|
+
description: "Daily project creation activity."
|
|
206
|
+
}
|
|
207
|
+
];
|
|
208
|
+
}
|
|
209
|
+
export {
|
|
210
|
+
createSaasVisualizationItems
|
|
211
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contractspec/example.saas-boilerplate",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.2",
|
|
4
4
|
"description": "SaaS Boilerplate - Users, Orgs, Projects, Billing, Settings",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -327,6 +327,13 @@
|
|
|
327
327
|
"node": "./dist/node/ui/SaasDashboard.js",
|
|
328
328
|
"default": "./dist/ui/SaasDashboard.js"
|
|
329
329
|
},
|
|
330
|
+
"./ui/SaasDashboard.visualizations": {
|
|
331
|
+
"types": "./dist/ui/SaasDashboard.visualizations.d.ts",
|
|
332
|
+
"browser": "./dist/browser/ui/SaasDashboard.visualizations.js",
|
|
333
|
+
"bun": "./dist/ui/SaasDashboard.visualizations.js",
|
|
334
|
+
"node": "./dist/node/ui/SaasDashboard.visualizations.js",
|
|
335
|
+
"default": "./dist/ui/SaasDashboard.visualizations.js"
|
|
336
|
+
},
|
|
330
337
|
"./ui/SaasProjectList": {
|
|
331
338
|
"types": "./dist/ui/SaasProjectList.d.ts",
|
|
332
339
|
"browser": "./dist/browser/ui/SaasProjectList.js",
|
|
@@ -340,6 +347,27 @@
|
|
|
340
347
|
"bun": "./dist/ui/SaasSettingsPanel.js",
|
|
341
348
|
"node": "./dist/node/ui/SaasSettingsPanel.js",
|
|
342
349
|
"default": "./dist/ui/SaasSettingsPanel.js"
|
|
350
|
+
},
|
|
351
|
+
"./visualizations": {
|
|
352
|
+
"types": "./dist/visualizations/index.d.ts",
|
|
353
|
+
"browser": "./dist/browser/visualizations/index.js",
|
|
354
|
+
"bun": "./dist/visualizations/index.js",
|
|
355
|
+
"node": "./dist/node/visualizations/index.js",
|
|
356
|
+
"default": "./dist/visualizations/index.js"
|
|
357
|
+
},
|
|
358
|
+
"./visualizations/catalog": {
|
|
359
|
+
"types": "./dist/visualizations/catalog.d.ts",
|
|
360
|
+
"browser": "./dist/browser/visualizations/catalog.js",
|
|
361
|
+
"bun": "./dist/visualizations/catalog.js",
|
|
362
|
+
"node": "./dist/node/visualizations/catalog.js",
|
|
363
|
+
"default": "./dist/visualizations/catalog.js"
|
|
364
|
+
},
|
|
365
|
+
"./visualizations/selectors": {
|
|
366
|
+
"types": "./dist/visualizations/selectors.d.ts",
|
|
367
|
+
"browser": "./dist/browser/visualizations/selectors.js",
|
|
368
|
+
"bun": "./dist/visualizations/selectors.js",
|
|
369
|
+
"node": "./dist/node/visualizations/selectors.js",
|
|
370
|
+
"default": "./dist/visualizations/selectors.js"
|
|
343
371
|
}
|
|
344
372
|
},
|
|
345
373
|
"scripts": {
|
|
@@ -351,31 +379,31 @@
|
|
|
351
379
|
"dev": "contractspec-bun-build dev",
|
|
352
380
|
"clean": "rimraf dist .turbo",
|
|
353
381
|
"lint": "bun lint:fix",
|
|
354
|
-
"lint:fix": "
|
|
355
|
-
"lint:check": "
|
|
382
|
+
"lint:fix": "biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .",
|
|
383
|
+
"lint:check": "biome check .",
|
|
356
384
|
"test": "bun test --pass-with-no-tests",
|
|
357
385
|
"prebuild": "contractspec-bun-build prebuild",
|
|
358
386
|
"typecheck": "tsc --noEmit"
|
|
359
387
|
},
|
|
360
388
|
"dependencies": {
|
|
361
|
-
"@contractspec/lib.identity-rbac": "3.7.
|
|
362
|
-
"@contractspec/lib.jobs": "3.7.
|
|
363
|
-
"@contractspec/module.audit-trail": "3.7.
|
|
364
|
-
"@contractspec/module.notifications": "3.7.
|
|
365
|
-
"@contractspec/lib.contracts-spec": "
|
|
366
|
-
"@contractspec/lib.schema": "3.7.
|
|
367
|
-
"@contractspec/lib.example-shared-ui": "6.0.
|
|
368
|
-
"@contractspec/lib.design-system": "3.
|
|
369
|
-
"@contractspec/lib.runtime-sandbox": "2.7.
|
|
389
|
+
"@contractspec/lib.identity-rbac": "3.7.10",
|
|
390
|
+
"@contractspec/lib.jobs": "3.7.10",
|
|
391
|
+
"@contractspec/module.audit-trail": "3.7.10",
|
|
392
|
+
"@contractspec/module.notifications": "3.7.10",
|
|
393
|
+
"@contractspec/lib.contracts-spec": "4.1.2",
|
|
394
|
+
"@contractspec/lib.schema": "3.7.8",
|
|
395
|
+
"@contractspec/lib.example-shared-ui": "6.0.10",
|
|
396
|
+
"@contractspec/lib.design-system": "3.8.3",
|
|
397
|
+
"@contractspec/lib.runtime-sandbox": "2.7.9",
|
|
370
398
|
"react": "19.2.0",
|
|
371
399
|
"react-dom": "19.2.0"
|
|
372
400
|
},
|
|
373
401
|
"devDependencies": {
|
|
374
|
-
"@contractspec/tool.typescript": "3.7.
|
|
402
|
+
"@contractspec/tool.typescript": "3.7.8",
|
|
375
403
|
"typescript": "^5.9.3",
|
|
376
404
|
"@types/react": "^19.2.14",
|
|
377
405
|
"@types/react-dom": "^19.2.2",
|
|
378
|
-
"@contractspec/tool.bun": "3.7.
|
|
406
|
+
"@contractspec/tool.bun": "3.7.8"
|
|
379
407
|
},
|
|
380
408
|
"publishConfig": {
|
|
381
409
|
"exports": {
|
|
@@ -701,6 +729,13 @@
|
|
|
701
729
|
"node": "./dist/node/ui/SaasDashboard.js",
|
|
702
730
|
"default": "./dist/ui/SaasDashboard.js"
|
|
703
731
|
},
|
|
732
|
+
"./ui/SaasDashboard.visualizations": {
|
|
733
|
+
"types": "./dist/ui/SaasDashboard.visualizations.d.ts",
|
|
734
|
+
"browser": "./dist/browser/ui/SaasDashboard.visualizations.js",
|
|
735
|
+
"bun": "./dist/ui/SaasDashboard.visualizations.js",
|
|
736
|
+
"node": "./dist/node/ui/SaasDashboard.visualizations.js",
|
|
737
|
+
"default": "./dist/ui/SaasDashboard.visualizations.js"
|
|
738
|
+
},
|
|
704
739
|
"./ui/SaasProjectList": {
|
|
705
740
|
"types": "./dist/ui/SaasProjectList.d.ts",
|
|
706
741
|
"browser": "./dist/browser/ui/SaasProjectList.js",
|
|
@@ -714,6 +749,27 @@
|
|
|
714
749
|
"bun": "./dist/ui/SaasSettingsPanel.js",
|
|
715
750
|
"node": "./dist/node/ui/SaasSettingsPanel.js",
|
|
716
751
|
"default": "./dist/ui/SaasSettingsPanel.js"
|
|
752
|
+
},
|
|
753
|
+
"./visualizations": {
|
|
754
|
+
"types": "./dist/visualizations/index.d.ts",
|
|
755
|
+
"browser": "./dist/browser/visualizations/index.js",
|
|
756
|
+
"bun": "./dist/visualizations/index.js",
|
|
757
|
+
"node": "./dist/node/visualizations/index.js",
|
|
758
|
+
"default": "./dist/visualizations/index.js"
|
|
759
|
+
},
|
|
760
|
+
"./visualizations/catalog": {
|
|
761
|
+
"types": "./dist/visualizations/catalog.d.ts",
|
|
762
|
+
"browser": "./dist/browser/visualizations/catalog.js",
|
|
763
|
+
"bun": "./dist/visualizations/catalog.js",
|
|
764
|
+
"node": "./dist/node/visualizations/catalog.js",
|
|
765
|
+
"default": "./dist/visualizations/catalog.js"
|
|
766
|
+
},
|
|
767
|
+
"./visualizations/selectors": {
|
|
768
|
+
"types": "./dist/visualizations/selectors.d.ts",
|
|
769
|
+
"browser": "./dist/browser/visualizations/selectors.js",
|
|
770
|
+
"bun": "./dist/visualizations/selectors.js",
|
|
771
|
+
"node": "./dist/node/visualizations/selectors.js",
|
|
772
|
+
"default": "./dist/visualizations/selectors.js"
|
|
717
773
|
}
|
|
718
774
|
},
|
|
719
775
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -1,158 +1,158 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
defineEntity,
|
|
3
|
+
defineEntityEnum,
|
|
4
|
+
field,
|
|
5
|
+
index,
|
|
6
6
|
} from '@contractspec/lib.schema';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Subscription status enum for entities.
|
|
10
10
|
*/
|
|
11
11
|
export const SubscriptionStatusEnum = defineEntityEnum({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
name: 'SubscriptionStatus',
|
|
13
|
+
values: ['TRIALING', 'ACTIVE', 'PAST_DUE', 'CANCELED', 'PAUSED'] as const,
|
|
14
|
+
schema: 'saas_app',
|
|
15
|
+
description: 'Status of a subscription.',
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Subscription entity - organization subscription info.
|
|
20
20
|
*/
|
|
21
21
|
export const SubscriptionEntity = defineEntity({
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
22
|
+
name: 'Subscription',
|
|
23
|
+
description: 'Organization subscription/plan information.',
|
|
24
|
+
schema: 'saas_app',
|
|
25
|
+
map: 'subscription',
|
|
26
|
+
fields: {
|
|
27
|
+
id: field.id(),
|
|
28
|
+
organizationId: field.foreignKey({ isUnique: true }),
|
|
29
|
+
|
|
30
|
+
// Plan info
|
|
31
|
+
planId: field.string({ description: 'Plan identifier' }),
|
|
32
|
+
planName: field.string({ description: 'Plan display name' }),
|
|
33
|
+
|
|
34
|
+
// Status
|
|
35
|
+
status: field.enum('SubscriptionStatus'),
|
|
36
|
+
|
|
37
|
+
// Billing cycle
|
|
38
|
+
currentPeriodStart: field.dateTime(),
|
|
39
|
+
currentPeriodEnd: field.dateTime(),
|
|
40
|
+
|
|
41
|
+
// Trial
|
|
42
|
+
trialEndsAt: field.dateTime({ isOptional: true }),
|
|
43
|
+
|
|
44
|
+
// Cancellation
|
|
45
|
+
cancelAtPeriodEnd: field.boolean({ default: false }),
|
|
46
|
+
canceledAt: field.dateTime({ isOptional: true }),
|
|
47
|
+
|
|
48
|
+
// External reference
|
|
49
|
+
stripeSubscriptionId: field.string({ isOptional: true }),
|
|
50
|
+
stripeCustomerId: field.string({ isOptional: true }),
|
|
51
|
+
|
|
52
|
+
// Metadata
|
|
53
|
+
metadata: field.json({ isOptional: true }),
|
|
54
|
+
|
|
55
|
+
// Timestamps
|
|
56
|
+
createdAt: field.createdAt(),
|
|
57
|
+
updatedAt: field.updatedAt(),
|
|
58
|
+
},
|
|
59
|
+
enums: [SubscriptionStatusEnum],
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* BillingUsage entity - track feature usage.
|
|
64
64
|
*/
|
|
65
65
|
export const BillingUsageEntity = defineEntity({
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
66
|
+
name: 'BillingUsage',
|
|
67
|
+
description: 'Track usage of metered features.',
|
|
68
|
+
schema: 'saas_app',
|
|
69
|
+
map: 'billing_usage',
|
|
70
|
+
fields: {
|
|
71
|
+
id: field.id(),
|
|
72
|
+
organizationId: field.foreignKey(),
|
|
73
|
+
|
|
74
|
+
// Feature
|
|
75
|
+
feature: field.string({
|
|
76
|
+
description: 'Feature being tracked (e.g., "api_calls", "storage_gb")',
|
|
77
|
+
}),
|
|
78
|
+
|
|
79
|
+
// Usage
|
|
80
|
+
quantity: field.int({ description: 'Usage quantity' }),
|
|
81
|
+
unit: field.string({
|
|
82
|
+
isOptional: true,
|
|
83
|
+
description: 'Unit of measurement',
|
|
84
|
+
}),
|
|
85
|
+
|
|
86
|
+
// Period
|
|
87
|
+
billingPeriod: field.string({
|
|
88
|
+
description: 'Billing period (e.g., "2024-01")',
|
|
89
|
+
}),
|
|
90
|
+
|
|
91
|
+
// Aggregation
|
|
92
|
+
recordedAt: field.dateTime({ description: 'When usage was recorded' }),
|
|
93
|
+
|
|
94
|
+
// Source
|
|
95
|
+
sourceId: field.string({
|
|
96
|
+
isOptional: true,
|
|
97
|
+
description: 'Source of usage (e.g., request ID)',
|
|
98
|
+
}),
|
|
99
|
+
sourceType: field.string({ isOptional: true }),
|
|
100
|
+
|
|
101
|
+
// Metadata
|
|
102
|
+
metadata: field.json({ isOptional: true }),
|
|
103
|
+
},
|
|
104
|
+
indexes: [
|
|
105
|
+
index.on(['organizationId', 'feature', 'billingPeriod']),
|
|
106
|
+
index.on(['organizationId', 'recordedAt']),
|
|
107
|
+
],
|
|
108
108
|
});
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
111
|
* UsageLimit entity - feature usage limits per plan.
|
|
112
112
|
*/
|
|
113
113
|
export const UsageLimitEntity = defineEntity({
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
114
|
+
name: 'UsageLimit',
|
|
115
|
+
description: 'Usage limits per plan/organization.',
|
|
116
|
+
schema: 'saas_app',
|
|
117
|
+
map: 'usage_limit',
|
|
118
|
+
fields: {
|
|
119
|
+
id: field.id(),
|
|
120
|
+
|
|
121
|
+
// Scope
|
|
122
|
+
planId: field.string({
|
|
123
|
+
isOptional: true,
|
|
124
|
+
description: 'Plan this limit applies to',
|
|
125
|
+
}),
|
|
126
|
+
organizationId: field.string({
|
|
127
|
+
isOptional: true,
|
|
128
|
+
description: 'Org-specific override',
|
|
129
|
+
}),
|
|
130
|
+
|
|
131
|
+
// Limit
|
|
132
|
+
feature: field.string({ description: 'Feature being limited' }),
|
|
133
|
+
limit: field.int({ description: 'Maximum allowed usage' }),
|
|
134
|
+
resetPeriod: field.string({
|
|
135
|
+
default: '"monthly"',
|
|
136
|
+
description: 'When limit resets',
|
|
137
|
+
}),
|
|
138
|
+
|
|
139
|
+
// Soft/hard limit
|
|
140
|
+
isSoftLimit: field.boolean({
|
|
141
|
+
default: false,
|
|
142
|
+
description: 'Whether to warn vs block',
|
|
143
|
+
}),
|
|
144
|
+
overage: field.boolean({
|
|
145
|
+
default: false,
|
|
146
|
+
description: 'Whether overage is allowed',
|
|
147
|
+
}),
|
|
148
|
+
overageRate: field.float({
|
|
149
|
+
isOptional: true,
|
|
150
|
+
description: 'Cost per unit over limit',
|
|
151
|
+
}),
|
|
152
|
+
|
|
153
|
+
// Timestamps
|
|
154
|
+
createdAt: field.createdAt(),
|
|
155
|
+
updatedAt: field.updatedAt(),
|
|
156
|
+
},
|
|
157
|
+
indexes: [index.unique(['planId', 'feature'])],
|
|
158
158
|
});
|
|
@@ -5,19 +5,19 @@ import { defineEnum } from '@contractspec/lib.schema';
|
|
|
5
5
|
* Note: Entity enum is defined separately in billing.entity.ts
|
|
6
6
|
*/
|
|
7
7
|
export const SubscriptionStatusSchemaEnum = defineEnum('SubscriptionStatus', [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
'TRIALING',
|
|
9
|
+
'ACTIVE',
|
|
10
|
+
'PAST_DUE',
|
|
11
|
+
'CANCELED',
|
|
12
|
+
'PAUSED',
|
|
13
13
|
]);
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Feature access reason enum.
|
|
17
17
|
*/
|
|
18
18
|
export const FeatureAccessReasonEnum = defineEnum('FeatureAccessReason', [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
'included',
|
|
20
|
+
'limit_available',
|
|
21
|
+
'limit_reached',
|
|
22
|
+
'not_in_plan',
|
|
23
23
|
]);
|