@contractspec/example.saas-boilerplate 3.7.5 → 3.7.7
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 +8 -8
- package/AGENTS.md +50 -27
- package/CHANGELOG.md +16 -0
- package/README.md +64 -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 +931 -932
- package/dist/browser/project/index.js +209 -209
- package/dist/browser/project/project.event.js +1 -1
- package/dist/browser/ui/SaasDashboard.js +45 -45
- 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 +483 -484
- 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 +112 -112
- package/dist/browser/ui/renderers/project-list.renderer.js +7 -7
- package/dist/handlers/index.d.ts +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +931 -932
- package/dist/node/billing/billing.event.js +1 -1
- package/dist/node/billing/index.js +1 -1
- package/dist/node/index.js +931 -932
- package/dist/node/project/index.js +209 -209
- package/dist/node/project/project.event.js +1 -1
- package/dist/node/ui/SaasDashboard.js +45 -45
- 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 +483 -484
- 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 +112 -112
- package/dist/node/ui/renderers/project-list.renderer.js +7 -7
- 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/settings/index.d.ts +1 -1
- package/dist/ui/SaasDashboard.js +45 -45
- 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 +483 -484
- 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 +112 -112
- package/dist/ui/renderers/project-list.renderer.d.ts +1 -1
- package/dist/ui/renderers/project-list.renderer.js +7 -7
- package/package.json +14 -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 +40 -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 +100 -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 +270 -270
- 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 +204 -204
- package/src/ui/renderers/project-list.renderer.tsx +14 -13
- package/tsconfig.json +7 -8
- package/tsdown.config.js +7 -3
|
@@ -10,65 +10,65 @@ import type { OverlayDefinition } from '../../shared/overlay-types';
|
|
|
10
10
|
* Free tier overlay - shows upgrade prompts and limits
|
|
11
11
|
*/
|
|
12
12
|
export const saasFreeUserOverlay: OverlayDefinition = {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
13
|
+
overlayId: 'saas-boilerplate.free-tier',
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
description: 'Shows limitations for free tier users',
|
|
16
|
+
appliesTo: {
|
|
17
|
+
feature: 'saas-boilerplate',
|
|
18
|
+
tier: 'free',
|
|
19
|
+
},
|
|
20
|
+
modifications: [
|
|
21
|
+
{
|
|
22
|
+
type: 'setLimit',
|
|
23
|
+
field: 'projects',
|
|
24
|
+
max: 3,
|
|
25
|
+
message: 'Upgrade to create more projects',
|
|
26
|
+
},
|
|
27
|
+
{ type: 'hideField', field: 'advancedSettings', reason: 'Pro feature' },
|
|
28
|
+
{
|
|
29
|
+
type: 'addBadge',
|
|
30
|
+
position: 'header',
|
|
31
|
+
label: 'Free Plan',
|
|
32
|
+
variant: 'default',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Demo user overlay
|
|
39
39
|
*/
|
|
40
40
|
export const saasDemoOverlay: OverlayDefinition = {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
41
|
+
overlayId: 'saas-boilerplate.demo-user',
|
|
42
|
+
version: '1.0.0',
|
|
43
|
+
description: 'Demo mode for SaaS boilerplate',
|
|
44
|
+
appliesTo: {
|
|
45
|
+
feature: 'saas-boilerplate',
|
|
46
|
+
role: 'demo',
|
|
47
|
+
},
|
|
48
|
+
modifications: [
|
|
49
|
+
{
|
|
50
|
+
type: 'hideField',
|
|
51
|
+
field: 'billingSection',
|
|
52
|
+
reason: 'Demo users cannot access billing',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: 'hideField',
|
|
56
|
+
field: 'deleteAccount',
|
|
57
|
+
reason: 'Not available in demo',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
type: 'addBadge',
|
|
61
|
+
position: 'header',
|
|
62
|
+
label: 'Demo Mode',
|
|
63
|
+
variant: 'warning',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* All overlays for saas-boilerplate
|
|
70
70
|
*/
|
|
71
71
|
export const saasOverlays: OverlayDefinition[] = [
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
saasFreeUserOverlay,
|
|
73
|
+
saasDemoOverlay,
|
|
74
74
|
];
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// SaaS renderers
|
|
2
|
-
|
|
2
|
+
|
|
3
3
|
export {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
projectListMarkdownRenderer,
|
|
5
|
+
saasBillingMarkdownRenderer,
|
|
6
|
+
saasDashboardMarkdownRenderer,
|
|
7
7
|
} from './project-list.markdown';
|
|
8
|
+
export { projectListReactRenderer } from './project-list.renderer';
|
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { PresentationRenderer } from '@contractspec/lib.contracts-spec/presentations/transform-engine';
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
mockGetSubscriptionHandler,
|
|
9
|
+
mockListProjectsHandler,
|
|
10
10
|
} from '../../handlers';
|
|
11
11
|
|
|
12
12
|
interface ProjectItem {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
status: string;
|
|
16
|
+
description?: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -21,60 +21,60 @@ interface ProjectItem {
|
|
|
21
21
|
* Only handles ProjectListView component
|
|
22
22
|
*/
|
|
23
23
|
export const projectListMarkdownRenderer: PresentationRenderer<{
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
mimeType: string;
|
|
25
|
+
body: string;
|
|
26
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
27
|
+
target: 'markdown',
|
|
28
|
+
render: async (desc, _ctx) => {
|
|
29
|
+
// Only handle ProjectListView
|
|
30
|
+
if (
|
|
31
|
+
desc.source.type !== 'component' ||
|
|
32
|
+
desc.source.componentKey !== 'ProjectListView'
|
|
33
|
+
) {
|
|
34
|
+
throw new Error('projectListMarkdownRenderer: not ProjectListView');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const data = await mockListProjectsHandler({
|
|
38
|
+
limit: 20,
|
|
39
|
+
offset: 0,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// The example handler returns 'projects', not 'items'
|
|
43
|
+
const items =
|
|
44
|
+
(data as { projects?: ProjectItem[]; items?: ProjectItem[] }).projects ??
|
|
45
|
+
(data as { items?: ProjectItem[] }).items ??
|
|
46
|
+
[];
|
|
47
|
+
|
|
48
|
+
const lines: string[] = [
|
|
49
|
+
'# Projects',
|
|
50
|
+
'',
|
|
51
|
+
`**Total**: ${data.total} projects`,
|
|
52
|
+
'',
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
if (items.length === 0) {
|
|
56
|
+
lines.push('_No projects found._');
|
|
57
|
+
} else {
|
|
58
|
+
lines.push('| Status | Project | Description |');
|
|
59
|
+
lines.push('|--------|---------|-------------|');
|
|
60
|
+
for (const project of items) {
|
|
61
|
+
const status =
|
|
62
|
+
project.status === 'ACTIVE'
|
|
63
|
+
? '✅'
|
|
64
|
+
: project.status === 'ARCHIVED'
|
|
65
|
+
? '📦'
|
|
66
|
+
: '⏸️';
|
|
67
|
+
lines.push(
|
|
68
|
+
`| ${status} | **${project.name}** | ${project.description ?? '-'} |`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
mimeType: 'text/markdown',
|
|
75
|
+
body: lines.join('\n'),
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
78
|
};
|
|
79
79
|
|
|
80
80
|
/**
|
|
@@ -82,89 +82,89 @@ export const projectListMarkdownRenderer: PresentationRenderer<{
|
|
|
82
82
|
* Only handles SaasDashboard component
|
|
83
83
|
*/
|
|
84
84
|
export const saasDashboardMarkdownRenderer: PresentationRenderer<{
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
mimeType: string;
|
|
86
|
+
body: string;
|
|
87
87
|
}> = {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
88
|
+
target: 'markdown',
|
|
89
|
+
render: async (desc, _ctx) => {
|
|
90
|
+
// Only handle SaasDashboard
|
|
91
|
+
if (
|
|
92
|
+
desc.source.type !== 'component' ||
|
|
93
|
+
desc.source.componentKey !== 'SaasDashboard'
|
|
94
|
+
) {
|
|
95
|
+
throw new Error('saasDashboardMarkdownRenderer: not SaasDashboard');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const [projectsData, subscription] = await Promise.all([
|
|
99
|
+
mockListProjectsHandler({ limit: 50 }),
|
|
100
|
+
mockGetSubscriptionHandler(),
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
const projects =
|
|
104
|
+
(projectsData as { projects?: ProjectItem[] }).projects ?? [];
|
|
105
|
+
const activeProjects = projects.filter((p) => p.status === 'ACTIVE').length;
|
|
106
|
+
const archivedProjects = projects.filter(
|
|
107
|
+
(p) => p.status === 'ARCHIVED'
|
|
108
|
+
).length;
|
|
109
|
+
|
|
110
|
+
const lines: string[] = [
|
|
111
|
+
'# SaaS Dashboard',
|
|
112
|
+
'',
|
|
113
|
+
'> Organization overview and usage summary',
|
|
114
|
+
'',
|
|
115
|
+
'## Summary',
|
|
116
|
+
'',
|
|
117
|
+
'| Metric | Value |',
|
|
118
|
+
'|--------|-------|',
|
|
119
|
+
`| Total Projects | ${projectsData.total} |`,
|
|
120
|
+
`| Active Projects | ${activeProjects} |`,
|
|
121
|
+
`| Archived Projects | ${archivedProjects} |`,
|
|
122
|
+
`| Subscription Plan | ${subscription.planName} |`,
|
|
123
|
+
`| Subscription Status | ${subscription.status} |`,
|
|
124
|
+
'',
|
|
125
|
+
'## Projects',
|
|
126
|
+
'',
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
if (projects.length === 0) {
|
|
130
|
+
lines.push('_No projects yet._');
|
|
131
|
+
} else {
|
|
132
|
+
lines.push('| Status | Project | Description |');
|
|
133
|
+
lines.push('|--------|---------|-------------|');
|
|
134
|
+
for (const project of projects.slice(0, 10)) {
|
|
135
|
+
const status =
|
|
136
|
+
project.status === 'ACTIVE'
|
|
137
|
+
? '✅'
|
|
138
|
+
: project.status === 'ARCHIVED'
|
|
139
|
+
? '📦'
|
|
140
|
+
: '⏸️';
|
|
141
|
+
lines.push(
|
|
142
|
+
`| ${status} | **${project.name}** | ${project.description ?? '-'} |`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
if (projects.length > 10) {
|
|
146
|
+
lines.push(
|
|
147
|
+
`| ... | ... | _${projectsData.total - 10} more projects_ |`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
lines.push('');
|
|
153
|
+
lines.push('## Subscription');
|
|
154
|
+
lines.push('');
|
|
155
|
+
lines.push(`- **Plan**: ${subscription.planName}`);
|
|
156
|
+
lines.push(`- **Status**: ${subscription.status}`);
|
|
157
|
+
if (subscription.currentPeriodEnd) {
|
|
158
|
+
lines.push(
|
|
159
|
+
`- **Period End**: ${new Date(subscription.currentPeriodEnd).toLocaleDateString()}`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
mimeType: 'text/markdown',
|
|
165
|
+
body: lines.join('\n'),
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
168
|
};
|
|
169
169
|
|
|
170
170
|
/**
|
|
@@ -172,68 +172,68 @@ export const saasDashboardMarkdownRenderer: PresentationRenderer<{
|
|
|
172
172
|
* Only handles SubscriptionView component
|
|
173
173
|
*/
|
|
174
174
|
export const saasBillingMarkdownRenderer: PresentationRenderer<{
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
mimeType: string;
|
|
176
|
+
body: string;
|
|
177
177
|
}> = {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
178
|
+
target: 'markdown',
|
|
179
|
+
render: async (desc, _ctx) => {
|
|
180
|
+
// Only handle SubscriptionView
|
|
181
|
+
if (
|
|
182
|
+
desc.source.type !== 'component' ||
|
|
183
|
+
desc.source.componentKey !== 'SubscriptionView'
|
|
184
|
+
) {
|
|
185
|
+
throw new Error('saasBillingMarkdownRenderer: not SubscriptionView');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const subscription = await mockGetSubscriptionHandler();
|
|
189
|
+
|
|
190
|
+
const lines: string[] = [
|
|
191
|
+
'# Billing & Subscription',
|
|
192
|
+
'',
|
|
193
|
+
'> Current subscription details and billing information',
|
|
194
|
+
'',
|
|
195
|
+
'## Subscription Details',
|
|
196
|
+
'',
|
|
197
|
+
'| Property | Value |',
|
|
198
|
+
'|----------|-------|',
|
|
199
|
+
`| Plan | ${subscription.planName} |`,
|
|
200
|
+
`| Status | ${subscription.status} |`,
|
|
201
|
+
`| ID | ${subscription.id} |`,
|
|
202
|
+
`| Period Start | ${new Date(subscription.currentPeriodStart).toLocaleDateString()} |`,
|
|
203
|
+
`| Period End | ${new Date(subscription.currentPeriodEnd).toLocaleDateString()} |`,
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
lines.push('');
|
|
207
|
+
lines.push('## Plan Limits');
|
|
208
|
+
lines.push('');
|
|
209
|
+
lines.push(`- **Projects**: ${subscription.limits.projects}`);
|
|
210
|
+
lines.push(`- **Users**: ${subscription.limits.users}`);
|
|
211
|
+
|
|
212
|
+
lines.push('');
|
|
213
|
+
lines.push('## Plan Features');
|
|
214
|
+
lines.push('');
|
|
215
|
+
|
|
216
|
+
if (subscription.planName.toLowerCase().includes('free')) {
|
|
217
|
+
lines.push('- ✅ Up to 3 projects');
|
|
218
|
+
lines.push('- ✅ Basic support');
|
|
219
|
+
lines.push('- ❌ Priority support');
|
|
220
|
+
lines.push('- ❌ Advanced analytics');
|
|
221
|
+
} else if (subscription.planName.toLowerCase().includes('pro')) {
|
|
222
|
+
lines.push('- ✅ Unlimited projects');
|
|
223
|
+
lines.push('- ✅ Priority support');
|
|
224
|
+
lines.push('- ✅ Advanced analytics');
|
|
225
|
+
lines.push('- ❌ Custom integrations');
|
|
226
|
+
} else {
|
|
227
|
+
lines.push('- ✅ Unlimited projects');
|
|
228
|
+
lines.push('- ✅ Priority support');
|
|
229
|
+
lines.push('- ✅ Advanced analytics');
|
|
230
|
+
lines.push('- ✅ Custom integrations');
|
|
231
|
+
lines.push('- ✅ Dedicated support');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
mimeType: 'text/markdown',
|
|
236
|
+
body: lines.join('\n'),
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
239
|
};
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* React renderer for SaaS Project List presentation
|
|
3
3
|
*/
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
import type { PresentationRenderer } from '@contractspec/lib.contracts-spec/presentations/transform-engine';
|
|
6
|
+
import * as React from 'react';
|
|
6
7
|
import { SaasProjectList } from '../SaasProjectList';
|
|
7
8
|
|
|
8
9
|
export const projectListReactRenderer: PresentationRenderer<React.ReactElement> =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
{
|
|
11
|
+
target: 'react',
|
|
12
|
+
render: async (desc, _ctx) => {
|
|
13
|
+
if (desc.source.type !== 'component') {
|
|
14
|
+
throw new Error('Invalid source type');
|
|
15
|
+
}
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
if (desc.source.componentKey !== 'SaasProjectListView') {
|
|
18
|
+
throw new Error(`Unknown component: ${desc.source.componentKey}`);
|
|
19
|
+
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
return <SaasProjectList />;
|
|
22
|
+
},
|
|
23
|
+
};
|
package/tsconfig.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
"extends": "@contractspec/tool.typescript/react-library.json",
|
|
3
|
+
"include": ["src"],
|
|
4
|
+
"exclude": ["node_modules", "dist"],
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"rootDir": "src",
|
|
7
|
+
"outDir": "dist"
|
|
8
|
+
}
|
|
9
9
|
}
|
|
10
|
-
|
package/tsdown.config.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
defineConfig,
|
|
3
|
+
moduleLibrary,
|
|
4
|
+
withDevExports,
|
|
5
|
+
} from '@contractspec/tool.bun';
|
|
2
6
|
|
|
3
7
|
export default defineConfig((options) => ({
|
|
4
|
-
|
|
5
|
-
|
|
8
|
+
...moduleLibrary,
|
|
9
|
+
...withDevExports,
|
|
6
10
|
}));
|