"use server"; import { createAdminClient } from "@/lib/supabase/server"; import { auth } from "@clerk/nextjs/server"; async function getOrganisationId(): Promise { const { userId } = await auth(); if (!userId) throw new Error("Unauthenticated"); const supabase = await createAdminClient(); const { data: member, error } = await supabase .from("organisation_members") .select("organisation_id") .eq("user_id", userId) .single(); if (error || !member) throw new Error("No organisation found"); return member.organisation_id; } export interface ConversationSummary { id: string; participant_name: string; participant_id: string; last_message: string | null; last_message_at: string | null; unread_count: number; } export interface Message { id: string; conversation_id: string; sender_id: string; sender_name: string; sender_role: string | null; body: string; created_at: string; read_at: string | null; } export async function fetchConversations(): Promise { const { userId } = await auth(); if (!userId) throw new Error("Unauthenticated"); const orgId = await getOrganisationId(); const supabase = await createAdminClient(); // Get conversations where current user is a participant const { data: participantIds } = await supabase .from("conversation_participants") .select("conversation_id") .eq("user_id", userId); const convoIds = participantIds?.map((p) => p.conversation_id) || []; if (convoIds.length === 0) return []; const { data: convos, error } = await supabase .from("conversations") .select("id") .eq("organisation_id", orgId) .in("id", convoIds); if (error) throw error; // For each conversation, get the other participant const results: ConversationSummary[] = []; for (const convo of convos || []) { const { data: otherParticipant } = await supabase .from("conversation_participants") .select("user_id") .eq("conversation_id", convo.id) .neq("user_id", userId) .single(); const { data: profile } = await supabase .from("profiles") .select("full_name") .eq("id", otherParticipant?.user_id) .single(); // Get last message for this conversation const { data: lastMsg } = await supabase .from("messages") .select("body, created_at") .eq("conversation_id", convo.id) .order("created_at", { ascending: false }) .limit(1); results.push({ id: convo.id, participant_name: profile?.full_name || "Unknown", participant_id: otherParticipant?.user_id || "", last_message: lastMsg?.[0]?.body || null, last_message_at: lastMsg?.[0]?.created_at || null, unread_count: 0, }); } results.sort((a, b) => { if (!a.last_message_at) return 1; if (!b.last_message_at) return -1; return new Date(b.last_message_at).getTime() - new Date(a.last_message_at).getTime(); }); return results; } export async function fetchMessages(conversationId: string): Promise { const { userId } = await auth(); if (!userId) throw new Error("Unauthenticated"); const supabase = await createAdminClient(); const { data, error } = await supabase .from("messages") .select("id, conversation_id, sender_id, body, created_at, read_at") .eq("conversation_id", conversationId) .order("created_at", { ascending: true }); if (error) throw error; // Get sender names + highest role const senderIds = [...new Set((data || []).map((m) => m.sender_id))]; const { data: profiles } = await supabase .from("profiles") .select("id, full_name") .in("id", senderIds); const nameMap: Record = {}; (profiles || []).forEach((p) => { nameMap[p.id] = p.full_name; }); // Get highest role for each sender const roleMap: Record = {}; for (const sid of senderIds) { const { data: emp } = await supabase .from("employees") .select("id, employee_roles(role:roles(name, hierarchy_level))") .eq("user_id", sid) .single(); if (emp?.employee_roles && (emp.employee_roles as any[]).length > 0) { const roles = (emp.employee_roles as any[]) .map((er: any) => er.role) .sort((a: any, b: any) => (b.hierarchy_level || 0) - (a.hierarchy_level || 0)); roleMap[sid] = roles[0]?.name || null; } else { roleMap[sid] = null; } } return (data || []).map((m) => ({ id: m.id, conversation_id: m.conversation_id, sender_id: m.sender_id, sender_name: nameMap[m.sender_id] || "Unknown", sender_role: roleMap[m.sender_id] || null, body: m.body, created_at: m.created_at, read_at: m.read_at, })); } export async function createOrFindConversation(participantId: string): Promise<{ conversationId: string; error?: string }> { const { userId } = await auth(); if (!userId) return { conversationId: "", error: "Unauthenticated" }; const orgId = await getOrganisationId(); const supabase = await createAdminClient(); // Check if conversation already exists const { data: myConvos } = await supabase .from("conversation_participants") .select("conversation_id") .eq("user_id", userId); for (const mc of myConvos || []) { const { data: other } = await supabase .from("conversation_participants") .select("user_id") .eq("conversation_id", mc.conversation_id) .neq("user_id", userId) .single(); if (other?.user_id === participantId) { return { conversationId: mc.conversation_id }; } } // Create new conversation const { data: convo, error: convoError } = await supabase .from("conversations") .insert({ organisation_id: orgId }) .select("id") .single(); if (convoError || !convo) return { conversationId: "", error: "Failed to create conversation" }; // Add both participants await supabase.from("conversation_participants").insert([ { conversation_id: convo.id, user_id: userId }, { conversation_id: convo.id, user_id: participantId }, ]); return { conversationId: convo.id }; } export async function sendMessage(conversationId: string, body: string): Promise<{ success: boolean; messageId?: string; error?: string }> { const { userId } = await auth(); if (!userId) return { success: false, error: "Unauthenticated" }; const supabase = await createAdminClient(); const { data, error } = await supabase .from("messages") .insert({ conversation_id: conversationId, sender_id: userId, body, }) .select("id") .single(); if (error) return { success: false, error: error.message }; return { success: true, messageId: data.id }; } export async function fetchOrgMembers(): Promise<{ id: string; name: string; email: string; role: string }[]> { const { userId } = await auth(); if (!userId) return []; const orgId = await getOrganisationId(); const supabase = await createAdminClient(); const { data, error } = await supabase .from("organisation_members") .select("user_id, role, profiles(full_name, email)") .eq("organisation_id", orgId); if (error) return []; const rolePriority: Record = { owner: 1, admin: 2, accountant: 3, hr_manager: 4, employee: 5, }; return (data || []) .filter((m: any) => m.user_id !== userId) .map((m: any) => ({ id: m.user_id, name: m.profiles?.full_name || "Unknown", email: m.profiles?.email || "", role: m.role || "employee", })) .sort((a, b) => { const priorityA = rolePriority[a.role] || 6; const priorityB = rolePriority[b.role] || 6; if (priorityA !== priorityB) return priorityA - priorityB; return a.name.localeCompare(b.name); }); }