Skip to content

Commit 114725a

Browse files
committed
fix(logic): some code fixes
1 parent ae58179 commit 114725a

6 files changed

Lines changed: 424 additions & 43 deletions

File tree

app/tools/template-manager/page.tsx

Lines changed: 87 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,76 @@
11
'use client';
22

3-
import { useState, useEffect } from 'react';
3+
import { useState, useEffect, useCallback } from 'react';
44
import { TemplateService } from '@/lib/db/services/templateService';
55
import { VariableService } from '@/lib/db/services/variableService';
66
import { Template, Variable } from '@/types';
77
import { TemplateEditor } from '@/components/template-editor';
88
import { VariableForm } from '@/components/variable-form';
99
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
10+
import { Input } from '@/components/ui/input';
11+
import { MultiSelect } from '@/components/ui/multi-select';
1012

1113
export default function TemplateManagerPage() {
1214
const [variables, setVariables] = useState<Variable[]>([]);
1315
const [templates, setTemplates] = useState<Template[]>([]);
1416
const [selectedTemplate, setSelectedTemplate] = useState<Template | null>(null);
1517
const [loading, setLoading] = useState(true);
18+
const [searchQuery, setSearchQuery] = useState('');
19+
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
20+
const [tags, setTags] = useState('');
21+
const [error, setError] = useState<string | null>(null);
1622

1723
useEffect(() => {
18-
loadData();
24+
const init = async () => {
25+
await TemplateService.initializeDefaultTemplates();
26+
await VariableService.initializeDefaultVariables();
27+
};
28+
init();
1929
}, []);
2030

21-
const loadData = async () => {
31+
const loadData = useCallback(async () => {
2232
try {
2333
setLoading(true);
34+
35+
const templatesData = await TemplateService.getFilteredTemplates({
36+
searchQuery,
37+
categories: selectedCategories,
38+
tags: tags.split(',').map(t => t.trim()).filter(t => t),
39+
});
2440

25-
// Initialize default data if needed
26-
await TemplateService.initializeDefaultTemplates();
27-
await VariableService.initializeDefaultVariables();
28-
29-
// Load data
30-
const [templatesData, variablesData] = await Promise.all([
31-
TemplateService.getAllTemplates(),
32-
VariableService.getAllVariables()
33-
]);
41+
const variablesData = await VariableService.getAllVariables();
3442

3543
setTemplates(templatesData);
3644
setVariables(variablesData);
3745

3846
if (templatesData.length > 0) {
3947
setSelectedTemplate(templatesData[0]);
48+
} else {
49+
setSelectedTemplate(null);
4050
}
4151
} catch (error) {
4252
console.error('Error loading data:', error);
4353
} finally {
4454
setLoading(false);
4555
}
46-
};
56+
}, [searchQuery, selectedCategories, tags]);
57+
58+
useEffect(() => {
59+
loadData();
60+
}, [loadData]);
4761
const handleCreateTemplate = async () => {
48-
const newTemplate = await TemplateService.createTemplate({
49-
name: 'Untitled',
50-
category: 'blog',
51-
content: 'New content...',
52-
});
53-
setTemplates([newTemplate, ...templates]);
54-
setSelectedTemplate(newTemplate);
62+
try {
63+
setError(null);
64+
const newTemplate = await TemplateService.createTemplate({
65+
name: 'Untitled',
66+
category: 'blog',
67+
content: 'New content...',
68+
});
69+
setTemplates([newTemplate, ...templates]);
70+
setSelectedTemplate(newTemplate);
71+
} catch (err) {
72+
setError('Failed to create template.');
73+
}
5574
};
5675

5776
const handleVariableChange = async (key: string, value: string) => {
@@ -71,6 +90,23 @@ export default function TemplateManagerPage() {
7190
}
7291
};
7392

93+
const getHighlightedText = (text: string, highlight: string) => {
94+
if (!highlight) return <span>{text}</span>;
95+
const escaped = highlight.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
96+
const parts = text.split(new RegExp(`(${escaped})`, 'gi'));
97+
return (
98+
<span>
99+
{parts.map((part, i) =>
100+
part.toLowerCase() === highlight.toLowerCase() ? (
101+
<strong key={i}>{part}</strong>
102+
) : (
103+
part
104+
)
105+
)}
106+
</span>
107+
);
108+
};
109+
74110
if (loading) {
75111
return (
76112
<div className="min-h-screen bg-background flex items-center justify-center">
@@ -91,9 +127,36 @@ export default function TemplateManagerPage() {
91127
Create and manage your professional templates with ease
92128
</p>
93129
</div>
94-
<button onClick={handleCreateTemplate} className="btn btn-primary">
95-
+ New Template
96-
</button>
130+
<div className="flex justify-between items-center">
131+
<div className="flex gap-4">
132+
<Input
133+
placeholder="Search templates..."
134+
value={searchQuery}
135+
onChange={(e) => setSearchQuery(e.target.value)}
136+
className="max-w-xs"
137+
/>
138+
<MultiSelect
139+
options={[
140+
{ label: 'Email', value: 'email' },
141+
{ label: 'Blog', value: 'blog' },
142+
{ label: 'Social', value: 'social' },
143+
]}
144+
onValueChange={setSelectedCategories}
145+
defaultValue={selectedCategories}
146+
placeholder="Filter by category"
147+
/>
148+
<Input
149+
placeholder="Filter by tags (comma-separated)"
150+
value={tags}
151+
onChange={(e) => setTags(e.target.value)}
152+
className="max-w-xs"
153+
/>
154+
</div>
155+
<button onClick={handleCreateTemplate} className="btn btn-primary">
156+
+ New Template
157+
</button>
158+
</div>
159+
{error && <p className="text-red-500">{error}</p>}
97160

98161
<div className="grid md:grid-cols-2 gap-8">
99162
<VariableForm
@@ -111,7 +174,7 @@ export default function TemplateManagerPage() {
111174
value={template.id}
112175
onClick={() => setSelectedTemplate(template)}
113176
>
114-
{template.name}
177+
{getHighlightedText(template.name, searchQuery)}
115178
</TabsTrigger>
116179
))}
117180
</TabsList>

components/template-editor.tsx

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { Template, Variable } from '@/types';
55
import { Button } from '@/components/ui/button';
66
import { Textarea } from '@/components/ui/textarea';
77
import { Card } from '@/components/ui/card';
8+
import { Input } from '@/components/ui/input';
9+
import { TemplateService } from '@/lib/db/services/templateService';
810

911
interface TemplateEditorProps {
1012
template: Template;
@@ -13,6 +15,25 @@ interface TemplateEditorProps {
1315

1416
export function TemplateEditor({ template, variables }: TemplateEditorProps) {
1517
const [content, setContent] = useState(template.content);
18+
const [category, setCategory] = useState(template.category);
19+
const [tags, setTags] = useState(template.tags.join(', '));
20+
const [error, setError] = useState<string | null>(null);
21+
const [success, setSuccess] = useState<string | null>(null);
22+
23+
const handleSave = async () => {
24+
try {
25+
setError(null);
26+
setSuccess(null);
27+
await TemplateService.updateTemplate(template.id, {
28+
content,
29+
category,
30+
tags: tags.split(',').map(t => t.trim()),
31+
});
32+
setSuccess('Template saved successfully!');
33+
} catch (err) {
34+
setError('Failed to save template.');
35+
}
36+
};
1637

1738
const generateContent = () => {
1839
let result = content;
@@ -31,6 +52,18 @@ export function TemplateEditor({ template, variables }: TemplateEditorProps) {
3152
<Card className="p-6">
3253
<div className="space-y-4">
3354
<h3 className="text-lg font-semibold">{template.name}</h3>
55+
<div className="grid grid-cols-2 gap-4">
56+
<Input
57+
value={category}
58+
onChange={(e) => setCategory(e.target.value)}
59+
placeholder="Category"
60+
/>
61+
<Input
62+
value={tags}
63+
onChange={(e) => setTags(e.target.value)}
64+
placeholder="Tags (comma-separated)"
65+
/>
66+
</div>
3467
<Textarea
3568
value={content}
3669
onChange={(e) => setContent(e.target.value)}
@@ -41,7 +74,12 @@ export function TemplateEditor({ template, variables }: TemplateEditorProps) {
4174
<div className="p-4 rounded-md bg-muted whitespace-pre-wrap">
4275
{generateContent()}
4376
</div>
44-
<Button onClick={copyToClipboard}>Copy to Clipboard</Button>
77+
<div className="flex gap-4">
78+
<Button onClick={handleSave}>Save</Button>
79+
<Button onClick={copyToClipboard}>Copy to Clipboard</Button>
80+
</div>
81+
{error && <p className="text-red-500">{error}</p>}
82+
{success && <p className="text-green-500">{success}</p>}
4583
</div>
4684
</div>
4785
</Card>

0 commit comments

Comments
 (0)