@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
package/dist/node/ui/index.js
CHANGED
|
@@ -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;
|
|
@@ -409,8 +616,8 @@ function useProjectList(options = {}) {
|
|
|
409
616
|
}
|
|
410
617
|
|
|
411
618
|
// src/ui/hooks/useProjectMutations.ts
|
|
412
|
-
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
413
619
|
import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
|
|
620
|
+
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
414
621
|
function useProjectMutations(options = {}) {
|
|
415
622
|
const { handlers, projectId } = useTemplateRuntime2();
|
|
416
623
|
const { saas: saas2 } = handlers;
|
|
@@ -497,9 +704,12 @@ function useProjectMutations(options = {}) {
|
|
|
497
704
|
};
|
|
498
705
|
}
|
|
499
706
|
|
|
707
|
+
// src/ui/hooks/index.ts
|
|
708
|
+
"use client";
|
|
709
|
+
|
|
500
710
|
// src/ui/modals/CreateProjectModal.tsx
|
|
501
|
-
import { useState as useState3 } from "react";
|
|
502
711
|
import { Button, Input } from "@contractspec/lib.design-system";
|
|
712
|
+
import { useState as useState3 } from "react";
|
|
503
713
|
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
504
714
|
"use client";
|
|
505
715
|
var TIERS = [
|
|
@@ -544,7 +754,7 @@ function CreateProjectModal({
|
|
|
544
754
|
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
545
755
|
children: [
|
|
546
756
|
/* @__PURE__ */ jsxDEV("div", {
|
|
547
|
-
className: "bg-background/80
|
|
757
|
+
className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
|
|
548
758
|
onClick: onClose,
|
|
549
759
|
role: "button",
|
|
550
760
|
tabIndex: 0,
|
|
@@ -555,10 +765,10 @@ function CreateProjectModal({
|
|
|
555
765
|
"aria-label": "Close modal"
|
|
556
766
|
}, undefined, false, undefined, this),
|
|
557
767
|
/* @__PURE__ */ jsxDEV("div", {
|
|
558
|
-
className: "
|
|
768
|
+
className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
|
|
559
769
|
children: [
|
|
560
770
|
/* @__PURE__ */ jsxDEV("h2", {
|
|
561
|
-
className: "mb-4 text-xl
|
|
771
|
+
className: "mb-4 font-semibold text-xl",
|
|
562
772
|
children: "Create New Project"
|
|
563
773
|
}, undefined, false, undefined, this),
|
|
564
774
|
/* @__PURE__ */ jsxDEV("form", {
|
|
@@ -569,7 +779,7 @@ function CreateProjectModal({
|
|
|
569
779
|
children: [
|
|
570
780
|
/* @__PURE__ */ jsxDEV("label", {
|
|
571
781
|
htmlFor: "project-name",
|
|
572
|
-
className: "
|
|
782
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
573
783
|
children: "Project Name *"
|
|
574
784
|
}, undefined, false, undefined, this),
|
|
575
785
|
/* @__PURE__ */ jsxDEV(Input, {
|
|
@@ -585,7 +795,7 @@ function CreateProjectModal({
|
|
|
585
795
|
children: [
|
|
586
796
|
/* @__PURE__ */ jsxDEV("label", {
|
|
587
797
|
htmlFor: "project-description",
|
|
588
|
-
className: "
|
|
798
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
589
799
|
children: "Description"
|
|
590
800
|
}, undefined, false, undefined, this),
|
|
591
801
|
/* @__PURE__ */ jsxDEV("textarea", {
|
|
@@ -595,7 +805,7 @@ function CreateProjectModal({
|
|
|
595
805
|
placeholder: "Describe what this project is about...",
|
|
596
806
|
rows: 3,
|
|
597
807
|
disabled: isLoading,
|
|
598
|
-
className: "
|
|
808
|
+
className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50"
|
|
599
809
|
}, undefined, false, undefined, this)
|
|
600
810
|
]
|
|
601
811
|
}, undefined, true, undefined, this),
|
|
@@ -603,7 +813,7 @@ function CreateProjectModal({
|
|
|
603
813
|
children: [
|
|
604
814
|
/* @__PURE__ */ jsxDEV("label", {
|
|
605
815
|
htmlFor: "project-tier",
|
|
606
|
-
className: "
|
|
816
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
607
817
|
children: "Tier"
|
|
608
818
|
}, undefined, false, undefined, this),
|
|
609
819
|
/* @__PURE__ */ jsxDEV("select", {
|
|
@@ -611,7 +821,7 @@ function CreateProjectModal({
|
|
|
611
821
|
value: tier,
|
|
612
822
|
onChange: (e) => setTier(e.target.value),
|
|
613
823
|
disabled: isLoading,
|
|
614
|
-
className: "
|
|
824
|
+
className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50",
|
|
615
825
|
children: TIERS.map((t) => /* @__PURE__ */ jsxDEV("option", {
|
|
616
826
|
value: t.value,
|
|
617
827
|
children: t.label
|
|
@@ -620,7 +830,7 @@ function CreateProjectModal({
|
|
|
620
830
|
]
|
|
621
831
|
}, undefined, true, undefined, this),
|
|
622
832
|
error && /* @__PURE__ */ jsxDEV("div", {
|
|
623
|
-
className: "bg-destructive/10
|
|
833
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
624
834
|
children: error
|
|
625
835
|
}, undefined, false, undefined, this),
|
|
626
836
|
/* @__PURE__ */ jsxDEV("div", {
|
|
@@ -649,8 +859,8 @@ function CreateProjectModal({
|
|
|
649
859
|
}
|
|
650
860
|
|
|
651
861
|
// src/ui/modals/ProjectActionsModal.tsx
|
|
652
|
-
import { useEffect as useEffect2, useState as useState4 } from "react";
|
|
653
862
|
import { Button as Button2, Input as Input2 } from "@contractspec/lib.design-system";
|
|
863
|
+
import { useEffect as useEffect2, useState as useState4 } from "react";
|
|
654
864
|
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
655
865
|
"use client";
|
|
656
866
|
function ProjectActionsModal({
|
|
@@ -743,7 +953,7 @@ function ProjectActionsModal({
|
|
|
743
953
|
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
744
954
|
children: [
|
|
745
955
|
/* @__PURE__ */ jsxDEV2("div", {
|
|
746
|
-
className: "bg-background/80
|
|
956
|
+
className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
|
|
747
957
|
onClick: handleClose,
|
|
748
958
|
role: "button",
|
|
749
959
|
tabIndex: 0,
|
|
@@ -754,13 +964,13 @@ function ProjectActionsModal({
|
|
|
754
964
|
"aria-label": "Close modal"
|
|
755
965
|
}, undefined, false, undefined, this),
|
|
756
966
|
/* @__PURE__ */ jsxDEV2("div", {
|
|
757
|
-
className: "
|
|
967
|
+
className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
|
|
758
968
|
children: [
|
|
759
969
|
/* @__PURE__ */ jsxDEV2("div", {
|
|
760
|
-
className: "
|
|
970
|
+
className: "mb-4 border-border border-b pb-4",
|
|
761
971
|
children: [
|
|
762
972
|
/* @__PURE__ */ jsxDEV2("h2", {
|
|
763
|
-
className: "text-xl
|
|
973
|
+
className: "font-semibold text-xl",
|
|
764
974
|
children: project.name
|
|
765
975
|
}, undefined, false, undefined, this),
|
|
766
976
|
/* @__PURE__ */ jsxDEV2("p", {
|
|
@@ -842,7 +1052,7 @@ function ProjectActionsModal({
|
|
|
842
1052
|
children: [
|
|
843
1053
|
/* @__PURE__ */ jsxDEV2("label", {
|
|
844
1054
|
htmlFor: "edit-name",
|
|
845
|
-
className: "
|
|
1055
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
846
1056
|
children: "Project Name *"
|
|
847
1057
|
}, undefined, false, undefined, this),
|
|
848
1058
|
/* @__PURE__ */ jsxDEV2(Input2, {
|
|
@@ -857,7 +1067,7 @@ function ProjectActionsModal({
|
|
|
857
1067
|
children: [
|
|
858
1068
|
/* @__PURE__ */ jsxDEV2("label", {
|
|
859
1069
|
htmlFor: "edit-description",
|
|
860
|
-
className: "
|
|
1070
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
861
1071
|
children: "Description"
|
|
862
1072
|
}, undefined, false, undefined, this),
|
|
863
1073
|
/* @__PURE__ */ jsxDEV2("textarea", {
|
|
@@ -866,12 +1076,12 @@ function ProjectActionsModal({
|
|
|
866
1076
|
onChange: (e) => setDescription(e.target.value),
|
|
867
1077
|
rows: 3,
|
|
868
1078
|
disabled: isLoading,
|
|
869
|
-
className: "
|
|
1079
|
+
className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50"
|
|
870
1080
|
}, undefined, false, undefined, this)
|
|
871
1081
|
]
|
|
872
1082
|
}, undefined, true, undefined, this),
|
|
873
1083
|
error && /* @__PURE__ */ jsxDEV2("div", {
|
|
874
|
-
className: "bg-destructive/10
|
|
1084
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
875
1085
|
children: error
|
|
876
1086
|
}, undefined, false, undefined, this),
|
|
877
1087
|
/* @__PURE__ */ jsxDEV2("div", {
|
|
@@ -901,7 +1111,7 @@ function ProjectActionsModal({
|
|
|
901
1111
|
"Are you sure you want to archive",
|
|
902
1112
|
" ",
|
|
903
1113
|
/* @__PURE__ */ jsxDEV2("span", {
|
|
904
|
-
className: "text-foreground
|
|
1114
|
+
className: "font-medium text-foreground",
|
|
905
1115
|
children: project.name
|
|
906
1116
|
}, undefined, false, undefined, this),
|
|
907
1117
|
"?"
|
|
@@ -912,7 +1122,7 @@ function ProjectActionsModal({
|
|
|
912
1122
|
children: "Archived projects can be restored later."
|
|
913
1123
|
}, undefined, false, undefined, this),
|
|
914
1124
|
error && /* @__PURE__ */ jsxDEV2("div", {
|
|
915
|
-
className: "bg-destructive/10
|
|
1125
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
916
1126
|
children: error
|
|
917
1127
|
}, undefined, false, undefined, this),
|
|
918
1128
|
/* @__PURE__ */ jsxDEV2("div", {
|
|
@@ -942,7 +1152,7 @@ function ProjectActionsModal({
|
|
|
942
1152
|
"Are you sure you want to delete",
|
|
943
1153
|
" ",
|
|
944
1154
|
/* @__PURE__ */ jsxDEV2("span", {
|
|
945
|
-
className: "text-foreground
|
|
1155
|
+
className: "font-medium text-foreground",
|
|
946
1156
|
children: project.name
|
|
947
1157
|
}, undefined, false, undefined, this),
|
|
948
1158
|
"?"
|
|
@@ -953,7 +1163,7 @@ function ProjectActionsModal({
|
|
|
953
1163
|
children: "This action cannot be undone."
|
|
954
1164
|
}, undefined, false, undefined, this),
|
|
955
1165
|
error && /* @__PURE__ */ jsxDEV2("div", {
|
|
956
|
-
className: "bg-destructive/10
|
|
1166
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
957
1167
|
children: error
|
|
958
1168
|
}, undefined, false, undefined, this),
|
|
959
1169
|
/* @__PURE__ */ jsxDEV2("div", {
|
|
@@ -980,141 +1190,518 @@ function ProjectActionsModal({
|
|
|
980
1190
|
]
|
|
981
1191
|
}, undefined, true, undefined, this);
|
|
982
1192
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
"
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1193
|
+
// src/ui/overlays/demo-overlays.ts
|
|
1194
|
+
var saasFreeUserOverlay = {
|
|
1195
|
+
overlayId: "saas-boilerplate.free-tier",
|
|
1196
|
+
version: "1.0.0",
|
|
1197
|
+
description: "Shows limitations for free tier users",
|
|
1198
|
+
appliesTo: {
|
|
1199
|
+
feature: "saas-boilerplate",
|
|
1200
|
+
tier: "free"
|
|
1201
|
+
},
|
|
1202
|
+
modifications: [
|
|
1203
|
+
{
|
|
1204
|
+
type: "setLimit",
|
|
1205
|
+
field: "projects",
|
|
1206
|
+
max: 3,
|
|
1207
|
+
message: "Upgrade to create more projects"
|
|
1208
|
+
},
|
|
1209
|
+
{ type: "hideField", field: "advancedSettings", reason: "Pro feature" },
|
|
1210
|
+
{
|
|
1211
|
+
type: "addBadge",
|
|
1212
|
+
position: "header",
|
|
1213
|
+
label: "Free Plan",
|
|
1214
|
+
variant: "default"
|
|
1215
|
+
}
|
|
1216
|
+
]
|
|
1217
|
+
};
|
|
1218
|
+
var saasDemoOverlay = {
|
|
1219
|
+
overlayId: "saas-boilerplate.demo-user",
|
|
1220
|
+
version: "1.0.0",
|
|
1221
|
+
description: "Demo mode for SaaS boilerplate",
|
|
1222
|
+
appliesTo: {
|
|
1223
|
+
feature: "saas-boilerplate",
|
|
1224
|
+
role: "demo"
|
|
1225
|
+
},
|
|
1226
|
+
modifications: [
|
|
1227
|
+
{
|
|
1228
|
+
type: "hideField",
|
|
1229
|
+
field: "billingSection",
|
|
1230
|
+
reason: "Demo users cannot access billing"
|
|
1231
|
+
},
|
|
1232
|
+
{
|
|
1233
|
+
type: "hideField",
|
|
1234
|
+
field: "deleteAccount",
|
|
1235
|
+
reason: "Not available in demo"
|
|
1236
|
+
},
|
|
1237
|
+
{
|
|
1238
|
+
type: "addBadge",
|
|
1239
|
+
position: "header",
|
|
1240
|
+
label: "Demo Mode",
|
|
1241
|
+
variant: "warning"
|
|
1242
|
+
}
|
|
1243
|
+
]
|
|
1244
|
+
};
|
|
1245
|
+
var saasOverlays = [
|
|
1246
|
+
saasFreeUserOverlay,
|
|
1247
|
+
saasDemoOverlay
|
|
1248
|
+
];
|
|
1249
|
+
// src/ui/renderers/project-list.markdown.ts
|
|
1250
|
+
var PROJECT_TIERS = [
|
|
1251
|
+
"FREE",
|
|
1252
|
+
"PRO",
|
|
1253
|
+
"ENTERPRISE"
|
|
1254
|
+
];
|
|
1255
|
+
function toVisualizationProject(project, index) {
|
|
1256
|
+
return {
|
|
1257
|
+
status: project.status === "DELETED" ? "ARCHIVED" : project.status,
|
|
1258
|
+
tier: PROJECT_TIERS[index % PROJECT_TIERS.length] ?? "FREE",
|
|
1259
|
+
createdAt: project.createdAt
|
|
1260
|
+
};
|
|
1009
1261
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
const { data, subscription, loading, error, stats, refetch } = useProjectList();
|
|
1016
|
-
const mutations = useProjectMutations({
|
|
1017
|
-
onSuccess: () => {
|
|
1018
|
-
refetch();
|
|
1262
|
+
var projectListMarkdownRenderer = {
|
|
1263
|
+
target: "markdown",
|
|
1264
|
+
render: async (desc, _ctx) => {
|
|
1265
|
+
if (desc.source.type !== "component" || desc.source.componentKey !== "ProjectListView") {
|
|
1266
|
+
throw new Error("projectListMarkdownRenderer: not ProjectListView");
|
|
1019
1267
|
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
}
|
|
1268
|
+
const data = await mockListProjectsHandler({
|
|
1269
|
+
limit: 20,
|
|
1270
|
+
offset: 0
|
|
1271
|
+
});
|
|
1272
|
+
const items = data.projects ?? [];
|
|
1273
|
+
const lines = [
|
|
1274
|
+
"# Projects",
|
|
1275
|
+
"",
|
|
1276
|
+
`**Total**: ${data.total} projects`,
|
|
1277
|
+
""
|
|
1278
|
+
];
|
|
1279
|
+
if (items.length === 0) {
|
|
1280
|
+
lines.push("_No projects found._");
|
|
1281
|
+
} else {
|
|
1282
|
+
lines.push("| Status | Project | Description |");
|
|
1283
|
+
lines.push("|--------|---------|-------------|");
|
|
1284
|
+
for (const project of items) {
|
|
1285
|
+
const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
|
|
1286
|
+
lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
return {
|
|
1290
|
+
mimeType: "text/markdown",
|
|
1291
|
+
body: lines.join(`
|
|
1292
|
+
`)
|
|
1293
|
+
};
|
|
1034
1294
|
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
}
|
|
1295
|
+
};
|
|
1296
|
+
var saasDashboardMarkdownRenderer = {
|
|
1297
|
+
target: "markdown",
|
|
1298
|
+
render: async (desc, _ctx) => {
|
|
1299
|
+
if (desc.source.type !== "component" || desc.source.componentKey !== "SaasDashboard") {
|
|
1300
|
+
throw new Error("saasDashboardMarkdownRenderer: not SaasDashboard");
|
|
1301
|
+
}
|
|
1302
|
+
const [projectsData, subscription] = await Promise.all([
|
|
1303
|
+
mockListProjectsHandler({ limit: 50 }),
|
|
1304
|
+
mockGetSubscriptionHandler()
|
|
1305
|
+
]);
|
|
1306
|
+
const projects = projectsData.projects ?? [];
|
|
1307
|
+
const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
|
|
1308
|
+
const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
|
|
1309
|
+
const visualizations = createSaasVisualizationItems(projects.map(toVisualizationProject), 10);
|
|
1310
|
+
const lines = [
|
|
1311
|
+
"# SaaS Dashboard",
|
|
1312
|
+
"",
|
|
1313
|
+
"> Organization overview and usage summary",
|
|
1314
|
+
"",
|
|
1315
|
+
"## Summary",
|
|
1316
|
+
"",
|
|
1317
|
+
"| Metric | Value |",
|
|
1318
|
+
"|--------|-------|",
|
|
1319
|
+
`| Total Projects | ${projectsData.total} |`,
|
|
1320
|
+
`| Active Projects | ${activeProjects} |`,
|
|
1321
|
+
`| Archived Projects | ${archivedProjects} |`,
|
|
1322
|
+
`| Subscription Plan | ${subscription.planName} |`,
|
|
1323
|
+
`| Subscription Status | ${subscription.status} |`,
|
|
1324
|
+
""
|
|
1325
|
+
];
|
|
1326
|
+
lines.push("## Visualization Overview");
|
|
1327
|
+
lines.push("");
|
|
1328
|
+
for (const item of visualizations) {
|
|
1329
|
+
lines.push(`- **${item.title}** via \`${item.spec.meta.key}\``);
|
|
1330
|
+
}
|
|
1331
|
+
lines.push("");
|
|
1332
|
+
lines.push("## Projects");
|
|
1333
|
+
lines.push("");
|
|
1334
|
+
if (projects.length === 0) {
|
|
1335
|
+
lines.push("_No projects yet._");
|
|
1336
|
+
} else {
|
|
1337
|
+
lines.push("| Status | Project | Description |");
|
|
1338
|
+
lines.push("|--------|---------|-------------|");
|
|
1339
|
+
for (const project of projects.slice(0, 10)) {
|
|
1340
|
+
const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
|
|
1341
|
+
lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
|
|
1342
|
+
}
|
|
1343
|
+
if (projects.length > 10) {
|
|
1344
|
+
lines.push(`| ... | ... | _${projectsData.total - 10} more projects_ |`);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
lines.push("");
|
|
1348
|
+
lines.push("## Subscription");
|
|
1349
|
+
lines.push("");
|
|
1350
|
+
lines.push(`- **Plan**: ${subscription.planName}`);
|
|
1351
|
+
lines.push(`- **Status**: ${subscription.status}`);
|
|
1352
|
+
if (subscription.currentPeriodEnd) {
|
|
1353
|
+
lines.push(`- **Period End**: ${new Date(subscription.currentPeriodEnd).toLocaleDateString()}`);
|
|
1354
|
+
}
|
|
1355
|
+
return {
|
|
1356
|
+
mimeType: "text/markdown",
|
|
1357
|
+
body: lines.join(`
|
|
1358
|
+
`)
|
|
1359
|
+
};
|
|
1042
1360
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1361
|
+
};
|
|
1362
|
+
var saasBillingMarkdownRenderer = {
|
|
1363
|
+
target: "markdown",
|
|
1364
|
+
render: async (desc, _ctx) => {
|
|
1365
|
+
if (desc.source.type !== "component" || desc.source.componentKey !== "SubscriptionView") {
|
|
1366
|
+
throw new Error("saasBillingMarkdownRenderer: not SubscriptionView");
|
|
1367
|
+
}
|
|
1368
|
+
const subscription = await mockGetSubscriptionHandler();
|
|
1369
|
+
const lines = [
|
|
1370
|
+
"# Billing & Subscription",
|
|
1371
|
+
"",
|
|
1372
|
+
"> Current subscription details and billing information",
|
|
1373
|
+
"",
|
|
1374
|
+
"## Subscription Details",
|
|
1375
|
+
"",
|
|
1376
|
+
"| Property | Value |",
|
|
1377
|
+
"|----------|-------|",
|
|
1378
|
+
`| Plan | ${subscription.planName} |`,
|
|
1379
|
+
`| Status | ${subscription.status} |`,
|
|
1380
|
+
`| ID | ${subscription.id} |`,
|
|
1381
|
+
`| Period Start | ${new Date(subscription.currentPeriodStart).toLocaleDateString()} |`,
|
|
1382
|
+
`| Period End | ${new Date(subscription.currentPeriodEnd).toLocaleDateString()} |`
|
|
1383
|
+
];
|
|
1384
|
+
lines.push("");
|
|
1385
|
+
lines.push("## Plan Limits");
|
|
1386
|
+
lines.push("");
|
|
1387
|
+
lines.push(`- **Projects**: ${subscription.limits.projects}`);
|
|
1388
|
+
lines.push(`- **Users**: ${subscription.limits.users}`);
|
|
1389
|
+
lines.push("");
|
|
1390
|
+
lines.push("## Plan Features");
|
|
1391
|
+
lines.push("");
|
|
1392
|
+
if (subscription.planName.toLowerCase().includes("free")) {
|
|
1393
|
+
lines.push("- ✅ Up to 3 projects");
|
|
1394
|
+
lines.push("- ✅ Basic support");
|
|
1395
|
+
lines.push("- ❌ Priority support");
|
|
1396
|
+
lines.push("- ❌ Advanced analytics");
|
|
1397
|
+
} else if (subscription.planName.toLowerCase().includes("pro")) {
|
|
1398
|
+
lines.push("- ✅ Unlimited projects");
|
|
1399
|
+
lines.push("- ✅ Priority support");
|
|
1400
|
+
lines.push("- ✅ Advanced analytics");
|
|
1401
|
+
lines.push("- ❌ Custom integrations");
|
|
1402
|
+
} else {
|
|
1403
|
+
lines.push("- ✅ Unlimited projects");
|
|
1404
|
+
lines.push("- ✅ Priority support");
|
|
1405
|
+
lines.push("- ✅ Advanced analytics");
|
|
1406
|
+
lines.push("- ✅ Custom integrations");
|
|
1407
|
+
lines.push("- ✅ Dedicated support");
|
|
1408
|
+
}
|
|
1409
|
+
return {
|
|
1410
|
+
mimeType: "text/markdown",
|
|
1411
|
+
body: lines.join(`
|
|
1412
|
+
`)
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
|
|
1417
|
+
// src/ui/SaasProjectList.tsx
|
|
1418
|
+
import {
|
|
1419
|
+
Button as Button3,
|
|
1420
|
+
EmptyState,
|
|
1421
|
+
EntityCard,
|
|
1422
|
+
ErrorState,
|
|
1423
|
+
LoaderBlock,
|
|
1424
|
+
StatCard,
|
|
1425
|
+
StatCardGroup,
|
|
1426
|
+
StatusChip
|
|
1427
|
+
} from "@contractspec/lib.design-system";
|
|
1428
|
+
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
1429
|
+
"use client";
|
|
1430
|
+
function getStatusTone(status) {
|
|
1431
|
+
switch (status) {
|
|
1432
|
+
case "ACTIVE":
|
|
1433
|
+
return "success";
|
|
1434
|
+
case "DRAFT":
|
|
1435
|
+
return "neutral";
|
|
1436
|
+
case "ARCHIVED":
|
|
1437
|
+
return "danger";
|
|
1438
|
+
default:
|
|
1439
|
+
return "neutral";
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
function SaasProjectList({
|
|
1443
|
+
onProjectClick,
|
|
1444
|
+
onCreateProject
|
|
1445
|
+
}) {
|
|
1446
|
+
const { data, loading, error, stats, refetch } = useProjectList();
|
|
1447
|
+
if (loading && !data) {
|
|
1448
|
+
return /* @__PURE__ */ jsxDEV3(LoaderBlock, {
|
|
1449
|
+
label: "Loading projects..."
|
|
1450
|
+
}, undefined, false, undefined, this);
|
|
1451
|
+
}
|
|
1452
|
+
if (error) {
|
|
1453
|
+
return /* @__PURE__ */ jsxDEV3(ErrorState, {
|
|
1454
|
+
title: "Failed to load projects",
|
|
1455
|
+
description: error.message,
|
|
1456
|
+
onRetry: refetch,
|
|
1457
|
+
retryLabel: "Retry"
|
|
1458
|
+
}, undefined, false, undefined, this);
|
|
1459
|
+
}
|
|
1460
|
+
if (!data?.items.length) {
|
|
1461
|
+
return /* @__PURE__ */ jsxDEV3(EmptyState, {
|
|
1462
|
+
title: "No projects found",
|
|
1463
|
+
description: "Create your first project to get started.",
|
|
1464
|
+
primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV3(Button3, {
|
|
1465
|
+
onPress: onCreateProject,
|
|
1466
|
+
children: "Create Project"
|
|
1467
|
+
}, undefined, false, undefined, this) : undefined
|
|
1468
|
+
}, undefined, false, undefined, this);
|
|
1469
|
+
}
|
|
1470
|
+
return /* @__PURE__ */ jsxDEV3("div", {
|
|
1471
|
+
className: "space-y-6",
|
|
1472
|
+
children: [
|
|
1473
|
+
stats && /* @__PURE__ */ jsxDEV3(StatCardGroup, {
|
|
1474
|
+
children: [
|
|
1475
|
+
/* @__PURE__ */ jsxDEV3(StatCard, {
|
|
1476
|
+
label: "Total Projects",
|
|
1477
|
+
value: stats.total.toString()
|
|
1478
|
+
}, undefined, false, undefined, this),
|
|
1479
|
+
/* @__PURE__ */ jsxDEV3(StatCard, {
|
|
1480
|
+
label: "Active",
|
|
1481
|
+
value: stats.activeCount.toString()
|
|
1482
|
+
}, undefined, false, undefined, this),
|
|
1483
|
+
/* @__PURE__ */ jsxDEV3(StatCard, {
|
|
1484
|
+
label: "Draft",
|
|
1485
|
+
value: stats.draftCount.toString()
|
|
1486
|
+
}, undefined, false, undefined, this)
|
|
1487
|
+
]
|
|
1488
|
+
}, undefined, true, undefined, this),
|
|
1489
|
+
/* @__PURE__ */ jsxDEV3("div", {
|
|
1490
|
+
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
|
|
1491
|
+
children: data.items.map((project) => /* @__PURE__ */ jsxDEV3(EntityCard, {
|
|
1492
|
+
cardTitle: project.name,
|
|
1493
|
+
cardSubtitle: project.tier,
|
|
1494
|
+
meta: /* @__PURE__ */ jsxDEV3("p", {
|
|
1495
|
+
className: "text-muted-foreground text-sm",
|
|
1496
|
+
children: project.description
|
|
1497
|
+
}, undefined, false, undefined, this),
|
|
1498
|
+
chips: /* @__PURE__ */ jsxDEV3(StatusChip, {
|
|
1499
|
+
tone: getStatusTone(project.status),
|
|
1500
|
+
label: project.status
|
|
1501
|
+
}, undefined, false, undefined, this),
|
|
1502
|
+
footer: /* @__PURE__ */ jsxDEV3("span", {
|
|
1503
|
+
className: "text-muted-foreground text-xs",
|
|
1504
|
+
children: project.updatedAt.toLocaleDateString()
|
|
1505
|
+
}, undefined, false, undefined, this),
|
|
1506
|
+
onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
|
|
1507
|
+
}, project.id, false, undefined, this))
|
|
1508
|
+
}, undefined, false, undefined, this)
|
|
1509
|
+
]
|
|
1510
|
+
}, undefined, true, undefined, this);
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
// src/ui/renderers/project-list.renderer.tsx
|
|
1514
|
+
import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
|
|
1515
|
+
var projectListReactRenderer = {
|
|
1516
|
+
target: "react",
|
|
1517
|
+
render: async (desc, _ctx) => {
|
|
1518
|
+
if (desc.source.type !== "component") {
|
|
1519
|
+
throw new Error("Invalid source type");
|
|
1520
|
+
}
|
|
1521
|
+
if (desc.source.componentKey !== "SaasProjectListView") {
|
|
1522
|
+
throw new Error(`Unknown component: ${desc.source.componentKey}`);
|
|
1523
|
+
}
|
|
1524
|
+
return /* @__PURE__ */ jsxDEV4(SaasProjectList, {}, undefined, false, undefined, this);
|
|
1525
|
+
}
|
|
1526
|
+
};
|
|
1527
|
+
// src/ui/SaasDashboard.visualizations.tsx
|
|
1528
|
+
import {
|
|
1529
|
+
VisualizationCard,
|
|
1530
|
+
VisualizationGrid
|
|
1531
|
+
} from "@contractspec/lib.design-system";
|
|
1532
|
+
import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
|
|
1533
|
+
"use client";
|
|
1534
|
+
function SaasVisualizationOverview({
|
|
1535
|
+
projects,
|
|
1536
|
+
projectLimit
|
|
1537
|
+
}) {
|
|
1538
|
+
const items = createSaasVisualizationItems(projects, projectLimit);
|
|
1539
|
+
return /* @__PURE__ */ jsxDEV5("section", {
|
|
1540
|
+
className: "space-y-3",
|
|
1541
|
+
children: [
|
|
1542
|
+
/* @__PURE__ */ jsxDEV5("div", {
|
|
1543
|
+
children: [
|
|
1544
|
+
/* @__PURE__ */ jsxDEV5("h3", {
|
|
1545
|
+
className: "font-semibold text-lg",
|
|
1546
|
+
children: "Portfolio Visualizations"
|
|
1547
|
+
}, undefined, false, undefined, this),
|
|
1548
|
+
/* @__PURE__ */ jsxDEV5("p", {
|
|
1549
|
+
className: "text-muted-foreground text-sm",
|
|
1550
|
+
children: "Contract-backed charts for project mix, capacity, and activity."
|
|
1551
|
+
}, undefined, false, undefined, this)
|
|
1552
|
+
]
|
|
1553
|
+
}, undefined, true, undefined, this),
|
|
1554
|
+
/* @__PURE__ */ jsxDEV5(VisualizationGrid, {
|
|
1555
|
+
children: items.map((item) => /* @__PURE__ */ jsxDEV5(VisualizationCard, {
|
|
1556
|
+
data: item.data,
|
|
1557
|
+
description: item.description,
|
|
1558
|
+
height: item.height,
|
|
1559
|
+
spec: item.spec,
|
|
1560
|
+
title: item.title
|
|
1561
|
+
}, item.key, false, undefined, this))
|
|
1562
|
+
}, undefined, false, undefined, this)
|
|
1563
|
+
]
|
|
1564
|
+
}, undefined, true, undefined, this);
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
// src/ui/SaasDashboard.tsx
|
|
1568
|
+
import {
|
|
1569
|
+
Button as Button4,
|
|
1570
|
+
EmptyState as EmptyState2,
|
|
1571
|
+
EntityCard as EntityCard2,
|
|
1572
|
+
ErrorState as ErrorState2,
|
|
1573
|
+
LoaderBlock as LoaderBlock2,
|
|
1574
|
+
StatCard as StatCard2,
|
|
1575
|
+
StatCardGroup as StatCardGroup2,
|
|
1576
|
+
StatusChip as StatusChip2
|
|
1577
|
+
} from "@contractspec/lib.design-system";
|
|
1578
|
+
import { useCallback as useCallback3, useState as useState5 } from "react";
|
|
1579
|
+
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
1580
|
+
"use client";
|
|
1581
|
+
function getStatusTone2(status) {
|
|
1582
|
+
switch (status) {
|
|
1583
|
+
case "ACTIVE":
|
|
1584
|
+
return "success";
|
|
1585
|
+
case "DRAFT":
|
|
1586
|
+
return "neutral";
|
|
1587
|
+
case "ARCHIVED":
|
|
1588
|
+
return "warning";
|
|
1589
|
+
default:
|
|
1590
|
+
return "neutral";
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
function SaasDashboard() {
|
|
1594
|
+
const [activeTab, setActiveTab] = useState5("projects");
|
|
1595
|
+
const [isCreateModalOpen, setIsCreateModalOpen] = useState5(false);
|
|
1596
|
+
const [selectedProject, setSelectedProject] = useState5(null);
|
|
1597
|
+
const [isProjectActionsOpen, setIsProjectActionsOpen] = useState5(false);
|
|
1598
|
+
const { data, subscription, loading, error, stats, refetch } = useProjectList();
|
|
1599
|
+
const mutations = useProjectMutations({
|
|
1600
|
+
onSuccess: () => {
|
|
1601
|
+
refetch();
|
|
1602
|
+
}
|
|
1603
|
+
});
|
|
1604
|
+
const handleProjectClick = useCallback3((project) => {
|
|
1605
|
+
setSelectedProject(project);
|
|
1606
|
+
setIsProjectActionsOpen(true);
|
|
1607
|
+
}, []);
|
|
1608
|
+
const tabs = [
|
|
1609
|
+
{ id: "projects", label: "Projects", icon: "\uD83D\uDCC1" },
|
|
1610
|
+
{ id: "billing", label: "Billing", icon: "\uD83D\uDCB3" },
|
|
1611
|
+
{ id: "settings", label: "Settings", icon: "⚙️" }
|
|
1612
|
+
];
|
|
1613
|
+
if (loading && !data) {
|
|
1614
|
+
return /* @__PURE__ */ jsxDEV6(LoaderBlock2, {
|
|
1615
|
+
label: "Loading dashboard..."
|
|
1616
|
+
}, undefined, false, undefined, this);
|
|
1617
|
+
}
|
|
1618
|
+
if (error) {
|
|
1619
|
+
return /* @__PURE__ */ jsxDEV6(ErrorState2, {
|
|
1620
|
+
title: "Failed to load dashboard",
|
|
1621
|
+
description: error.message,
|
|
1622
|
+
onRetry: refetch,
|
|
1623
|
+
retryLabel: "Retry"
|
|
1624
|
+
}, undefined, false, undefined, this);
|
|
1625
|
+
}
|
|
1626
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1627
|
+
className: "space-y-6",
|
|
1628
|
+
children: [
|
|
1629
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1630
|
+
className: "flex items-center justify-between",
|
|
1631
|
+
children: [
|
|
1632
|
+
/* @__PURE__ */ jsxDEV6("h2", {
|
|
1633
|
+
className: "font-bold text-2xl",
|
|
1634
|
+
children: "SaaS Dashboard"
|
|
1635
|
+
}, undefined, false, undefined, this),
|
|
1636
|
+
activeTab === "projects" && /* @__PURE__ */ jsxDEV6(Button4, {
|
|
1637
|
+
onPress: () => setIsCreateModalOpen(true),
|
|
1638
|
+
children: [
|
|
1639
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
1640
|
+
className: "mr-2",
|
|
1641
|
+
children: "+"
|
|
1059
1642
|
}, undefined, false, undefined, this),
|
|
1060
1643
|
" New Project"
|
|
1061
1644
|
]
|
|
1062
1645
|
}, undefined, true, undefined, this)
|
|
1063
1646
|
]
|
|
1064
1647
|
}, undefined, true, undefined, this),
|
|
1065
|
-
stats && subscription && /* @__PURE__ */
|
|
1648
|
+
stats && subscription && /* @__PURE__ */ jsxDEV6(StatCardGroup2, {
|
|
1066
1649
|
children: [
|
|
1067
|
-
/* @__PURE__ */
|
|
1650
|
+
/* @__PURE__ */ jsxDEV6(StatCard2, {
|
|
1068
1651
|
label: "Projects",
|
|
1069
1652
|
value: stats.total.toString()
|
|
1070
1653
|
}, undefined, false, undefined, this),
|
|
1071
|
-
/* @__PURE__ */
|
|
1654
|
+
/* @__PURE__ */ jsxDEV6(StatCard2, {
|
|
1072
1655
|
label: "Active",
|
|
1073
1656
|
value: stats.activeCount.toString()
|
|
1074
1657
|
}, undefined, false, undefined, this),
|
|
1075
|
-
/* @__PURE__ */
|
|
1658
|
+
/* @__PURE__ */ jsxDEV6(StatCard2, {
|
|
1076
1659
|
label: "Draft",
|
|
1077
1660
|
value: stats.draftCount.toString()
|
|
1078
1661
|
}, undefined, false, undefined, this),
|
|
1079
|
-
/* @__PURE__ */
|
|
1662
|
+
/* @__PURE__ */ jsxDEV6(StatCard2, {
|
|
1080
1663
|
label: "Plan",
|
|
1081
1664
|
value: subscription.plan,
|
|
1082
1665
|
hint: subscription.status
|
|
1083
1666
|
}, undefined, false, undefined, this)
|
|
1084
1667
|
]
|
|
1085
1668
|
}, undefined, true, undefined, this),
|
|
1086
|
-
/* @__PURE__ */
|
|
1087
|
-
|
|
1669
|
+
data && stats && /* @__PURE__ */ jsxDEV6(SaasVisualizationOverview, {
|
|
1670
|
+
projectLimit: stats.projectLimit,
|
|
1671
|
+
projects: data.items
|
|
1672
|
+
}, undefined, false, undefined, this),
|
|
1673
|
+
/* @__PURE__ */ jsxDEV6("nav", {
|
|
1674
|
+
className: "flex gap-1 rounded-lg bg-muted p-1",
|
|
1088
1675
|
role: "tablist",
|
|
1089
|
-
children: tabs.map((tab) => /* @__PURE__ */
|
|
1676
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsxDEV6("button", {
|
|
1090
1677
|
type: "button",
|
|
1091
1678
|
role: "tab",
|
|
1092
1679
|
"aria-selected": activeTab === tab.id,
|
|
1093
1680
|
onClick: () => setActiveTab(tab.id),
|
|
1094
|
-
className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 text-sm
|
|
1681
|
+
className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 font-medium text-sm transition-colors ${activeTab === tab.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
|
|
1095
1682
|
children: [
|
|
1096
|
-
/* @__PURE__ */
|
|
1683
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
1097
1684
|
children: tab.icon
|
|
1098
1685
|
}, undefined, false, undefined, this),
|
|
1099
1686
|
tab.label
|
|
1100
1687
|
]
|
|
1101
1688
|
}, tab.id, true, undefined, this))
|
|
1102
1689
|
}, undefined, false, undefined, this),
|
|
1103
|
-
/* @__PURE__ */
|
|
1690
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1104
1691
|
className: "min-h-[400px]",
|
|
1105
1692
|
role: "tabpanel",
|
|
1106
1693
|
children: [
|
|
1107
|
-
activeTab === "projects" && /* @__PURE__ */
|
|
1694
|
+
activeTab === "projects" && /* @__PURE__ */ jsxDEV6(ProjectsTab, {
|
|
1108
1695
|
data,
|
|
1109
1696
|
onProjectClick: handleProjectClick
|
|
1110
1697
|
}, undefined, false, undefined, this),
|
|
1111
|
-
activeTab === "billing" && /* @__PURE__ */
|
|
1698
|
+
activeTab === "billing" && /* @__PURE__ */ jsxDEV6(BillingTab, {
|
|
1112
1699
|
subscription
|
|
1113
1700
|
}, undefined, false, undefined, this),
|
|
1114
|
-
activeTab === "settings" && /* @__PURE__ */
|
|
1701
|
+
activeTab === "settings" && /* @__PURE__ */ jsxDEV6(SettingsTab, {}, undefined, false, undefined, this)
|
|
1115
1702
|
]
|
|
1116
1703
|
}, undefined, true, undefined, this),
|
|
1117
|
-
/* @__PURE__ */
|
|
1704
|
+
/* @__PURE__ */ jsxDEV6(CreateProjectModal, {
|
|
1118
1705
|
isOpen: isCreateModalOpen,
|
|
1119
1706
|
onClose: () => setIsCreateModalOpen(false),
|
|
1120
1707
|
onSubmit: async (input) => {
|
|
@@ -1122,7 +1709,7 @@ function SaasDashboard() {
|
|
|
1122
1709
|
},
|
|
1123
1710
|
isLoading: mutations.createState.loading
|
|
1124
1711
|
}, undefined, false, undefined, this),
|
|
1125
|
-
/* @__PURE__ */
|
|
1712
|
+
/* @__PURE__ */ jsxDEV6(ProjectActionsModal, {
|
|
1126
1713
|
isOpen: isProjectActionsOpen,
|
|
1127
1714
|
project: selectedProject,
|
|
1128
1715
|
onClose: () => {
|
|
@@ -1148,34 +1735,34 @@ function SaasDashboard() {
|
|
|
1148
1735
|
}
|
|
1149
1736
|
function ProjectsTab({ data, onProjectClick }) {
|
|
1150
1737
|
if (!data?.items.length) {
|
|
1151
|
-
return /* @__PURE__ */
|
|
1738
|
+
return /* @__PURE__ */ jsxDEV6(EmptyState2, {
|
|
1152
1739
|
title: "No projects yet",
|
|
1153
1740
|
description: "Create your first project to get started."
|
|
1154
1741
|
}, undefined, false, undefined, this);
|
|
1155
1742
|
}
|
|
1156
|
-
return /* @__PURE__ */
|
|
1743
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1157
1744
|
className: "space-y-4",
|
|
1158
|
-
children: /* @__PURE__ */
|
|
1745
|
+
children: /* @__PURE__ */ jsxDEV6("div", {
|
|
1159
1746
|
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
|
|
1160
|
-
children: data.items.map((project) => /* @__PURE__ */
|
|
1747
|
+
children: data.items.map((project) => /* @__PURE__ */ jsxDEV6(EntityCard2, {
|
|
1161
1748
|
cardTitle: project.name,
|
|
1162
1749
|
cardSubtitle: project.tier,
|
|
1163
|
-
meta: /* @__PURE__ */
|
|
1750
|
+
meta: /* @__PURE__ */ jsxDEV6("p", {
|
|
1164
1751
|
className: "text-muted-foreground text-sm",
|
|
1165
1752
|
children: project.description
|
|
1166
1753
|
}, undefined, false, undefined, this),
|
|
1167
|
-
chips: /* @__PURE__ */
|
|
1168
|
-
tone:
|
|
1754
|
+
chips: /* @__PURE__ */ jsxDEV6(StatusChip2, {
|
|
1755
|
+
tone: getStatusTone2(project.status),
|
|
1169
1756
|
label: project.status
|
|
1170
1757
|
}, undefined, false, undefined, this),
|
|
1171
|
-
footer: /* @__PURE__ */
|
|
1758
|
+
footer: /* @__PURE__ */ jsxDEV6("div", {
|
|
1172
1759
|
className: "flex w-full items-center justify-between",
|
|
1173
1760
|
children: [
|
|
1174
|
-
/* @__PURE__ */
|
|
1761
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
1175
1762
|
className: "text-muted-foreground text-xs",
|
|
1176
1763
|
children: project.updatedAt.toLocaleDateString()
|
|
1177
1764
|
}, undefined, false, undefined, this),
|
|
1178
|
-
/* @__PURE__ */
|
|
1765
|
+
/* @__PURE__ */ jsxDEV6(Button4, {
|
|
1179
1766
|
variant: "ghost",
|
|
1180
1767
|
size: "sm",
|
|
1181
1768
|
onPress: () => onProjectClick?.(project),
|
|
@@ -1190,25 +1777,25 @@ function ProjectsTab({ data, onProjectClick }) {
|
|
|
1190
1777
|
function BillingTab({ subscription }) {
|
|
1191
1778
|
if (!subscription)
|
|
1192
1779
|
return null;
|
|
1193
|
-
return /* @__PURE__ */
|
|
1780
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1194
1781
|
className: "space-y-6",
|
|
1195
1782
|
children: [
|
|
1196
|
-
/* @__PURE__ */
|
|
1197
|
-
className: "border-border bg-card
|
|
1783
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1784
|
+
className: "rounded-xl border border-border bg-card p-6",
|
|
1198
1785
|
children: [
|
|
1199
|
-
/* @__PURE__ */
|
|
1786
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1200
1787
|
className: "flex items-start justify-between",
|
|
1201
1788
|
children: [
|
|
1202
|
-
/* @__PURE__ */
|
|
1789
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1203
1790
|
children: [
|
|
1204
|
-
/* @__PURE__ */
|
|
1205
|
-
className: "text-lg
|
|
1791
|
+
/* @__PURE__ */ jsxDEV6("h3", {
|
|
1792
|
+
className: "font-semibold text-lg",
|
|
1206
1793
|
children: [
|
|
1207
1794
|
subscription.plan,
|
|
1208
1795
|
" Plan"
|
|
1209
1796
|
]
|
|
1210
1797
|
}, undefined, true, undefined, this),
|
|
1211
|
-
/* @__PURE__ */
|
|
1798
|
+
/* @__PURE__ */ jsxDEV6("p", {
|
|
1212
1799
|
className: "text-muted-foreground text-sm",
|
|
1213
1800
|
children: [
|
|
1214
1801
|
"Current period:",
|
|
@@ -1219,7 +1806,7 @@ function BillingTab({ subscription }) {
|
|
|
1219
1806
|
subscription.currentPeriodEnd.toLocaleDateString()
|
|
1220
1807
|
]
|
|
1221
1808
|
}, undefined, true, undefined, this),
|
|
1222
|
-
/* @__PURE__ */
|
|
1809
|
+
/* @__PURE__ */ jsxDEV6("p", {
|
|
1223
1810
|
className: "text-muted-foreground text-sm",
|
|
1224
1811
|
children: [
|
|
1225
1812
|
"Billing cycle: ",
|
|
@@ -1228,21 +1815,21 @@ function BillingTab({ subscription }) {
|
|
|
1228
1815
|
}, undefined, true, undefined, this)
|
|
1229
1816
|
]
|
|
1230
1817
|
}, undefined, true, undefined, this),
|
|
1231
|
-
/* @__PURE__ */
|
|
1818
|
+
/* @__PURE__ */ jsxDEV6(StatusChip2, {
|
|
1232
1819
|
tone: "success",
|
|
1233
1820
|
label: subscription.status
|
|
1234
1821
|
}, undefined, false, undefined, this)
|
|
1235
1822
|
]
|
|
1236
1823
|
}, undefined, true, undefined, this),
|
|
1237
|
-
/* @__PURE__ */
|
|
1824
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1238
1825
|
className: "mt-4 flex gap-3",
|
|
1239
1826
|
children: [
|
|
1240
|
-
/* @__PURE__ */
|
|
1827
|
+
/* @__PURE__ */ jsxDEV6(Button4, {
|
|
1241
1828
|
variant: "outline",
|
|
1242
1829
|
onPress: () => alert("Upgrade clicked!"),
|
|
1243
1830
|
children: "Upgrade Plan"
|
|
1244
1831
|
}, undefined, false, undefined, this),
|
|
1245
|
-
/* @__PURE__ */
|
|
1832
|
+
/* @__PURE__ */ jsxDEV6(Button4, {
|
|
1246
1833
|
variant: "ghost",
|
|
1247
1834
|
onPress: () => alert("Manage Billing clicked!"),
|
|
1248
1835
|
children: "Manage Billing"
|
|
@@ -1251,10 +1838,10 @@ function BillingTab({ subscription }) {
|
|
|
1251
1838
|
}, undefined, true, undefined, this)
|
|
1252
1839
|
]
|
|
1253
1840
|
}, undefined, true, undefined, this),
|
|
1254
|
-
subscription.cancelAtPeriodEnd && /* @__PURE__ */
|
|
1255
|
-
className: "border-border bg-destructive/10 text-destructive
|
|
1256
|
-
children: /* @__PURE__ */
|
|
1257
|
-
className: "text-sm
|
|
1841
|
+
subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsxDEV6("div", {
|
|
1842
|
+
className: "rounded-xl border border-border bg-destructive/10 p-4 text-destructive",
|
|
1843
|
+
children: /* @__PURE__ */ jsxDEV6("p", {
|
|
1844
|
+
className: "font-medium text-sm",
|
|
1258
1845
|
children: "⚠️ Your subscription will be cancelled at the end of the current period."
|
|
1259
1846
|
}, undefined, false, undefined, this)
|
|
1260
1847
|
}, undefined, false, undefined, this)
|
|
@@ -1262,63 +1849,63 @@ function BillingTab({ subscription }) {
|
|
|
1262
1849
|
}, undefined, true, undefined, this);
|
|
1263
1850
|
}
|
|
1264
1851
|
function SettingsTab() {
|
|
1265
|
-
return /* @__PURE__ */
|
|
1852
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
1266
1853
|
className: "space-y-6",
|
|
1267
|
-
children: /* @__PURE__ */
|
|
1268
|
-
className: "border-border bg-card
|
|
1854
|
+
children: /* @__PURE__ */ jsxDEV6("div", {
|
|
1855
|
+
className: "rounded-xl border border-border bg-card p-6",
|
|
1269
1856
|
children: [
|
|
1270
|
-
/* @__PURE__ */
|
|
1271
|
-
className: "mb-4 text-lg
|
|
1857
|
+
/* @__PURE__ */ jsxDEV6("h3", {
|
|
1858
|
+
className: "mb-4 font-semibold text-lg",
|
|
1272
1859
|
children: "Organization Settings"
|
|
1273
1860
|
}, undefined, false, undefined, this),
|
|
1274
|
-
/* @__PURE__ */
|
|
1861
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1275
1862
|
className: "space-y-4",
|
|
1276
1863
|
children: [
|
|
1277
|
-
/* @__PURE__ */
|
|
1864
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1278
1865
|
children: [
|
|
1279
|
-
/* @__PURE__ */
|
|
1866
|
+
/* @__PURE__ */ jsxDEV6("label", {
|
|
1280
1867
|
htmlFor: "org-name",
|
|
1281
|
-
className: "text-sm
|
|
1868
|
+
className: "font-medium text-sm",
|
|
1282
1869
|
children: "Organization Name"
|
|
1283
1870
|
}, undefined, false, undefined, this),
|
|
1284
|
-
/* @__PURE__ */
|
|
1871
|
+
/* @__PURE__ */ jsxDEV6("input", {
|
|
1285
1872
|
id: "org-name",
|
|
1286
1873
|
type: "text",
|
|
1287
1874
|
defaultValue: "Demo Organization",
|
|
1288
|
-
className: "
|
|
1875
|
+
className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2"
|
|
1289
1876
|
}, undefined, false, undefined, this)
|
|
1290
1877
|
]
|
|
1291
1878
|
}, undefined, true, undefined, this),
|
|
1292
|
-
/* @__PURE__ */
|
|
1879
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1293
1880
|
children: [
|
|
1294
|
-
/* @__PURE__ */
|
|
1881
|
+
/* @__PURE__ */ jsxDEV6("label", {
|
|
1295
1882
|
htmlFor: "timezone",
|
|
1296
|
-
className: "text-sm
|
|
1883
|
+
className: "font-medium text-sm",
|
|
1297
1884
|
children: "Default Timezone"
|
|
1298
1885
|
}, undefined, false, undefined, this),
|
|
1299
|
-
/* @__PURE__ */
|
|
1886
|
+
/* @__PURE__ */ jsxDEV6("select", {
|
|
1300
1887
|
id: "timezone",
|
|
1301
|
-
className: "
|
|
1888
|
+
className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2",
|
|
1302
1889
|
children: [
|
|
1303
|
-
/* @__PURE__ */
|
|
1890
|
+
/* @__PURE__ */ jsxDEV6("option", {
|
|
1304
1891
|
children: "UTC"
|
|
1305
1892
|
}, undefined, false, undefined, this),
|
|
1306
|
-
/* @__PURE__ */
|
|
1893
|
+
/* @__PURE__ */ jsxDEV6("option", {
|
|
1307
1894
|
children: "America/New_York"
|
|
1308
1895
|
}, undefined, false, undefined, this),
|
|
1309
|
-
/* @__PURE__ */
|
|
1896
|
+
/* @__PURE__ */ jsxDEV6("option", {
|
|
1310
1897
|
children: "Europe/London"
|
|
1311
1898
|
}, undefined, false, undefined, this),
|
|
1312
|
-
/* @__PURE__ */
|
|
1899
|
+
/* @__PURE__ */ jsxDEV6("option", {
|
|
1313
1900
|
children: "Asia/Tokyo"
|
|
1314
1901
|
}, undefined, false, undefined, this)
|
|
1315
1902
|
]
|
|
1316
1903
|
}, undefined, true, undefined, this)
|
|
1317
1904
|
]
|
|
1318
1905
|
}, undefined, true, undefined, this),
|
|
1319
|
-
/* @__PURE__ */
|
|
1906
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
1320
1907
|
className: "pt-2",
|
|
1321
|
-
children: /* @__PURE__ */
|
|
1908
|
+
children: /* @__PURE__ */ jsxDEV6(Button4, {
|
|
1322
1909
|
onPress: () => alert("Settings saved!"),
|
|
1323
1910
|
children: "Save Settings"
|
|
1324
1911
|
}, undefined, false, undefined, this)
|
|
@@ -1330,165 +1917,69 @@ function SettingsTab() {
|
|
|
1330
1917
|
}, undefined, false, undefined, this);
|
|
1331
1918
|
}
|
|
1332
1919
|
|
|
1333
|
-
// src/ui/SaasProjectList.tsx
|
|
1334
|
-
import {
|
|
1335
|
-
StatCard as StatCard2,
|
|
1336
|
-
StatCardGroup as StatCardGroup2,
|
|
1337
|
-
StatusChip as StatusChip2,
|
|
1338
|
-
EntityCard as EntityCard2,
|
|
1339
|
-
EmptyState as EmptyState2,
|
|
1340
|
-
LoaderBlock as LoaderBlock2,
|
|
1341
|
-
ErrorState as ErrorState2,
|
|
1342
|
-
Button as Button4
|
|
1343
|
-
} from "@contractspec/lib.design-system";
|
|
1344
|
-
import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
|
|
1345
|
-
"use client";
|
|
1346
|
-
function getStatusTone2(status) {
|
|
1347
|
-
switch (status) {
|
|
1348
|
-
case "ACTIVE":
|
|
1349
|
-
return "success";
|
|
1350
|
-
case "DRAFT":
|
|
1351
|
-
return "neutral";
|
|
1352
|
-
case "ARCHIVED":
|
|
1353
|
-
return "danger";
|
|
1354
|
-
default:
|
|
1355
|
-
return "neutral";
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
function SaasProjectList({
|
|
1359
|
-
onProjectClick,
|
|
1360
|
-
onCreateProject
|
|
1361
|
-
}) {
|
|
1362
|
-
const { data, loading, error, stats, refetch } = useProjectList();
|
|
1363
|
-
if (loading && !data) {
|
|
1364
|
-
return /* @__PURE__ */ jsxDEV4(LoaderBlock2, {
|
|
1365
|
-
label: "Loading projects..."
|
|
1366
|
-
}, undefined, false, undefined, this);
|
|
1367
|
-
}
|
|
1368
|
-
if (error) {
|
|
1369
|
-
return /* @__PURE__ */ jsxDEV4(ErrorState2, {
|
|
1370
|
-
title: "Failed to load projects",
|
|
1371
|
-
description: error.message,
|
|
1372
|
-
onRetry: refetch,
|
|
1373
|
-
retryLabel: "Retry"
|
|
1374
|
-
}, undefined, false, undefined, this);
|
|
1375
|
-
}
|
|
1376
|
-
if (!data?.items.length) {
|
|
1377
|
-
return /* @__PURE__ */ jsxDEV4(EmptyState2, {
|
|
1378
|
-
title: "No projects found",
|
|
1379
|
-
description: "Create your first project to get started.",
|
|
1380
|
-
primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV4(Button4, {
|
|
1381
|
-
onPress: onCreateProject,
|
|
1382
|
-
children: "Create Project"
|
|
1383
|
-
}, undefined, false, undefined, this) : undefined
|
|
1384
|
-
}, undefined, false, undefined, this);
|
|
1385
|
-
}
|
|
1386
|
-
return /* @__PURE__ */ jsxDEV4("div", {
|
|
1387
|
-
className: "space-y-6",
|
|
1388
|
-
children: [
|
|
1389
|
-
stats && /* @__PURE__ */ jsxDEV4(StatCardGroup2, {
|
|
1390
|
-
children: [
|
|
1391
|
-
/* @__PURE__ */ jsxDEV4(StatCard2, {
|
|
1392
|
-
label: "Total Projects",
|
|
1393
|
-
value: stats.total.toString()
|
|
1394
|
-
}, undefined, false, undefined, this),
|
|
1395
|
-
/* @__PURE__ */ jsxDEV4(StatCard2, {
|
|
1396
|
-
label: "Active",
|
|
1397
|
-
value: stats.activeCount.toString()
|
|
1398
|
-
}, undefined, false, undefined, this),
|
|
1399
|
-
/* @__PURE__ */ jsxDEV4(StatCard2, {
|
|
1400
|
-
label: "Draft",
|
|
1401
|
-
value: stats.draftCount.toString()
|
|
1402
|
-
}, undefined, false, undefined, this)
|
|
1403
|
-
]
|
|
1404
|
-
}, undefined, true, undefined, this),
|
|
1405
|
-
/* @__PURE__ */ jsxDEV4("div", {
|
|
1406
|
-
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
|
|
1407
|
-
children: data.items.map((project) => /* @__PURE__ */ jsxDEV4(EntityCard2, {
|
|
1408
|
-
cardTitle: project.name,
|
|
1409
|
-
cardSubtitle: project.tier,
|
|
1410
|
-
meta: /* @__PURE__ */ jsxDEV4("p", {
|
|
1411
|
-
className: "text-muted-foreground text-sm",
|
|
1412
|
-
children: project.description
|
|
1413
|
-
}, undefined, false, undefined, this),
|
|
1414
|
-
chips: /* @__PURE__ */ jsxDEV4(StatusChip2, {
|
|
1415
|
-
tone: getStatusTone2(project.status),
|
|
1416
|
-
label: project.status
|
|
1417
|
-
}, undefined, false, undefined, this),
|
|
1418
|
-
footer: /* @__PURE__ */ jsxDEV4("span", {
|
|
1419
|
-
className: "text-muted-foreground text-xs",
|
|
1420
|
-
children: project.updatedAt.toLocaleDateString()
|
|
1421
|
-
}, undefined, false, undefined, this),
|
|
1422
|
-
onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
|
|
1423
|
-
}, project.id, false, undefined, this))
|
|
1424
|
-
}, undefined, false, undefined, this)
|
|
1425
|
-
]
|
|
1426
|
-
}, undefined, true, undefined, this);
|
|
1427
|
-
}
|
|
1428
|
-
|
|
1429
1920
|
// src/ui/SaasSettingsPanel.tsx
|
|
1430
|
-
import { useState as useState6 } from "react";
|
|
1431
1921
|
import { Button as Button5 } from "@contractspec/lib.design-system";
|
|
1432
|
-
import {
|
|
1922
|
+
import { useState as useState6 } from "react";
|
|
1923
|
+
import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
|
|
1433
1924
|
"use client";
|
|
1434
1925
|
function SaasSettingsPanel() {
|
|
1435
1926
|
const [orgName, setOrgName] = useState6("Demo Organization");
|
|
1436
1927
|
const [timezone, setTimezone] = useState6("UTC");
|
|
1437
|
-
return /* @__PURE__ */
|
|
1928
|
+
return /* @__PURE__ */ jsxDEV7("div", {
|
|
1438
1929
|
className: "space-y-6",
|
|
1439
1930
|
children: [
|
|
1440
|
-
/* @__PURE__ */
|
|
1441
|
-
className: "border-border bg-card
|
|
1931
|
+
/* @__PURE__ */ jsxDEV7("div", {
|
|
1932
|
+
className: "rounded-xl border border-border bg-card p-6",
|
|
1442
1933
|
children: [
|
|
1443
|
-
/* @__PURE__ */
|
|
1444
|
-
className: "mb-4 text-lg
|
|
1934
|
+
/* @__PURE__ */ jsxDEV7("h3", {
|
|
1935
|
+
className: "mb-4 font-semibold text-lg",
|
|
1445
1936
|
children: "Organization Settings"
|
|
1446
1937
|
}, undefined, false, undefined, this),
|
|
1447
|
-
/* @__PURE__ */
|
|
1938
|
+
/* @__PURE__ */ jsxDEV7("div", {
|
|
1448
1939
|
className: "space-y-4",
|
|
1449
1940
|
children: [
|
|
1450
|
-
/* @__PURE__ */
|
|
1941
|
+
/* @__PURE__ */ jsxDEV7("div", {
|
|
1451
1942
|
children: [
|
|
1452
|
-
/* @__PURE__ */
|
|
1943
|
+
/* @__PURE__ */ jsxDEV7("label", {
|
|
1453
1944
|
htmlFor: "setting-org-name",
|
|
1454
|
-
className: "block text-sm
|
|
1945
|
+
className: "block font-medium text-sm",
|
|
1455
1946
|
children: "Organization Name"
|
|
1456
1947
|
}, undefined, false, undefined, this),
|
|
1457
|
-
/* @__PURE__ */
|
|
1948
|
+
/* @__PURE__ */ jsxDEV7("input", {
|
|
1458
1949
|
id: "setting-org-name",
|
|
1459
1950
|
type: "text",
|
|
1460
1951
|
value: orgName,
|
|
1461
1952
|
onChange: (e) => setOrgName(e.target.value),
|
|
1462
|
-
className: "
|
|
1953
|
+
className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2"
|
|
1463
1954
|
}, undefined, false, undefined, this)
|
|
1464
1955
|
]
|
|
1465
1956
|
}, undefined, true, undefined, this),
|
|
1466
|
-
/* @__PURE__ */
|
|
1957
|
+
/* @__PURE__ */ jsxDEV7("div", {
|
|
1467
1958
|
children: [
|
|
1468
|
-
/* @__PURE__ */
|
|
1959
|
+
/* @__PURE__ */ jsxDEV7("label", {
|
|
1469
1960
|
htmlFor: "setting-timezone",
|
|
1470
|
-
className: "block text-sm
|
|
1961
|
+
className: "block font-medium text-sm",
|
|
1471
1962
|
children: "Default Timezone"
|
|
1472
1963
|
}, undefined, false, undefined, this),
|
|
1473
|
-
/* @__PURE__ */
|
|
1964
|
+
/* @__PURE__ */ jsxDEV7("select", {
|
|
1474
1965
|
id: "setting-timezone",
|
|
1475
1966
|
value: timezone,
|
|
1476
1967
|
onChange: (e) => setTimezone(e.target.value),
|
|
1477
|
-
className: "
|
|
1968
|
+
className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2",
|
|
1478
1969
|
children: [
|
|
1479
|
-
/* @__PURE__ */
|
|
1970
|
+
/* @__PURE__ */ jsxDEV7("option", {
|
|
1480
1971
|
value: "UTC",
|
|
1481
1972
|
children: "UTC"
|
|
1482
1973
|
}, undefined, false, undefined, this),
|
|
1483
|
-
/* @__PURE__ */
|
|
1974
|
+
/* @__PURE__ */ jsxDEV7("option", {
|
|
1484
1975
|
value: "America/New_York",
|
|
1485
1976
|
children: "America/New_York"
|
|
1486
1977
|
}, undefined, false, undefined, this),
|
|
1487
|
-
/* @__PURE__ */
|
|
1978
|
+
/* @__PURE__ */ jsxDEV7("option", {
|
|
1488
1979
|
value: "Europe/London",
|
|
1489
1980
|
children: "Europe/London"
|
|
1490
1981
|
}, undefined, false, undefined, this),
|
|
1491
|
-
/* @__PURE__ */
|
|
1982
|
+
/* @__PURE__ */ jsxDEV7("option", {
|
|
1492
1983
|
value: "Asia/Tokyo",
|
|
1493
1984
|
children: "Asia/Tokyo"
|
|
1494
1985
|
}, undefined, false, undefined, this)
|
|
@@ -1498,37 +1989,37 @@ function SaasSettingsPanel() {
|
|
|
1498
1989
|
}, undefined, true, undefined, this)
|
|
1499
1990
|
]
|
|
1500
1991
|
}, undefined, true, undefined, this),
|
|
1501
|
-
/* @__PURE__ */
|
|
1992
|
+
/* @__PURE__ */ jsxDEV7("div", {
|
|
1502
1993
|
className: "mt-6",
|
|
1503
|
-
children: /* @__PURE__ */
|
|
1994
|
+
children: /* @__PURE__ */ jsxDEV7(Button5, {
|
|
1504
1995
|
variant: "default",
|
|
1505
1996
|
children: "Save Changes"
|
|
1506
1997
|
}, undefined, false, undefined, this)
|
|
1507
1998
|
}, undefined, false, undefined, this)
|
|
1508
1999
|
]
|
|
1509
2000
|
}, undefined, true, undefined, this),
|
|
1510
|
-
/* @__PURE__ */
|
|
1511
|
-
className: "border-border bg-card
|
|
2001
|
+
/* @__PURE__ */ jsxDEV7("div", {
|
|
2002
|
+
className: "rounded-xl border border-border bg-card p-6",
|
|
1512
2003
|
children: [
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
className: "mb-4 text-lg
|
|
2004
|
+
/* @__PURE__ */ jsxDEV7("h3", {
|
|
2005
|
+
className: "mb-4 font-semibold text-lg",
|
|
1515
2006
|
children: "Notifications"
|
|
1516
2007
|
}, undefined, false, undefined, this),
|
|
1517
|
-
/* @__PURE__ */
|
|
2008
|
+
/* @__PURE__ */ jsxDEV7("div", {
|
|
1518
2009
|
className: "space-y-3",
|
|
1519
2010
|
children: [
|
|
1520
2011
|
{ label: "Email notifications", defaultChecked: true },
|
|
1521
2012
|
{ label: "Usage alerts", defaultChecked: true },
|
|
1522
2013
|
{ label: "Weekly digest", defaultChecked: false }
|
|
1523
|
-
].map((item) => /* @__PURE__ */
|
|
2014
|
+
].map((item) => /* @__PURE__ */ jsxDEV7("label", {
|
|
1524
2015
|
className: "flex items-center gap-3",
|
|
1525
2016
|
children: [
|
|
1526
|
-
/* @__PURE__ */
|
|
2017
|
+
/* @__PURE__ */ jsxDEV7("input", {
|
|
1527
2018
|
type: "checkbox",
|
|
1528
2019
|
defaultChecked: item.defaultChecked,
|
|
1529
|
-
className: "
|
|
2020
|
+
className: "h-4 w-4 rounded border-input"
|
|
1530
2021
|
}, undefined, false, undefined, this),
|
|
1531
|
-
/* @__PURE__ */
|
|
2022
|
+
/* @__PURE__ */ jsxDEV7("span", {
|
|
1532
2023
|
className: "text-sm",
|
|
1533
2024
|
children: item.label
|
|
1534
2025
|
}, undefined, false, undefined, this)
|
|
@@ -1537,26 +2028,26 @@ function SaasSettingsPanel() {
|
|
|
1537
2028
|
}, undefined, false, undefined, this)
|
|
1538
2029
|
]
|
|
1539
2030
|
}, undefined, true, undefined, this),
|
|
1540
|
-
/* @__PURE__ */
|
|
2031
|
+
/* @__PURE__ */ jsxDEV7("div", {
|
|
1541
2032
|
className: "rounded-xl border border-red-200 bg-red-50 p-6 dark:border-red-900 dark:bg-red-950/20",
|
|
1542
2033
|
children: [
|
|
1543
|
-
/* @__PURE__ */
|
|
1544
|
-
className: "mb-2
|
|
2034
|
+
/* @__PURE__ */ jsxDEV7("h3", {
|
|
2035
|
+
className: "mb-2 font-semibold text-lg text-red-700 dark:text-red-400",
|
|
1545
2036
|
children: "Danger Zone"
|
|
1546
2037
|
}, undefined, false, undefined, this),
|
|
1547
|
-
/* @__PURE__ */
|
|
1548
|
-
className: "mb-4 text-
|
|
2038
|
+
/* @__PURE__ */ jsxDEV7("p", {
|
|
2039
|
+
className: "mb-4 text-red-600 text-sm dark:text-red-300",
|
|
1549
2040
|
children: "These actions are irreversible. Please proceed with caution."
|
|
1550
2041
|
}, undefined, false, undefined, this),
|
|
1551
|
-
/* @__PURE__ */
|
|
2042
|
+
/* @__PURE__ */ jsxDEV7("div", {
|
|
1552
2043
|
className: "flex gap-3",
|
|
1553
2044
|
children: [
|
|
1554
|
-
/* @__PURE__ */
|
|
2045
|
+
/* @__PURE__ */ jsxDEV7(Button5, {
|
|
1555
2046
|
variant: "secondary",
|
|
1556
2047
|
size: "sm",
|
|
1557
2048
|
children: "Export Data"
|
|
1558
2049
|
}, undefined, false, undefined, this),
|
|
1559
|
-
/* @__PURE__ */
|
|
2050
|
+
/* @__PURE__ */ jsxDEV7(Button5, {
|
|
1560
2051
|
variant: "secondary",
|
|
1561
2052
|
size: "sm",
|
|
1562
2053
|
children: "Delete Organization"
|
|
@@ -1568,228 +2059,6 @@ function SaasSettingsPanel() {
|
|
|
1568
2059
|
]
|
|
1569
2060
|
}, undefined, true, undefined, this);
|
|
1570
2061
|
}
|
|
1571
|
-
// src/ui/hooks/index.ts
|
|
1572
|
-
"use client";
|
|
1573
|
-
|
|
1574
|
-
// src/ui/renderers/project-list.renderer.tsx
|
|
1575
|
-
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
1576
|
-
var projectListReactRenderer = {
|
|
1577
|
-
target: "react",
|
|
1578
|
-
render: async (desc, _ctx) => {
|
|
1579
|
-
if (desc.source.type !== "component") {
|
|
1580
|
-
throw new Error("Invalid source type");
|
|
1581
|
-
}
|
|
1582
|
-
if (desc.source.componentKey !== "SaasProjectListView") {
|
|
1583
|
-
throw new Error(`Unknown component: ${desc.source.componentKey}`);
|
|
1584
|
-
}
|
|
1585
|
-
return /* @__PURE__ */ jsxDEV6(SaasProjectList, {}, undefined, false, undefined, this);
|
|
1586
|
-
}
|
|
1587
|
-
};
|
|
1588
|
-
|
|
1589
|
-
// src/ui/renderers/project-list.markdown.ts
|
|
1590
|
-
var projectListMarkdownRenderer = {
|
|
1591
|
-
target: "markdown",
|
|
1592
|
-
render: async (desc, _ctx) => {
|
|
1593
|
-
if (desc.source.type !== "component" || desc.source.componentKey !== "ProjectListView") {
|
|
1594
|
-
throw new Error("projectListMarkdownRenderer: not ProjectListView");
|
|
1595
|
-
}
|
|
1596
|
-
const data = await mockListProjectsHandler({
|
|
1597
|
-
limit: 20,
|
|
1598
|
-
offset: 0
|
|
1599
|
-
});
|
|
1600
|
-
const items = data.projects ?? data.items ?? [];
|
|
1601
|
-
const lines = [
|
|
1602
|
-
"# Projects",
|
|
1603
|
-
"",
|
|
1604
|
-
`**Total**: ${data.total} projects`,
|
|
1605
|
-
""
|
|
1606
|
-
];
|
|
1607
|
-
if (items.length === 0) {
|
|
1608
|
-
lines.push("_No projects found._");
|
|
1609
|
-
} else {
|
|
1610
|
-
lines.push("| Status | Project | Description |");
|
|
1611
|
-
lines.push("|--------|---------|-------------|");
|
|
1612
|
-
for (const project of items) {
|
|
1613
|
-
const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
|
|
1614
|
-
lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
return {
|
|
1618
|
-
mimeType: "text/markdown",
|
|
1619
|
-
body: lines.join(`
|
|
1620
|
-
`)
|
|
1621
|
-
};
|
|
1622
|
-
}
|
|
1623
|
-
};
|
|
1624
|
-
var saasDashboardMarkdownRenderer = {
|
|
1625
|
-
target: "markdown",
|
|
1626
|
-
render: async (desc, _ctx) => {
|
|
1627
|
-
if (desc.source.type !== "component" || desc.source.componentKey !== "SaasDashboard") {
|
|
1628
|
-
throw new Error("saasDashboardMarkdownRenderer: not SaasDashboard");
|
|
1629
|
-
}
|
|
1630
|
-
const [projectsData, subscription] = await Promise.all([
|
|
1631
|
-
mockListProjectsHandler({ limit: 50 }),
|
|
1632
|
-
mockGetSubscriptionHandler()
|
|
1633
|
-
]);
|
|
1634
|
-
const projects = projectsData.projects ?? [];
|
|
1635
|
-
const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
|
|
1636
|
-
const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
|
|
1637
|
-
const lines = [
|
|
1638
|
-
"# SaaS Dashboard",
|
|
1639
|
-
"",
|
|
1640
|
-
"> Organization overview and usage summary",
|
|
1641
|
-
"",
|
|
1642
|
-
"## Summary",
|
|
1643
|
-
"",
|
|
1644
|
-
"| Metric | Value |",
|
|
1645
|
-
"|--------|-------|",
|
|
1646
|
-
`| Total Projects | ${projectsData.total} |`,
|
|
1647
|
-
`| Active Projects | ${activeProjects} |`,
|
|
1648
|
-
`| Archived Projects | ${archivedProjects} |`,
|
|
1649
|
-
`| Subscription Plan | ${subscription.planName} |`,
|
|
1650
|
-
`| Subscription Status | ${subscription.status} |`,
|
|
1651
|
-
"",
|
|
1652
|
-
"## Projects",
|
|
1653
|
-
""
|
|
1654
|
-
];
|
|
1655
|
-
if (projects.length === 0) {
|
|
1656
|
-
lines.push("_No projects yet._");
|
|
1657
|
-
} else {
|
|
1658
|
-
lines.push("| Status | Project | Description |");
|
|
1659
|
-
lines.push("|--------|---------|-------------|");
|
|
1660
|
-
for (const project of projects.slice(0, 10)) {
|
|
1661
|
-
const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
|
|
1662
|
-
lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
|
|
1663
|
-
}
|
|
1664
|
-
if (projects.length > 10) {
|
|
1665
|
-
lines.push(`| ... | ... | _${projectsData.total - 10} more projects_ |`);
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
lines.push("");
|
|
1669
|
-
lines.push("## Subscription");
|
|
1670
|
-
lines.push("");
|
|
1671
|
-
lines.push(`- **Plan**: ${subscription.planName}`);
|
|
1672
|
-
lines.push(`- **Status**: ${subscription.status}`);
|
|
1673
|
-
if (subscription.currentPeriodEnd) {
|
|
1674
|
-
lines.push(`- **Period End**: ${new Date(subscription.currentPeriodEnd).toLocaleDateString()}`);
|
|
1675
|
-
}
|
|
1676
|
-
return {
|
|
1677
|
-
mimeType: "text/markdown",
|
|
1678
|
-
body: lines.join(`
|
|
1679
|
-
`)
|
|
1680
|
-
};
|
|
1681
|
-
}
|
|
1682
|
-
};
|
|
1683
|
-
var saasBillingMarkdownRenderer = {
|
|
1684
|
-
target: "markdown",
|
|
1685
|
-
render: async (desc, _ctx) => {
|
|
1686
|
-
if (desc.source.type !== "component" || desc.source.componentKey !== "SubscriptionView") {
|
|
1687
|
-
throw new Error("saasBillingMarkdownRenderer: not SubscriptionView");
|
|
1688
|
-
}
|
|
1689
|
-
const subscription = await mockGetSubscriptionHandler();
|
|
1690
|
-
const lines = [
|
|
1691
|
-
"# Billing & Subscription",
|
|
1692
|
-
"",
|
|
1693
|
-
"> Current subscription details and billing information",
|
|
1694
|
-
"",
|
|
1695
|
-
"## Subscription Details",
|
|
1696
|
-
"",
|
|
1697
|
-
"| Property | Value |",
|
|
1698
|
-
"|----------|-------|",
|
|
1699
|
-
`| Plan | ${subscription.planName} |`,
|
|
1700
|
-
`| Status | ${subscription.status} |`,
|
|
1701
|
-
`| ID | ${subscription.id} |`,
|
|
1702
|
-
`| Period Start | ${new Date(subscription.currentPeriodStart).toLocaleDateString()} |`,
|
|
1703
|
-
`| Period End | ${new Date(subscription.currentPeriodEnd).toLocaleDateString()} |`
|
|
1704
|
-
];
|
|
1705
|
-
lines.push("");
|
|
1706
|
-
lines.push("## Plan Limits");
|
|
1707
|
-
lines.push("");
|
|
1708
|
-
lines.push(`- **Projects**: ${subscription.limits.projects}`);
|
|
1709
|
-
lines.push(`- **Users**: ${subscription.limits.users}`);
|
|
1710
|
-
lines.push("");
|
|
1711
|
-
lines.push("## Plan Features");
|
|
1712
|
-
lines.push("");
|
|
1713
|
-
if (subscription.planName.toLowerCase().includes("free")) {
|
|
1714
|
-
lines.push("- ✅ Up to 3 projects");
|
|
1715
|
-
lines.push("- ✅ Basic support");
|
|
1716
|
-
lines.push("- ❌ Priority support");
|
|
1717
|
-
lines.push("- ❌ Advanced analytics");
|
|
1718
|
-
} else if (subscription.planName.toLowerCase().includes("pro")) {
|
|
1719
|
-
lines.push("- ✅ Unlimited projects");
|
|
1720
|
-
lines.push("- ✅ Priority support");
|
|
1721
|
-
lines.push("- ✅ Advanced analytics");
|
|
1722
|
-
lines.push("- ❌ Custom integrations");
|
|
1723
|
-
} else {
|
|
1724
|
-
lines.push("- ✅ Unlimited projects");
|
|
1725
|
-
lines.push("- ✅ Priority support");
|
|
1726
|
-
lines.push("- ✅ Advanced analytics");
|
|
1727
|
-
lines.push("- ✅ Custom integrations");
|
|
1728
|
-
lines.push("- ✅ Dedicated support");
|
|
1729
|
-
}
|
|
1730
|
-
return {
|
|
1731
|
-
mimeType: "text/markdown",
|
|
1732
|
-
body: lines.join(`
|
|
1733
|
-
`)
|
|
1734
|
-
};
|
|
1735
|
-
}
|
|
1736
|
-
};
|
|
1737
|
-
// src/ui/overlays/demo-overlays.ts
|
|
1738
|
-
var saasFreeUserOverlay = {
|
|
1739
|
-
overlayId: "saas-boilerplate.free-tier",
|
|
1740
|
-
version: "1.0.0",
|
|
1741
|
-
description: "Shows limitations for free tier users",
|
|
1742
|
-
appliesTo: {
|
|
1743
|
-
feature: "saas-boilerplate",
|
|
1744
|
-
tier: "free"
|
|
1745
|
-
},
|
|
1746
|
-
modifications: [
|
|
1747
|
-
{
|
|
1748
|
-
type: "setLimit",
|
|
1749
|
-
field: "projects",
|
|
1750
|
-
max: 3,
|
|
1751
|
-
message: "Upgrade to create more projects"
|
|
1752
|
-
},
|
|
1753
|
-
{ type: "hideField", field: "advancedSettings", reason: "Pro feature" },
|
|
1754
|
-
{
|
|
1755
|
-
type: "addBadge",
|
|
1756
|
-
position: "header",
|
|
1757
|
-
label: "Free Plan",
|
|
1758
|
-
variant: "default"
|
|
1759
|
-
}
|
|
1760
|
-
]
|
|
1761
|
-
};
|
|
1762
|
-
var saasDemoOverlay = {
|
|
1763
|
-
overlayId: "saas-boilerplate.demo-user",
|
|
1764
|
-
version: "1.0.0",
|
|
1765
|
-
description: "Demo mode for SaaS boilerplate",
|
|
1766
|
-
appliesTo: {
|
|
1767
|
-
feature: "saas-boilerplate",
|
|
1768
|
-
role: "demo"
|
|
1769
|
-
},
|
|
1770
|
-
modifications: [
|
|
1771
|
-
{
|
|
1772
|
-
type: "hideField",
|
|
1773
|
-
field: "billingSection",
|
|
1774
|
-
reason: "Demo users cannot access billing"
|
|
1775
|
-
},
|
|
1776
|
-
{
|
|
1777
|
-
type: "hideField",
|
|
1778
|
-
field: "deleteAccount",
|
|
1779
|
-
reason: "Not available in demo"
|
|
1780
|
-
},
|
|
1781
|
-
{
|
|
1782
|
-
type: "addBadge",
|
|
1783
|
-
position: "header",
|
|
1784
|
-
label: "Demo Mode",
|
|
1785
|
-
variant: "warning"
|
|
1786
|
-
}
|
|
1787
|
-
]
|
|
1788
|
-
};
|
|
1789
|
-
var saasOverlays = [
|
|
1790
|
-
saasFreeUserOverlay,
|
|
1791
|
-
saasDemoOverlay
|
|
1792
|
-
];
|
|
1793
2062
|
export {
|
|
1794
2063
|
useProjectMutations,
|
|
1795
2064
|
useProjectList,
|