122 lines
4.0 KiB
TypeScript
122 lines
4.0 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import TemplateList from './components/TemplateList';
|
|
import TemplateEditor from './components/TemplateEditor';
|
|
import { ViewState, EmailTemplate, SQL_SCHEMA } from './types';
|
|
import { getTemplates, deleteTemplate } from './services/storage';
|
|
import { Database } from 'lucide-react';
|
|
|
|
const App: React.FC = () => {
|
|
const [view, setView] = useState<ViewState>('dashboard');
|
|
const [templates, setTemplates] = useState<EmailTemplate[]>([]);
|
|
const [selectedTemplate, setSelectedTemplate] = useState<EmailTemplate | undefined>(undefined);
|
|
const [showSchema, setShowSchema] = useState(false);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
useEffect(() => {
|
|
refreshTemplates();
|
|
}, []);
|
|
|
|
const refreshTemplates = async () => {
|
|
setIsLoading(true);
|
|
const data = await getTemplates();
|
|
setTemplates(data);
|
|
setIsLoading(false);
|
|
};
|
|
|
|
const handleCreate = () => {
|
|
setSelectedTemplate(undefined);
|
|
setView('editor');
|
|
};
|
|
|
|
const handleEdit = (t: EmailTemplate) => {
|
|
setSelectedTemplate(t);
|
|
setView('editor');
|
|
};
|
|
|
|
const handleDelete = async (id: string) => {
|
|
if (window.confirm("Are you sure you want to delete this template?")) {
|
|
await deleteTemplate(id);
|
|
await refreshTemplates();
|
|
}
|
|
};
|
|
|
|
const handleSave = async () => {
|
|
await refreshTemplates();
|
|
setView('dashboard');
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-slate-50 text-slate-900 font-sans">
|
|
{view === 'dashboard' && (
|
|
<>
|
|
{isLoading ? (
|
|
<div className="flex h-screen items-center justify-center text-slate-500">
|
|
Loading templates...
|
|
</div>
|
|
) : (
|
|
<TemplateList
|
|
templates={templates}
|
|
onCreate={handleCreate}
|
|
onEdit={handleEdit}
|
|
onDelete={handleDelete}
|
|
/>
|
|
)}
|
|
<div className="fixed bottom-6 right-6">
|
|
<button
|
|
onClick={() => setShowSchema(true)}
|
|
className="bg-slate-800 text-white px-4 py-3 rounded-full shadow-lg hover:bg-slate-900 flex items-center gap-2 text-sm font-medium transition-transform hover:scale-105"
|
|
>
|
|
<Database size={18} />
|
|
DB Schema
|
|
</button>
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
{view === 'editor' && (
|
|
<TemplateEditor
|
|
initialTemplate={selectedTemplate}
|
|
onBack={() => setView('dashboard')}
|
|
onSave={handleSave}
|
|
/>
|
|
)}
|
|
|
|
{/* Database Schema Modal (Global Help) */}
|
|
{showSchema && (
|
|
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 p-4">
|
|
<div className="bg-white rounded-xl shadow-2xl max-w-2xl w-full p-6">
|
|
<h2 className="text-xl font-bold mb-4 flex items-center gap-2">
|
|
<Database className="text-brand-600"/>
|
|
Database Setup for n8n
|
|
</h2>
|
|
<p className="text-slate-600 text-sm mb-4">
|
|
To make this app work with your internal software, create this table in your MySQL or PostgreSQL database.
|
|
</p>
|
|
<div className="bg-slate-900 text-slate-100 p-4 rounded-lg overflow-x-auto custom-scrollbar mb-4">
|
|
<pre className="text-xs font-mono leading-relaxed">{SQL_SCHEMA}</pre>
|
|
</div>
|
|
<div className="flex justify-end gap-3">
|
|
<button
|
|
onClick={() => {
|
|
navigator.clipboard.writeText(SQL_SCHEMA);
|
|
alert("Schema copied!");
|
|
}}
|
|
className="px-4 py-2 bg-slate-100 text-slate-700 font-medium rounded hover:bg-slate-200"
|
|
>
|
|
Copy SQL
|
|
</button>
|
|
<button
|
|
onClick={() => setShowSchema(false)}
|
|
className="px-4 py-2 bg-brand-600 text-white font-medium rounded hover:bg-brand-700"
|
|
>
|
|
Close
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default App; |