feat(14-02): add LeadsSearch, rewire leads pages, surface tags in LeadDetail (CRM-08/09)

- New LeadsSearch.tsx: client-side instant filter across name/email/
  company/status/tags (mirrors CatalogSearch.tsx)
- page.tsx: fetch via getLeadsWithTags()/getLeadFieldOptions(), render
  LeadsSearch + CreateLeadModal, revalidate=0
- [id]/page.tsx: fetch getLeadsWithTags()/getLeadFieldOptions(), find
  lead by id, pass tags/tagOptions to LeadDetail
- LeadDetail.tsx: add OptionMultiSelect for lead tags in the "Profilo"
  card, wired to addLeadTag/removeLeadTag/renameLeadTag with error display
This commit is contained in:
2026-06-14 13:21:55 +02:00
parent 4887a319f7
commit ab7fa62059
4 changed files with 110 additions and 16 deletions
+35
View File
@@ -1,9 +1,13 @@
"use client";
import { useState, useTransition } from "react";
import { useRouter } from "next/navigation";
import { Lead, Activity, Reminder } from "@/db/schema";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { OptionMultiSelect } from "@/components/ui/option-multi-select";
import { addLeadTag, removeLeadTag, renameLeadTag } from "@/app/admin/leads/actions";
import { formatDistanceToNow, format } from "date-fns";
import { it } from "date-fns/locale";
import { LogActivityModal } from "./LogActivityModal";
@@ -30,11 +34,31 @@ export function LeadDetail({
lead,
activities,
reminders,
tags,
tagOptions,
}: {
lead: Lead;
activities: Activity[];
reminders: Reminder[];
tags: string[];
tagOptions: string[];
}) {
const router = useRouter();
const [, startTransition] = useTransition();
const [tagError, setTagError] = useState<string | null>(null);
function run(fn: () => Promise<unknown>) {
setTagError(null);
startTransition(async () => {
try {
await fn();
router.refresh();
} catch (e) {
setTagError(e instanceof Error ? e.message : "Errore nel salvataggio");
}
});
}
return (
<div className="space-y-6">
{/* Header + Actions */}
@@ -63,6 +87,17 @@ export function LeadDetail({
{lead.status.replace(/_/g, " ")}
</Badge>
</div>
<div>
<label className="text-sm text-gray-600">Tag</label>
<OptionMultiSelect
values={tags}
options={tagOptions}
onAdd={(v) => run(() => addLeadTag(lead.id, v))}
onRemove={(v) => run(() => removeLeadTag(lead.id, v))}
onRename={(o, n) => run(() => renameLeadTag(o, n))}
/>
{tagError && <p className="text-xs text-red-600 mt-1">{tagError}</p>}
</div>
<div>
<label className="text-sm text-gray-600">Email</label>
<p>{lead.email || "—"}</p>