mirror of
https://github.com/melgarafael/DeskcommCRM.git
synced 2026-10-02 01:28:34 +08:00
GET /api/v1/admin/inbox/conversations: requirePlatformAdmin, service-role cross-tenant query joining organizations + contacts + last_message_preview + unread_count. Cursor pagination by last_inbound_at desc. GET /api/v1/admin/inbox/conversations/[id]: detail with last 50 messages. useAdminInboxRealtime subscribes to postgres_changes on messages INSERT (no org filter — admin sees all tenants). TanStack invalidation on event. UI: 3-col layout reusing MessageBubble from EPIC-03. TenantBadge in every list item + thread header. Composer disabled (read-only) — agent response only via Impersonate (S-11.07). Audit: platform_admin.inbox_listed + platform_admin.conversation_viewed (lightweight, tenant_id only — no PII). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
69 lines
1.8 KiB
TypeScript
69 lines
1.8 KiB
TypeScript
"use client";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { apiClient } from "@/lib/api/client";
|
|
import type { Message } from "@/lib/types/messaging";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Types
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface AdminConversationDetail {
|
|
id: string;
|
|
organization_id: string;
|
|
contact_id: string | null;
|
|
channel: string;
|
|
status: string;
|
|
assigned_to_user_id: string | null;
|
|
last_inbound_at: string | null;
|
|
last_message_at: string | null;
|
|
last_message_preview: string | null;
|
|
unread_count_for_assignee: number;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
export interface AdminOrganizationInfo {
|
|
id: string;
|
|
display_name: string;
|
|
slug: string;
|
|
status: string;
|
|
}
|
|
|
|
export interface AdminContactInfo {
|
|
id: string;
|
|
name: string | null;
|
|
phone_number: string | null;
|
|
email: string | null;
|
|
is_anonymized: boolean;
|
|
is_blocked: boolean;
|
|
}
|
|
|
|
export interface AdminConversationDetailResponse {
|
|
conversation: AdminConversationDetail;
|
|
organization: AdminOrganizationInfo | null;
|
|
contact: AdminContactInfo | null;
|
|
messages: Message[];
|
|
}
|
|
|
|
interface ApiResponse {
|
|
data: AdminConversationDetailResponse;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Hook
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function useAdminConversation(conversationId: string | null) {
|
|
return useQuery({
|
|
queryKey: ["admin", "inbox", conversationId] as const,
|
|
enabled: !!conversationId,
|
|
queryFn: async () => {
|
|
const res = await apiClient.get<ApiResponse>(
|
|
`/api/v1/admin/inbox/conversations/${conversationId}`,
|
|
);
|
|
return res.data;
|
|
},
|
|
staleTime: 10_000,
|
|
});
|
|
}
|