import { HELP_CENTER_SEED_ARTICLES, HELP_CENTER_SEED_CATEGORIES } from '~/data/help-center-seed'; export type HelpArticle = { id: string; slug: string; title: string; summary: string; categoryKey: string; category: string; role: 'ALL' | 'company' | 'jobSeeker' | 'professional' | 'customer' | 'platform'; tags: string[]; updatedAt: string; content: string; }; export type HelpCategory = { id: string; key: string; title: string; }; // ── API fetchers ────────────────────────────────────────────────────────────── export async function fetchHelpCenterArticles(input: { role?: string; categoryKey?: string; q?: string; }): Promise { const params = new URLSearchParams(); if (input.role && input.role !== 'ALL') params.set('role', input.role); if (input.categoryKey) params.set('category', input.categoryKey); if (input.q) params.set('q', input.q); try { const res = await fetch(`/api/kb/articles?${params.toString()}`); if (!res.ok) return filterArticles(HELP_CENTER_SEED_ARTICLES as HelpArticle[], input); const data = await res.json(); const raw: any[] = Array.isArray(data) ? data : (data.articles ?? []); let items = raw.map(normalizeArticle); // Fallback: when backend search returns sparse/empty data, apply local filtering // so users can still find articles by simple keywords. if (input.q && items.length === 0) { const allRes = await fetch('/api/kb/articles'); if (allRes.ok) { const allData = await allRes.json(); const allRaw: any[] = Array.isArray(allData) ? allData : (allData.articles ?? []); items = allRaw.map(normalizeArticle); } } if (items.length === 0) { items = HELP_CENTER_SEED_ARTICLES as HelpArticle[]; } return filterArticles(items, input); } catch { return filterArticles(HELP_CENTER_SEED_ARTICLES as HelpArticle[], input); } } export async function fetchHelpCenterCategories(): Promise { try { const res = await fetch('/api/kb/categories'); if (!res.ok) return HELP_CENTER_SEED_CATEGORIES; const data = await res.json(); const raw: any[] = Array.isArray(data) ? data : (data.categories ?? []); const mapped = raw.map((c) => ({ id: c.id, key: c.slug ?? c.key, title: c.name ?? c.title, })); return mapped.length > 0 ? mapped : HELP_CENTER_SEED_CATEGORIES; } catch { return HELP_CENTER_SEED_CATEGORIES; } } export async function fetchArticleBySlug(slug: string): Promise { try { const res = await fetch(`/api/kb/articles/${slug}`); if (!res.ok) return (HELP_CENTER_SEED_ARTICLES as HelpArticle[]).find((a) => a.slug === slug) ?? null; const data = await res.json(); const normalized = normalizeArticle(data); if (!normalized.slug) { return (HELP_CENTER_SEED_ARTICLES as HelpArticle[]).find((a) => a.slug === slug) ?? null; } return normalized; } catch { return (HELP_CENTER_SEED_ARTICLES as HelpArticle[]).find((a) => a.slug === slug) ?? null; } } export async function fetchRelatedArticles(input: { article: HelpArticle; limit?: number; }): Promise { try { const res = await fetch('/api/kb/articles'); if (!res.ok) return pickRelated(HELP_CENTER_SEED_ARTICLES as HelpArticle[], input.article, input.limit ?? 4); const data = await res.json(); const raw: any[] = Array.isArray(data) ? data : (data.articles ?? []); const all = raw.map(normalizeArticle); if (all.length === 0) return pickRelated(HELP_CENTER_SEED_ARTICLES as HelpArticle[], input.article, input.limit ?? 4); return pickRelated(all, input.article, input.limit ?? 4); } catch { return pickRelated(HELP_CENTER_SEED_ARTICLES as HelpArticle[], input.article, input.limit ?? 4); } } // ── Normalizer ──────────────────────────────────────────────────────────────── function normalizeArticle(raw: any): HelpArticle { return { id: raw.id ?? '', slug: raw.slug ?? '', title: raw.title ?? '', summary: raw.summary ?? raw.content?.slice(0, 160) ?? '', categoryKey: raw.categoryKey ?? raw.category_key ?? raw.categorySlug ?? '', category: raw.category ?? raw.categoryKey ?? '', role: (raw.role ?? 'ALL') as HelpArticle['role'], tags: Array.isArray(raw.tags) ? raw.tags : [], updatedAt: raw.updatedAt ?? raw.updated_at ?? new Date().toISOString(), content: raw.content ?? raw.body ?? '', }; } // ── Legacy sync shims (kept for any remaining call sites) ───────────────────── // These return empty data synchronously — pages should use the async fetch* functions above. export function listHelpCenterArticles(_input: { role?: string; categoryKey?: string; q?: string }): HelpArticle[] { return []; } export function listHelpCenterCategories(): HelpCategory[] { return []; } export function getArticleBySlug(_slug: string): HelpArticle | null { return null; } function articleMatchesQuery(article: HelpArticle, needle: string): boolean { const haystack = [article.title, article.summary, article.content, article.category, article.categoryKey, article.tags.join(' ')].join(' ').toLowerCase(); return haystack.includes(needle); } function filterArticles(items: HelpArticle[], input: { role?: string; categoryKey?: string; q?: string }): HelpArticle[] { let filtered = items; if (input.role && input.role !== 'ALL') { const roleNeedle = input.role.toLowerCase(); filtered = filtered.filter((a) => a.role === 'ALL' || String(a.role).toLowerCase() === roleNeedle); } if (input.categoryKey) { const needle = input.categoryKey.toLowerCase(); filtered = filtered.filter((a) => [a.categoryKey, a.category].some((v) => (v || '').toLowerCase().includes(needle))); } if (input.q) { const needle = input.q.toLowerCase(); filtered = filtered.filter((a) => articleMatchesQuery(a, needle)); } return filtered; } function pickRelated(allItems: HelpArticle[], current: HelpArticle, limit: number): HelpArticle[] { const all = allItems.filter((a) => a.slug !== current.slug); const sameCategory = all.filter((a) => { const left = `${a.categoryKey}|${a.category}`.toLowerCase(); const right = `${current.categoryKey}|${current.category}`.toLowerCase(); return left && right && left === right; }); const fallback = all.filter((a) => !sameCategory.some((x) => x.slug === a.slug)); return [...sameCategory, ...fallback].slice(0, limit); }