Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | 1x 12x 12x 4x 8x 1x 7x 1x 16x 12x 1x 5x 5x 12x 3x 2x | /**
* Resolves legacy `/interior-designers/*` paths to their live `/interiors/*`
* equivalents. The URL prefix was renamed pre-launch (see
* docs/wip/plans/seo-aeo-hyderabad.md); these 301s catch any external link,
* bookmark, or citation that still uses the old (competitor-canonical) pattern.
*
* Hyderabad is the only city with `/interiors` content today, so every fallback
* lands on the Hyderabad city hub — never on a known 404.
*/
const HUB = "/interiors/hyderabad";
/**
* Minimal structural view of the `getSeoCity` result — only the slug lists this
* resolver needs to classify a path segment. Decoupled from the full client
* return type so the resolver stays a pure, easily-tested function.
*/
export interface SeoCityClassification {
localities: { slug: string | null; zoneSlug: string | null }[];
zones: { slug: string | null }[];
services: { slug: string }[];
bhkTypes: { slug: string }[];
}
/**
* @param segments path parts after `/interior-designers/`, already lowercased
* and stripped of empties (e.g. `["hyderabad", "gachibowli"]`)
* @param cityData `getSeoCity("hyderabad")` result, or `null` when the city is
* not Hyderabad or the lookup failed — both collapse to the hub fallback.
* @returns an absolute `/interiors/*` path. Always a live route; never 301→404.
*/
export function resolveInteriorDesignersRedirect(
segments: string[],
cityData: SeoCityClassification | null,
): string {
const [city, seg, locality] = segments;
// No city, a non-Hyderabad city (no content), or a failed lookup → hub.
if (city !== "hyderabad" || !cityData) {
return HUB;
}
// `/interior-designers/hyderabad`
if (!seg) {
return HUB;
}
// `/interior-designers/hyderabad/<zone>/<locality>` already zone-shaped:
// pass through; the destination route validates and 404s a bad combo.
if (locality) {
return `${HUB}/${seg}/${locality}`;
}
// Single segment: locality URLs lack the zone, so inject it from cityData.
// Optional-chain every list: a partial API payload must degrade to the hub
// fallback, never throw and crash the SSR redirect.
const matchedLocality = cityData.localities?.find((l) => l.slug === seg);
if (matchedLocality?.zoneSlug) {
return `${HUB}/${matchedLocality.zoneSlug}/${seg}`;
}
// Zone / service / bhk all live directly under the city hub.
const isSingleSegmentRoute =
cityData.zones?.some((z) => z.slug === seg) ||
cityData.services?.some((s) => s.slug === seg) ||
cityData.bhkTypes?.some((b) => b.slug === seg);
if (isSingleSegmentRoute) {
return `${HUB}/${seg}`;
}
// Unknown segment (e.g. a room category like "kitchen", or a stale slug).
return HUB;
}
|