145 lines
3.6 KiB
TypeScript
145 lines
3.6 KiB
TypeScript
/**
|
|
* Helpers for Repair/Services page CMS + catalog device types.
|
|
*/
|
|
import type { LucideIcon } from 'lucide-react';
|
|
import {
|
|
Award,
|
|
Clock,
|
|
Heart,
|
|
HelpCircle,
|
|
Laptop,
|
|
ShieldCheck,
|
|
Smartphone,
|
|
Tablet,
|
|
ThumbsUp,
|
|
Watch,
|
|
Wrench,
|
|
} from 'lucide-react';
|
|
import {
|
|
LayoutItem,
|
|
normalizeLayoutPayload,
|
|
parseMetadata,
|
|
resolveIsActive,
|
|
} from '@/lib/layoutConfig';
|
|
|
|
export const SERVICE_REGIONS = [
|
|
'service_hero',
|
|
'service_how_it_works',
|
|
'service_why_us',
|
|
'service_faq',
|
|
] as const;
|
|
|
|
export type ServiceRegion = (typeof SERVICE_REGIONS)[number];
|
|
|
|
const DEVICE_ICON_MAP: Record<string, LucideIcon> = {
|
|
mobile: Smartphone,
|
|
phone: Smartphone,
|
|
smartphone: Smartphone,
|
|
laptop: Laptop,
|
|
notebook: Laptop,
|
|
tablet: Tablet,
|
|
smartwatch: Watch,
|
|
watch: Watch,
|
|
wearable: Watch,
|
|
};
|
|
|
|
const WHY_ICON_MAP: Record<string, LucideIcon> = {
|
|
ShieldCheck,
|
|
Clock,
|
|
ThumbsUp,
|
|
Wrench,
|
|
Award,
|
|
Heart,
|
|
HelpCircle,
|
|
};
|
|
|
|
const DEFAULT_DEVICE_TYPES = ['mobile', 'laptop', 'tablet', 'smartwatch'];
|
|
|
|
export function normalizeServiceLayout(raw: unknown): LayoutItem[] {
|
|
return normalizeLayoutPayload(raw)
|
|
.map((item) => ({
|
|
...item,
|
|
metadata: parseMetadata(item),
|
|
metadata_json: parseMetadata(item),
|
|
}))
|
|
.sort(
|
|
(a, b) =>
|
|
(Number(a.display_order) || 0) - (Number(b.display_order) || 0)
|
|
);
|
|
}
|
|
|
|
export function findServiceSection(
|
|
layout: LayoutItem[],
|
|
region: ServiceRegion
|
|
): LayoutItem | null {
|
|
return (
|
|
layout.find((s) => {
|
|
const key =
|
|
parseMetadata(s).section_key ||
|
|
s.region ||
|
|
s.type ||
|
|
'';
|
|
return key === region || s.region === region || s.type === region;
|
|
}) || null
|
|
);
|
|
}
|
|
|
|
export function isServiceSectionVisible(
|
|
layout: LayoutItem[],
|
|
region: ServiceRegion
|
|
): boolean {
|
|
const section = findServiceSection(layout, region);
|
|
if (!section) return true; // show defaults when CMS row missing
|
|
return resolveIsActive(section);
|
|
}
|
|
|
|
export function formatDeviceTypeLabel(id: string): string {
|
|
const known: Record<string, string> = {
|
|
mobile: 'Mobile',
|
|
laptop: 'Laptop',
|
|
tablet: 'Tablet',
|
|
smartwatch: 'Smartwatch',
|
|
phone: 'Phone',
|
|
smartphone: 'Smartphone',
|
|
watch: 'Watch',
|
|
};
|
|
if (known[id]) return known[id];
|
|
return String(id || '')
|
|
.replace(/[_-]+/g, ' ')
|
|
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
}
|
|
|
|
export function getDeviceTypeIcon(id: string): LucideIcon {
|
|
return DEVICE_ICON_MAP[String(id || '').toLowerCase()] || Wrench;
|
|
}
|
|
|
|
export function getWhyUsIcon(name?: string): LucideIcon {
|
|
if (!name) return ShieldCheck;
|
|
return WHY_ICON_MAP[name] || ShieldCheck;
|
|
}
|
|
|
|
export function normalizeDeviceTypes(raw: unknown): string[] {
|
|
if (!Array.isArray(raw) || raw.length === 0) return DEFAULT_DEVICE_TYPES;
|
|
const ids = raw
|
|
.map((item) => {
|
|
if (typeof item === 'string') return item.trim().toLowerCase();
|
|
if (item && typeof item === 'object') {
|
|
const o = item as Record<string, unknown>;
|
|
const id = o.id ?? o.value ?? o.slug ?? o.device_type ?? o.name;
|
|
return String(id || '')
|
|
.trim()
|
|
.toLowerCase();
|
|
}
|
|
return '';
|
|
})
|
|
.filter(Boolean);
|
|
return ids.length > 0 ? Array.from(new Set(ids)) : DEFAULT_DEVICE_TYPES;
|
|
}
|
|
|
|
export function resolveHeroImage(section: LayoutItem | null): string {
|
|
if (!section) return '';
|
|
const meta = parseMetadata(section);
|
|
return String(
|
|
meta.image_url || section.image_url || section.image || ''
|
|
).trim();
|
|
}
|