Files
webshop/shop/app/admin/partners/page.tsx
DanielS 4e86ad7a9d
All checks were successful
Staging Build / build (push) Successful in 2m44s
Rename companies to partners and update UI
2026-06-24 22:29:51 +02:00

181 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState, useEffect } from 'react'
import { Building2, Plus, Trash2, Loader2 } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { getPartners, createPartner, deletePartner } from '@/lib/actions/partners'
export default function PartnersPage() {
const [partners, setPartners] = useState<any[]>([])
const [loading, setLoading] = useState(true)
const [isCreateOpen, setIsCreateOpen] = useState(false)
const [name, setName] = useState('')
const [creating, setCreating] = useState(false)
const [createError, setCreateError] = useState<string | null>(null)
const [deletingId, setDeletingId] = useState<string | null>(null)
const loadPartners = async () => {
setLoading(true)
try {
const data = await getPartners()
setPartners(data)
} catch (err) {
console.error('Fehler beim Laden der Partner:', err)
} finally {
setLoading(false)
}
}
useEffect(() => {
loadPartners()
}, [])
const handleCreate = async (e: React.FormEvent) => {
e.preventDefault()
setCreating(true)
setCreateError(null)
try {
await createPartner(name)
setIsCreateOpen(false)
setName('')
loadPartners()
} catch (err: any) {
setCreateError(err.message || 'Fehler beim Anlegen.')
} finally {
setCreating(false)
}
}
const handleDelete = async (id: string, partnerName: string) => {
if (!confirm(`Partner "${partnerName}" wirklich löschen?`)) return
setDeletingId(id)
try {
await deletePartner(id)
loadPartners()
} catch (err) {
console.error('Fehler beim Löschen:', err)
} finally {
setDeletingId(null)
}
}
return (
<div className="p-6 max-w-4xl mx-auto text-slate-900 dark:text-white space-y-8">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-extrabold tracking-tight flex items-center gap-3">
<Building2 className="w-8 h-8 text-blue-500" /> Partnerverwaltung
</h1>
<p className="text-slate-500 dark:text-slate-400 text-sm mt-1">
Verwalten Sie Partner.
</p>
</div>
<Button onClick={() => setIsCreateOpen(true)} className="bg-blue-600 hover:bg-blue-700 text-white font-semibold">
<Plus className="w-4 h-4 mr-2" /> Partner anlegen
</Button>
</div>
{/* Partner Table */}
<div className="p-5 bg-white dark:bg-slate-900/50 rounded-xl border border-slate-200 dark:border-white/10 shadow-sm">
{loading ? (
<div className="flex justify-center items-center py-16">
<Loader2 className="w-8 h-8 text-blue-500 animate-spin" />
<span className="ml-3 text-slate-400 text-sm">Lade Partner</span>
</div>
) : partners.length === 0 ? (
<div className="text-center py-16 border-2 border-dashed border-slate-200 dark:border-white/5 rounded-xl text-slate-500">
<Building2 className="w-10 h-10 mx-auto mb-3 opacity-30" />
Noch keine Partner angelegt.
</div>
) : (
<table className="w-full text-left">
<thead>
<tr className="border-b border-slate-200 dark:border-white/10 text-slate-400 text-xs font-semibold uppercase">
<th className="pb-3">Partnername</th>
<th className="pb-3">Erstellt am</th>
<th className="pb-3 text-right">Aktionen</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100 dark:divide-white/5">
{partners.map((partner) => (
<tr key={partner.id} className="hover:bg-slate-50 dark:hover:bg-white/3 transition-colors">
<td className="py-4 font-semibold text-slate-900 dark:text-white">
<div className="flex items-center gap-2">
<Building2 className="w-4 h-4 text-blue-500 shrink-0" />
{partner.name}
</div>
</td>
<td className="py-4 text-sm text-slate-500 dark:text-slate-400">
{partner.created_at ? new Date(partner.created_at).toLocaleDateString('de-DE') : ''}
</td>
<td className="py-4 text-right">
<Button
variant="ghost"
size="icon"
disabled={deletingId === partner.id}
onClick={() => handleDelete(partner.id, partner.name)}
className="h-8 w-8 text-red-500 hover:text-red-400 hover:bg-red-500/10"
title="Partner löschen"
>
{deletingId === partner.id ? <Loader2 className="h-4 w-4 animate-spin" /> : <Trash2 className="h-4 w-4" />}
</Button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
{/* Dialog: Neuer Partner */}
<Dialog open={isCreateOpen} onOpenChange={setIsCreateOpen}>
<DialogContent className="bg-white dark:bg-slate-950 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white shadow-2xl">
<DialogHeader>
<DialogTitle>Neuen Partner anlegen</DialogTitle>
<DialogDescription className="text-slate-500 dark:text-slate-400">
Geben Sie den Namen des Partners ein.
</DialogDescription>
</DialogHeader>
<form onSubmit={handleCreate} className="space-y-4 mt-2">
<div className="space-y-2">
<Label htmlFor="partner-name">Partnername</Label>
<Input
id="partner-name"
type="text"
required
placeholder="Muster Partner GmbH"
value={name}
onChange={(e) => setName(e.target.value)}
className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10"
/>
</div>
{createError && (
<p className="text-sm text-red-600 dark:text-red-400">{createError}</p>
)}
<DialogFooter className="pt-2">
<Button type="button" variant="outline" onClick={() => setIsCreateOpen(false)} disabled={creating}>
Abbrechen
</Button>
<Button type="submit" disabled={creating} className="bg-blue-600 hover:bg-blue-700 text-white font-semibold">
{creating ? <Loader2 className="w-4 h-4 mr-2 animate-spin" /> : null}
Anlegen
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
</div>
)
}