diff --git a/src/components/inbox/message-thread.tsx b/src/components/inbox/message-thread.tsx
index 85044c7..b80e8a1 100644
--- a/src/components/inbox/message-thread.tsx
+++ b/src/components/inbox/message-thread.tsx
@@ -2,6 +2,7 @@
import { useState, useEffect, useCallback, useRef, useMemo } from "react";
import { createClient } from "@/lib/supabase/client";
+import { useAuth } from "@/hooks/use-auth";
import { cn } from "@/lib/utils";
import type {
Conversation,
@@ -9,21 +10,23 @@ import type {
Contact,
ConversationStatus,
MessageTemplate,
+ Profile,
} from "@/types";
import {
MessageSquare,
ChevronDown,
UserPlus,
+ Check,
Clock,
ArrowLeft,
} from "lucide-react";
import { format, isToday, isYesterday, differenceInHours } from "date-fns";
import { Badge } from "@/components/ui/badge";
-import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
+ DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { ScrollArea } from "@/components/ui/scroll-area";
@@ -47,6 +50,10 @@ interface MessageThreadProps {
onNewMessage: (message: Message) => void;
onUpdateMessage: (id: string, updates: Partial
) => void;
onStatusChange: (conversationId: string, status: ConversationStatus) => void;
+ onAssignChange: (
+ conversationId: string,
+ assignedAgentId: string | null,
+ ) => void;
/**
* On mobile, the thread is shown full-screen with the conversation list
* hidden. This callback lets the page deselect the active conversation
@@ -94,11 +101,37 @@ export function MessageThread({
onNewMessage,
onUpdateMessage,
onStatusChange,
+ onAssignChange,
onBack,
}: MessageThreadProps) {
+ const { user } = useAuth();
const [loading, setLoading] = useState(false);
const scrollRef = useRef(null);
const [templateModalOpen, setTemplateModalOpen] = useState(false);
+ const [profiles, setProfiles] = useState([]);
+
+ // Profiles are bounded by RLS to rows the current user is allowed to
+ // see — today that's just the current user, but the dropdown keeps the
+ // shape ready for shared-team workspaces without a refactor.
+ useEffect(() => {
+ let cancelled = false;
+ const supabase = createClient();
+ supabase
+ .from("profiles")
+ .select("*")
+ .order("full_name")
+ .then(({ data, error }) => {
+ if (cancelled) return;
+ if (error) {
+ console.error("Failed to fetch profiles:", error);
+ return;
+ }
+ setProfiles((data as Profile[]) ?? []);
+ });
+ return () => {
+ cancelled = true;
+ };
+ }, []);
// 24-hour session timer
const sessionInfo = useMemo(() => {
@@ -332,6 +365,27 @@ export function MessageThread({
[conversation, onNewMessage, onUpdateMessage],
);
+ const handleAssignChange = useCallback(
+ async (agentId: string | null) => {
+ if (!conversation) return;
+
+ const supabase = createClient();
+ const { error } = await supabase
+ .from("conversations")
+ .update({ assigned_agent_id: agentId })
+ .eq("id", conversation.id);
+
+ if (error) {
+ console.error("Failed to update assignment:", error);
+ toast.error("Failed to update assignment");
+ return;
+ }
+
+ onAssignChange(conversation.id, agentId);
+ },
+ [conversation, onAssignChange],
+ );
+
// Empty state
if (!conversation || !contact) {
return (
@@ -354,6 +408,11 @@ export function MessageThread({
const currentStatus = STATUS_OPTIONS.find(
(s) => s.value === conversation.status
);
+ const assignedAgentId = conversation.assigned_agent_id ?? null;
+ const currentAssignee = profiles.find((p) => p.user_id === assignedAgentId);
+ const assignLabel = assignedAgentId
+ ? (currentAssignee?.full_name ?? "Assigned")
+ : "Assign";
return (
@@ -419,15 +478,60 @@ export function MessageThread({
- {/* Assign button */}
-
+ {/* Assign dropdown */}
+
+
+
+ {assignLabel}
+
+
+
+ {profiles.length === 0 ? (
+
+ No teammates available
+
+ ) : (
+ profiles.map((p) => {
+ const isSelected = p.user_id === assignedAgentId;
+ return (
+ handleAssignChange(p.user_id)}
+ className={cn(
+ "text-sm",
+ isSelected ? "text-violet-400" : "text-slate-300"
+ )}
+ >
+
+ {p.full_name}
+ {p.user_id === user?.id ? " (me)" : ""}
+
+ {isSelected && }
+
+ );
+ })
+ )}
+ {assignedAgentId && (
+ <>
+
+ handleAssignChange(null)}
+ className="text-sm text-slate-400"
+ >
+ Unassign
+
+ >
+ )}
+
+