@contractspec/bundle.marketing 3.8.8 → 3.8.10
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 +73 -29
- package/CHANGELOG.md +63 -0
- package/dist/browser/components/templates/TemplateCard.js +83 -0
- package/dist/browser/components/templates/TemplateCommandDialog.js +110 -0
- package/dist/browser/components/templates/TemplatePreviewContent.js +96 -0
- package/dist/browser/components/templates/TemplatesBrowseControls.js +130 -0
- package/dist/browser/components/templates/TemplatesCatalogSection.js +307 -0
- package/dist/browser/components/templates/TemplatesClientPage.js +1020 -917
- package/dist/browser/components/templates/TemplatesHeroSection.js +87 -0
- package/dist/browser/components/templates/TemplatesNextStepsSection.js +126 -0
- package/dist/browser/components/templates/TemplatesOverlays.js +2874 -0
- package/dist/browser/components/templates/TemplatesPreviewModal.js +136 -126
- package/dist/browser/components/templates/index.js +1055 -952
- package/dist/browser/components/templates/template-catalog.js +83 -0
- package/dist/browser/components/templates/template-filters.js +99 -0
- package/dist/browser/components/templates/template-new.js +23 -0
- package/dist/browser/components/templates/template-preview.js +43 -0
- package/dist/browser/components/templates/template-source.js +19 -0
- package/dist/browser/components/templates/template-tag-visibility.js +40 -0
- package/dist/browser/components/templates/useTemplateBrowseState.js +191 -0
- package/dist/browser/index.js +1055 -952
- package/dist/components/templates/TemplateCard.d.ts +12 -0
- package/dist/components/templates/TemplateCard.js +78 -0
- package/dist/components/templates/TemplateCommandDialog.d.ts +6 -0
- package/dist/components/templates/TemplateCommandDialog.js +105 -0
- package/dist/components/templates/TemplatePreviewContent.d.ts +5 -0
- package/dist/components/templates/TemplatePreviewContent.js +91 -0
- package/dist/components/templates/TemplatesBrowseControls.d.ts +18 -0
- package/dist/components/templates/TemplatesBrowseControls.js +125 -0
- package/dist/components/templates/TemplatesCatalogSection.d.ts +17 -0
- package/dist/components/templates/TemplatesCatalogSection.js +302 -0
- package/dist/components/templates/TemplatesClientPage.js +1020 -917
- package/dist/components/templates/TemplatesHeroSection.d.ts +5 -0
- package/dist/components/templates/TemplatesHeroSection.js +82 -0
- package/dist/components/templates/TemplatesNextStepsSection.d.ts +1 -0
- package/dist/components/templates/TemplatesNextStepsSection.js +121 -0
- package/dist/components/templates/TemplatesOverlays.d.ts +10 -0
- package/dist/components/templates/TemplatesOverlays.js +2869 -0
- package/dist/components/templates/TemplatesPreviewModal.d.ts +3 -4
- package/dist/components/templates/TemplatesPreviewModal.js +136 -126
- package/dist/components/templates/index.js +1055 -952
- package/dist/components/templates/template-catalog.d.ts +28 -0
- package/dist/components/templates/template-catalog.js +78 -0
- package/dist/components/templates/template-catalog.test.d.ts +1 -0
- package/dist/components/templates/template-filters.d.ts +12 -0
- package/dist/components/templates/template-filters.js +94 -0
- package/dist/components/templates/template-new.d.ts +2 -0
- package/dist/components/templates/template-new.js +18 -0
- package/dist/components/templates/template-preview.d.ts +18 -0
- package/dist/components/templates/template-preview.js +38 -0
- package/dist/components/templates/template-source.d.ts +3 -0
- package/dist/components/templates/template-source.js +14 -0
- package/dist/components/templates/template-tag-visibility.d.ts +10 -0
- package/dist/components/templates/template-tag-visibility.js +35 -0
- package/dist/components/templates/useTemplateBrowseState.d.ts +22 -0
- package/dist/components/templates/useTemplateBrowseState.js +186 -0
- package/dist/index.js +1055 -952
- package/dist/node/components/templates/TemplateCard.js +78 -0
- package/dist/node/components/templates/TemplateCommandDialog.js +105 -0
- package/dist/node/components/templates/TemplatePreviewContent.js +91 -0
- package/dist/node/components/templates/TemplatesBrowseControls.js +125 -0
- package/dist/node/components/templates/TemplatesCatalogSection.js +302 -0
- package/dist/node/components/templates/TemplatesClientPage.js +1020 -917
- package/dist/node/components/templates/TemplatesHeroSection.js +82 -0
- package/dist/node/components/templates/TemplatesNextStepsSection.js +121 -0
- package/dist/node/components/templates/TemplatesOverlays.js +2869 -0
- package/dist/node/components/templates/TemplatesPreviewModal.js +136 -126
- package/dist/node/components/templates/index.js +1055 -952
- package/dist/node/components/templates/template-catalog.js +78 -0
- package/dist/node/components/templates/template-filters.js +94 -0
- package/dist/node/components/templates/template-new.js +18 -0
- package/dist/node/components/templates/template-preview.js +38 -0
- package/dist/node/components/templates/template-source.js +14 -0
- package/dist/node/components/templates/template-tag-visibility.js +35 -0
- package/dist/node/components/templates/useTemplateBrowseState.js +186 -0
- package/dist/node/index.js +1055 -952
- package/package.json +237 -26
- package/src/components/templates/TemplateCard.tsx +74 -0
- package/src/components/templates/TemplateCommandDialog.tsx +92 -0
- package/src/components/templates/TemplatePreviewContent.tsx +182 -0
- package/src/components/templates/TemplatesBrowseControls.tsx +144 -0
- package/src/components/templates/TemplatesCatalogSection.tsx +191 -0
- package/src/components/templates/TemplatesClientPage.tsx +85 -773
- package/src/components/templates/TemplatesHeroSection.tsx +41 -0
- package/src/components/templates/TemplatesNextStepsSection.tsx +80 -0
- package/src/components/templates/TemplatesOverlays.tsx +65 -0
- package/src/components/templates/TemplatesPreviewModal.tsx +19 -294
- package/src/components/templates/template-catalog.test.ts +162 -0
- package/src/components/templates/template-catalog.ts +140 -0
- package/src/components/templates/template-filters.ts +57 -0
- package/src/components/templates/template-new.ts +12 -0
- package/src/components/templates/template-preview.ts +57 -0
- package/src/components/templates/template-source.ts +13 -0
- package/src/components/templates/template-tag-visibility.ts +58 -0
- package/src/components/templates/useTemplateBrowseState.ts +101 -0
- package/.turbo/turbo-prebuild.log +0 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface TemplatesHeroSectionProps {
|
|
2
|
+
localTemplateCount: number;
|
|
3
|
+
sourceCount: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function TemplatesHeroSection({
|
|
7
|
+
localTemplateCount,
|
|
8
|
+
sourceCount,
|
|
9
|
+
}: TemplatesHeroSectionProps) {
|
|
10
|
+
return (
|
|
11
|
+
<section className="section-padding hero-gradient border-border/70 border-b">
|
|
12
|
+
<div className="editorial-shell space-y-8">
|
|
13
|
+
<div className="max-w-4xl space-y-5">
|
|
14
|
+
<p className="editorial-kicker">Proof through real scenarios</p>
|
|
15
|
+
<h1 className="editorial-title">
|
|
16
|
+
Templates that show the open system in practice.
|
|
17
|
+
</h1>
|
|
18
|
+
<p className="editorial-subtitle">
|
|
19
|
+
These scenarios are the fastest way to understand ContractSpec:
|
|
20
|
+
explicit contracts, aligned surfaces, and an adoption path from OSS
|
|
21
|
+
exploration into Studio deployment.
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
<div className="editorial-proof-strip">
|
|
25
|
+
<div className="editorial-stat">
|
|
26
|
+
<span className="editorial-stat-value">{localTemplateCount}</span>
|
|
27
|
+
<span className="editorial-label">curated scenarios</span>
|
|
28
|
+
</div>
|
|
29
|
+
<div className="editorial-stat">
|
|
30
|
+
<span className="editorial-stat-value">{sourceCount}</span>
|
|
31
|
+
<span className="editorial-label">entry paths</span>
|
|
32
|
+
</div>
|
|
33
|
+
<div className="editorial-stat">
|
|
34
|
+
<span className="editorial-stat-value">OSS</span>
|
|
35
|
+
<span className="editorial-label">first, Studio second</span>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
</section>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import Link from 'next/link';
|
|
2
|
+
|
|
3
|
+
export function TemplatesNextStepsSection() {
|
|
4
|
+
return (
|
|
5
|
+
<section className="editorial-section bg-striped">
|
|
6
|
+
<div className="editorial-shell space-y-8">
|
|
7
|
+
<div className="max-w-3xl space-y-4">
|
|
8
|
+
<p className="editorial-kicker">From template to real system</p>
|
|
9
|
+
<h2 className="font-serif text-4xl tracking-[-0.04em] md:text-5xl">
|
|
10
|
+
Templates become useful when the system can absorb more context.
|
|
11
|
+
</h2>
|
|
12
|
+
<p className="editorial-copy">
|
|
13
|
+
Use templates to prove the base flow, then layer integrations,
|
|
14
|
+
knowledge, and runtime behavior on top without losing the same
|
|
15
|
+
contract source.
|
|
16
|
+
</p>
|
|
17
|
+
</div>
|
|
18
|
+
<div className="grid gap-6 md:grid-cols-3">
|
|
19
|
+
<div className="editorial-panel space-y-4">
|
|
20
|
+
<div className="text-3xl">💳</div>
|
|
21
|
+
<h3 className="font-serif text-2xl tracking-[-0.03em]">
|
|
22
|
+
Add payments
|
|
23
|
+
</h3>
|
|
24
|
+
<p className="text-muted-foreground text-sm">
|
|
25
|
+
Connect Stripe to any template for payment processing,
|
|
26
|
+
subscriptions, and invoicing. Type-safe and policy-enforced.
|
|
27
|
+
</p>
|
|
28
|
+
<Link
|
|
29
|
+
href="/docs/integrations/stripe"
|
|
30
|
+
className="font-medium text-[color:var(--blue)] text-sm hover:opacity-80"
|
|
31
|
+
>
|
|
32
|
+
Learn more →
|
|
33
|
+
</Link>
|
|
34
|
+
</div>
|
|
35
|
+
<div className="editorial-panel space-y-4">
|
|
36
|
+
<div className="text-3xl">📧</div>
|
|
37
|
+
<h3 className="font-serif text-2xl tracking-[-0.03em]">
|
|
38
|
+
Add notifications
|
|
39
|
+
</h3>
|
|
40
|
+
<p className="text-muted-foreground text-sm">
|
|
41
|
+
Send transactional emails via Postmark or Resend. Process inbound
|
|
42
|
+
emails with Gmail API. SMS via Twilio.
|
|
43
|
+
</p>
|
|
44
|
+
<Link
|
|
45
|
+
href="/docs/integrations"
|
|
46
|
+
className="font-medium text-[color:var(--blue)] text-sm hover:opacity-80"
|
|
47
|
+
>
|
|
48
|
+
View integrations →
|
|
49
|
+
</Link>
|
|
50
|
+
</div>
|
|
51
|
+
<div className="editorial-panel space-y-4">
|
|
52
|
+
<div className="text-3xl">🧠</div>
|
|
53
|
+
<h3 className="font-serif text-2xl tracking-[-0.03em]">
|
|
54
|
+
Add AI and knowledge
|
|
55
|
+
</h3>
|
|
56
|
+
<p className="text-muted-foreground text-sm">
|
|
57
|
+
Power templates with OpenAI, vector search via Qdrant, and
|
|
58
|
+
structured knowledge spaces for context-aware workflows.
|
|
59
|
+
</p>
|
|
60
|
+
<Link
|
|
61
|
+
href="/docs/knowledge"
|
|
62
|
+
className="font-medium text-[color:var(--blue)] text-sm hover:opacity-80"
|
|
63
|
+
>
|
|
64
|
+
Learn about knowledge →
|
|
65
|
+
</Link>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
<div className="pt-4 text-center">
|
|
69
|
+
<p className="mb-4 text-muted-foreground text-sm">
|
|
70
|
+
All integrations are configured per-tenant with automatic health
|
|
71
|
+
checks and credential rotation.
|
|
72
|
+
</p>
|
|
73
|
+
<Link href="/docs/architecture" className="btn-primary">
|
|
74
|
+
View Architecture
|
|
75
|
+
</Link>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</section>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Dialog,
|
|
5
|
+
DialogContent,
|
|
6
|
+
DialogDescription,
|
|
7
|
+
DialogHeader,
|
|
8
|
+
DialogTitle,
|
|
9
|
+
} from '@contractspec/lib.ui-kit-web/ui/dialog';
|
|
10
|
+
import { StudioSignupSection } from '../marketing';
|
|
11
|
+
import { TemplateCommandDialog } from './TemplateCommandDialog';
|
|
12
|
+
import { TemplatePreviewModal } from './TemplatesPreviewModal';
|
|
13
|
+
|
|
14
|
+
export interface TemplatesOverlaysProps {
|
|
15
|
+
previewTemplateId: string | null;
|
|
16
|
+
onPreviewClose: () => void;
|
|
17
|
+
studioSignupModalOpen: boolean;
|
|
18
|
+
onStudioSignupModalOpenChange: (open: boolean) => void;
|
|
19
|
+
selectedTemplateId: string | null;
|
|
20
|
+
onTemplateCommandClose: () => void;
|
|
21
|
+
onDeployStudio: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function TemplatesOverlays({
|
|
25
|
+
previewTemplateId,
|
|
26
|
+
onPreviewClose,
|
|
27
|
+
studioSignupModalOpen,
|
|
28
|
+
onStudioSignupModalOpenChange,
|
|
29
|
+
selectedTemplateId,
|
|
30
|
+
onTemplateCommandClose,
|
|
31
|
+
onDeployStudio,
|
|
32
|
+
}: TemplatesOverlaysProps) {
|
|
33
|
+
return (
|
|
34
|
+
<>
|
|
35
|
+
{previewTemplateId ? (
|
|
36
|
+
<TemplatePreviewModal
|
|
37
|
+
templateId={previewTemplateId}
|
|
38
|
+
onClose={onPreviewClose}
|
|
39
|
+
/>
|
|
40
|
+
) : null}
|
|
41
|
+
|
|
42
|
+
<Dialog
|
|
43
|
+
open={studioSignupModalOpen}
|
|
44
|
+
onOpenChange={onStudioSignupModalOpenChange}
|
|
45
|
+
>
|
|
46
|
+
<DialogContent className="max-h-[90vh] max-w-2xl overflow-y-auto">
|
|
47
|
+
<DialogHeader>
|
|
48
|
+
<DialogTitle>Deploy in Studio</DialogTitle>
|
|
49
|
+
<DialogDescription>
|
|
50
|
+
Deploy templates in ContractSpec Studio and run the full
|
|
51
|
+
evidence-to-spec loop with your team.
|
|
52
|
+
</DialogDescription>
|
|
53
|
+
</DialogHeader>
|
|
54
|
+
<StudioSignupSection variant="compact" />
|
|
55
|
+
</DialogContent>
|
|
56
|
+
</Dialog>
|
|
57
|
+
|
|
58
|
+
<TemplateCommandDialog
|
|
59
|
+
templateId={selectedTemplateId}
|
|
60
|
+
onClose={onTemplateCommandClose}
|
|
61
|
+
onDeployStudio={onDeployStudio}
|
|
62
|
+
/>
|
|
63
|
+
</>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -1,313 +1,38 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import type { TemplateId } from '@contractspec/lib.example-shared-ui';
|
|
4
|
-
import { LoadingSpinner } from '@contractspec/lib.ui-kit-web/ui/atoms/LoadingSpinner';
|
|
5
4
|
import { Dialog, DialogContent } from '@contractspec/lib.ui-kit-web/ui/dialog';
|
|
6
5
|
import { ScrollArea } from '@contractspec/lib.ui-kit-web/ui/scroll-area';
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
6
|
+
import { TemplateRuntimeProvider } from '@contractspec/module.examples';
|
|
7
|
+
import { TemplatePreviewContent } from './TemplatePreviewContent';
|
|
8
|
+
import { supportsInlineTemplatePreview } from './template-preview';
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
() =>
|
|
13
|
-
import('@contractspec/lib.example-shared-ui').then(
|
|
14
|
-
(mod) => mod.TemplateShell
|
|
15
|
-
),
|
|
16
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
const TodosTaskList = dynamic(
|
|
20
|
-
() =>
|
|
21
|
-
import(
|
|
22
|
-
'@contractspec/bundle.library/components/templates/todos/TaskList'
|
|
23
|
-
).then((mod) => mod.TaskList),
|
|
24
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
const MessagingWorkspace = dynamic(
|
|
28
|
-
() =>
|
|
29
|
-
import(
|
|
30
|
-
'@contractspec/bundle.library/components/templates/messaging/MessagingWorkspace'
|
|
31
|
-
).then((mod) => mod.MessagingWorkspace),
|
|
32
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
const RecipesExperience = dynamic(
|
|
36
|
-
() =>
|
|
37
|
-
import(
|
|
38
|
-
'@contractspec/bundle.library/components/templates/recipes/RecipeList'
|
|
39
|
-
).then((mod) => mod.RecipeList),
|
|
40
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const SaasDashboard = dynamic(
|
|
44
|
-
() =>
|
|
45
|
-
import('@contractspec/example.saas-boilerplate').then(
|
|
46
|
-
(mod) => mod.SaasDashboard
|
|
47
|
-
),
|
|
48
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
const CrmDashboard = dynamic(
|
|
52
|
-
() =>
|
|
53
|
-
import('@contractspec/example.crm-pipeline').then(
|
|
54
|
-
(mod) => mod.CrmDashboard
|
|
55
|
-
),
|
|
56
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
const DataGridShowcase = dynamic(
|
|
60
|
-
() =>
|
|
61
|
-
import('@contractspec/example.data-grid-showcase/ui').then(
|
|
62
|
-
(mod) => mod.DataGridShowcase
|
|
63
|
-
),
|
|
64
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
const VisualizationShowcase = dynamic(
|
|
68
|
-
() =>
|
|
69
|
-
import('@contractspec/example.visualization-showcase/ui').then(
|
|
70
|
-
(mod) => mod.VisualizationShowcase
|
|
71
|
-
),
|
|
72
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const AgentDashboard = dynamic(
|
|
76
|
-
() =>
|
|
77
|
-
import('@contractspec/example.agent-console/ui').then(
|
|
78
|
-
(mod) => mod.AgentDashboard
|
|
79
|
-
),
|
|
80
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
const WorkflowDashboard = dynamic(
|
|
84
|
-
() =>
|
|
85
|
-
import('@contractspec/example.workflow-system/ui').then(
|
|
86
|
-
(mod) => mod.WorkflowDashboard
|
|
87
|
-
),
|
|
88
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
const MarketplaceDashboard = dynamic(
|
|
92
|
-
() =>
|
|
93
|
-
import('@contractspec/example.marketplace/ui').then(
|
|
94
|
-
(mod) => mod.MarketplaceDashboard
|
|
95
|
-
),
|
|
96
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
const IntegrationDashboard = dynamic(
|
|
100
|
-
() =>
|
|
101
|
-
import('@contractspec/example.integration-hub/ui').then(
|
|
102
|
-
(mod) => mod.IntegrationDashboard
|
|
103
|
-
),
|
|
104
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
const AnalyticsDashboard = dynamic(
|
|
108
|
-
() =>
|
|
109
|
-
import('@contractspec/example.analytics-dashboard').then(
|
|
110
|
-
(mod) => mod.AnalyticsDashboard
|
|
111
|
-
),
|
|
112
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
const AiChatAssistantDashboard = dynamic(
|
|
116
|
-
() =>
|
|
117
|
-
import('@contractspec/example.ai-chat-assistant').then(
|
|
118
|
-
(mod) => mod.AiChatAssistantDashboard
|
|
119
|
-
),
|
|
120
|
-
{ ssr: false, loading: () => <LoadingSpinner /> }
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
interface TemplatePreviewModalProps {
|
|
124
|
-
templateId: TemplateId | null;
|
|
10
|
+
export interface TemplatePreviewModalProps {
|
|
11
|
+
templateId: TemplateId;
|
|
125
12
|
onClose: () => void;
|
|
126
13
|
}
|
|
127
|
-
//
|
|
128
|
-
// return (
|
|
129
|
-
// <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 px-4 py-10">
|
|
130
|
-
// <div className="bg-background relative h-full max-h-[90vh] w-full max-w-5xl overflow-y-auto rounded-3xl p-6 shadow-2xl">
|
|
131
|
-
// <button
|
|
132
|
-
// type="button"
|
|
133
|
-
// className="btn-ghost absolute top-4 right-4 text-sm"
|
|
134
|
-
// onClick={onClose}
|
|
135
|
-
// >
|
|
136
|
-
// Close
|
|
137
|
-
// </button>
|
|
138
|
-
// {previewComponent}
|
|
139
|
-
// </div>
|
|
140
|
-
// </div>
|
|
141
|
-
// );
|
|
142
|
-
// };
|
|
143
14
|
|
|
144
|
-
export
|
|
15
|
+
export function TemplatePreviewModal({
|
|
145
16
|
templateId,
|
|
146
17
|
onClose,
|
|
147
|
-
}: TemplatePreviewModalProps)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return (
|
|
152
|
-
<TemplateShell
|
|
153
|
-
title="Starter tasks"
|
|
154
|
-
description="Track work items with filters, priorities, and per-tenant data isolation."
|
|
155
|
-
showSaveAction={false}
|
|
156
|
-
>
|
|
157
|
-
<TodosTaskList />
|
|
158
|
-
</TemplateShell>
|
|
159
|
-
);
|
|
160
|
-
case 'messaging-app':
|
|
161
|
-
return (
|
|
162
|
-
<TemplateShell
|
|
163
|
-
title="Messaging workspace"
|
|
164
|
-
description="Realtime-ready messaging surface with optimistic delivery."
|
|
165
|
-
showSaveAction={false}
|
|
166
|
-
>
|
|
167
|
-
<MessagingWorkspace />
|
|
168
|
-
</TemplateShell>
|
|
169
|
-
);
|
|
170
|
-
case 'recipe-app-i18n':
|
|
171
|
-
return (
|
|
172
|
-
<TemplateShell
|
|
173
|
-
title="Ceremony recipes"
|
|
174
|
-
description="Switch locales and preview how rituals translate across teams."
|
|
175
|
-
showSaveAction={false}
|
|
176
|
-
>
|
|
177
|
-
<RecipesExperience />
|
|
178
|
-
</TemplateShell>
|
|
179
|
-
);
|
|
180
|
-
case 'saas-boilerplate':
|
|
181
|
-
return (
|
|
182
|
-
<TemplateShell
|
|
183
|
-
title="SaaS Boilerplate"
|
|
184
|
-
description="Multi-tenant organizations, projects, settings, and billing usage tracking."
|
|
185
|
-
showSaveAction={false}
|
|
186
|
-
>
|
|
187
|
-
<SaasDashboard />
|
|
188
|
-
</TemplateShell>
|
|
189
|
-
);
|
|
190
|
-
case 'crm-pipeline':
|
|
191
|
-
return (
|
|
192
|
-
<TemplateShell
|
|
193
|
-
title="CRM Pipeline"
|
|
194
|
-
description="Sales CRM with contacts, companies, deals, and pipeline stages."
|
|
195
|
-
showSaveAction={false}
|
|
196
|
-
>
|
|
197
|
-
<CrmDashboard />
|
|
198
|
-
</TemplateShell>
|
|
199
|
-
);
|
|
200
|
-
case 'data-grid-showcase':
|
|
201
|
-
return (
|
|
202
|
-
<TemplateShell
|
|
203
|
-
title="Data Grid Showcase"
|
|
204
|
-
description="Shared ContractSpec table primitives with client, server, and DataView-driven lanes."
|
|
205
|
-
showSaveAction={false}
|
|
206
|
-
>
|
|
207
|
-
<DataGridShowcase />
|
|
208
|
-
</TemplateShell>
|
|
209
|
-
);
|
|
210
|
-
case 'visualization-showcase':
|
|
211
|
-
return (
|
|
212
|
-
<TemplateShell
|
|
213
|
-
title="Visualization Showcase"
|
|
214
|
-
description="ContractSpec-owned chart primitives rendered through shared visualization contracts and design-system wrappers."
|
|
215
|
-
showSaveAction={false}
|
|
216
|
-
>
|
|
217
|
-
<VisualizationShowcase />
|
|
218
|
-
</TemplateShell>
|
|
219
|
-
);
|
|
220
|
-
case 'agent-console':
|
|
221
|
-
return (
|
|
222
|
-
<TemplateShell
|
|
223
|
-
title="AI Agent Console"
|
|
224
|
-
description="AI agent orchestration with tools, agents, runs, and execution logs."
|
|
225
|
-
showSaveAction={false}
|
|
226
|
-
>
|
|
227
|
-
<AgentDashboard />
|
|
228
|
-
</TemplateShell>
|
|
229
|
-
);
|
|
230
|
-
case 'ai-chat-assistant':
|
|
231
|
-
return (
|
|
232
|
-
<TemplateShell
|
|
233
|
-
title="AI Chat Assistant"
|
|
234
|
-
description="Focused assistant surface with reasoning, sources, suggestions, and MCP-aware tools."
|
|
235
|
-
showSaveAction={false}
|
|
236
|
-
>
|
|
237
|
-
<AiChatAssistantDashboard />
|
|
238
|
-
</TemplateShell>
|
|
239
|
-
);
|
|
240
|
-
case 'workflow-system':
|
|
241
|
-
return (
|
|
242
|
-
<TemplateShell
|
|
243
|
-
title="Workflow System"
|
|
244
|
-
description="Multi-step workflows with role-based approvals."
|
|
245
|
-
showSaveAction={false}
|
|
246
|
-
>
|
|
247
|
-
<WorkflowDashboard />
|
|
248
|
-
</TemplateShell>
|
|
249
|
-
);
|
|
250
|
-
case 'marketplace':
|
|
251
|
-
return (
|
|
252
|
-
<TemplateShell
|
|
253
|
-
title="Marketplace"
|
|
254
|
-
description="Two-sided marketplace with stores, products, and orders."
|
|
255
|
-
showSaveAction={false}
|
|
256
|
-
>
|
|
257
|
-
<MarketplaceDashboard />
|
|
258
|
-
</TemplateShell>
|
|
259
|
-
);
|
|
260
|
-
case 'integration-hub':
|
|
261
|
-
return (
|
|
262
|
-
<TemplateShell
|
|
263
|
-
title="Integration Hub"
|
|
264
|
-
description="Third-party integrations with sync and field mapping."
|
|
265
|
-
showSaveAction={false}
|
|
266
|
-
>
|
|
267
|
-
<IntegrationDashboard />
|
|
268
|
-
</TemplateShell>
|
|
269
|
-
);
|
|
270
|
-
case 'analytics-dashboard':
|
|
271
|
-
return (
|
|
272
|
-
<TemplateShell
|
|
273
|
-
title="Analytics Dashboard"
|
|
274
|
-
description="Custom dashboards with widgets and queries."
|
|
275
|
-
showSaveAction={false}
|
|
276
|
-
>
|
|
277
|
-
<AnalyticsDashboard />
|
|
278
|
-
</TemplateShell>
|
|
279
|
-
);
|
|
280
|
-
case null:
|
|
281
|
-
return null;
|
|
282
|
-
default:
|
|
283
|
-
return null;
|
|
284
|
-
}
|
|
285
|
-
}, [templateId]);
|
|
18
|
+
}: TemplatePreviewModalProps) {
|
|
19
|
+
if (!supportsInlineTemplatePreview(templateId)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
286
22
|
|
|
287
23
|
return (
|
|
288
|
-
<Dialog open
|
|
289
|
-
{/*<DialogTrigger asChild>*/}
|
|
290
|
-
{/* <Button variant="outline">Fullscreen Dialog</Button>*/}
|
|
291
|
-
{/*</DialogTrigger>*/}
|
|
24
|
+
<Dialog open onOpenChange={(open) => !open && onClose()}>
|
|
292
25
|
<DialogContent className="mb-8 flex h-[calc(100vh-2rem)] min-w-[calc(100vw-2rem)] flex-col justify-between gap-0 p-0">
|
|
293
26
|
<ScrollArea className="flex flex-col justify-between overflow-hidden">
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
27
|
+
<TemplateRuntimeProvider
|
|
28
|
+
key={templateId}
|
|
29
|
+
templateId={templateId}
|
|
30
|
+
projectId={`marketing-preview-${templateId}`}
|
|
31
|
+
>
|
|
32
|
+
<TemplatePreviewContent templateId={templateId} />
|
|
33
|
+
</TemplateRuntimeProvider>
|
|
300
34
|
</ScrollArea>
|
|
301
|
-
{/*<DialogFooter className="px-6 pb-6 sm:justify-end">*/}
|
|
302
|
-
{/* <DialogClose asChild>*/}
|
|
303
|
-
{/* <Button variant="outline">*/}
|
|
304
|
-
{/* <ChevronLeftIcon />*/}
|
|
305
|
-
{/* Back*/}
|
|
306
|
-
{/* </Button>*/}
|
|
307
|
-
{/* </DialogClose>*/}
|
|
308
|
-
{/* <Button type="button">Read More</Button>*/}
|
|
309
|
-
{/*</DialogFooter>*/}
|
|
310
35
|
</DialogContent>
|
|
311
36
|
</Dialog>
|
|
312
37
|
);
|
|
313
|
-
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import { listExamples, listTemplates } from '@contractspec/module.examples';
|
|
3
|
+
import {
|
|
4
|
+
buildLocalTemplateCatalog,
|
|
5
|
+
matchesTemplateFilters,
|
|
6
|
+
} from './template-catalog';
|
|
7
|
+
import { buildTemplateFilterState } from './template-filters';
|
|
8
|
+
import { NEW_TEMPLATE_IDS } from './template-new';
|
|
9
|
+
import {
|
|
10
|
+
getAvailableTemplateSources,
|
|
11
|
+
isRegistryConfigured,
|
|
12
|
+
} from './template-source';
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_VISIBLE_TEMPLATE_TAGS,
|
|
15
|
+
getVisibleTemplateTagFacets,
|
|
16
|
+
} from './template-tag-visibility';
|
|
17
|
+
|
|
18
|
+
describe('template catalog', () => {
|
|
19
|
+
test('includes every public example exposed as a template', () => {
|
|
20
|
+
const catalog = buildLocalTemplateCatalog(listExamples(), listTemplates());
|
|
21
|
+
const actualIds = [...catalog].map((template) => template.id).sort();
|
|
22
|
+
const expectedIds = listExamples()
|
|
23
|
+
.filter(
|
|
24
|
+
(example) =>
|
|
25
|
+
example.meta.visibility === 'public' && example.surfaces.templates
|
|
26
|
+
)
|
|
27
|
+
.map((example) => example.meta.key)
|
|
28
|
+
.sort();
|
|
29
|
+
|
|
30
|
+
expect(actualIds).toEqual(expectedIds);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('drops legacy conceptual cards and applies curated new badges', () => {
|
|
34
|
+
const catalog = buildLocalTemplateCatalog(listExamples(), listTemplates());
|
|
35
|
+
const ids = new Set(catalog.map((template) => template.id));
|
|
36
|
+
const newIds = catalog
|
|
37
|
+
.filter((template) => template.isNew)
|
|
38
|
+
.map((template) => template.id)
|
|
39
|
+
.sort();
|
|
40
|
+
|
|
41
|
+
expect(ids.has('plumber-ops')).toBe(false);
|
|
42
|
+
expect(ids.has('coliving-management')).toBe(false);
|
|
43
|
+
expect(ids.has('content-review')).toBe(false);
|
|
44
|
+
expect(newIds).toEqual([...NEW_TEMPLATE_IDS].sort());
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('derives searchable tags from real example metadata', () => {
|
|
48
|
+
const catalog = buildLocalTemplateCatalog(listExamples(), listTemplates());
|
|
49
|
+
const tags = new Set(catalog.flatMap((template) => template.tags));
|
|
50
|
+
|
|
51
|
+
expect(tags.has('quickstart')).toBe(true);
|
|
52
|
+
expect(tags.has('gradium')).toBe(true);
|
|
53
|
+
expect(
|
|
54
|
+
catalog.some((template) =>
|
|
55
|
+
matchesTemplateFilters(template, 'voice gradium', null)
|
|
56
|
+
)
|
|
57
|
+
).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('derives tag facets from the templates remaining after search', () => {
|
|
61
|
+
const catalog = buildLocalTemplateCatalog(listExamples(), listTemplates());
|
|
62
|
+
const state = buildTemplateFilterState(
|
|
63
|
+
catalog,
|
|
64
|
+
'agent',
|
|
65
|
+
null,
|
|
66
|
+
(template) => template
|
|
67
|
+
);
|
|
68
|
+
const tags = state.tagFacets.map((facet) => facet.tag);
|
|
69
|
+
|
|
70
|
+
expect(state.searchScopedTemplates.length).toBeGreaterThan(0);
|
|
71
|
+
expect(tags).toContain('agents');
|
|
72
|
+
expect(tags).not.toContain('billing');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('applies selected tags after search scoping', () => {
|
|
76
|
+
const catalog = buildLocalTemplateCatalog(listExamples(), listTemplates());
|
|
77
|
+
const state = buildTemplateFilterState(
|
|
78
|
+
catalog,
|
|
79
|
+
'agent',
|
|
80
|
+
'telegram',
|
|
81
|
+
(template) => template
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
expect(state.searchScopedTemplates.length).toBeGreaterThan(
|
|
85
|
+
state.finalTemplates.length
|
|
86
|
+
);
|
|
87
|
+
expect(
|
|
88
|
+
state.finalTemplates.every((template) =>
|
|
89
|
+
template.tags.includes('telegram')
|
|
90
|
+
)
|
|
91
|
+
).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('caps default tag visibility and keeps selected hidden tags visible', () => {
|
|
95
|
+
const tagFacets = Array.from(
|
|
96
|
+
{ length: DEFAULT_VISIBLE_TEMPLATE_TAGS + 2 },
|
|
97
|
+
(_, index) => ({
|
|
98
|
+
tag: `tag-${index}`,
|
|
99
|
+
count: DEFAULT_VISIBLE_TEMPLATE_TAGS + 2 - index,
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
const { visibleTagFacets, hiddenTagFacets } = getVisibleTemplateTagFacets(
|
|
103
|
+
tagFacets,
|
|
104
|
+
'tag-11',
|
|
105
|
+
false
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
expect(visibleTagFacets).toHaveLength(DEFAULT_VISIBLE_TEMPLATE_TAGS + 1);
|
|
109
|
+
expect(visibleTagFacets.some((facet) => facet.tag === 'tag-11')).toBe(true);
|
|
110
|
+
expect(hiddenTagFacets.some((facet) => facet.tag === 'tag-11')).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('recomputes source-specific tags from the active source only', () => {
|
|
114
|
+
const localTemplates = [
|
|
115
|
+
{
|
|
116
|
+
title: 'Local agent console',
|
|
117
|
+
description: 'Agent workflows',
|
|
118
|
+
tags: ['agents', 'local'],
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
const registryTemplates = [
|
|
122
|
+
{
|
|
123
|
+
title: 'Registry recipe app',
|
|
124
|
+
description: 'Cooking workflows',
|
|
125
|
+
tags: ['recipes', 'community'],
|
|
126
|
+
},
|
|
127
|
+
];
|
|
128
|
+
const localState = buildTemplateFilterState(
|
|
129
|
+
localTemplates,
|
|
130
|
+
'',
|
|
131
|
+
null,
|
|
132
|
+
(template) => template
|
|
133
|
+
);
|
|
134
|
+
const registryState = buildTemplateFilterState(
|
|
135
|
+
registryTemplates,
|
|
136
|
+
'',
|
|
137
|
+
null,
|
|
138
|
+
(template) => template
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
expect(localState.tagFacets.map((facet) => facet.tag)).toEqual([
|
|
142
|
+
'agents',
|
|
143
|
+
'local',
|
|
144
|
+
]);
|
|
145
|
+
expect(registryState.tagFacets.map((facet) => facet.tag)).toEqual([
|
|
146
|
+
'community',
|
|
147
|
+
'recipes',
|
|
148
|
+
]);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('template source configuration', () => {
|
|
153
|
+
test('only exposes community source when a registry url is configured', () => {
|
|
154
|
+
expect(isRegistryConfigured(undefined)).toBe(false);
|
|
155
|
+
expect(isRegistryConfigured(' ')).toBe(false);
|
|
156
|
+
expect(isRegistryConfigured('https://registry.contractspec.io')).toBe(true);
|
|
157
|
+
expect(getAvailableTemplateSources(undefined)).toEqual(['local']);
|
|
158
|
+
expect(
|
|
159
|
+
getAvailableTemplateSources('https://registry.contractspec.io')
|
|
160
|
+
).toEqual(['local', 'registry']);
|
|
161
|
+
});
|
|
162
|
+
});
|