Files
myeasycms-v2/packages/features/verbandsverwaltung/src/components/shared-templates.tsx

264 lines
8.8 KiB
TypeScript

'use client';
import { useState } from 'react';
import { Copy, FileText, Mail } from 'lucide-react';
import { useAction } from 'next-safe-action/hooks';
import { formatDate } from '@kit/shared/dates';
import { Badge } from '@kit/ui/badge';
import { Button } from '@kit/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@kit/ui/card';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@kit/ui/dialog';
import { Input } from '@kit/ui/input';
import { toast } from '@kit/ui/sonner';
import { cloneTemplate } from '../server/actions/hierarchy-actions';
interface SharedTemplate {
id: string;
account_id: string;
account_name: string;
template_source: 'newsletter' | 'document';
name: string;
description: string | null;
template_type: string;
shared_with_hierarchy: boolean;
created_at: string;
}
interface SharedTemplatesProps {
accountId: string;
templates: SharedTemplate[];
}
type FilterType = 'all' | 'newsletter' | 'document';
export function SharedTemplates({
accountId,
templates,
}: SharedTemplatesProps) {
const [filter, setFilter] = useState<FilterType>('all');
const [cloneDialogOpen, setCloneDialogOpen] = useState(false);
const [selectedTemplate, setSelectedTemplate] =
useState<SharedTemplate | null>(null);
const [newName, setNewName] = useState('');
const { execute: executeClone, isPending: isCloning } = useAction(
cloneTemplate,
{
onSuccess: () => {
toast.success('Vorlage wurde erfolgreich geklont');
setCloneDialogOpen(false);
setSelectedTemplate(null);
setNewName('');
},
onError: ({ error }) => {
toast.error(error.serverError ?? 'Fehler beim Klonen der Vorlage');
},
},
);
const filteredTemplates = templates.filter((t) => {
if (filter === 'all') return true;
return t.template_source === filter;
});
function openCloneDialog(template: SharedTemplate) {
setSelectedTemplate(template);
setNewName(`${template.name} (Kopie)`);
setCloneDialogOpen(true);
}
function handleClone() {
if (!selectedTemplate) return;
executeClone({
templateType: selectedTemplate.template_source,
templateId: selectedTemplate.id,
targetAccountId: accountId,
newName: newName.trim() || undefined,
});
}
const filterButtons: { value: FilterType; label: string }[] = [
{ value: 'all', label: 'Alle' },
{ value: 'newsletter', label: 'Newsletter' },
{ value: 'document', label: 'Dokumente' },
];
function getTemplateTypeLabel(type: string): string {
const labels: Record<string, string> = {
generic: 'Allgemein',
member_card: 'Mitgliedsausweis',
invoice: 'Rechnung',
certificate: 'Urkunde',
confirmation: 'Bestätigung',
letter: 'Brief',
};
return labels[type] ?? type;
}
return (
<>
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<CardTitle>Geteilte Vorlagen</CardTitle>
<div className="flex gap-1">
{filterButtons.map((btn) => (
<Button
key={btn.value}
data-test={`templates-filter-${btn.value}`}
variant={filter === btn.value ? 'default' : 'outline'}
size="sm"
onClick={() => setFilter(btn.value)}
>
{btn.label}
</Button>
))}
</div>
</div>
</CardHeader>
<CardContent>
{filteredTemplates.length === 0 ? (
<div className="text-muted-foreground flex flex-col items-center justify-center py-12 text-center">
<FileText className="mb-3 h-10 w-10 opacity-50" />
<p className="text-sm font-medium">
Keine geteilten Vorlagen vorhanden
</p>
<p className="mt-1 text-xs">
Vorlagen, die von anderen Organisationen in Ihrer Hierarchie
geteilt werden, erscheinen hier.
</p>
</div>
) : (
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="text-muted-foreground border-b text-left">
<th className="pr-4 pb-2 font-medium">Name</th>
<th className="pr-4 pb-2 font-medium">Typ</th>
<th className="pr-4 pb-2 font-medium">Template-Typ</th>
<th className="pr-4 pb-2 font-medium">Organisation</th>
<th className="pr-4 pb-2 font-medium">Erstellt</th>
<th className="pb-2 font-medium">Aktion</th>
</tr>
</thead>
<tbody>
{filteredTemplates.map((template) => (
<tr
key={`${template.template_source}-${template.id}`}
className="border-b last:border-0"
>
<td className="py-3 pr-4">
<div className="flex items-center gap-2">
{template.template_source === 'newsletter' ? (
<Mail className="text-muted-foreground h-4 w-4" />
) : (
<FileText className="text-muted-foreground h-4 w-4" />
)}
<span className="font-medium">{template.name}</span>
</div>
{template.description && (
<p className="text-muted-foreground mt-0.5 text-xs">
{template.description}
</p>
)}
</td>
<td className="py-3 pr-4">
<Badge
variant={
template.template_source === 'newsletter'
? 'default'
: 'secondary'
}
>
{template.template_source === 'newsletter'
? 'Newsletter'
: 'Dokument'}
</Badge>
</td>
<td className="text-muted-foreground py-3 pr-4">
{getTemplateTypeLabel(template.template_type)}
</td>
<td className="text-muted-foreground py-3 pr-4">
{template.account_name}
</td>
<td className="text-muted-foreground py-3 pr-4 whitespace-nowrap">
{formatDate(template.created_at)}
</td>
<td className="py-3">
<Button
data-test="template-clone-btn"
variant="outline"
size="sm"
onClick={() => openCloneDialog(template)}
>
<Copy className="mr-1.5 h-3.5 w-3.5" />
Klonen
</Button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</CardContent>
</Card>
<Dialog open={cloneDialogOpen} onOpenChange={setCloneDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Vorlage klonen</DialogTitle>
<DialogDescription>
Erstellen Sie eine Kopie der Vorlage{' '}
<strong>{selectedTemplate?.name}</strong> in Ihrer Organisation.
</DialogDescription>
</DialogHeader>
<div className="py-4">
<label
htmlFor="clone-name"
className="mb-2 block text-sm font-medium"
>
Name der Kopie
</label>
<Input
id="clone-name"
data-test="template-clone-name"
value={newName}
onChange={(e) => setNewName(e.target.value)}
placeholder="Name der neuen Vorlage"
maxLength={200}
/>
</div>
<DialogFooter>
<Button
variant="outline"
onClick={() => setCloneDialogOpen(false)}
disabled={isCloning}
>
Abbrechen
</Button>
<Button
data-test="template-clone-confirm"
onClick={handleClone}
disabled={isCloning}
>
{isCloning ? 'Wird geklont...' : 'Vorlage klonen'}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
);
}