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
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:
@@ -1,5 +1,6 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { api } from '@/shared/lib/apiClient';
|
||||
import { stableArray, unwrapArray, unwrapObject } from '@/shared/lib/apiShape';
|
||||
|
||||
/**
|
||||
* useStudioProperties — Studio Pillar property listing
|
||||
@@ -8,15 +9,12 @@ export function useStudioProperties() {
|
||||
const query = useQuery({
|
||||
queryKey: ['studio-properties'],
|
||||
queryFn: async () => {
|
||||
const response = await api.get<InventoryPropertiesResponse | StudioProperty[]>('/inventory/properties');
|
||||
const rawProperties = Array.isArray(response)
|
||||
? response
|
||||
: Array.isArray(response.properties)
|
||||
? response.properties
|
||||
: [];
|
||||
const response = await api.get<unknown>('/inventory/properties?limit=100&offset=0');
|
||||
const rawProperties = unwrapArray<InventoryPropertyRecord>(response, ['properties']);
|
||||
return rawProperties.map(mapInventoryProperty);
|
||||
},
|
||||
staleTime: 120_000,
|
||||
refetchOnMount: 'always',
|
||||
});
|
||||
return { properties: query.data ?? [], isLoading: query.isLoading };
|
||||
}
|
||||
@@ -27,9 +25,10 @@ export function useStudioProperties() {
|
||||
export function useProperty(propertyId: string) {
|
||||
const query = useQuery({
|
||||
queryKey: ['property', propertyId],
|
||||
queryFn: async () => mapInventoryPropertyDetail(
|
||||
await api.get<InventoryPropertyRecord>(`/inventory/properties/${propertyId}`)
|
||||
),
|
||||
queryFn: async () => {
|
||||
const payload = await api.get<unknown>(`/inventory/properties/${propertyId}`);
|
||||
return mapInventoryPropertyDetail(unwrapObject<InventoryPropertyRecord>(payload) ?? {});
|
||||
},
|
||||
staleTime: 120_000,
|
||||
enabled: !!propertyId,
|
||||
});
|
||||
@@ -60,10 +59,6 @@ export interface PropertyDetail {
|
||||
amenities?: string[];
|
||||
}
|
||||
|
||||
interface InventoryPropertiesResponse {
|
||||
properties?: InventoryPropertyRecord[];
|
||||
}
|
||||
|
||||
interface InventoryPropertyRecord {
|
||||
property_id?: string;
|
||||
id?: string;
|
||||
@@ -77,11 +72,11 @@ interface InventoryPropertyRecord {
|
||||
area?: string;
|
||||
address?: string;
|
||||
};
|
||||
price_bands?: Array<{ label?: string; min?: number; max?: number; currency?: string }>;
|
||||
unit_mix?: Array<{ configuration?: string; count?: number; available?: number; area?: string; price?: string }>;
|
||||
price_bands?: unknown;
|
||||
unit_mix?: unknown;
|
||||
amenities?: string[];
|
||||
media?: Array<{ url?: string; thumbnail_url?: string }>;
|
||||
images?: string[];
|
||||
media?: unknown;
|
||||
images?: unknown;
|
||||
thumbnailUrl?: string;
|
||||
availableUnits?: number;
|
||||
}
|
||||
@@ -92,11 +87,12 @@ function mapLocation(location: InventoryPropertyRecord['location']): string {
|
||||
}
|
||||
|
||||
function mapPriceRange(priceBands: InventoryPropertyRecord['price_bands']): string | undefined {
|
||||
if (!Array.isArray(priceBands) || priceBands.length === 0) return undefined;
|
||||
const mins = priceBands.map((band) => Number(band.min)).filter(Number.isFinite);
|
||||
const maxes = priceBands.map((band) => Number(band.max)).filter(Number.isFinite);
|
||||
if (!mins.length && !maxes.length) return priceBands[0]?.label;
|
||||
const currency = priceBands[0]?.currency ?? 'INR';
|
||||
const bands = stableArray<{ label?: string; min?: number; max?: number; currency?: string }>(priceBands);
|
||||
if (bands.length === 0) return undefined;
|
||||
const mins = bands.map((band) => Number(band.min)).filter(Number.isFinite);
|
||||
const maxes = bands.map((band) => Number(band.max)).filter(Number.isFinite);
|
||||
if (!mins.length && !maxes.length) return bands[0]?.label;
|
||||
const currency = bands[0]?.currency ?? 'INR';
|
||||
const min = mins.length ? Math.min(...mins) : undefined;
|
||||
const max = maxes.length ? Math.max(...maxes) : undefined;
|
||||
if (min !== undefined && max !== undefined) return `${currency} ${min} - ${max}`;
|
||||
@@ -105,9 +101,11 @@ function mapPriceRange(priceBands: InventoryPropertyRecord['price_bands']): stri
|
||||
}
|
||||
|
||||
function mapInventoryProperty(record: InventoryPropertyRecord): StudioProperty {
|
||||
const mediaThumb = Array.isArray(record.media) ? record.media.find((item) => item.thumbnail_url || item.url) : undefined;
|
||||
const availableUnits = Array.isArray(record.unit_mix)
|
||||
? record.unit_mix.reduce((sum, unit) => sum + Number(unit.available ?? unit.count ?? 0), 0)
|
||||
const media = stableArray<{ url?: string; thumbnail_url?: string }>(record.media);
|
||||
const unitMix = stableArray<{ configuration?: string; count?: number; available?: number; area?: string; price?: string }>(record.unit_mix);
|
||||
const mediaThumb = media.find((item) => item.thumbnail_url || item.url);
|
||||
const availableUnits = unitMix.length
|
||||
? unitMix.reduce((sum, unit) => sum + Number(unit.available ?? unit.count ?? 0), 0)
|
||||
: record.availableUnits;
|
||||
const stableId = record.property_id ?? record.id ?? record.project_name ?? record.name ?? 'unknown-property';
|
||||
return {
|
||||
@@ -122,12 +120,12 @@ function mapInventoryProperty(record: InventoryPropertyRecord): StudioProperty {
|
||||
|
||||
function mapInventoryPropertyDetail(record: InventoryPropertyRecord): PropertyDetail {
|
||||
const base = mapInventoryProperty(record);
|
||||
const primaryUnit = Array.isArray(record.unit_mix) ? record.unit_mix[0] : undefined;
|
||||
const images = Array.isArray(record.images)
|
||||
? record.images
|
||||
: Array.isArray(record.media)
|
||||
? record.media.map((item) => item.url).filter((url): url is string => Boolean(url))
|
||||
: [];
|
||||
const unitMix = stableArray<{ configuration?: string; area?: string; price?: string }>(record.unit_mix);
|
||||
const media = stableArray<{ url?: string }>(record.media);
|
||||
const primaryUnit = unitMix[0];
|
||||
const images = stableArray<string>(record.images).length
|
||||
? stableArray<string>(record.images)
|
||||
: media.map((item) => item.url).filter((url): url is string => Boolean(url));
|
||||
return {
|
||||
...base,
|
||||
config: primaryUnit?.configuration ?? record.property_type ?? 'Mixed configuration',
|
||||
|
||||
Reference in New Issue
Block a user