fix: harden pipeline navigation data loading
Some checks failed
Velocity-OS Deployment Pipeline / lint (push) Has been cancelled
Velocity-OS Deployment Pipeline / build-and-push (agents) (push) Has been cancelled
Velocity-OS Deployment Pipeline / build-and-push (core) (push) Has been cancelled
Velocity-OS Deployment Pipeline / build-and-push (media-engine) (push) Has been cancelled
Velocity-OS Deployment Pipeline / build-and-push (webos) (push) Has been cancelled
Velocity-OS Deployment Pipeline / sign-images (agents) (push) Has been cancelled
Velocity-OS Deployment Pipeline / sign-images (core) (push) Has been cancelled
Velocity-OS Deployment Pipeline / sign-images (media-engine) (push) Has been cancelled
Velocity-OS Deployment Pipeline / sign-images (webos) (push) Has been cancelled
Velocity-OS Deployment Pipeline / notify-ingress (push) Has been cancelled

This commit is contained in:
2026-05-02 13:05:26 +05:30
parent 08a19db035
commit 58628dac35
7 changed files with 169 additions and 57 deletions

View File

@@ -1,5 +1,6 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { api } from '@/shared/lib/apiClient';
import { stableArray, unwrapObject } from '@/shared/lib/apiShape';
/**
* useClient360 — fetch unified client entity
@@ -9,8 +10,8 @@ export function useClient360(personId: string) {
const query = useQuery({
queryKey: ['client360', personId],
queryFn: async () => {
const payload = await api.get<Wrapped<Client360Snapshot>>(`/crm/client-360/${personId}`);
return mapClient360(payload.data);
const payload = await api.get<unknown>(`/crm/client-360/${personId}`);
return mapClient360(normalizeSnapshot(payload));
},
staleTime: 30_000,
enabled: !!personId,
@@ -26,8 +27,8 @@ export function useConversations(personId: string) {
const query = useQuery({
queryKey: ['conversations', personId],
queryFn: async () => {
const payload = await api.get<Wrapped<Client360Snapshot>>(`/crm/client-360/${personId}`);
return mapConversationEvents(payload.data);
const payload = await api.get<unknown>(`/crm/client-360/${personId}`);
return mapConversationEvents(normalizeSnapshot(payload));
},
staleTime: 10_000,
enabled: !!personId,
@@ -47,8 +48,8 @@ export function useClientProperties(personId: string) {
const query = useQuery({
queryKey: ['client-properties', personId],
queryFn: async () => {
const payload = await api.get<Wrapped<Client360Snapshot>>(`/crm/client-360/${personId}`);
return mapPropertyInterests(payload.data);
const payload = await api.get<unknown>(`/crm/client-360/${personId}`);
return mapPropertyInterests(normalizeSnapshot(payload));
},
staleTime: 60_000,
enabled: !!personId,
@@ -64,8 +65,8 @@ export function useClientTasks(personId: string) {
const query = useQuery({
queryKey: ['client-tasks', personId],
queryFn: async () => {
const payload = await api.get<Wrapped<Client360Snapshot>>(`/crm/client-360/${personId}`);
return mapTasks(payload.data);
const payload = await api.get<unknown>(`/crm/client-360/${personId}`);
return mapTasks(normalizeSnapshot(payload));
},
staleTime: 30_000,
enabled: !!personId,
@@ -144,11 +145,6 @@ interface Task {
isAIGenerated?: boolean;
}
interface Wrapped<T> {
status: string;
data: T;
}
interface Client360Snapshot {
client_ref?: string;
identity?: {
@@ -203,6 +199,22 @@ interface Client360Snapshot {
recommended_next_actions?: string[];
}
function normalizeSnapshot(payload: unknown): Client360Snapshot {
const snapshot = unwrapObject<Client360Snapshot>(payload);
if (!snapshot) return {};
return {
...snapshot,
identity: snapshot.identity ?? {},
current_lead: snapshot.current_lead ?? {},
active_opportunities: stableArray(snapshot.active_opportunities),
recent_interactions: stableArray(snapshot.recent_interactions),
property_interests: stableArray(snapshot.property_interests),
tasks: stableArray(snapshot.tasks),
risk_flags: stableArray(snapshot.risk_flags),
recommended_next_actions: stableArray(snapshot.recommended_next_actions),
};
}
function scoreToPercent(value: number | null | undefined): number {
const safe = Number(value ?? 0);
const pct = safe <= 1 ? safe * 100 : safe;