feat: wire help center and article pages to live KB API
- help-center.ts: replace static HELP_ARTICLES array with async fetch* functions (fetchHelpCenterArticles, fetchHelpCenterCategories, fetchArticleBySlug); legacy sync shims kept for safety - support/index.tsx: switched from createMemo(static) to createResource(async API) with loading states - help-center/article/[slug].tsx: now fetches article from API via createResource; renders paragraphs split by double-newline; proper loading and not-found states - New server-side API routes: /api/kb/articles, /api/kb/categories, /api/kb/articles/[slug] (proxy to Rust gateway, no auth required) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
055dcd4175
commit
3209d13011
6 changed files with 287 additions and 162 deletions
|
|
@ -4,80 +4,97 @@ export type HelpArticle = {
|
|||
title: string;
|
||||
summary: string;
|
||||
categoryKey: string;
|
||||
category: string;
|
||||
role: 'ALL' | 'company' | 'jobSeeker' | 'professional' | 'customer' | 'platform';
|
||||
tags: string[];
|
||||
updatedAt: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
export const HELP_ARTICLES: HelpArticle[] = [
|
||||
{
|
||||
id: 'hc-1',
|
||||
slug: 'how-verification-works',
|
||||
title: 'How verification works',
|
||||
summary: 'Understand document review steps, approval outcomes, and timeline.',
|
||||
categoryKey: 'verification',
|
||||
role: 'ALL',
|
||||
tags: ['verification', 'documents', 'approval'],
|
||||
updatedAt: '2026-03-17T00:00:00Z',
|
||||
content: 'After signup, complete onboarding for one path and submit required documents. Admin review updates your status as pending, document required, approved, or rejected.',
|
||||
},
|
||||
{
|
||||
id: 'hc-2',
|
||||
slug: 'customer-post-requirement',
|
||||
title: 'How customers post requirements',
|
||||
summary: 'Choose profession intent, add requirements, and track verified responses.',
|
||||
categoryKey: 'requirements',
|
||||
role: 'customer',
|
||||
tags: ['customer', 'requirements'],
|
||||
updatedAt: '2026-03-17T00:00:00Z',
|
||||
content: 'Customer flow starts with selecting the professional category, then requirement details, budget, and timeline. After review, qualified professionals can respond.',
|
||||
},
|
||||
{
|
||||
id: 'hc-3',
|
||||
slug: 'professional-onboarding-guide',
|
||||
title: 'Professional onboarding guide',
|
||||
summary: 'Choose your profession, upload portfolio, submit PDF ID documents, and wait for approval.',
|
||||
categoryKey: 'onboarding',
|
||||
role: 'professional',
|
||||
tags: ['professional', 'onboarding', 'portfolio'],
|
||||
updatedAt: '2026-03-17T00:00:00Z',
|
||||
content: 'Each profession in Solid has its own onboarding and service configuration. Complete all steps and verification to unlock your full dashboard.',
|
||||
},
|
||||
];
|
||||
export type HelpCategory = {
|
||||
id: string;
|
||||
key: string;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export function listHelpCenterArticles(input: { role?: string; categoryKey?: string; q?: string }) {
|
||||
const role = String(input.role || 'ALL');
|
||||
const categoryKey = String(input.categoryKey || '').trim();
|
||||
const q = String(input.q || '').trim().toLowerCase();
|
||||
// ── API fetchers ──────────────────────────────────────────────────────────────
|
||||
|
||||
return HELP_ARTICLES.filter((article) => {
|
||||
const roleOk = role === 'ALL' || article.role === 'ALL' || article.role === role;
|
||||
const categoryOk = !categoryKey || article.categoryKey === categoryKey;
|
||||
const queryOk = !q || `${article.title} ${article.summary} ${article.tags.join(' ')}`.toLowerCase().includes(q);
|
||||
return roleOk && categoryOk && queryOk;
|
||||
});
|
||||
}
|
||||
export async function fetchHelpCenterArticles(input: {
|
||||
role?: string;
|
||||
categoryKey?: string;
|
||||
q?: string;
|
||||
}): Promise<HelpArticle[]> {
|
||||
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);
|
||||
|
||||
export function listHelpCenterCategories() {
|
||||
const keys = new Map<string, string>();
|
||||
for (const article of HELP_ARTICLES) {
|
||||
if (!keys.has(article.categoryKey)) {
|
||||
const title = article.categoryKey
|
||||
.split('-')
|
||||
.map((chunk) => chunk.charAt(0).toUpperCase() + chunk.slice(1))
|
||||
.join(' ');
|
||||
keys.set(article.categoryKey, title);
|
||||
}
|
||||
try {
|
||||
const res = await fetch(`/api/kb/articles?${params.toString()}`);
|
||||
if (!res.ok) return [];
|
||||
const data = await res.json();
|
||||
const raw: any[] = Array.isArray(data) ? data : (data.articles ?? []);
|
||||
return raw.map(normalizeArticle);
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Array.from(keys.entries()).map(([key, title], idx) => ({
|
||||
id: `cat-${idx + 1}`,
|
||||
key,
|
||||
title,
|
||||
}));
|
||||
}
|
||||
|
||||
export function getArticleBySlug(slug: string) {
|
||||
return HELP_ARTICLES.find((article) => article.slug === slug) || null;
|
||||
export async function fetchHelpCenterCategories(): Promise<HelpCategory[]> {
|
||||
try {
|
||||
const res = await fetch('/api/kb/categories');
|
||||
if (!res.ok) return [];
|
||||
const data = await res.json();
|
||||
const raw: any[] = Array.isArray(data) ? data : (data.categories ?? []);
|
||||
return raw.map((c) => ({
|
||||
id: c.id,
|
||||
key: c.slug,
|
||||
title: c.name,
|
||||
}));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchArticleBySlug(slug: string): Promise<HelpArticle | null> {
|
||||
try {
|
||||
const res = await fetch(`/api/kb/articles/${slug}`);
|
||||
if (!res.ok) return null;
|
||||
const data = await res.json();
|
||||
return normalizeArticle(data);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ── 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;
|
||||
}
|
||||
|
|
|
|||
19
src/routes/api/kb/articles.ts
Normal file
19
src/routes/api/kb/articles.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { gatewayUrl } from '~/lib/server/gateway';
|
||||
|
||||
export async function GET({ request }: { request: Request }) {
|
||||
const url = new URL(request.url);
|
||||
const upstream = gatewayUrl('/api/kb/articles' + url.search);
|
||||
try {
|
||||
const res = await fetch(upstream, { cache: 'no-store' });
|
||||
const body = await res.text();
|
||||
return new Response(body, {
|
||||
status: res.status,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
} catch (err: any) {
|
||||
return new Response(JSON.stringify({ error: err?.message || 'Gateway error' }), {
|
||||
status: 502,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
}
|
||||
18
src/routes/api/kb/articles/[slug].ts
Normal file
18
src/routes/api/kb/articles/[slug].ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { gatewayUrl } from '~/lib/server/gateway';
|
||||
|
||||
export async function GET({ params }: { params: { slug: string } }) {
|
||||
const upstream = gatewayUrl(`/api/kb/articles/${params.slug}`);
|
||||
try {
|
||||
const res = await fetch(upstream, { cache: 'no-store' });
|
||||
const body = await res.text();
|
||||
return new Response(body, {
|
||||
status: res.status,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
} catch (err: any) {
|
||||
return new Response(JSON.stringify({ error: err?.message || 'Gateway error' }), {
|
||||
status: 502,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
}
|
||||
18
src/routes/api/kb/categories.ts
Normal file
18
src/routes/api/kb/categories.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { gatewayUrl } from '~/lib/server/gateway';
|
||||
|
||||
export async function GET() {
|
||||
const upstream = gatewayUrl('/api/kb/categories');
|
||||
try {
|
||||
const res = await fetch(upstream, { cache: 'no-store' });
|
||||
const body = await res.text();
|
||||
return new Response(body, {
|
||||
status: res.status,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
} catch (err: any) {
|
||||
return new Response(JSON.stringify({ error: err?.message || 'Gateway error' }), {
|
||||
status: 502,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { A, useParams } from '@solidjs/router';
|
||||
import { createSignal, onCleanup, onMount } from 'solid-js';
|
||||
import { getArticleBySlug } from '~/lib/help-center';
|
||||
import { Show, For, createSignal, createResource, onCleanup, onMount } from 'solid-js';
|
||||
import { fetchArticleBySlug } from '~/lib/help-center';
|
||||
import PublicBackground from '~/components/PublicBackground';
|
||||
import PublicHeader from '~/components/PublicHeader';
|
||||
import PublicFooter from '~/components/PublicFooter';
|
||||
|
|
@ -15,9 +15,10 @@ function categoryTitle(input: string) {
|
|||
|
||||
export default function HelpCenterArticlePage() {
|
||||
const params = useParams();
|
||||
const article = getArticleBySlug(params.slug || '');
|
||||
const [scrollY, setScrollY] = createSignal(0);
|
||||
|
||||
const [article] = createResource(() => params.slug, fetchArticleBySlug);
|
||||
|
||||
onMount(() => {
|
||||
const onScroll = () => setScrollY(window.scrollY || 0);
|
||||
onScroll();
|
||||
|
|
@ -25,12 +26,21 @@ export default function HelpCenterArticlePage() {
|
|||
onCleanup(() => window.removeEventListener('scroll', onScroll));
|
||||
});
|
||||
|
||||
if (!article) {
|
||||
return (
|
||||
<main class="lp-main">
|
||||
<PublicBackground scrollY={scrollY()} />
|
||||
<div class="lp-content">
|
||||
<PublicHeader />
|
||||
return (
|
||||
<main class="lp-main">
|
||||
<PublicBackground scrollY={scrollY()} />
|
||||
<div class="lp-content">
|
||||
<PublicHeader />
|
||||
|
||||
<Show when={article.loading}>
|
||||
<section class="public-section scene-dark">
|
||||
<div class="container panel panel-light" style={{ 'max-width': '960px' }}>
|
||||
<p style="color:#94a3b8;padding:40px 0;text-align:center">Loading article…</p>
|
||||
</div>
|
||||
</section>
|
||||
</Show>
|
||||
|
||||
<Show when={!article.loading && !article()}>
|
||||
<section class="public-section scene-dark">
|
||||
<div class="container panel panel-light">
|
||||
<h1 class="title">Article not found</h1>
|
||||
|
|
@ -40,49 +50,55 @@ export default function HelpCenterArticlePage() {
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<PublicFooter />
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
</Show>
|
||||
|
||||
return (
|
||||
<main class="lp-main">
|
||||
<PublicBackground scrollY={scrollY()} />
|
||||
<div class="lp-content">
|
||||
<PublicHeader />
|
||||
<section class="public-section scene-dark">
|
||||
<div class="container panel panel-light" style={{ 'max-width': '960px' }}>
|
||||
<p class="eyebrow">{categoryTitle(article.categoryKey)}</p>
|
||||
<h1 class="title">{article.title}</h1>
|
||||
<p class="subtitle">{article.summary}</p>
|
||||
<Show when={!article.loading && article()}>
|
||||
{(a) => (
|
||||
<>
|
||||
<section class="public-section scene-dark">
|
||||
<div class="container panel panel-light" style={{ 'max-width': '960px' }}>
|
||||
<p class="eyebrow">{a().category || categoryTitle(a().categoryKey)}</p>
|
||||
<h1 class="title">{a().title}</h1>
|
||||
<p class="subtitle">{a().summary}</p>
|
||||
|
||||
<div class="help-article-tags" style={{ 'margin-top': '10px' }}>
|
||||
{article.tags.map((tag) => <span class="help-article-tag">{tag}</span>)}
|
||||
</div>
|
||||
<p class="note">Updated {new Date(article.updatedAt).toLocaleDateString()}</p>
|
||||
<Show when={a().tags.length > 0}>
|
||||
<div class="help-article-tags" style={{ 'margin-top': '10px' }}>
|
||||
<For each={a().tags}>{(tag) => <span class="help-article-tag">{tag}</span>}</For>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<div class="help-article-body">
|
||||
<p>{article.content}</p>
|
||||
</div>
|
||||
<p class="note">Updated {new Date(a().updatedAt).toLocaleDateString()}</p>
|
||||
|
||||
<div class="actions">
|
||||
<A class="btn" href="/help-center">Back to Help Center</A>
|
||||
<A class="btn primary" href="/auth/register?intent=customer&redirect=/users/onboarding/customer">Get Started</A>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="help-article-body">
|
||||
<For each={a().content.split('\n\n').filter(Boolean)}>
|
||||
{(para) => <p>{para}</p>}
|
||||
</For>
|
||||
</div>
|
||||
|
||||
<section class="public-section scene-dark">
|
||||
<div class="container panel panel-light" style={{ 'max-width': '960px' }}>
|
||||
<h2>Need more help?</h2>
|
||||
<p class="sub">If this article does not solve your issue, send your question with context to support.</p>
|
||||
<div class="actions">
|
||||
<a class="btn primary" href="mailto:support@nxtgauge.com?subject=Nxtgauge%20Help%20Center%20Question">Email support</a>
|
||||
<A class="btn" href="/help-center">Browse more articles</A>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div class="actions">
|
||||
<A class="btn" href="/help-center">Back to Help Center</A>
|
||||
<A class="btn primary" href="/auth/register">Get Started</A>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="public-section scene-dark">
|
||||
<div class="container panel panel-light" style={{ 'max-width': '960px' }}>
|
||||
<h2>Need more help?</h2>
|
||||
<p class="sub">
|
||||
If this article does not solve your issue, send your question with context to support.
|
||||
</p>
|
||||
<div class="actions">
|
||||
<a class="btn primary" href="mailto:support@nxtgauge.com?subject=Nxtgauge%20Help%20Center%20Question">
|
||||
Email support
|
||||
</a>
|
||||
<A class="btn" href="/help-center">Browse more articles</A>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
</Show>
|
||||
|
||||
<PublicFooter />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { A, useSearchParams } from '@solidjs/router';
|
||||
import { For, createMemo, createSignal, onCleanup, onMount } from 'solid-js';
|
||||
import { listHelpCenterArticles, listHelpCenterCategories } from '~/lib/help-center';
|
||||
import { For, Show, createMemo, createResource, createSignal, onCleanup, onMount } from 'solid-js';
|
||||
import { fetchHelpCenterArticles, fetchHelpCenterCategories } from '~/lib/help-center';
|
||||
import PublicBackground from '~/components/PublicBackground';
|
||||
import PublicHeader from '~/components/PublicHeader';
|
||||
import PublicFooter from '~/components/PublicFooter';
|
||||
|
|
@ -30,22 +30,38 @@ export default function SupportPage() {
|
|||
const category = createMemo(() => String(search.category || ''));
|
||||
const q = createMemo(() => String(search.q || ''));
|
||||
|
||||
const categories = createMemo(() => listHelpCenterCategories());
|
||||
const articles = createMemo(() =>
|
||||
listHelpCenterArticles({ role: role(), categoryKey: category() || undefined, q: q() || undefined }),
|
||||
);
|
||||
const [categories] = createResource(fetchHelpCenterCategories);
|
||||
|
||||
const articleParams = createMemo(() => ({
|
||||
role: role(),
|
||||
categoryKey: category() || undefined,
|
||||
q: q() || undefined,
|
||||
}));
|
||||
|
||||
const [articles] = createResource(articleParams, (p) => fetchHelpCenterArticles(p));
|
||||
|
||||
const visibleCategories = createMemo(() => {
|
||||
if (categories().length > 0) return categories();
|
||||
const cats = categories();
|
||||
if (cats && cats.length > 0) return cats;
|
||||
const arts = articles();
|
||||
if (!arts) return [];
|
||||
const seen = new Set<string>();
|
||||
return articles()
|
||||
return arts
|
||||
.filter((item) => {
|
||||
if (seen.has(item.categoryKey)) return false;
|
||||
seen.add(item.categoryKey);
|
||||
return true;
|
||||
})
|
||||
.map((item, idx) => ({ id: `derived-${idx + 1}`, key: item.categoryKey, title: categoryTitle(item.categoryKey) }));
|
||||
.map((item, idx) => ({
|
||||
id: `derived-${idx + 1}`,
|
||||
key: item.categoryKey,
|
||||
title: item.category || categoryTitle(item.categoryKey),
|
||||
}));
|
||||
});
|
||||
const categoryName = createMemo(() => visibleCategories().find((cat) => cat.key === category())?.title || categoryTitle(category()));
|
||||
|
||||
const categoryName = createMemo(
|
||||
() => visibleCategories().find((cat) => cat.key === category())?.title || categoryTitle(category()),
|
||||
);
|
||||
|
||||
onMount(() => {
|
||||
const onScroll = () => setScrollY(window.scrollY || 0);
|
||||
|
|
@ -66,7 +82,7 @@ export default function SupportPage() {
|
|||
<p class="eyebrow">Help Center</p>
|
||||
<h1 class="title">Get answers quickly</h1>
|
||||
<p class="subtitle">
|
||||
Articles are loaded at runtime from published Help Center management content so public users always see the latest approved guidance.
|
||||
Browse articles by role or category, or search for what you need.
|
||||
</p>
|
||||
|
||||
<form method="GET" action="/help-center" class="help-search-grid">
|
||||
|
|
@ -82,28 +98,33 @@ export default function SupportPage() {
|
|||
|
||||
<div class="help-category-head">
|
||||
<p class="help-category-kicker">Categories</p>
|
||||
{category() && (
|
||||
<Show when={category()}>
|
||||
<A
|
||||
class="help-clear-filter"
|
||||
href={`/help-center?${new URLSearchParams({ ...(role() !== 'ALL' ? { role: role() } : {}), ...(q() ? { q: q() } : {}) }).toString()}`}
|
||||
>
|
||||
Clear category filter
|
||||
</A>
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<div class="help-category-row">
|
||||
<For each={visibleCategories()}>
|
||||
{(cat) => (
|
||||
<A
|
||||
class={`help-category-pill ${category() === cat.key ? 'help-category-pill-active' : ''}`}
|
||||
href={`/help-center?${new URLSearchParams({ ...(role() !== 'ALL' ? { role: role() } : {}), ...(q() ? { q: q() } : {}), category: cat.key }).toString()}`}
|
||||
>
|
||||
{cat.title}
|
||||
</A>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
<Show when={categories.loading}>
|
||||
<div class="help-category-row" style="color:#94a3b8;font-size:14px">Loading categories…</div>
|
||||
</Show>
|
||||
<Show when={!categories.loading}>
|
||||
<div class="help-category-row">
|
||||
<For each={visibleCategories()}>
|
||||
{(cat) => (
|
||||
<A
|
||||
class={`help-category-pill ${category() === cat.key ? 'help-category-pill-active' : ''}`}
|
||||
href={`/help-center?${new URLSearchParams({ ...(role() !== 'ALL' ? { role: role() } : {}), ...(q() ? { q: q() } : {}), category: cat.key }).toString()}`}
|
||||
>
|
||||
{cat.title}
|
||||
</A>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -117,32 +138,44 @@ export default function SupportPage() {
|
|||
? 'Latest articles'
|
||||
: `${ROLE_LABELS[role()] || 'Role'} articles`}
|
||||
</h2>
|
||||
<span>{articles().length} articles</span>
|
||||
<span>{articles()?.length ?? 0} articles</span>
|
||||
</div>
|
||||
|
||||
<div class="help-article-list">
|
||||
<For each={articles()}>
|
||||
{(article) => (
|
||||
<article class="help-article-card">
|
||||
<p class="note">{categoryTitle(article.categoryKey)}</p>
|
||||
<h3>
|
||||
<A class="help-article-link" href={`/help-center/article/${article.slug}`}>{article.title}</A>
|
||||
</h3>
|
||||
<p class="help-article-summary">{article.summary}</p>
|
||||
<div class="help-article-tags">
|
||||
<For each={article.tags}>
|
||||
{(tag) => <span class="help-article-tag">{tag}</span>}
|
||||
</For>
|
||||
</div>
|
||||
<div class="help-article-meta">
|
||||
<span>Updated {new Date(article.updatedAt).toLocaleDateString()}</span>
|
||||
<A class="help-read-link" href={`/help-center/article/${article.slug}`}>Read article</A>
|
||||
</div>
|
||||
</article>
|
||||
)}
|
||||
</For>
|
||||
{articles().length === 0 && <article class="help-empty-card">No Help Center articles matched your filters.</article>}
|
||||
</div>
|
||||
<Show when={articles.loading}>
|
||||
<div style="padding:40px 0;text-align:center;color:#94a3b8">Loading articles…</div>
|
||||
</Show>
|
||||
|
||||
<Show when={!articles.loading}>
|
||||
<div class="help-article-list">
|
||||
<For each={articles() ?? []}>
|
||||
{(article) => (
|
||||
<article class="help-article-card">
|
||||
<p class="note">{article.category || categoryTitle(article.categoryKey)}</p>
|
||||
<h3>
|
||||
<A class="help-article-link" href={`/help-center/article/${article.slug}`}>
|
||||
{article.title}
|
||||
</A>
|
||||
</h3>
|
||||
<p class="help-article-summary">{article.summary}</p>
|
||||
<div class="help-article-tags">
|
||||
<For each={article.tags}>
|
||||
{(tag) => <span class="help-article-tag">{tag}</span>}
|
||||
</For>
|
||||
</div>
|
||||
<div class="help-article-meta">
|
||||
<span>Updated {new Date(article.updatedAt).toLocaleDateString()}</span>
|
||||
<A class="help-read-link" href={`/help-center/article/${article.slug}`}>
|
||||
Read article
|
||||
</A>
|
||||
</div>
|
||||
</article>
|
||||
)}
|
||||
</For>
|
||||
<Show when={(articles()?.length ?? 0) === 0}>
|
||||
<article class="help-empty-card">No Help Center articles matched your filters.</article>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
|
@ -151,10 +184,14 @@ export default function SupportPage() {
|
|||
<div>
|
||||
<p class="eyebrow">Still have questions?</p>
|
||||
<h2>Ask the support team</h2>
|
||||
<p class="sub">Share your role, what you tried, and which article you checked so support can respond faster.</p>
|
||||
<p class="sub">
|
||||
Share your role, what you tried, and which article you checked so support can respond faster.
|
||||
</p>
|
||||
</div>
|
||||
<div class="hero-actions">
|
||||
<a class="lp-primary-btn" href="mailto:support@nxtgauge.com?subject=Nxtgauge%20Help%20Center%20Question">Email support</a>
|
||||
<a class="lp-primary-btn" href="mailto:support@nxtgauge.com?subject=Nxtgauge%20Help%20Center%20Question">
|
||||
Email support
|
||||
</a>
|
||||
<A class="lp-ghost-btn" href="/contact">Contact page</A>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue