@contractspec/bundle.marketing 3.8.9 → 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 +54 -42
- package/CHANGELOG.md +33 -0
- package/dist/browser/components/templates/TemplatesBrowseControls.js +37 -22
- package/dist/browser/components/templates/TemplatesCatalogSection.js +29 -6
- package/dist/browser/components/templates/TemplatesClientPage.js +269 -89
- package/dist/browser/components/templates/TemplatesOverlays.js +2874 -0
- package/dist/browser/components/templates/index.js +301 -121
- package/dist/browser/components/templates/template-catalog.js +5 -3
- package/dist/browser/components/templates/template-filters.js +99 -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 +301 -121
- package/dist/components/templates/TemplatesBrowseControls.d.ts +7 -2
- package/dist/components/templates/TemplatesBrowseControls.js +37 -22
- package/dist/components/templates/TemplatesCatalogSection.d.ts +4 -1
- package/dist/components/templates/TemplatesCatalogSection.js +29 -6
- package/dist/components/templates/TemplatesClientPage.js +269 -89
- package/dist/components/templates/TemplatesOverlays.d.ts +10 -0
- package/dist/components/templates/TemplatesOverlays.js +2869 -0
- package/dist/components/templates/index.js +301 -121
- package/dist/components/templates/template-catalog.d.ts +1 -0
- package/dist/components/templates/template-catalog.js +5 -3
- package/dist/components/templates/template-filters.d.ts +12 -0
- package/dist/components/templates/template-filters.js +94 -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 +301 -121
- package/dist/node/components/templates/TemplatesBrowseControls.js +37 -22
- package/dist/node/components/templates/TemplatesCatalogSection.js +29 -6
- package/dist/node/components/templates/TemplatesClientPage.js +269 -89
- package/dist/node/components/templates/TemplatesOverlays.js +2869 -0
- package/dist/node/components/templates/index.js +301 -121
- package/dist/node/components/templates/template-catalog.js +5 -3
- package/dist/node/components/templates/template-filters.js +94 -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 +301 -121
- package/package.json +82 -26
- package/src/components/templates/TemplatesBrowseControls.tsx +59 -35
- package/src/components/templates/TemplatesCatalogSection.tsx +29 -4
- package/src/components/templates/TemplatesClientPage.tsx +41 -97
- package/src/components/templates/TemplatesOverlays.tsx +65 -0
- package/src/components/templates/template-catalog.test.ts +96 -0
- package/src/components/templates/template-catalog.ts +14 -6
- package/src/components/templates/template-filters.ts +57 -0
- package/src/components/templates/template-tag-visibility.ts +58 -0
- package/src/components/templates/useTemplateBrowseState.ts +101 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contractspec/bundle.marketing",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"clean": "rm -rf dist",
|
|
@@ -211,6 +211,13 @@
|
|
|
211
211
|
"node": "./dist/node/components/templates/template-catalog.js",
|
|
212
212
|
"default": "./dist/components/templates/template-catalog.js"
|
|
213
213
|
},
|
|
214
|
+
"./components/templates/template-filters": {
|
|
215
|
+
"types": "./dist/components/templates/template-filters.d.ts",
|
|
216
|
+
"browser": "./dist/browser/components/templates/template-filters.js",
|
|
217
|
+
"bun": "./dist/components/templates/template-filters.js",
|
|
218
|
+
"node": "./dist/node/components/templates/template-filters.js",
|
|
219
|
+
"default": "./dist/components/templates/template-filters.js"
|
|
220
|
+
},
|
|
214
221
|
"./components/templates/template-new": {
|
|
215
222
|
"types": "./dist/components/templates/template-new.d.ts",
|
|
216
223
|
"browser": "./dist/browser/components/templates/template-new.js",
|
|
@@ -232,6 +239,13 @@
|
|
|
232
239
|
"node": "./dist/node/components/templates/template-source.js",
|
|
233
240
|
"default": "./dist/components/templates/template-source.js"
|
|
234
241
|
},
|
|
242
|
+
"./components/templates/template-tag-visibility": {
|
|
243
|
+
"types": "./dist/components/templates/template-tag-visibility.d.ts",
|
|
244
|
+
"browser": "./dist/browser/components/templates/template-tag-visibility.js",
|
|
245
|
+
"bun": "./dist/components/templates/template-tag-visibility.js",
|
|
246
|
+
"node": "./dist/node/components/templates/template-tag-visibility.js",
|
|
247
|
+
"default": "./dist/components/templates/template-tag-visibility.js"
|
|
248
|
+
},
|
|
235
249
|
"./components/templates/TemplateCard": {
|
|
236
250
|
"types": "./dist/components/templates/TemplateCard.d.ts",
|
|
237
251
|
"browser": "./dist/browser/components/templates/TemplateCard.js",
|
|
@@ -288,6 +302,13 @@
|
|
|
288
302
|
"node": "./dist/node/components/templates/TemplatesNextStepsSection.js",
|
|
289
303
|
"default": "./dist/components/templates/TemplatesNextStepsSection.js"
|
|
290
304
|
},
|
|
305
|
+
"./components/templates/TemplatesOverlays": {
|
|
306
|
+
"types": "./dist/components/templates/TemplatesOverlays.d.ts",
|
|
307
|
+
"browser": "./dist/browser/components/templates/TemplatesOverlays.js",
|
|
308
|
+
"bun": "./dist/components/templates/TemplatesOverlays.js",
|
|
309
|
+
"node": "./dist/node/components/templates/TemplatesOverlays.js",
|
|
310
|
+
"default": "./dist/components/templates/TemplatesOverlays.js"
|
|
311
|
+
},
|
|
291
312
|
"./components/templates/TemplatesPage": {
|
|
292
313
|
"types": "./dist/components/templates/TemplatesPage.d.ts",
|
|
293
314
|
"browser": "./dist/browser/components/templates/TemplatesPage.js",
|
|
@@ -302,6 +323,13 @@
|
|
|
302
323
|
"node": "./dist/node/components/templates/TemplatesPreviewModal.js",
|
|
303
324
|
"default": "./dist/components/templates/TemplatesPreviewModal.js"
|
|
304
325
|
},
|
|
326
|
+
"./components/templates/useTemplateBrowseState": {
|
|
327
|
+
"types": "./dist/components/templates/useTemplateBrowseState.d.ts",
|
|
328
|
+
"browser": "./dist/browser/components/templates/useTemplateBrowseState.js",
|
|
329
|
+
"bun": "./dist/components/templates/useTemplateBrowseState.js",
|
|
330
|
+
"node": "./dist/node/components/templates/useTemplateBrowseState.js",
|
|
331
|
+
"default": "./dist/components/templates/useTemplateBrowseState.js"
|
|
332
|
+
},
|
|
305
333
|
"./libs/email/client": {
|
|
306
334
|
"types": "./dist/libs/email/client.d.ts",
|
|
307
335
|
"browser": "./dist/browser/libs/email/client.js",
|
|
@@ -417,29 +445,29 @@
|
|
|
417
445
|
},
|
|
418
446
|
"types": "./dist/index.d.ts",
|
|
419
447
|
"dependencies": {
|
|
420
|
-
"@contractspec/bundle.library": "3.8.
|
|
421
|
-
"@contractspec/lib.surface-runtime": "0.5.
|
|
422
|
-
"@contractspec/example.agent-console": "3.8.
|
|
423
|
-
"@contractspec/example.ai-chat-assistant": "3.8.
|
|
424
|
-
"@contractspec/example.analytics-dashboard": "3.9.
|
|
425
|
-
"@contractspec/example.crm-pipeline": "3.7.
|
|
426
|
-
"@contractspec/example.data-grid-showcase": "3.8.
|
|
427
|
-
"@contractspec/example.integration-hub": "3.8.
|
|
428
|
-
"@contractspec/example.marketplace": "3.8.
|
|
429
|
-
"@contractspec/example.saas-boilerplate": "3.8.
|
|
430
|
-
"@contractspec/example.visualization-showcase": "3.9.
|
|
431
|
-
"@contractspec/example.workflow-system": "3.8.
|
|
432
|
-
"@contractspec/lib.contracts-spec": "5.0
|
|
433
|
-
"@contractspec/lib.contracts-runtime-client-react": "3.8.
|
|
434
|
-
"@contractspec/lib.design-system": "3.8.
|
|
435
|
-
"@contractspec/lib.email": "3.7.
|
|
436
|
-
"@contractspec/lib.example-shared-ui": "6.0.
|
|
437
|
-
"@contractspec/lib.logger": "3.7.
|
|
438
|
-
"@contractspec/lib.runtime-sandbox": "2.7.
|
|
439
|
-
"@contractspec/lib.ui-kit-core": "3.7.
|
|
440
|
-
"@contractspec/lib.ui-kit-web": "3.9.
|
|
441
|
-
"@contractspec/lib.ui-link": "3.7.
|
|
442
|
-
"@contractspec/module.examples": "3.8.
|
|
448
|
+
"@contractspec/bundle.library": "3.8.10",
|
|
449
|
+
"@contractspec/lib.surface-runtime": "0.5.17",
|
|
450
|
+
"@contractspec/example.agent-console": "3.8.9",
|
|
451
|
+
"@contractspec/example.ai-chat-assistant": "3.8.9",
|
|
452
|
+
"@contractspec/example.analytics-dashboard": "3.9.9",
|
|
453
|
+
"@contractspec/example.crm-pipeline": "3.7.17",
|
|
454
|
+
"@contractspec/example.data-grid-showcase": "3.8.9",
|
|
455
|
+
"@contractspec/example.integration-hub": "3.8.9",
|
|
456
|
+
"@contractspec/example.marketplace": "3.8.9",
|
|
457
|
+
"@contractspec/example.saas-boilerplate": "3.8.9",
|
|
458
|
+
"@contractspec/example.visualization-showcase": "3.9.9",
|
|
459
|
+
"@contractspec/example.workflow-system": "3.8.9",
|
|
460
|
+
"@contractspec/lib.contracts-spec": "5.1.0",
|
|
461
|
+
"@contractspec/lib.contracts-runtime-client-react": "3.8.5",
|
|
462
|
+
"@contractspec/lib.design-system": "3.8.10",
|
|
463
|
+
"@contractspec/lib.email": "3.7.13",
|
|
464
|
+
"@contractspec/lib.example-shared-ui": "6.0.17",
|
|
465
|
+
"@contractspec/lib.logger": "3.7.13",
|
|
466
|
+
"@contractspec/lib.runtime-sandbox": "2.7.14",
|
|
467
|
+
"@contractspec/lib.ui-kit-core": "3.7.13",
|
|
468
|
+
"@contractspec/lib.ui-kit-web": "3.9.9",
|
|
469
|
+
"@contractspec/lib.ui-link": "3.7.13",
|
|
470
|
+
"@contractspec/module.examples": "3.8.9",
|
|
443
471
|
"@electric-sql/pglite": "^0.4.2",
|
|
444
472
|
"@hookform/resolvers": "^5.2.2",
|
|
445
473
|
"@scaleway/sdk": "^3.4.1",
|
|
@@ -454,9 +482,9 @@
|
|
|
454
482
|
},
|
|
455
483
|
"devDependencies": {
|
|
456
484
|
"@types/react": "~19.2.14",
|
|
457
|
-
"@contractspec/tool.typescript": "3.7.
|
|
485
|
+
"@contractspec/tool.typescript": "3.7.13",
|
|
458
486
|
"typescript": "^5.9.3",
|
|
459
|
-
"@contractspec/tool.bun": "3.7.
|
|
487
|
+
"@contractspec/tool.bun": "3.7.13"
|
|
460
488
|
},
|
|
461
489
|
"publishConfig": {
|
|
462
490
|
"access": "public",
|
|
@@ -651,6 +679,13 @@
|
|
|
651
679
|
"node": "./dist/node/components/templates/template-catalog.js",
|
|
652
680
|
"default": "./dist/components/templates/template-catalog.js"
|
|
653
681
|
},
|
|
682
|
+
"./components/templates/template-filters": {
|
|
683
|
+
"types": "./dist/components/templates/template-filters.d.ts",
|
|
684
|
+
"browser": "./dist/browser/components/templates/template-filters.js",
|
|
685
|
+
"bun": "./dist/components/templates/template-filters.js",
|
|
686
|
+
"node": "./dist/node/components/templates/template-filters.js",
|
|
687
|
+
"default": "./dist/components/templates/template-filters.js"
|
|
688
|
+
},
|
|
654
689
|
"./components/templates/template-new": {
|
|
655
690
|
"types": "./dist/components/templates/template-new.d.ts",
|
|
656
691
|
"browser": "./dist/browser/components/templates/template-new.js",
|
|
@@ -672,6 +707,13 @@
|
|
|
672
707
|
"node": "./dist/node/components/templates/template-source.js",
|
|
673
708
|
"default": "./dist/components/templates/template-source.js"
|
|
674
709
|
},
|
|
710
|
+
"./components/templates/template-tag-visibility": {
|
|
711
|
+
"types": "./dist/components/templates/template-tag-visibility.d.ts",
|
|
712
|
+
"browser": "./dist/browser/components/templates/template-tag-visibility.js",
|
|
713
|
+
"bun": "./dist/components/templates/template-tag-visibility.js",
|
|
714
|
+
"node": "./dist/node/components/templates/template-tag-visibility.js",
|
|
715
|
+
"default": "./dist/components/templates/template-tag-visibility.js"
|
|
716
|
+
},
|
|
675
717
|
"./components/templates/TemplateCard": {
|
|
676
718
|
"types": "./dist/components/templates/TemplateCard.d.ts",
|
|
677
719
|
"browser": "./dist/browser/components/templates/TemplateCard.js",
|
|
@@ -728,6 +770,13 @@
|
|
|
728
770
|
"node": "./dist/node/components/templates/TemplatesNextStepsSection.js",
|
|
729
771
|
"default": "./dist/components/templates/TemplatesNextStepsSection.js"
|
|
730
772
|
},
|
|
773
|
+
"./components/templates/TemplatesOverlays": {
|
|
774
|
+
"types": "./dist/components/templates/TemplatesOverlays.d.ts",
|
|
775
|
+
"browser": "./dist/browser/components/templates/TemplatesOverlays.js",
|
|
776
|
+
"bun": "./dist/components/templates/TemplatesOverlays.js",
|
|
777
|
+
"node": "./dist/node/components/templates/TemplatesOverlays.js",
|
|
778
|
+
"default": "./dist/components/templates/TemplatesOverlays.js"
|
|
779
|
+
},
|
|
731
780
|
"./components/templates/TemplatesPage": {
|
|
732
781
|
"types": "./dist/components/templates/TemplatesPage.d.ts",
|
|
733
782
|
"browser": "./dist/browser/components/templates/TemplatesPage.js",
|
|
@@ -742,6 +791,13 @@
|
|
|
742
791
|
"node": "./dist/node/components/templates/TemplatesPreviewModal.js",
|
|
743
792
|
"default": "./dist/components/templates/TemplatesPreviewModal.js"
|
|
744
793
|
},
|
|
794
|
+
"./components/templates/useTemplateBrowseState": {
|
|
795
|
+
"types": "./dist/components/templates/useTemplateBrowseState.d.ts",
|
|
796
|
+
"browser": "./dist/browser/components/templates/useTemplateBrowseState.js",
|
|
797
|
+
"bun": "./dist/components/templates/useTemplateBrowseState.js",
|
|
798
|
+
"node": "./dist/node/components/templates/useTemplateBrowseState.js",
|
|
799
|
+
"default": "./dist/components/templates/useTemplateBrowseState.js"
|
|
800
|
+
},
|
|
745
801
|
"./libs/email/client": {
|
|
746
802
|
"types": "./dist/libs/email/client.d.ts",
|
|
747
803
|
"browser": "./dist/browser/libs/email/client.js",
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { cn } from '@contractspec/lib.ui-kit-core/utils';
|
|
4
4
|
import { Search } from 'lucide-react';
|
|
5
5
|
import type { TemplateSource } from './template-source';
|
|
6
|
+
import type { TemplateTagFacet } from './template-tag-visibility';
|
|
6
7
|
|
|
7
8
|
export interface TemplatesBrowseControlsProps {
|
|
8
9
|
registryConfigured: boolean;
|
|
@@ -13,7 +14,11 @@ export interface TemplatesBrowseControlsProps {
|
|
|
13
14
|
onSearchChange: (value: string) => void;
|
|
14
15
|
selectedTag: string | null;
|
|
15
16
|
onTagChange: (tag: string | null) => void;
|
|
16
|
-
|
|
17
|
+
showTagFilters: boolean;
|
|
18
|
+
visibleTagFacets: readonly TemplateTagFacet[];
|
|
19
|
+
hiddenTagFacets: readonly TemplateTagFacet[];
|
|
20
|
+
showAllTags: boolean;
|
|
21
|
+
onShowAllTagsChange: (expanded: boolean) => void;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
export function TemplatesBrowseControls({
|
|
@@ -25,7 +30,11 @@ export function TemplatesBrowseControls({
|
|
|
25
30
|
onSearchChange,
|
|
26
31
|
selectedTag,
|
|
27
32
|
onTagChange,
|
|
28
|
-
|
|
33
|
+
showTagFilters,
|
|
34
|
+
visibleTagFacets,
|
|
35
|
+
hiddenTagFacets,
|
|
36
|
+
showAllTags,
|
|
37
|
+
onShowAllTagsChange,
|
|
29
38
|
}: TemplatesBrowseControlsProps) {
|
|
30
39
|
return (
|
|
31
40
|
<section className="editorial-section">
|
|
@@ -80,39 +89,54 @@ export function TemplatesBrowseControls({
|
|
|
80
89
|
aria-label="Search templates"
|
|
81
90
|
/>
|
|
82
91
|
</div>
|
|
83
|
-
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
92
|
+
{showTagFilters ? (
|
|
93
|
+
<div className="space-y-3">
|
|
94
|
+
<div className="flex flex-wrap gap-2">
|
|
95
|
+
<button
|
|
96
|
+
onClick={() => onTagChange(null)}
|
|
97
|
+
className={cn(
|
|
98
|
+
'rounded-full px-4 py-2 font-medium text-sm transition-colors',
|
|
99
|
+
{
|
|
100
|
+
'bg-primary text-primary-foreground':
|
|
101
|
+
selectedTag === null,
|
|
102
|
+
'border border-border bg-card hover:bg-card/80':
|
|
103
|
+
selectedTag !== null,
|
|
104
|
+
}
|
|
105
|
+
)}
|
|
106
|
+
aria-pressed={selectedTag === null}
|
|
107
|
+
>
|
|
108
|
+
All
|
|
109
|
+
</button>
|
|
110
|
+
{visibleTagFacets.map((facet) => (
|
|
111
|
+
<button
|
|
112
|
+
key={facet.tag}
|
|
113
|
+
onClick={() => onTagChange(facet.tag)}
|
|
114
|
+
className={cn(
|
|
115
|
+
'rounded-full px-4 py-2 font-medium text-sm transition-colors',
|
|
116
|
+
{
|
|
117
|
+
'bg-primary text-primary-foreground':
|
|
118
|
+
selectedTag === facet.tag,
|
|
119
|
+
'border border-border bg-card hover:bg-card/80':
|
|
120
|
+
selectedTag !== facet.tag,
|
|
121
|
+
}
|
|
122
|
+
)}
|
|
123
|
+
aria-pressed={selectedTag === facet.tag}
|
|
124
|
+
>
|
|
125
|
+
{facet.tag}
|
|
126
|
+
</button>
|
|
127
|
+
))}
|
|
128
|
+
</div>
|
|
129
|
+
{hiddenTagFacets.length > 0 || showAllTags ? (
|
|
130
|
+
<button
|
|
131
|
+
type="button"
|
|
132
|
+
onClick={() => onShowAllTagsChange(!showAllTags)}
|
|
133
|
+
className="text-muted-foreground text-sm transition-colors hover:text-foreground"
|
|
134
|
+
>
|
|
135
|
+
{showAllTags ? 'Show fewer' : 'More tags'}
|
|
136
|
+
</button>
|
|
137
|
+
) : null}
|
|
138
|
+
</div>
|
|
139
|
+
) : null}
|
|
116
140
|
</div>
|
|
117
141
|
</div>
|
|
118
142
|
</section>
|
|
@@ -18,24 +18,31 @@ export interface TemplatesCatalogSectionProps {
|
|
|
18
18
|
source: TemplateSource;
|
|
19
19
|
registryConfigured: boolean;
|
|
20
20
|
registryLoading: boolean;
|
|
21
|
+
registryHasTemplates: boolean;
|
|
21
22
|
localTemplates: readonly LocalTemplateCatalogItem[];
|
|
22
23
|
registryTemplates: readonly RegistryTemplate[];
|
|
23
24
|
localTemplateById: ReadonlyMap<string, LocalTemplateCatalogItem>;
|
|
24
25
|
onPreview: (templateId: string) => void;
|
|
25
26
|
onUseTemplate: (templateId: string, source: TemplateSource) => void;
|
|
27
|
+
hasSearch: boolean;
|
|
28
|
+
selectedTag: string | null;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export function TemplatesCatalogSection({
|
|
29
32
|
source,
|
|
30
33
|
registryConfigured,
|
|
31
34
|
registryLoading,
|
|
35
|
+
registryHasTemplates,
|
|
32
36
|
localTemplates,
|
|
33
37
|
registryTemplates,
|
|
34
38
|
localTemplateById,
|
|
35
39
|
onPreview,
|
|
36
40
|
onUseTemplate,
|
|
41
|
+
hasSearch,
|
|
42
|
+
selectedTag,
|
|
37
43
|
}: TemplatesCatalogSectionProps) {
|
|
38
44
|
const showRegistry = source === 'registry' && registryConfigured;
|
|
45
|
+
const emptyStateMessage = getEmptyStateMessage(hasSearch, selectedTag);
|
|
39
46
|
|
|
40
47
|
return (
|
|
41
48
|
<section className="section-padding">
|
|
@@ -47,12 +54,16 @@ export function TemplatesCatalogSection({
|
|
|
47
54
|
Loading community templates…
|
|
48
55
|
</p>
|
|
49
56
|
</div>
|
|
50
|
-
) :
|
|
57
|
+
) : !registryHasTemplates ? (
|
|
51
58
|
<div className="py-12 text-center">
|
|
52
59
|
<p className="text-muted-foreground">
|
|
53
60
|
No community templates found.
|
|
54
61
|
</p>
|
|
55
62
|
</div>
|
|
63
|
+
) : registryTemplates.length === 0 ? (
|
|
64
|
+
<div className="py-12 text-center">
|
|
65
|
+
<p className="text-muted-foreground">{emptyStateMessage}</p>
|
|
66
|
+
</div>
|
|
56
67
|
) : (
|
|
57
68
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
58
69
|
{registryTemplates.map((template) => {
|
|
@@ -109,9 +120,7 @@ export function TemplatesCatalogSection({
|
|
|
109
120
|
)
|
|
110
121
|
) : localTemplates.length === 0 ? (
|
|
111
122
|
<div className="py-12 text-center">
|
|
112
|
-
<p className="text-muted-foreground">
|
|
113
|
-
No templates match your filters. Try a different search.
|
|
114
|
-
</p>
|
|
123
|
+
<p className="text-muted-foreground">{emptyStateMessage}</p>
|
|
115
124
|
</div>
|
|
116
125
|
) : (
|
|
117
126
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
@@ -164,3 +173,19 @@ export function TemplatesCatalogSection({
|
|
|
164
173
|
</section>
|
|
165
174
|
);
|
|
166
175
|
}
|
|
176
|
+
|
|
177
|
+
function getEmptyStateMessage(
|
|
178
|
+
hasSearch: boolean,
|
|
179
|
+
selectedTag: string | null
|
|
180
|
+
): string {
|
|
181
|
+
if (selectedTag !== null && hasSearch) {
|
|
182
|
+
return 'No templates match this tag for the current search.';
|
|
183
|
+
}
|
|
184
|
+
if (selectedTag !== null) {
|
|
185
|
+
return 'No templates match this tag. Try another tag or reset filters.';
|
|
186
|
+
}
|
|
187
|
+
if (hasSearch) {
|
|
188
|
+
return 'No templates match your search. Try a different keyword.';
|
|
189
|
+
}
|
|
190
|
+
return 'No templates match your filters. Try a different search.';
|
|
191
|
+
}
|
|
@@ -4,37 +4,15 @@ import {
|
|
|
4
4
|
analyticsEventNames,
|
|
5
5
|
captureAnalyticsEvent,
|
|
6
6
|
} from '@contractspec/bundle.library/libs/posthog/client';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
Dialog,
|
|
10
|
-
DialogContent,
|
|
11
|
-
DialogDescription,
|
|
12
|
-
DialogHeader,
|
|
13
|
-
DialogTitle,
|
|
14
|
-
} from '@contractspec/lib.ui-kit-web/ui/dialog';
|
|
15
|
-
import { useMemo, useState } from 'react';
|
|
16
|
-
import { StudioSignupSection } from '../marketing';
|
|
17
|
-
import { TemplateCommandDialog } from './TemplateCommandDialog';
|
|
7
|
+
import { useState } from 'react';
|
|
18
8
|
import { TemplatesBrowseControls } from './TemplatesBrowseControls';
|
|
19
9
|
import { TemplatesCatalogSection } from './TemplatesCatalogSection';
|
|
20
10
|
import { TemplatesHeroSection } from './TemplatesHeroSection';
|
|
21
11
|
import { TemplatesNextStepsSection } from './TemplatesNextStepsSection';
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
buildLocalTemplateCatalog,
|
|
25
|
-
matchesTemplateFilters,
|
|
26
|
-
} from './template-catalog';
|
|
27
|
-
import {
|
|
28
|
-
getAvailableTemplateSources,
|
|
29
|
-
isRegistryConfigured,
|
|
30
|
-
type TemplateSource,
|
|
31
|
-
} from './template-source';
|
|
32
|
-
|
|
33
|
-
const REGISTRY_URL = process.env.NEXT_PUBLIC_CONTRACTSPEC_REGISTRY_URL;
|
|
12
|
+
import { TemplatesOverlays } from './TemplatesOverlays';
|
|
13
|
+
import { useTemplateBrowseState } from './useTemplateBrowseState';
|
|
34
14
|
|
|
35
15
|
export const TemplatesPage = () => {
|
|
36
|
-
const [selectedTag, setSelectedTag] = useState<string | null>(null);
|
|
37
|
-
const [search, setSearch] = useState('');
|
|
38
16
|
const [previewTemplateId, setPreviewTemplateId] = useState<string | null>(
|
|
39
17
|
null
|
|
40
18
|
);
|
|
@@ -42,49 +20,27 @@ export const TemplatesPage = () => {
|
|
|
42
20
|
const [selectedTemplateId, setSelectedTemplateId] = useState<string | null>(
|
|
43
21
|
null
|
|
44
22
|
);
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
() =>
|
|
67
|
-
localTemplates.filter((template) =>
|
|
68
|
-
matchesTemplateFilters(template, search, selectedTag)
|
|
69
|
-
),
|
|
70
|
-
[localTemplates, search, selectedTag]
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
const filteredRegistryTemplates = useMemo(
|
|
74
|
-
() =>
|
|
75
|
-
registryTemplates.filter((template) =>
|
|
76
|
-
matchesTemplateFilters(
|
|
77
|
-
{
|
|
78
|
-
title: template.name,
|
|
79
|
-
description: template.description,
|
|
80
|
-
tags: template.tags,
|
|
81
|
-
},
|
|
82
|
-
search,
|
|
83
|
-
selectedTag
|
|
84
|
-
)
|
|
85
|
-
),
|
|
86
|
-
[registryTemplates, search, selectedTag]
|
|
87
|
-
);
|
|
23
|
+
const {
|
|
24
|
+
selectedTag,
|
|
25
|
+
setSelectedTag,
|
|
26
|
+
search,
|
|
27
|
+
setSearch,
|
|
28
|
+
source,
|
|
29
|
+
setSource,
|
|
30
|
+
showAllTags,
|
|
31
|
+
setShowAllTags,
|
|
32
|
+
registryConfigured,
|
|
33
|
+
availableSources,
|
|
34
|
+
localTemplates,
|
|
35
|
+
localTemplateById,
|
|
36
|
+
registryTemplates,
|
|
37
|
+
registryLoading,
|
|
38
|
+
localFilterState,
|
|
39
|
+
registryFilterState,
|
|
40
|
+
visibleTagFacets,
|
|
41
|
+
hiddenTagFacets,
|
|
42
|
+
showTagFilters,
|
|
43
|
+
} = useTemplateBrowseState();
|
|
88
44
|
|
|
89
45
|
return (
|
|
90
46
|
<>
|
|
@@ -102,14 +58,19 @@ export const TemplatesPage = () => {
|
|
|
102
58
|
onSearchChange={setSearch}
|
|
103
59
|
selectedTag={selectedTag}
|
|
104
60
|
onTagChange={setSelectedTag}
|
|
105
|
-
|
|
61
|
+
showTagFilters={showTagFilters}
|
|
62
|
+
visibleTagFacets={visibleTagFacets}
|
|
63
|
+
hiddenTagFacets={hiddenTagFacets}
|
|
64
|
+
showAllTags={showAllTags}
|
|
65
|
+
onShowAllTagsChange={setShowAllTags}
|
|
106
66
|
/>
|
|
107
67
|
<TemplatesCatalogSection
|
|
108
68
|
source={source}
|
|
109
69
|
registryConfigured={registryConfigured}
|
|
110
70
|
registryLoading={registryLoading}
|
|
111
|
-
|
|
112
|
-
|
|
71
|
+
registryHasTemplates={registryTemplates.length > 0}
|
|
72
|
+
localTemplates={localFilterState.finalTemplates}
|
|
73
|
+
registryTemplates={registryFilterState.finalTemplates}
|
|
113
74
|
localTemplateById={localTemplateById}
|
|
114
75
|
onPreview={setPreviewTemplateId}
|
|
115
76
|
onUseTemplate={(templateId, templateSource) => {
|
|
@@ -120,36 +81,19 @@ export const TemplatesPage = () => {
|
|
|
120
81
|
});
|
|
121
82
|
setSelectedTemplateId(templateId);
|
|
122
83
|
}}
|
|
84
|
+
hasSearch={search.trim().length > 0}
|
|
85
|
+
selectedTag={selectedTag}
|
|
123
86
|
/>
|
|
124
87
|
<TemplatesNextStepsSection />
|
|
125
88
|
</main>
|
|
126
89
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<Dialog
|
|
135
|
-
open={studioSignupModalOpen}
|
|
136
|
-
onOpenChange={setStudioSignupModalOpen}
|
|
137
|
-
>
|
|
138
|
-
<DialogContent className="max-h-[90vh] max-w-2xl overflow-y-auto">
|
|
139
|
-
<DialogHeader>
|
|
140
|
-
<DialogTitle>Deploy in Studio</DialogTitle>
|
|
141
|
-
<DialogDescription>
|
|
142
|
-
Deploy templates in ContractSpec Studio and run the full
|
|
143
|
-
evidence-to-spec loop with your team.
|
|
144
|
-
</DialogDescription>
|
|
145
|
-
</DialogHeader>
|
|
146
|
-
<StudioSignupSection variant="compact" />
|
|
147
|
-
</DialogContent>
|
|
148
|
-
</Dialog>
|
|
149
|
-
|
|
150
|
-
<TemplateCommandDialog
|
|
151
|
-
templateId={selectedTemplateId}
|
|
152
|
-
onClose={() => setSelectedTemplateId(null)}
|
|
90
|
+
<TemplatesOverlays
|
|
91
|
+
previewTemplateId={previewTemplateId}
|
|
92
|
+
onPreviewClose={() => setPreviewTemplateId(null)}
|
|
93
|
+
studioSignupModalOpen={studioSignupModalOpen}
|
|
94
|
+
onStudioSignupModalOpenChange={setStudioSignupModalOpen}
|
|
95
|
+
selectedTemplateId={selectedTemplateId}
|
|
96
|
+
onTemplateCommandClose={() => setSelectedTemplateId(null)}
|
|
153
97
|
onDeployStudio={() => {
|
|
154
98
|
setSelectedTemplateId(null);
|
|
155
99
|
setStudioSignupModalOpen(true);
|
|
@@ -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
|
+
}
|