fix: complete Velocity-OS feature migration wiring
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 16:39:10 +05:30
parent 58628dac35
commit 8b2d836589
13 changed files with 779 additions and 381 deletions

View File

@@ -81,6 +81,17 @@ interface InventoryPropertyRecord {
availableUnits?: number;
}
interface InventoryMediaRecord {
url?: string;
thumbnail_url?: string;
thumbnailUrl?: string;
media_type?: string;
type?: string;
kind?: string;
label?: string;
metadata?: Record<string, unknown>;
}
function mapLocation(location: InventoryPropertyRecord['location']): string {
if (!location) return 'Location pending';
return [location.district, location.area, location.city, location.address].filter(Boolean).join(', ') || 'Location pending';
@@ -101,9 +112,9 @@ function mapPriceRange(priceBands: InventoryPropertyRecord['price_bands']): stri
}
function mapInventoryProperty(record: InventoryPropertyRecord): StudioProperty {
const media = stableArray<{ url?: string; thumbnail_url?: string }>(record.media);
const media = normalizeMedia(record);
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 mediaThumb = media.find((item) => item.thumbnail_url || item.thumbnailUrl || item.url);
const availableUnits = unitMix.length
? unitMix.reduce((sum, unit) => sum + Number(unit.available ?? unit.count ?? 0), 0)
: record.availableUnits;
@@ -113,7 +124,7 @@ function mapInventoryProperty(record: InventoryPropertyRecord): StudioProperty {
name: record.project_name ?? record.name ?? 'Unnamed property',
location: mapLocation(record.location),
priceRange: mapPriceRange(record.price_bands),
thumbnailUrl: record.thumbnailUrl ?? mediaThumb?.thumbnail_url ?? mediaThumb?.url,
thumbnailUrl: record.thumbnailUrl ?? mediaThumb?.thumbnail_url ?? mediaThumb?.thumbnailUrl ?? mediaThumb?.url,
availableUnits,
};
}
@@ -121,18 +132,62 @@ function mapInventoryProperty(record: InventoryPropertyRecord): StudioProperty {
function mapInventoryPropertyDetail(record: InventoryPropertyRecord): PropertyDetail {
const base = mapInventoryProperty(record);
const unitMix = stableArray<{ configuration?: string; area?: string; price?: string }>(record.unit_mix);
const media = stableArray<{ url?: string }>(record.media);
const media = normalizeMedia(record);
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));
const imageUrls = stableArray<string>(record.images);
const mediaImages = media
.filter((item) => isImageMedia(item))
.map((item) => item.url ?? item.thumbnail_url ?? item.thumbnailUrl)
.filter((url): url is string => Boolean(url));
const images = imageUrls.length ? imageUrls : mediaImages;
const modelUrl = media.find((item) => isModelMedia(item))?.url;
const interiorImageUrl = findInteriorImage(media) ?? images[0] ?? base.thumbnailUrl;
return {
...base,
config: primaryUnit?.configuration ?? record.property_type ?? 'Mixed configuration',
area: primaryUnit?.area ?? 'Area pending',
price: primaryUnit?.price ?? base.priceRange ?? 'Price pending',
description: `${record.developer_name ?? 'Developer'} property in ${base.location}`,
interiorImageUrl,
modelUrl,
images,
amenities: Array.isArray(record.amenities) ? record.amenities : [],
};
}
function normalizeMedia(record: InventoryPropertyRecord): InventoryMediaRecord[] {
const media = stableArray<InventoryMediaRecord>(record.media);
const imageRecords = stableArray<InventoryMediaRecord>(record.images);
const imageStrings: InventoryMediaRecord[] = stableArray<string>(record.images).map((url) => ({
url,
media_type: 'image',
}));
return [...media, ...imageRecords, ...imageStrings].filter((item) => Boolean(item.url ?? item.thumbnail_url ?? item.thumbnailUrl));
}
function mediaKind(item: InventoryMediaRecord): string {
return [item.media_type, item.type, item.kind, item.label]
.filter(Boolean)
.join(' ')
.toLowerCase();
}
function isImageMedia(item: InventoryMediaRecord): boolean {
const url = (item.url ?? item.thumbnail_url ?? item.thumbnailUrl ?? '').toLowerCase();
const kind = mediaKind(item);
return kind.includes('image') || kind.includes('photo') || /\.(png|jpe?g|webp|gif|avif)(\?|$)/.test(url);
}
function isModelMedia(item: InventoryMediaRecord): boolean {
const url = (item.url ?? '').toLowerCase();
const kind = mediaKind(item);
return kind.includes('model') || kind.includes('3d') || kind.includes('vr') || /\.(glb|gltf)(\?|$)/.test(url);
}
function findInteriorImage(media: InventoryMediaRecord[]): string | undefined {
const item = media.find((entry) => {
const kind = mediaKind(entry);
return isImageMedia(entry) && (kind.includes('interior') || kind.includes('room') || kind.includes('staging'));
});
return item?.url ?? item?.thumbnail_url ?? item?.thumbnailUrl;
}