All files / src/lib/seo live-directory.ts

100% Statements 10/10
100% Branches 4/4
100% Functions 4/4
100% Lines 9/9

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                            5x                                     10x 10x 8x   2x   3x       14x 39x     18x      
import type { MarketplaceCity } from "@interioring/api-types";
 
/**
 * Public city directory for pages that must follow the live rule.
 * The API is the authority (`GET /api/marketplace/seo/cities`). When that
 * call fails or returns nothing, Hyderabad is the fallback — it is pinned
 * live, so the footer and sitemap never go blank during an API blip.
 */
 
/** `MarketplaceCity` with `state` and `publishedProCount` optional, so the
 *  hard-coded fallback and older payloads still fit. */
export type PublicCity = Omit<MarketplaceCity, "state" | "publishedProCount"> &
	Partial<Pick<MarketplaceCity, "state" | "publishedProCount">>;
 
export const HYDERABAD_FALLBACK: PublicCity = {
	id: "hyderabad",
	slug: "hyderabad",
	publicName: "Hyderabad",
	state: "Telangana",
	sortOrder: 1,
	marketplaceStatus: "live",
	publishedProCount: 0,
	pricesApproved: true,
	wikidataId: "Q1361",
};
 
type CitiesClient = {
	getSeoCities: () => Promise<{ cities: PublicCity[] }>;
};
 
export async function loadPublicCities(
	api: CitiesClient,
): Promise<PublicCity[]> {
	try {
		const res = await api.getSeoCities();
		if (res.cities?.length) return res.cities;
	} catch (err) {
		console.error("[cities] getSeoCities failed:", err);
	}
	return [HYDERABAD_FALLBACK];
}
 
export function liveCities(cities: PublicCity[]): PublicCity[] {
	return cities
		.filter((city) => city.marketplaceStatus === "live")
		.sort(
			(a, b) =>
				a.sortOrder - b.sortOrder || a.publicName.localeCompare(b.publicName),
		);
}