"use client"; import { archiveClientRecord, createClientRecord, updateClientRecord, updateClientPipelineStage, } from "@/app/(dashboard)/clients/actions"; import { Badge, Button, Card, CardContent, Input, Label, Textarea } from "poyraz-ui/atoms"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Tabs, TabsContent, TabsList, TabsTrigger, } from "poyraz-ui/molecules"; import { Archive, ExternalLink, Mail, PauseCircle, Pencil, Phone, Plus, UserCheck, Users, Wallet, Clock, ArrowRight, type LucideIcon, } from "lucide-react"; import Link from "next/link"; import { useState } from "react"; import { format, isPast, isToday } from "date-fns"; import { tr } from "date-fns/locale"; import { DndContext, DragEndEvent, DragOverlay, DragStartEvent, PointerSensor, useSensor, useSensors, closestCorners, useDroppable, useDraggable, } from "@dnd-kit/core"; import { CSS } from "@dnd-kit/utilities"; import { useEffect } from "react"; import { cn } from "@/lib/utils"; export type ClientListItem = { id: string; name: string; company_name: string | null; email: string | null; phone: string | null; website: string | null; status: "active" | "paused" | "archived"; notes: string | null; created_at: string; projectCount: number; revenueTotal: number; // CRM fields pipeline_stage: "lead" | "contacted" | "proposal_sent" | "won" | "lost"; next_follow_up_date: string | null; last_contact_date: string | null; client_value_score: number; }; const statusLabels = { active: "Aktif", paused: "Duraklatıldı", archived: "Arşivlendi", }; const statusClasses = { active: "border-emerald-200 bg-emerald-50 text-emerald-700", paused: "border-amber-200 bg-amber-50 text-amber-700", archived: "border-zinc-200 bg-zinc-50 text-zinc-600", }; const pipelineStages = [ { id: "lead", label: "Potansiyel (Lead)", color: "border-slate-200 bg-slate-50 text-slate-700" }, { id: "contacted", label: "İletişime Geçildi", color: "border-blue-200 bg-blue-50 text-blue-700" }, { id: "proposal_sent", label: "Teklif İletildi", color: "border-amber-200 bg-amber-50 text-amber-700" }, { id: "won", label: "Kazanıldı (Won)", color: "border-emerald-200 bg-emerald-50 text-emerald-700" }, { id: "lost", label: "Kaybedildi (Lost)", color: "border-rose-200 bg-rose-50 text-rose-700" }, ]; type ClientsClientProps = { clients: ClientListItem[]; totalRevenue: number; activeCount: number; pausedCount: number; archivedCount: number; }; export function ClientsClient({ clients, totalRevenue, activeCount, pausedCount, archivedCount, }: ClientsClientProps) { const [query, setQuery] = useState(""); const normalizedQuery = query.trim().toLowerCase(); const [activeDragClient, setActiveDragClient] = useState(null); const [localClients, setLocalClients] = useState(clients); useEffect(() => { setLocalClients(clients); }, [clients]); const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 5, }, }) ); function handleDragStart(event: DragStartEvent) { const { active } = event; const client = localClients.find(c => c.id === active.id); if (client) setActiveDragClient(client); } async function handleDragEnd(event: DragEndEvent) { const { active, over } = event; setActiveDragClient(null); if (!over) return; const clientId = active.id as string; const newStage = over.id as string; const client = localClients.find(c => c.id === clientId); if (!client || client.pipeline_stage === newStage) return; setLocalClients(prev => prev.map(c => c.id === clientId ? { ...c, pipeline_stage: newStage as any } : c) ); try { await updateClientPipelineStage(clientId, newStage); } catch (error) { setLocalClients(clients); } } const filteredClients = normalizedQuery ? localClients.filter((client) => [ client.name, client.company_name, client.email, client.phone, client.website, client.notes, ] .filter(Boolean) .some((value) => value!.toLowerCase().includes(normalizedQuery)), ) : localClients; return (
CRM & Operasyon

CRM & Müşteriler

Potansiyel müşterilerini pipeline üzerinden takip et ve müşteri ilişkilerini yönet.

c.pipeline_stage === 'lead' || c.pipeline_stage === 'contacted').length.toString()} icon={Users} iconClassName="bg-blue-50 text-blue-700" /> c.next_follow_up_date && (isPast(new Date(c.next_follow_up_date)) || isToday(new Date(c.next_follow_up_date)))).length.toString()} icon={Clock} iconClassName="bg-rose-50 text-rose-700" />
Pipeline (Kanban) Müşteri Listesi setQuery(event.target.value)} placeholder="Müşteri, firma, e-posta veya not ara" className="md:max-w-sm" />
{pipelineStages.map(stage => { const stageClients = filteredClients.filter(c => c.pipeline_stage === stage.id && c.status !== 'archived'); return ( {stageClients.map(client => ( ))} {stageClients.length === 0 && (
Boş
)}
); })}
{activeDragClient ? ( ) : null}
{filteredClients.length > 0 ? (
Müşteri İletişim Aşama Follow-up Projeler İşlem
{filteredClients.map((client) => ( ))}
) : ( )}
); } function DroppableColumn({ id, title, count, color, children }: { id: string, title: string, count: number, color: string, children: React.ReactNode }) { const { isOver, setNodeRef } = useDroppable({ id }); return (

{title}

{count}
{children}
); } function DraggableClientCard({ client, isOverlay }: { client: ClientListItem, isOverlay?: boolean }) { const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({ id: client.id, data: client, }); const style = { transform: CSS.Translate.toString(transform), opacity: isDragging && !isOverlay ? 0.3 : 1, zIndex: isDragging ? 999 : "auto", }; return (
e.stopPropagation()}> {client.name}
e.stopPropagation()}> } />
{client.company_name &&

{client.company_name}

} {client.next_follow_up_date && (
{format(new Date(client.next_follow_up_date), 'd MMM yyyy', { locale: tr })}
)}
); } function ClientRow({ client }: { client: ClientListItem }) { const isFollowUpOverdue = client.next_follow_up_date && (isPast(new Date(client.next_follow_up_date)) || isToday(new Date(client.next_follow_up_date))); const stage = pipelineStages.find(s => s.id === client.pipeline_stage) || pipelineStages[0]; return (
{getInitials(client.name)}
{client.name}
{client.company_name || "Firma bilgisi yok"}
{client.email ? ( {client.email} ) : null} {client.phone ? ( {client.phone} ) : null} {!client.email && !client.phone && !client.website ? ( İletişim bilgisi yok ) : null}
{stage.label}
{client.next_follow_up_date ? (
{format(new Date(client.next_follow_up_date), 'd MMM yyyy', { locale: tr })}
) : ( - )}
{client.projectCount} Proje
{formatCurrency(client.revenueTotal)}
Düzenle} />
); } function ClientDialog({ mode, client, trigger }: { mode: "create" | "edit"; client?: ClientListItem; trigger?: React.ReactNode; }) { const [open, setOpen] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const action = mode === "create" ? createClientRecord : updateClientRecord; async function handleSubmit(formData: FormData) { setIsSubmitting(true); try { await action(formData); setOpen(false); } finally { setIsSubmitting(false); } } return ( {trigger || ( )}
{client ? : null} {mode === "create" ? "Yeni müşteri" : "Müşteriyi düzenle"} Müşterinin iletişim ve CRM detaylarını girin.
); } function ClientFormFields({ client }: { client?: ClientListItem }) { return (