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