Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
67 lines
1.6 KiB
TypeScript
67 lines
1.6 KiB
TypeScript
/** Map.ir reverse geocode (client; NEXT_PUBLIC_MAP_API_KEY). https://map.ir/reverse/no */
|
|
|
|
import { texts } from '@/texts'
|
|
|
|
const REVERSE_BASE = 'https://map.ir/reverse/no'
|
|
const v = texts.validation.map
|
|
|
|
/** Map.ir response address field only (official API) */
|
|
function pickAddressFromBody(data: unknown): string | null {
|
|
if (!data || typeof data !== 'object') return null
|
|
const address = (data as Record<string, unknown>).address
|
|
|
|
if (typeof address === 'string' && address.trim()) return address.trim()
|
|
|
|
return null
|
|
}
|
|
|
|
/** @returns `address` field or empty string; throws Error on failure */
|
|
export async function reverseGeocodeMapIr(lat: number, lng: number, apiKey: string): Promise<string> {
|
|
if (!Number.isFinite(lat) || !Number.isFinite(lng)) {
|
|
throw new Error(v.invalidCoords)
|
|
}
|
|
|
|
const key = apiKey.trim()
|
|
|
|
if (!key) {
|
|
throw new Error(v.mapKeyMissing)
|
|
}
|
|
|
|
const url = `${REVERSE_BASE}?lat=${encodeURIComponent(String(lat))}&lon=${encodeURIComponent(String(lng))}`
|
|
|
|
let res: Response
|
|
|
|
try {
|
|
res = await fetch(url, {
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'x-api-key': key,
|
|
},
|
|
})
|
|
} catch {
|
|
throw new Error(v.mapServiceError)
|
|
}
|
|
|
|
let data: unknown
|
|
|
|
try {
|
|
data = await res.json()
|
|
} catch {
|
|
throw new Error(v.mapInvalidResponse)
|
|
}
|
|
|
|
if (!res.ok) {
|
|
throw new Error(v.mapAddressFailed)
|
|
}
|
|
|
|
const statusField = (data as { status?: unknown })?.status
|
|
|
|
if (typeof statusField === 'number' && statusField >= 400) {
|
|
throw new Error(v.mapAddressFailed)
|
|
}
|
|
|
|
const picked = pickAddressFromBody(data)
|
|
|
|
return picked ?? ''
|
|
}
|