95 lines
3.8 KiB
TypeScript
95 lines
3.8 KiB
TypeScript
// /opt/erp-system/app/search/page.tsx
|
|
'use client';
|
|
|
|
import { useSearchParams } from 'next/navigation';
|
|
import { useEffect, useState, Suspense } from 'react';
|
|
import Link from 'next/link';
|
|
import { Search, Ticket, Users } from 'lucide-react';
|
|
import { getStatusBadge } from '../components/AppShell';
|
|
|
|
function SearchResults() {
|
|
const searchParams = useSearchParams();
|
|
const q = searchParams.get('q');
|
|
|
|
const [results, setResults] = useState({ tickets: [], customers: [] });
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (q) {
|
|
setLoading(true);
|
|
fetch(`/api/search?q=${encodeURIComponent(q)}`)
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
setResults(data);
|
|
setLoading(false);
|
|
});
|
|
}
|
|
}, [q]);
|
|
|
|
if (!q) return <div className="text-slate-500 font-medium">Bitte gib einen Suchbegriff ein.</div>;
|
|
|
|
return (
|
|
<div className="max-w-5xl mx-auto space-y-6">
|
|
<h1 className="text-2xl font-bold text-slate-900 flex items-center gap-2">
|
|
<Search className="w-6 h-6 text-indigo-600" /> Suchergebnisse für "{q}"
|
|
</h1>
|
|
|
|
{loading ? (
|
|
<div className="text-slate-500 font-medium animate-pulse">Suche läuft...</div>
|
|
) : (
|
|
<div className="space-y-8">
|
|
|
|
<div className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
|
|
<div className="p-4 border-b border-slate-200 bg-slate-50 flex items-center gap-2 font-semibold text-slate-800">
|
|
<Users className="w-5 h-5 text-slate-500" /> Kunden ({results.customers?.length || 0})
|
|
</div>
|
|
<div className="divide-y divide-slate-100">
|
|
{results.customers?.length > 0 ? results.customers.map((c: any) => (
|
|
<Link key={c.id} href={`/customers/${c.id}`} className="block p-4 hover:bg-slate-50 transition">
|
|
<p className="font-semibold text-indigo-600">{c.companyName || `${c.firstName} ${c.lastName}`}</p>
|
|
<p className="text-sm text-slate-500">{c.email}</p>
|
|
</Link>
|
|
)) : (
|
|
<div className="p-4 text-sm text-slate-500">Keine Kunden gefunden.</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
|
|
<div className="p-4 border-b border-slate-200 bg-slate-50 flex items-center gap-2 font-semibold text-slate-800">
|
|
<Ticket className="w-5 h-5 text-slate-500" /> Tickets ({results.tickets?.length || 0})
|
|
</div>
|
|
<div className="divide-y divide-slate-100">
|
|
{results.tickets?.length > 0 ? results.tickets.map((t: any) => (
|
|
<Link key={t.id} href={`/tickets/${t.id}`} className="block p-4 hover:bg-slate-50 transition flex justify-between items-start">
|
|
<div>
|
|
<p className="font-semibold text-indigo-600">#{t.id} - {t.title}</p>
|
|
<p className="text-sm text-slate-500 mt-1 line-clamp-1">{t.description}</p>
|
|
<p className="text-xs text-slate-400 mt-1">
|
|
Kunde: {t.customer.companyName || `${t.customer.firstName} ${t.customer.lastName}`}
|
|
</p>
|
|
</div>
|
|
<div className="ml-4 shrink-0">
|
|
{getStatusBadge(t.status)}
|
|
</div>
|
|
</Link>
|
|
)) : (
|
|
<div className="p-4 text-sm text-slate-500">Keine Tickets gefunden.</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function SearchPage() {
|
|
return (
|
|
<Suspense fallback={<div className="text-slate-500">Lade Suche...</div>}>
|
|
<SearchResults />
|
|
</Suspense>
|
|
);
|
|
}
|