@contractspec/bundle.marketing 3.8.7 → 3.8.9
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 +64 -32
- 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 +115 -0
- package/dist/browser/components/templates/TemplatesCatalogSection.js +284 -0
- package/dist/browser/components/templates/TemplatesClientPage.js +840 -917
- package/dist/browser/components/templates/TemplatesHeroSection.js +87 -0
- package/dist/browser/components/templates/TemplatesNextStepsSection.js +126 -0
- package/dist/browser/components/templates/TemplatesPreviewModal.js +136 -126
- package/dist/browser/components/templates/index.js +873 -950
- package/dist/browser/components/templates/template-catalog.js +81 -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/index.js +873 -950
- 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 +13 -0
- package/dist/components/templates/TemplatesBrowseControls.js +110 -0
- package/dist/components/templates/TemplatesCatalogSection.d.ts +14 -0
- package/dist/components/templates/TemplatesCatalogSection.js +279 -0
- package/dist/components/templates/TemplatesClientPage.js +840 -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/TemplatesPreviewModal.d.ts +3 -4
- package/dist/components/templates/TemplatesPreviewModal.js +136 -126
- package/dist/components/templates/index.js +873 -950
- package/dist/components/templates/template-catalog.d.ts +27 -0
- package/dist/components/templates/template-catalog.js +76 -0
- package/dist/components/templates/template-catalog.test.d.ts +1 -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/index.js +873 -950
- 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 +110 -0
- package/dist/node/components/templates/TemplatesCatalogSection.js +279 -0
- package/dist/node/components/templates/TemplatesClientPage.js +840 -917
- package/dist/node/components/templates/TemplatesHeroSection.js +82 -0
- package/dist/node/components/templates/TemplatesNextStepsSection.js +121 -0
- package/dist/node/components/templates/TemplatesPreviewModal.js +136 -126
- package/dist/node/components/templates/index.js +873 -950
- package/dist/node/components/templates/template-catalog.js +76 -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/index.js +873 -950
- package/package.json +185 -30
- 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 +120 -0
- package/src/components/templates/TemplatesCatalogSection.tsx +166 -0
- package/src/components/templates/TemplatesClientPage.tsx +109 -741
- package/src/components/templates/TemplatesHeroSection.tsx +41 -0
- package/src/components/templates/TemplatesNextStepsSection.tsx +80 -0
- package/src/components/templates/TemplatesPreviewModal.tsx +19 -294
- package/src/components/templates/template-catalog.test.ts +66 -0
- package/src/components/templates/template-catalog.ts +132 -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/.turbo/turbo-prebuild.log +0 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { cn } from '@contractspec/lib.ui-kit-core/utils';
|
|
4
|
+
import { Search } from 'lucide-react';
|
|
5
|
+
import type { TemplateSource } from './template-source';
|
|
6
|
+
|
|
7
|
+
export interface TemplatesBrowseControlsProps {
|
|
8
|
+
registryConfigured: boolean;
|
|
9
|
+
availableSources: readonly TemplateSource[];
|
|
10
|
+
source: TemplateSource;
|
|
11
|
+
onSourceChange: (source: TemplateSource) => void;
|
|
12
|
+
search: string;
|
|
13
|
+
onSearchChange: (value: string) => void;
|
|
14
|
+
selectedTag: string | null;
|
|
15
|
+
onTagChange: (tag: string | null) => void;
|
|
16
|
+
availableTags: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function TemplatesBrowseControls({
|
|
20
|
+
registryConfigured,
|
|
21
|
+
availableSources,
|
|
22
|
+
source,
|
|
23
|
+
onSourceChange,
|
|
24
|
+
search,
|
|
25
|
+
onSearchChange,
|
|
26
|
+
selectedTag,
|
|
27
|
+
onTagChange,
|
|
28
|
+
availableTags,
|
|
29
|
+
}: TemplatesBrowseControlsProps) {
|
|
30
|
+
return (
|
|
31
|
+
<section className="editorial-section">
|
|
32
|
+
<div className="editorial-shell space-y-6">
|
|
33
|
+
<div className="flex flex-col gap-6 lg:flex-row lg:items-end lg:justify-between">
|
|
34
|
+
<div className="max-w-3xl space-y-3">
|
|
35
|
+
<p className="editorial-kicker">Browse by source</p>
|
|
36
|
+
<h2 className="font-serif text-4xl tracking-[-0.04em]">
|
|
37
|
+
Use local scenarios for core proof, then scan the community.
|
|
38
|
+
</h2>
|
|
39
|
+
<p className="text-muted-foreground text-sm leading-7">
|
|
40
|
+
{registryConfigured
|
|
41
|
+
? 'Local templates show the official adoption path. Community templates show where the ecosystem is pushing the system next.'
|
|
42
|
+
: 'Local templates show the official adoption path. Community browsing appears automatically when a registry URL is configured.'}
|
|
43
|
+
</p>
|
|
44
|
+
</div>
|
|
45
|
+
{registryConfigured ? (
|
|
46
|
+
<div className="flex gap-2">
|
|
47
|
+
{availableSources.map((option) => (
|
|
48
|
+
<button
|
|
49
|
+
key={option}
|
|
50
|
+
onClick={() => onSourceChange(option)}
|
|
51
|
+
className={cn(
|
|
52
|
+
'rounded-full px-4 py-2 font-medium text-sm transition-colors',
|
|
53
|
+
{
|
|
54
|
+
'bg-primary text-primary-foreground': source === option,
|
|
55
|
+
'border border-border bg-card hover:bg-card/80':
|
|
56
|
+
source !== option,
|
|
57
|
+
}
|
|
58
|
+
)}
|
|
59
|
+
aria-pressed={source === option}
|
|
60
|
+
>
|
|
61
|
+
{option === 'local' ? 'Local' : 'Community'}
|
|
62
|
+
</button>
|
|
63
|
+
))}
|
|
64
|
+
</div>
|
|
65
|
+
) : null}
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<div className="editorial-panel space-y-5">
|
|
69
|
+
<div className="relative">
|
|
70
|
+
<Search
|
|
71
|
+
className="absolute top-3.5 left-4 text-muted-foreground"
|
|
72
|
+
size={18}
|
|
73
|
+
/>
|
|
74
|
+
<input
|
|
75
|
+
type="text"
|
|
76
|
+
placeholder="Search scenarios, industries, or tags"
|
|
77
|
+
value={search}
|
|
78
|
+
onChange={(event) => onSearchChange(event.target.value)}
|
|
79
|
+
className="w-full rounded-full border border-border bg-background px-12 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
80
|
+
aria-label="Search templates"
|
|
81
|
+
/>
|
|
82
|
+
</div>
|
|
83
|
+
<div className="flex flex-wrap gap-2">
|
|
84
|
+
<button
|
|
85
|
+
onClick={() => onTagChange(null)}
|
|
86
|
+
className={cn(
|
|
87
|
+
'rounded-full px-4 py-2 font-medium text-sm transition-colors',
|
|
88
|
+
{
|
|
89
|
+
'bg-primary text-primary-foreground': selectedTag === null,
|
|
90
|
+
'border border-border bg-card hover:bg-card/80':
|
|
91
|
+
selectedTag !== null,
|
|
92
|
+
}
|
|
93
|
+
)}
|
|
94
|
+
aria-pressed={selectedTag === null}
|
|
95
|
+
>
|
|
96
|
+
All
|
|
97
|
+
</button>
|
|
98
|
+
{availableTags.map((tag) => (
|
|
99
|
+
<button
|
|
100
|
+
key={tag}
|
|
101
|
+
onClick={() => onTagChange(tag)}
|
|
102
|
+
className={cn(
|
|
103
|
+
'rounded-full px-4 py-2 font-medium text-sm transition-colors',
|
|
104
|
+
{
|
|
105
|
+
'bg-primary text-primary-foreground': selectedTag === tag,
|
|
106
|
+
'border border-border bg-card hover:bg-card/80':
|
|
107
|
+
selectedTag !== tag,
|
|
108
|
+
}
|
|
109
|
+
)}
|
|
110
|
+
aria-pressed={selectedTag === tag}
|
|
111
|
+
>
|
|
112
|
+
{tag}
|
|
113
|
+
</button>
|
|
114
|
+
))}
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</section>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { RegistryTemplate } from '@contractspec/lib.example-shared-ui';
|
|
4
|
+
import Link from 'next/link';
|
|
5
|
+
import { TemplateCard } from './TemplateCard';
|
|
6
|
+
import {
|
|
7
|
+
formatExampleKindLabel,
|
|
8
|
+
formatStabilityLabel,
|
|
9
|
+
type LocalTemplateCatalogItem,
|
|
10
|
+
} from './template-catalog';
|
|
11
|
+
import {
|
|
12
|
+
getLocalTemplatePreviewAction,
|
|
13
|
+
getRegistryTemplatePreviewAction,
|
|
14
|
+
} from './template-preview';
|
|
15
|
+
import type { TemplateSource } from './template-source';
|
|
16
|
+
|
|
17
|
+
export interface TemplatesCatalogSectionProps {
|
|
18
|
+
source: TemplateSource;
|
|
19
|
+
registryConfigured: boolean;
|
|
20
|
+
registryLoading: boolean;
|
|
21
|
+
localTemplates: readonly LocalTemplateCatalogItem[];
|
|
22
|
+
registryTemplates: readonly RegistryTemplate[];
|
|
23
|
+
localTemplateById: ReadonlyMap<string, LocalTemplateCatalogItem>;
|
|
24
|
+
onPreview: (templateId: string) => void;
|
|
25
|
+
onUseTemplate: (templateId: string, source: TemplateSource) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function TemplatesCatalogSection({
|
|
29
|
+
source,
|
|
30
|
+
registryConfigured,
|
|
31
|
+
registryLoading,
|
|
32
|
+
localTemplates,
|
|
33
|
+
registryTemplates,
|
|
34
|
+
localTemplateById,
|
|
35
|
+
onPreview,
|
|
36
|
+
onUseTemplate,
|
|
37
|
+
}: TemplatesCatalogSectionProps) {
|
|
38
|
+
const showRegistry = source === 'registry' && registryConfigured;
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<section className="section-padding">
|
|
42
|
+
<div className="editorial-shell">
|
|
43
|
+
{showRegistry ? (
|
|
44
|
+
registryLoading ? (
|
|
45
|
+
<div className="py-12 text-center">
|
|
46
|
+
<p className="text-muted-foreground">
|
|
47
|
+
Loading community templates…
|
|
48
|
+
</p>
|
|
49
|
+
</div>
|
|
50
|
+
) : registryTemplates.length === 0 ? (
|
|
51
|
+
<div className="py-12 text-center">
|
|
52
|
+
<p className="text-muted-foreground">
|
|
53
|
+
No community templates found.
|
|
54
|
+
</p>
|
|
55
|
+
</div>
|
|
56
|
+
) : (
|
|
57
|
+
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
58
|
+
{registryTemplates.map((template) => {
|
|
59
|
+
const localTemplate = localTemplateById.get(template.id);
|
|
60
|
+
const previewAction = getRegistryTemplatePreviewAction(
|
|
61
|
+
template,
|
|
62
|
+
localTemplate
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<TemplateCard
|
|
67
|
+
key={template.id}
|
|
68
|
+
title={template.name}
|
|
69
|
+
description={template.description}
|
|
70
|
+
metaBadges={['Community']}
|
|
71
|
+
tags={template.tags}
|
|
72
|
+
previewAction={
|
|
73
|
+
previewAction.kind === 'modal' ? (
|
|
74
|
+
<button
|
|
75
|
+
className="btn-ghost flex-1 text-center text-xs"
|
|
76
|
+
onClick={() => onPreview(template.id)}
|
|
77
|
+
>
|
|
78
|
+
Preview
|
|
79
|
+
</button>
|
|
80
|
+
) : previewAction.kind === 'sandbox' ? (
|
|
81
|
+
<Link
|
|
82
|
+
href={previewAction.href}
|
|
83
|
+
className="btn-ghost flex-1 text-center text-xs"
|
|
84
|
+
>
|
|
85
|
+
Open Sandbox
|
|
86
|
+
</Link>
|
|
87
|
+
) : (
|
|
88
|
+
<button
|
|
89
|
+
className="btn-ghost flex-1 cursor-not-allowed text-center text-xs opacity-60"
|
|
90
|
+
type="button"
|
|
91
|
+
disabled
|
|
92
|
+
>
|
|
93
|
+
Preview Unavailable
|
|
94
|
+
</button>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
useAction={
|
|
98
|
+
<button
|
|
99
|
+
className="btn-primary flex-1 text-center text-xs"
|
|
100
|
+
onClick={() => onUseTemplate(template.id, 'registry')}
|
|
101
|
+
>
|
|
102
|
+
Use Template
|
|
103
|
+
</button>
|
|
104
|
+
}
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
107
|
+
})}
|
|
108
|
+
</div>
|
|
109
|
+
)
|
|
110
|
+
) : localTemplates.length === 0 ? (
|
|
111
|
+
<div className="py-12 text-center">
|
|
112
|
+
<p className="text-muted-foreground">
|
|
113
|
+
No templates match your filters. Try a different search.
|
|
114
|
+
</p>
|
|
115
|
+
</div>
|
|
116
|
+
) : (
|
|
117
|
+
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
118
|
+
{localTemplates.map((template) => {
|
|
119
|
+
const previewAction = getLocalTemplatePreviewAction(template);
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<TemplateCard
|
|
123
|
+
key={template.id}
|
|
124
|
+
title={template.title}
|
|
125
|
+
description={template.description}
|
|
126
|
+
isNew={template.isNew}
|
|
127
|
+
metaBadges={[
|
|
128
|
+
formatExampleKindLabel(template.kind),
|
|
129
|
+
formatStabilityLabel(template.stability),
|
|
130
|
+
]}
|
|
131
|
+
tags={template.tags}
|
|
132
|
+
featureList={template.featureList}
|
|
133
|
+
previewAction={
|
|
134
|
+
previewAction.kind === 'modal' ? (
|
|
135
|
+
<button
|
|
136
|
+
className="btn-ghost flex-1 text-center text-xs"
|
|
137
|
+
onClick={() => onPreview(template.id)}
|
|
138
|
+
>
|
|
139
|
+
Preview
|
|
140
|
+
</button>
|
|
141
|
+
) : (
|
|
142
|
+
<Link
|
|
143
|
+
href={previewAction.href}
|
|
144
|
+
className="btn-ghost flex-1 text-center text-xs"
|
|
145
|
+
>
|
|
146
|
+
Open Sandbox
|
|
147
|
+
</Link>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
useAction={
|
|
151
|
+
<button
|
|
152
|
+
className="btn-primary flex-1 text-center text-xs"
|
|
153
|
+
onClick={() => onUseTemplate(template.id, 'local')}
|
|
154
|
+
>
|
|
155
|
+
Use Template
|
|
156
|
+
</button>
|
|
157
|
+
}
|
|
158
|
+
/>
|
|
159
|
+
);
|
|
160
|
+
})}
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
</div>
|
|
164
|
+
</section>
|
|
165
|
+
);
|
|
166
|
+
}
|