@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
|
@@ -344,9 +344,216 @@ function createSaasHandlers(db) {
|
|
|
344
344
|
getSubscription
|
|
345
345
|
};
|
|
346
346
|
}
|
|
347
|
+
// src/visualizations/catalog.ts
|
|
348
|
+
import {
|
|
349
|
+
defineVisualization,
|
|
350
|
+
VisualizationRegistry
|
|
351
|
+
} from "@contractspec/lib.contracts-spec/visualizations";
|
|
352
|
+
var PROJECT_LIST_REF = {
|
|
353
|
+
key: "saas.project.list",
|
|
354
|
+
version: "1.0.0"
|
|
355
|
+
};
|
|
356
|
+
var META = {
|
|
357
|
+
version: "1.0.0",
|
|
358
|
+
domain: "saas",
|
|
359
|
+
stability: "experimental",
|
|
360
|
+
owners: ["@example.saas-boilerplate"],
|
|
361
|
+
tags: ["saas", "visualization", "projects"]
|
|
362
|
+
};
|
|
363
|
+
var SaasProjectUsageVisualization = defineVisualization({
|
|
364
|
+
meta: {
|
|
365
|
+
...META,
|
|
366
|
+
key: "saas-boilerplate.visualization.project-usage",
|
|
367
|
+
title: "Project Capacity",
|
|
368
|
+
description: "Current project count against the current plan limit.",
|
|
369
|
+
goal: "Show usage against the active plan allowance.",
|
|
370
|
+
context: "SaaS account overview."
|
|
371
|
+
},
|
|
372
|
+
source: { primary: PROJECT_LIST_REF, resultPath: "data" },
|
|
373
|
+
visualization: {
|
|
374
|
+
kind: "metric",
|
|
375
|
+
measure: "totalProjects",
|
|
376
|
+
comparisonMeasure: "projectLimit",
|
|
377
|
+
measures: [
|
|
378
|
+
{
|
|
379
|
+
key: "totalProjects",
|
|
380
|
+
label: "Projects",
|
|
381
|
+
dataPath: "totalProjects",
|
|
382
|
+
format: "number"
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
key: "projectLimit",
|
|
386
|
+
label: "Plan Limit",
|
|
387
|
+
dataPath: "projectLimit",
|
|
388
|
+
format: "number"
|
|
389
|
+
}
|
|
390
|
+
],
|
|
391
|
+
table: { caption: "Current project count and plan limit." }
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
var SaasProjectStatusVisualization = defineVisualization({
|
|
395
|
+
meta: {
|
|
396
|
+
...META,
|
|
397
|
+
key: "saas-boilerplate.visualization.project-status",
|
|
398
|
+
title: "Project Status",
|
|
399
|
+
description: "Distribution of project states.",
|
|
400
|
+
goal: "Show the mix of active, draft, and archived projects.",
|
|
401
|
+
context: "Project portfolio overview."
|
|
402
|
+
},
|
|
403
|
+
source: { primary: PROJECT_LIST_REF, resultPath: "data" },
|
|
404
|
+
visualization: {
|
|
405
|
+
kind: "pie",
|
|
406
|
+
nameDimension: "status",
|
|
407
|
+
valueMeasure: "projects",
|
|
408
|
+
dimensions: [
|
|
409
|
+
{ key: "status", label: "Status", dataPath: "status", type: "category" }
|
|
410
|
+
],
|
|
411
|
+
measures: [
|
|
412
|
+
{
|
|
413
|
+
key: "projects",
|
|
414
|
+
label: "Projects",
|
|
415
|
+
dataPath: "projects",
|
|
416
|
+
format: "number"
|
|
417
|
+
}
|
|
418
|
+
],
|
|
419
|
+
table: { caption: "Project counts by status." }
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
var SaasProjectTierVisualization = defineVisualization({
|
|
423
|
+
meta: {
|
|
424
|
+
...META,
|
|
425
|
+
key: "saas-boilerplate.visualization.project-tiers",
|
|
426
|
+
title: "Tier Comparison",
|
|
427
|
+
description: "Distribution of projects across tiers.",
|
|
428
|
+
goal: "Compare how the current portfolio is distributed by tier.",
|
|
429
|
+
context: "Plan and packaging overview."
|
|
430
|
+
},
|
|
431
|
+
source: { primary: PROJECT_LIST_REF, resultPath: "data" },
|
|
432
|
+
visualization: {
|
|
433
|
+
kind: "cartesian",
|
|
434
|
+
variant: "bar",
|
|
435
|
+
xDimension: "tier",
|
|
436
|
+
yMeasures: ["projects"],
|
|
437
|
+
dimensions: [
|
|
438
|
+
{ key: "tier", label: "Tier", dataPath: "tier", type: "category" }
|
|
439
|
+
],
|
|
440
|
+
measures: [
|
|
441
|
+
{
|
|
442
|
+
key: "projects",
|
|
443
|
+
label: "Projects",
|
|
444
|
+
dataPath: "projects",
|
|
445
|
+
format: "number",
|
|
446
|
+
color: "#1d4ed8"
|
|
447
|
+
}
|
|
448
|
+
],
|
|
449
|
+
table: { caption: "Project counts by tier." }
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
var SaasProjectActivityVisualization = defineVisualization({
|
|
453
|
+
meta: {
|
|
454
|
+
...META,
|
|
455
|
+
key: "saas-boilerplate.visualization.project-activity",
|
|
456
|
+
title: "Recent Project Activity",
|
|
457
|
+
description: "Daily project creation activity.",
|
|
458
|
+
goal: "Show recent project activity over time.",
|
|
459
|
+
context: "Project portfolio trend view."
|
|
460
|
+
},
|
|
461
|
+
source: { primary: PROJECT_LIST_REF, resultPath: "data" },
|
|
462
|
+
visualization: {
|
|
463
|
+
kind: "cartesian",
|
|
464
|
+
variant: "line",
|
|
465
|
+
xDimension: "day",
|
|
466
|
+
yMeasures: ["projects"],
|
|
467
|
+
dimensions: [{ key: "day", label: "Day", dataPath: "day", type: "time" }],
|
|
468
|
+
measures: [
|
|
469
|
+
{
|
|
470
|
+
key: "projects",
|
|
471
|
+
label: "Projects",
|
|
472
|
+
dataPath: "projects",
|
|
473
|
+
format: "number",
|
|
474
|
+
color: "#0f766e"
|
|
475
|
+
}
|
|
476
|
+
],
|
|
477
|
+
table: { caption: "Daily project creation counts." }
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
var SaasVisualizationSpecs = [
|
|
481
|
+
SaasProjectUsageVisualization,
|
|
482
|
+
SaasProjectStatusVisualization,
|
|
483
|
+
SaasProjectTierVisualization,
|
|
484
|
+
SaasProjectActivityVisualization
|
|
485
|
+
];
|
|
486
|
+
var SaasVisualizationRegistry = new VisualizationRegistry([
|
|
487
|
+
...SaasVisualizationSpecs
|
|
488
|
+
]);
|
|
489
|
+
var SaasVisualizationRefs = SaasVisualizationSpecs.map((spec) => ({
|
|
490
|
+
key: spec.meta.key,
|
|
491
|
+
version: spec.meta.version
|
|
492
|
+
}));
|
|
493
|
+
|
|
494
|
+
// src/visualizations/selectors.ts
|
|
495
|
+
function toDayKey(value) {
|
|
496
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
497
|
+
return date.toISOString().slice(0, 10);
|
|
498
|
+
}
|
|
499
|
+
function createSaasVisualizationItems(projects, projectLimit = 10) {
|
|
500
|
+
const statusCounts = new Map;
|
|
501
|
+
const tierCounts = new Map;
|
|
502
|
+
const activityCounts = new Map;
|
|
503
|
+
for (const project of projects) {
|
|
504
|
+
statusCounts.set(project.status, (statusCounts.get(project.status) ?? 0) + 1);
|
|
505
|
+
tierCounts.set(project.tier, (tierCounts.get(project.tier) ?? 0) + 1);
|
|
506
|
+
const day = toDayKey(project.createdAt);
|
|
507
|
+
activityCounts.set(day, (activityCounts.get(day) ?? 0) + 1);
|
|
508
|
+
}
|
|
509
|
+
return [
|
|
510
|
+
{
|
|
511
|
+
key: "saas-capacity",
|
|
512
|
+
spec: SaasProjectUsageVisualization,
|
|
513
|
+
data: { data: [{ totalProjects: projects.length, projectLimit }] },
|
|
514
|
+
title: "Project Capacity",
|
|
515
|
+
description: "Current project count compared to the active limit.",
|
|
516
|
+
height: 220
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
key: "saas-status",
|
|
520
|
+
spec: SaasProjectStatusVisualization,
|
|
521
|
+
data: {
|
|
522
|
+
data: Array.from(statusCounts.entries()).map(([status, count]) => ({
|
|
523
|
+
status,
|
|
524
|
+
projects: count
|
|
525
|
+
}))
|
|
526
|
+
},
|
|
527
|
+
title: "Project Status",
|
|
528
|
+
description: "Status mix across the current project portfolio.",
|
|
529
|
+
height: 260
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
key: "saas-tier",
|
|
533
|
+
spec: SaasProjectTierVisualization,
|
|
534
|
+
data: {
|
|
535
|
+
data: Array.from(tierCounts.entries()).map(([tier, count]) => ({
|
|
536
|
+
tier,
|
|
537
|
+
projects: count
|
|
538
|
+
}))
|
|
539
|
+
},
|
|
540
|
+
title: "Tier Comparison",
|
|
541
|
+
description: "How projects are distributed across tiers."
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
key: "saas-activity",
|
|
545
|
+
spec: SaasProjectActivityVisualization,
|
|
546
|
+
data: {
|
|
547
|
+
data: Array.from(activityCounts.entries()).sort(([left], [right]) => left.localeCompare(right)).map(([day, count]) => ({ day, projects: count }))
|
|
548
|
+
},
|
|
549
|
+
title: "Recent Project Activity",
|
|
550
|
+
description: "Daily project creation activity."
|
|
551
|
+
}
|
|
552
|
+
];
|
|
553
|
+
}
|
|
347
554
|
// src/ui/hooks/useProjectList.ts
|
|
348
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
349
555
|
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
556
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
350
557
|
function useProjectList(options = {}) {
|
|
351
558
|
const { handlers, projectId } = useTemplateRuntime();
|
|
352
559
|
const { saas: saas2 } = handlers;
|
|
@@ -408,118 +615,19 @@ function useProjectList(options = {}) {
|
|
|
408
615
|
};
|
|
409
616
|
}
|
|
410
617
|
|
|
411
|
-
// src/ui/SaasProjectList.tsx
|
|
412
|
-
import {
|
|
413
|
-
StatCard,
|
|
414
|
-
StatCardGroup,
|
|
415
|
-
StatusChip,
|
|
416
|
-
EntityCard,
|
|
417
|
-
EmptyState,
|
|
418
|
-
LoaderBlock,
|
|
419
|
-
ErrorState,
|
|
420
|
-
Button
|
|
421
|
-
} from "@contractspec/lib.design-system";
|
|
422
|
-
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
423
|
-
"use client";
|
|
424
|
-
function getStatusTone(status) {
|
|
425
|
-
switch (status) {
|
|
426
|
-
case "ACTIVE":
|
|
427
|
-
return "success";
|
|
428
|
-
case "DRAFT":
|
|
429
|
-
return "neutral";
|
|
430
|
-
case "ARCHIVED":
|
|
431
|
-
return "danger";
|
|
432
|
-
default:
|
|
433
|
-
return "neutral";
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
function SaasProjectList({
|
|
437
|
-
onProjectClick,
|
|
438
|
-
onCreateProject
|
|
439
|
-
}) {
|
|
440
|
-
const { data, loading, error, stats, refetch } = useProjectList();
|
|
441
|
-
if (loading && !data) {
|
|
442
|
-
return /* @__PURE__ */ jsxDEV(LoaderBlock, {
|
|
443
|
-
label: "Loading projects..."
|
|
444
|
-
}, undefined, false, undefined, this);
|
|
445
|
-
}
|
|
446
|
-
if (error) {
|
|
447
|
-
return /* @__PURE__ */ jsxDEV(ErrorState, {
|
|
448
|
-
title: "Failed to load projects",
|
|
449
|
-
description: error.message,
|
|
450
|
-
onRetry: refetch,
|
|
451
|
-
retryLabel: "Retry"
|
|
452
|
-
}, undefined, false, undefined, this);
|
|
453
|
-
}
|
|
454
|
-
if (!data?.items.length) {
|
|
455
|
-
return /* @__PURE__ */ jsxDEV(EmptyState, {
|
|
456
|
-
title: "No projects found",
|
|
457
|
-
description: "Create your first project to get started.",
|
|
458
|
-
primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV(Button, {
|
|
459
|
-
onPress: onCreateProject,
|
|
460
|
-
children: "Create Project"
|
|
461
|
-
}, undefined, false, undefined, this) : undefined
|
|
462
|
-
}, undefined, false, undefined, this);
|
|
463
|
-
}
|
|
464
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
465
|
-
className: "space-y-6",
|
|
466
|
-
children: [
|
|
467
|
-
stats && /* @__PURE__ */ jsxDEV(StatCardGroup, {
|
|
468
|
-
children: [
|
|
469
|
-
/* @__PURE__ */ jsxDEV(StatCard, {
|
|
470
|
-
label: "Total Projects",
|
|
471
|
-
value: stats.total.toString()
|
|
472
|
-
}, undefined, false, undefined, this),
|
|
473
|
-
/* @__PURE__ */ jsxDEV(StatCard, {
|
|
474
|
-
label: "Active",
|
|
475
|
-
value: stats.activeCount.toString()
|
|
476
|
-
}, undefined, false, undefined, this),
|
|
477
|
-
/* @__PURE__ */ jsxDEV(StatCard, {
|
|
478
|
-
label: "Draft",
|
|
479
|
-
value: stats.draftCount.toString()
|
|
480
|
-
}, undefined, false, undefined, this)
|
|
481
|
-
]
|
|
482
|
-
}, undefined, true, undefined, this),
|
|
483
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
484
|
-
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
|
|
485
|
-
children: data.items.map((project) => /* @__PURE__ */ jsxDEV(EntityCard, {
|
|
486
|
-
cardTitle: project.name,
|
|
487
|
-
cardSubtitle: project.tier,
|
|
488
|
-
meta: /* @__PURE__ */ jsxDEV("p", {
|
|
489
|
-
className: "text-muted-foreground text-sm",
|
|
490
|
-
children: project.description
|
|
491
|
-
}, undefined, false, undefined, this),
|
|
492
|
-
chips: /* @__PURE__ */ jsxDEV(StatusChip, {
|
|
493
|
-
tone: getStatusTone(project.status),
|
|
494
|
-
label: project.status
|
|
495
|
-
}, undefined, false, undefined, this),
|
|
496
|
-
footer: /* @__PURE__ */ jsxDEV("span", {
|
|
497
|
-
className: "text-muted-foreground text-xs",
|
|
498
|
-
children: project.updatedAt.toLocaleDateString()
|
|
499
|
-
}, undefined, false, undefined, this),
|
|
500
|
-
onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
|
|
501
|
-
}, project.id, false, undefined, this))
|
|
502
|
-
}, undefined, false, undefined, this)
|
|
503
|
-
]
|
|
504
|
-
}, undefined, true, undefined, this);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// src/ui/renderers/project-list.renderer.tsx
|
|
508
|
-
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
509
|
-
var projectListReactRenderer = {
|
|
510
|
-
target: "react",
|
|
511
|
-
render: async (desc, _ctx) => {
|
|
512
|
-
if (desc.source.type !== "component") {
|
|
513
|
-
throw new Error("Invalid source type");
|
|
514
|
-
}
|
|
515
|
-
if (desc.source.componentKey !== "SaasProjectListView") {
|
|
516
|
-
throw new Error(`Unknown component: ${desc.source.componentKey}`);
|
|
517
|
-
}
|
|
518
|
-
return /* @__PURE__ */ jsxDEV2(SaasProjectList, {}, undefined, false, undefined, this);
|
|
519
|
-
}
|
|
520
|
-
};
|
|
521
|
-
|
|
522
618
|
// src/ui/renderers/project-list.markdown.ts
|
|
619
|
+
var PROJECT_TIERS = [
|
|
620
|
+
"FREE",
|
|
621
|
+
"PRO",
|
|
622
|
+
"ENTERPRISE"
|
|
623
|
+
];
|
|
624
|
+
function toVisualizationProject(project, index) {
|
|
625
|
+
return {
|
|
626
|
+
status: project.status === "DELETED" ? "ARCHIVED" : project.status,
|
|
627
|
+
tier: PROJECT_TIERS[index % PROJECT_TIERS.length] ?? "FREE",
|
|
628
|
+
createdAt: project.createdAt
|
|
629
|
+
};
|
|
630
|
+
}
|
|
523
631
|
var projectListMarkdownRenderer = {
|
|
524
632
|
target: "markdown",
|
|
525
633
|
render: async (desc, _ctx) => {
|
|
@@ -530,7 +638,7 @@ var projectListMarkdownRenderer = {
|
|
|
530
638
|
limit: 20,
|
|
531
639
|
offset: 0
|
|
532
640
|
});
|
|
533
|
-
const items = data.projects ??
|
|
641
|
+
const items = data.projects ?? [];
|
|
534
642
|
const lines = [
|
|
535
643
|
"# Projects",
|
|
536
644
|
"",
|
|
@@ -567,6 +675,7 @@ var saasDashboardMarkdownRenderer = {
|
|
|
567
675
|
const projects = projectsData.projects ?? [];
|
|
568
676
|
const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
|
|
569
677
|
const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
|
|
678
|
+
const visualizations = createSaasVisualizationItems(projects.map(toVisualizationProject), 10);
|
|
570
679
|
const lines = [
|
|
571
680
|
"# SaaS Dashboard",
|
|
572
681
|
"",
|
|
@@ -581,10 +690,16 @@ var saasDashboardMarkdownRenderer = {
|
|
|
581
690
|
`| Archived Projects | ${archivedProjects} |`,
|
|
582
691
|
`| Subscription Plan | ${subscription.planName} |`,
|
|
583
692
|
`| Subscription Status | ${subscription.status} |`,
|
|
584
|
-
"",
|
|
585
|
-
"## Projects",
|
|
586
693
|
""
|
|
587
694
|
];
|
|
695
|
+
lines.push("## Visualization Overview");
|
|
696
|
+
lines.push("");
|
|
697
|
+
for (const item of visualizations) {
|
|
698
|
+
lines.push(`- **${item.title}** via \`${item.spec.meta.key}\``);
|
|
699
|
+
}
|
|
700
|
+
lines.push("");
|
|
701
|
+
lines.push("## Projects");
|
|
702
|
+
lines.push("");
|
|
588
703
|
if (projects.length === 0) {
|
|
589
704
|
lines.push("_No projects yet._");
|
|
590
705
|
} else {
|
|
@@ -667,6 +782,117 @@ var saasBillingMarkdownRenderer = {
|
|
|
667
782
|
};
|
|
668
783
|
}
|
|
669
784
|
};
|
|
785
|
+
|
|
786
|
+
// src/ui/SaasProjectList.tsx
|
|
787
|
+
import {
|
|
788
|
+
Button,
|
|
789
|
+
EmptyState,
|
|
790
|
+
EntityCard,
|
|
791
|
+
ErrorState,
|
|
792
|
+
LoaderBlock,
|
|
793
|
+
StatCard,
|
|
794
|
+
StatCardGroup,
|
|
795
|
+
StatusChip
|
|
796
|
+
} from "@contractspec/lib.design-system";
|
|
797
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
798
|
+
"use client";
|
|
799
|
+
function getStatusTone(status) {
|
|
800
|
+
switch (status) {
|
|
801
|
+
case "ACTIVE":
|
|
802
|
+
return "success";
|
|
803
|
+
case "DRAFT":
|
|
804
|
+
return "neutral";
|
|
805
|
+
case "ARCHIVED":
|
|
806
|
+
return "danger";
|
|
807
|
+
default:
|
|
808
|
+
return "neutral";
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
function SaasProjectList({
|
|
812
|
+
onProjectClick,
|
|
813
|
+
onCreateProject
|
|
814
|
+
}) {
|
|
815
|
+
const { data, loading, error, stats, refetch } = useProjectList();
|
|
816
|
+
if (loading && !data) {
|
|
817
|
+
return /* @__PURE__ */ jsxDEV(LoaderBlock, {
|
|
818
|
+
label: "Loading projects..."
|
|
819
|
+
}, undefined, false, undefined, this);
|
|
820
|
+
}
|
|
821
|
+
if (error) {
|
|
822
|
+
return /* @__PURE__ */ jsxDEV(ErrorState, {
|
|
823
|
+
title: "Failed to load projects",
|
|
824
|
+
description: error.message,
|
|
825
|
+
onRetry: refetch,
|
|
826
|
+
retryLabel: "Retry"
|
|
827
|
+
}, undefined, false, undefined, this);
|
|
828
|
+
}
|
|
829
|
+
if (!data?.items.length) {
|
|
830
|
+
return /* @__PURE__ */ jsxDEV(EmptyState, {
|
|
831
|
+
title: "No projects found",
|
|
832
|
+
description: "Create your first project to get started.",
|
|
833
|
+
primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV(Button, {
|
|
834
|
+
onPress: onCreateProject,
|
|
835
|
+
children: "Create Project"
|
|
836
|
+
}, undefined, false, undefined, this) : undefined
|
|
837
|
+
}, undefined, false, undefined, this);
|
|
838
|
+
}
|
|
839
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
840
|
+
className: "space-y-6",
|
|
841
|
+
children: [
|
|
842
|
+
stats && /* @__PURE__ */ jsxDEV(StatCardGroup, {
|
|
843
|
+
children: [
|
|
844
|
+
/* @__PURE__ */ jsxDEV(StatCard, {
|
|
845
|
+
label: "Total Projects",
|
|
846
|
+
value: stats.total.toString()
|
|
847
|
+
}, undefined, false, undefined, this),
|
|
848
|
+
/* @__PURE__ */ jsxDEV(StatCard, {
|
|
849
|
+
label: "Active",
|
|
850
|
+
value: stats.activeCount.toString()
|
|
851
|
+
}, undefined, false, undefined, this),
|
|
852
|
+
/* @__PURE__ */ jsxDEV(StatCard, {
|
|
853
|
+
label: "Draft",
|
|
854
|
+
value: stats.draftCount.toString()
|
|
855
|
+
}, undefined, false, undefined, this)
|
|
856
|
+
]
|
|
857
|
+
}, undefined, true, undefined, this),
|
|
858
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
859
|
+
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
|
|
860
|
+
children: data.items.map((project) => /* @__PURE__ */ jsxDEV(EntityCard, {
|
|
861
|
+
cardTitle: project.name,
|
|
862
|
+
cardSubtitle: project.tier,
|
|
863
|
+
meta: /* @__PURE__ */ jsxDEV("p", {
|
|
864
|
+
className: "text-muted-foreground text-sm",
|
|
865
|
+
children: project.description
|
|
866
|
+
}, undefined, false, undefined, this),
|
|
867
|
+
chips: /* @__PURE__ */ jsxDEV(StatusChip, {
|
|
868
|
+
tone: getStatusTone(project.status),
|
|
869
|
+
label: project.status
|
|
870
|
+
}, undefined, false, undefined, this),
|
|
871
|
+
footer: /* @__PURE__ */ jsxDEV("span", {
|
|
872
|
+
className: "text-muted-foreground text-xs",
|
|
873
|
+
children: project.updatedAt.toLocaleDateString()
|
|
874
|
+
}, undefined, false, undefined, this),
|
|
875
|
+
onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
|
|
876
|
+
}, project.id, false, undefined, this))
|
|
877
|
+
}, undefined, false, undefined, this)
|
|
878
|
+
]
|
|
879
|
+
}, undefined, true, undefined, this);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// src/ui/renderers/project-list.renderer.tsx
|
|
883
|
+
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
884
|
+
var projectListReactRenderer = {
|
|
885
|
+
target: "react",
|
|
886
|
+
render: async (desc, _ctx) => {
|
|
887
|
+
if (desc.source.type !== "component") {
|
|
888
|
+
throw new Error("Invalid source type");
|
|
889
|
+
}
|
|
890
|
+
if (desc.source.componentKey !== "SaasProjectListView") {
|
|
891
|
+
throw new Error(`Unknown component: ${desc.source.componentKey}`);
|
|
892
|
+
}
|
|
893
|
+
return /* @__PURE__ */ jsxDEV2(SaasProjectList, {}, undefined, false, undefined, this);
|
|
894
|
+
}
|
|
895
|
+
};
|
|
670
896
|
export {
|
|
671
897
|
saasDashboardMarkdownRenderer,
|
|
672
898
|
saasBillingMarkdownRenderer,
|