diff --git a/app/(dashboard)/chat/page.tsx b/app/(dashboard)/chat/page.tsx index 04ccea7..3badbd4 100644 --- a/app/(dashboard)/chat/page.tsx +++ b/app/(dashboard)/chat/page.tsx @@ -4,7 +4,7 @@ import { useEffect, useState, useRef } from "react"; import { Brain, Send, MessageSquare, Plus, Trash2, Loader2, Wrench } from "lucide-react"; import { createClient } from "@/lib/supabase/client"; import { Button } from "poyraz-ui/atoms"; -import { useChat } from "ai/react"; +import { useChat } from "@ai-sdk/react"; interface ChatSession { id: string; @@ -107,10 +107,10 @@ export default function AIChatPage() { const customHandleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - if (!input.trim() || isLoading) return; + if (!(input || "").trim() || isLoading) return; let sessionId = activeSessionId; - const currentInput = input; + const currentInput = input || ""; setInput(""); if (!sessionId) { @@ -309,8 +309,8 @@ export default function AIChatPage() { diff --git a/app/(dashboard)/clients/[id]/actions.ts b/app/(dashboard)/clients/[id]/actions.ts new file mode 100644 index 0000000..374eefc --- /dev/null +++ b/app/(dashboard)/clients/[id]/actions.ts @@ -0,0 +1,49 @@ +"use server"; + +import { createClient } from "@/lib/supabase/server"; +import { revalidatePath } from "next/cache"; + +function cleanText(value: FormDataEntryValue | null) { + const text = typeof value === "string" ? value.trim() : ""; + return text.length > 0 ? text : null; +} + +export async function addClientActivity(clientId: string, formData: FormData) { + const supabase = await createClient(); + const { + data: { user }, + error: userError, + } = await supabase.auth.getUser(); + + if (userError || !user) { + throw new Error("Kullanıcı bulunamadı."); + } + + const title = cleanText(formData.get("title")); + if (!title) { + throw new Error("Aktivite başlığı zorunludur."); + } + + const { error } = await supabase.from("client_activities").insert({ + user_id: user.id, + client_id: clientId, + type: formData.get("type") as string || "note", + title, + content: cleanText(formData.get("content")), + activity_date: formData.get("activity_date") as string || new Date().toISOString(), + }); + + if (error) { + throw new Error(`Aktivite eklenemedi: ${error.message}`); + } + + // Update client's last_contact_date + await supabase + .from("clients") + .update({ last_contact_date: new Date().toISOString() }) + .eq("id", clientId) + .eq("user_id", user.id); + + revalidatePath(`/clients/${clientId}`); + revalidatePath(`/clients`); +} diff --git a/app/(dashboard)/clients/[id]/client-detail-client.tsx b/app/(dashboard)/clients/[id]/client-detail-client.tsx new file mode 100644 index 0000000..00fdf30 --- /dev/null +++ b/app/(dashboard)/clients/[id]/client-detail-client.tsx @@ -0,0 +1,223 @@ +"use client"; + +import { useState } from "react"; +import { format } from "date-fns"; +import { tr } from "date-fns/locale"; +import { Card, CardContent, Badge, Button, Input, Textarea, Label } from "poyraz-ui/atoms"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "poyraz-ui/molecules"; +import { Phone, Mail, ExternalLink, Calendar, Plus, MessageSquare, Briefcase, FileText } from "lucide-react"; +import Link from "next/link"; +import { addClientActivity } from "./actions"; + +export type ClientDetailData = { + id: string; + name: string; + company_name: string | null; + email: string | null; + phone: string | null; + website: string | null; + pipeline_stage: string; + status: string; + notes: string | null; +}; + +export type ClientActivity = { + id: string; + type: "note" | "call" | "meeting" | "email"; + title: string; + content: string | null; + activity_date: string; + created_at: string; +}; + +export function ClientDetailClient({ client, activities }: { client: ClientDetailData; activities: ClientActivity[] }) { + const [isAddingActivity, setIsAddingActivity] = useState(false); + const [openDialog, setOpenDialog] = useState(false); + + const getActivityIcon = (type: string) => { + switch (type) { + case "call": return ; + case "meeting": return ; + case "email": return ; + default: return ; + } + }; + + const getActivityBadge = (type: string) => { + switch (type) { + case "call": return Arama; + case "meeting": return Toplantı; + case "email": return E-posta; + default: return Not; + } + }; + + async function handleAddActivity(formData: FormData) { + setIsAddingActivity(true); + try { + await addClientActivity(client.id, formData); + setOpenDialog(false); + } finally { + setIsAddingActivity(false); + } + } + + return ( +
+ {/* Header Info */} +
+
+
+ {client.name.split(" ").slice(0, 2).map(n => n[0]?.toUpperCase()).join("")} +
+
+

{client.name}

+ {client.company_name &&

{client.company_name}

} +
+
+
+ {client.status} + + {client.pipeline_stage.replace('_', ' ')} + +
+
+ +
+ {/* Left Column: Contact & Details */} +
+ + +

İletişim Bilgileri

+
+ {client.email ? ( + + ) : null} + {client.phone ? ( + + ) : null} + {client.website ? ( + + ) : null} + {!client.email && !client.phone && !client.website && ( +

İletişim bilgisi girilmemiş.

+ )} +
+
+
+ + + +

Genel Notlar

+ {client.notes ? ( +

{client.notes}

+ ) : ( +

Müşteriye ait genel not bulunmuyor.

+ )} +
+
+
+ + {/* Right Column: Activities & Timeline */} +
+ + +
+

Aktivite Geçmişi

+ + + + + + +
+ + Yeni Aktivite Ekle + +
+
+ + +
+
+ + +
+
+ + +
+
+ +