From e46f01fd2cc83773e14caf062407e56cc59621d3 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 00:44:31 +0000 Subject: [PATCH] Refactor template UI text and sections --- src/components/TemplateExplorer.tsx | 326 +++++++++++++++----------- src/components/TemplateSelector.tsx | 330 +++++++++++++++++---------- src/config/template-ui.json | 76 ++++++ src/hooks/data/use-seed-templates.ts | 2 +- 4 files changed, 475 insertions(+), 259 deletions(-) create mode 100644 src/config/template-ui.json diff --git a/src/components/TemplateExplorer.tsx b/src/components/TemplateExplorer.tsx index 7e5420a..82ed9ba 100644 --- a/src/components/TemplateExplorer.tsx +++ b/src/components/TemplateExplorer.tsx @@ -9,6 +9,175 @@ import { Badge } from '@/components/ui/badge' import { useSeedTemplates } from '@/hooks/data/use-seed-templates' import { Copy, Download } from '@phosphor-icons/react' import { toast } from 'sonner' +import templateUi from '@/config/template-ui.json' + +const ui = templateUi.explorer + +type TemplateData = Record + +type TemplateExplorerHeaderProps = { + onExport: () => void +} + +type TemplateListProps = { + templates: Array<{ id: string; name: string; icon: string }> + selectedTemplate: string + onSelect: (templateId: string) => void +} + +type TemplateDetailProps = { + template: { + name: string + description: string + icon: string + data: TemplateData + features: string[] + } + onDownload: () => void + onCopyJson: () => void +} + +const TemplateExplorerHeader = ({ onExport }: TemplateExplorerHeaderProps) => ( +
+
+

{ui.title}

+

{ui.description}

+
+ +
+) + +const TemplateList = ({ templates, selectedTemplate, onSelect }: TemplateListProps) => ( +
+ {templates.map((template) => ( + onSelect(template.id)} + > + +
+ {template.icon} + {template.name} +
+
+
+ ))} +
+) + +const TemplateOverviewTab = ({ data, features }: { data: TemplateData; features: string[] }) => ( + +
+

{ui.sections.features}

+
+ {features.map((feature, idx) => ( + + {feature} + + ))} +
+
+ +
+ {Object.entries(data).map(([key, value]) => ( + + + {key.replace('project-', '')} + + {Array.isArray(value) ? `${value.length} ${ui.labels.itemsSuffix}` : ui.labels.configuration} + + + + ))} +
+
+) + +const TemplateStructureTab = ({ data }: { data: TemplateData }) => ( + + + {Object.entries(data).map(([key, value]) => ( +
+
+

{key}

+ + {Array.isArray(value) ? `${value.length} ${ui.labels.itemsSuffix}` : ui.labels.object} + +
+ {Array.isArray(value) && value.length > 0 && ( +
+ {value.slice(0, 3).map((item: any, idx: number) => ( +
+ • {item.name || item.title || item.id} +
+ ))} + {value.length > 3 && ( +
+ {`${ui.labels.morePrefix} ${value.length - 3} ${ui.labels.moreSuffix}`} +
+ )} +
+ )} +
+ ))} +
+
+) + +const TemplateJsonTab = ({ data, onCopy }: { data: TemplateData; onCopy: () => void }) => ( + +
+ + +
{JSON.stringify(data, null, 2)}
+
+
+
+) + +const TemplateDetails = ({ template, onDownload, onCopyJson }: TemplateDetailProps) => ( + + +
+
+ {template.icon} +
+ {template.name} + {template.description} +
+
+ +
+
+ + + + {ui.tabs.overview} + {ui.tabs.structure} + {ui.tabs.json} + + + + + + + +
+) export function TemplateExplorer() { const { templates } = useSeedTemplates() @@ -18,12 +187,12 @@ export function TemplateExplorer() { const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text) - toast.success('Copied to clipboard') + toast.success(ui.toasts.copySuccess) } const downloadJSON = () => { if (!currentTemplate) return - + const dataStr = JSON.stringify(currentTemplate.data, null, 2) const blob = new Blob([dataStr], { type: 'application/json' }) const url = URL.createObjectURL(blob) @@ -34,18 +203,18 @@ export function TemplateExplorer() { link.click() document.body.removeChild(link) URL.revokeObjectURL(url) - - toast.success('Template downloaded') + + toast.success(ui.toasts.downloadSuccess) } const exportCurrentData = async () => { const keys = await window.spark.kv.keys() const data: Record = {} - + for (const key of keys) { data[key] = await window.spark.kv.get(key) } - + const dataStr = JSON.stringify(data, null, 2) const blob = new Blob([dataStr], { type: 'application/json' }) const url = URL.createObjectURL(blob) @@ -56,144 +225,29 @@ export function TemplateExplorer() { link.click() document.body.removeChild(link) URL.revokeObjectURL(url) - - toast.success('Current project data exported') + + toast.success(ui.toasts.exportSuccess) } if (!currentTemplate) return null + const handleCopyJson = () => copyToClipboard(JSON.stringify(currentTemplate.data, null, 2)) + return (
-
-
-

Template Explorer

-

- Browse and inspect template structures -

-
- -
+
-
- {templates.map((template) => ( - setSelectedTemplate(template.id)} - > - -
- {template.icon} - {template.name} -
-
-
- ))} -
- - - -
-
- {currentTemplate.icon} -
- {currentTemplate.name} - {currentTemplate.description} -
-
- -
-
- - - - Overview - Structure - JSON - - - -
-

Features

-
- {currentTemplate.features.map((feature, idx) => ( - - {feature} - - ))} -
-
- -
- {Object.entries(currentTemplate.data).map(([key, value]) => ( - - - {key.replace('project-', '')} - - {Array.isArray(value) ? `${value.length} items` : 'Configuration'} - - - - ))} -
-
- - - - {Object.entries(currentTemplate.data).map(([key, value]) => ( -
-
-

{key}

- - {Array.isArray(value) ? `${value.length} items` : 'object'} - -
- {Array.isArray(value) && value.length > 0 && ( -
- {value.slice(0, 3).map((item: any, idx: number) => ( -
- • {item.name || item.title || item.id} -
- ))} - {value.length > 3 && ( -
... and {value.length - 3} more
- )} -
- )} -
- ))} -
-
- - -
- - -
-                      {JSON.stringify(currentTemplate.data, null, 2)}
-                    
-
-
-
-
-
-
+ +
) diff --git a/src/components/TemplateSelector.tsx b/src/components/TemplateSelector.tsx index 1bd7882..1a75595 100644 --- a/src/components/TemplateSelector.tsx +++ b/src/components/TemplateSelector.tsx @@ -3,121 +3,242 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' -import { useSeedTemplates, type TemplateType } from '@/hooks/data/use-seed-templates' +import { useSeedTemplates, type Template, type TemplateType } from '@/hooks/data/use-seed-templates' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Alert, AlertDescription } from '@/components/ui/alert' import { TemplateExplorer } from './TemplateExplorer' import { toast } from 'sonner' import { Download, Package, Plus, Trash } from '@phosphor-icons/react' +import templateUi from '@/config/template-ui.json' + +const ui = templateUi.selector + +type TemplateSelectorHeaderProps = { + title: string + description: string +} + +type TemplateCardProps = { + template: Template + isLoading: boolean + onSelect: (templateId: TemplateType, action: 'replace' | 'merge') => void +} + +type TemplateActionsAlertProps = { + loadTitle: string + loadDescription: string + mergeTitle: string + mergeDescription: string +} + +type ConfirmDialogState = { + open: boolean + actionType: 'replace' | 'merge' + template: TemplateType | null +} + +type ConfirmDialogProps = ConfirmDialogState & { + onCancel: () => void + onConfirm: () => void + onOpenChange: (open: boolean) => void +} + +const TemplateSelectorHeader = ({ title, description }: TemplateSelectorHeaderProps) => ( +
+

{title}

+

{description}

+
+) + +const TemplateCard = ({ template, isLoading, onSelect }: TemplateCardProps) => ( + + +
+
+ {template.icon} +
+ {template.name} + {template.description} +
+
+
+
+ +
+ {template.features.map((feature, idx) => ( + + {feature} + + ))} +
+
+ + +
+
+
+) + +const TemplateActionsAlert = ({ + loadTitle, + loadDescription, + mergeTitle, + mergeDescription +}: TemplateActionsAlertProps) => ( + + + + {loadTitle} {loadDescription} +
+ {mergeTitle} {mergeDescription} +
+
+) + +const ConfirmDialog = ({ + open, + actionType, + template, + onCancel, + onConfirm, + onOpenChange +}: ConfirmDialogProps) => ( + + + + + {actionType === 'replace' ? ui.dialog.replaceTitle : ui.dialog.mergeTitle} + + + {actionType === 'replace' ? ( + <> + {ui.dialog.replace.prefix}{' '} + {ui.dialog.replace.emphasis} {ui.dialog.replace.middle}{' '} + {template} {ui.dialog.replace.suffix} + + ) : ( + <> + {ui.dialog.merge.prefix}{' '} + {ui.dialog.merge.emphasis} {ui.dialog.merge.middle}{' '} + {template} {ui.dialog.merge.suffix} + + )} + + + + + + + + +) + +const formatToastDescription = (actionType: 'replace' | 'merge', template: TemplateType) => { + const description = actionType === 'replace' + ? ui.toasts.replaceDescription + : ui.toasts.mergeDescription + return description.replace('{template}', template) +} export function TemplateSelector() { const { templates, isLoading, clearAndLoadTemplate, mergeTemplate } = useSeedTemplates() - const [selectedTemplate, setSelectedTemplate] = useState(null) - const [showConfirmDialog, setShowConfirmDialog] = useState(false) - const [actionType, setActionType] = useState<'replace' | 'merge'>('replace') + const [confirmDialog, setConfirmDialog] = useState({ + open: false, + actionType: 'replace', + template: null + }) const handleSelectTemplate = (templateId: TemplateType, action: 'replace' | 'merge') => { - setSelectedTemplate(templateId) - setActionType(action) - setShowConfirmDialog(true) + setConfirmDialog({ open: true, actionType: action, template: templateId }) } const handleConfirmLoad = async () => { - if (!selectedTemplate) return + if (!confirmDialog.template) return - setShowConfirmDialog(false) - - const success = actionType === 'replace' - ? await clearAndLoadTemplate(selectedTemplate) - : await mergeTemplate(selectedTemplate) + setConfirmDialog(prevState => ({ ...prevState, open: false })) + + const success = confirmDialog.actionType === 'replace' + ? await clearAndLoadTemplate(confirmDialog.template) + : await mergeTemplate(confirmDialog.template) if (success) { - toast.success(`Template loaded successfully!`, { - description: `${actionType === 'replace' ? 'Replaced' : 'Merged'} with ${selectedTemplate} template` + toast.success(ui.toasts.successTitle, { + description: formatToastDescription(confirmDialog.actionType, confirmDialog.template) }) window.location.reload() } else { - toast.error('Failed to load template', { - description: 'Please try again or check the console for errors' + toast.error(ui.toasts.errorTitle, { + description: ui.toasts.errorDescription }) } } + const handleDialogToggle = (open: boolean) => { + if (!open) { + setConfirmDialog(prevState => ({ ...prevState, open })) + } + } + return ( <> - Load Templates - Explore Templates + {ui.tabs.templates} + {ui.tabs.explorer} -
-

Project Templates

-

- Start your project with pre-configured templates including models, components, and workflows -

-
+
{templates.map((template) => ( - - -
-
- {template.icon} -
- {template.name} - - {template.description} - -
-
-
-
- -
- {template.features.map((feature, idx) => ( - - {feature} - - ))} -
-
- - -
-
-
+ ))}
- - - - Load Template: Replaces all existing data with the selected template. -
- Merge: Adds template data to your existing project without removing current data. -
-
+
@@ -125,49 +246,14 @@ export function TemplateSelector() {
- - - - - {actionType === 'replace' ? 'Replace Project Data?' : 'Merge Template Data?'} - - - {actionType === 'replace' ? ( - <> - This will delete all existing data and load the{' '} - {selectedTemplate} template. This action cannot be undone. - - ) : ( - <> - This will add the {selectedTemplate} template data to your - existing project without removing current data. - - )} - - - - - - - - + handleDialogToggle(false)} + onConfirm={handleConfirmLoad} + onOpenChange={handleDialogToggle} + /> ) } diff --git a/src/config/template-ui.json b/src/config/template-ui.json new file mode 100644 index 0000000..17a46bf --- /dev/null +++ b/src/config/template-ui.json @@ -0,0 +1,76 @@ +{ + "explorer": { + "title": "Template Explorer", + "description": "Browse and inspect template structures", + "buttons": { + "exportCurrentData": "Export Current Data", + "download": "Download" + }, + "tabs": { + "overview": "Overview", + "structure": "Structure", + "json": "JSON" + }, + "sections": { + "features": "Features" + }, + "labels": { + "configuration": "Configuration", + "object": "object", + "itemsSuffix": "items", + "morePrefix": "... and", + "moreSuffix": "more" + }, + "toasts": { + "copySuccess": "Copied to clipboard", + "downloadSuccess": "Template downloaded", + "exportSuccess": "Current project data exported" + } + }, + "selector": { + "tabs": { + "templates": "Load Templates", + "explorer": "Explore Templates" + }, + "header": { + "title": "Project Templates", + "description": "Start your project with pre-configured templates including models, components, and workflows" + }, + "buttons": { + "loadTemplate": "Load Template", + "merge": "Merge", + "cancel": "Cancel", + "replaceAllData": "Replace All Data", + "mergeData": "Merge Data" + }, + "alerts": { + "loadTitle": "Load Template:", + "loadDescription": "Replaces all existing data with the selected template.", + "mergeTitle": "Merge:", + "mergeDescription": "Adds template data to your existing project without removing current data." + }, + "dialog": { + "replaceTitle": "Replace Project Data?", + "mergeTitle": "Merge Template Data?", + "replace": { + "prefix": "This will", + "emphasis": "delete all existing data", + "middle": "and load the", + "suffix": "template. This action cannot be undone." + }, + "merge": { + "prefix": "This will", + "emphasis": "add", + "middle": "the", + "suffix": "template data to your existing project without removing current data." + } + }, + "toasts": { + "successTitle": "Template loaded successfully!", + "replaceDescription": "Replaced with {template} template", + "mergeDescription": "Merged with {template} template", + "errorTitle": "Failed to load template", + "errorDescription": "Please try again or check the console for errors" + } + } +} diff --git a/src/hooks/data/use-seed-templates.ts b/src/hooks/data/use-seed-templates.ts index 1023675..0d51b2a 100644 --- a/src/hooks/data/use-seed-templates.ts +++ b/src/hooks/data/use-seed-templates.ts @@ -6,7 +6,7 @@ import defaultTemplate from '@/config/seed-data.json' export type TemplateType = 'default' | 'e-commerce' | 'blog' | 'dashboard' -interface Template { +export interface Template { id: TemplateType name: string description: string