feat(management-parity): replace employee/kb alias routes with real solid pages
This commit is contained in:
parent
cd780906b1
commit
95af9c2788
8 changed files with 219 additions and 56 deletions
|
|
@ -58,7 +58,11 @@ function isActive(e: Employee): boolean {
|
||||||
return s === 'ACTIVE' || s === 'TRUE' || s === '1';
|
return s === 'ACTIVE' || s === 'TRUE' || s === '1';
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EmployeesPage() {
|
type EmployeesPageProps = {
|
||||||
|
initialView?: 'list' | 'create';
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function EmployeesPage(props: EmployeesPageProps = {}) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
|
|
@ -66,7 +70,7 @@ export default function EmployeesPage() {
|
||||||
const [roles] = createResource(loadRoles);
|
const [roles] = createResource(loadRoles);
|
||||||
|
|
||||||
// tabs: list | create
|
// tabs: list | create
|
||||||
const [view, setView] = createSignal<'list' | 'create'>('list');
|
const [view, setView] = createSignal<'list' | 'create'>(props.initialView || 'list');
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (searchParams.tab === 'create') {
|
if (searchParams.tab === 'create') {
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,5 @@
|
||||||
import { onMount } from 'solid-js';
|
import EmployeesPage from '~/routes/admin/employees';
|
||||||
import { useNavigate } from '@solidjs/router';
|
|
||||||
import AdminShell from '~/components/AdminShell';
|
|
||||||
|
|
||||||
export default function CreateEmployeeAliasPage() {
|
export default function CreateEmployeePage() {
|
||||||
const navigate = useNavigate();
|
return <EmployeesPage initialView="create" />;
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
navigate('/admin/employees?tab=create', { replace: true });
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AdminShell>
|
|
||||||
<div class="card">
|
|
||||||
<p class="notice">Redirecting to employee create form...</p>
|
|
||||||
</div>
|
|
||||||
</AdminShell>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,12 @@ async function loadArticles(): Promise<KbArticle[]> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function KbPage() {
|
type KbPageProps = {
|
||||||
const [tab, setTab] = createSignal<'categories' | 'articles' | 'create-article'>('categories');
|
initialTab?: 'categories' | 'articles' | 'create-article';
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function KbPage(props: KbPageProps = {}) {
|
||||||
|
const [tab, setTab] = createSignal<'categories' | 'articles' | 'create-article'>(props.initialTab || 'categories');
|
||||||
|
|
||||||
// Categories resource
|
// Categories resource
|
||||||
const [categories, { refetch: refetchCategories }] = createResource(loadCategories);
|
const [categories, { refetch: refetchCategories }] = createResource(loadCategories);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
import { onMount } from 'solid-js';
|
import KbPage from '~/routes/admin/kb';
|
||||||
import { useNavigate } from '@solidjs/router';
|
|
||||||
import AdminShell from '~/components/AdminShell';
|
|
||||||
|
|
||||||
export default function KbArticlesAliasPage() {
|
export default function KbArticlesPage() {
|
||||||
const navigate = useNavigate();
|
return <KbPage initialTab="articles" />;
|
||||||
onMount(() => navigate('/admin/kb', { replace: true }));
|
|
||||||
return <AdminShell><div class="card"><p class="notice">Redirecting to knowledge base...</p></div></AdminShell>;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,77 @@
|
||||||
import { onMount } from 'solid-js';
|
import { A, useParams } from '@solidjs/router';
|
||||||
import { A, useNavigate, useParams } from '@solidjs/router';
|
import { createResource, Show } from 'solid-js';
|
||||||
import AdminShell from '~/components/AdminShell';
|
import AdminShell from '~/components/AdminShell';
|
||||||
|
|
||||||
export default function KbArticleAliasPage() {
|
const API = '/api/gateway';
|
||||||
const navigate = useNavigate();
|
|
||||||
|
type KbArticle = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
slug?: string;
|
||||||
|
content?: string;
|
||||||
|
body?: string;
|
||||||
|
status?: string;
|
||||||
|
category?: string;
|
||||||
|
category_id?: string;
|
||||||
|
updated_at?: string;
|
||||||
|
created_at?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function loadArticle(id: string): Promise<KbArticle | null> {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API}/api/admin/kb/articles/${id}`);
|
||||||
|
if (!res.ok) return null;
|
||||||
|
return res.json();
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function KbArticleDetailPage() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
onMount(() => navigate('/admin/kb', { replace: true }));
|
const [article] = createResource(() => params.id, loadArticle);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminShell>
|
<AdminShell>
|
||||||
<div class="card">
|
<div class="page-hero-card page-actions">
|
||||||
<p class="notice">Redirecting article <strong>{params.id}</strong> to knowledge base module...</p>
|
<div>
|
||||||
<div class="actions"><A class="btn" href="/admin/kb">Open Knowledge Base</A></div>
|
<h1 class="page-title">KB Article Detail</h1>
|
||||||
|
<p class="page-subtitle">Metadata and safe content preview for this article.</p>
|
||||||
|
</div>
|
||||||
|
<div class="page-actions-right">
|
||||||
|
<A class="btn" href="/admin/kb/articles">Back to Articles</A>
|
||||||
|
<A class="btn primary" href={`/admin/kb/articles/${params.id}/edit`}>Edit Article</A>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Show when={article.loading}>
|
||||||
|
<section class="card"><p class="notice">Loading article...</p></section>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={!article.loading && !article()}>
|
||||||
|
<section class="card"><p class="notice">Article not found.</p></section>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={article()}>
|
||||||
|
<div class="detail-layout">
|
||||||
|
<section class="card">
|
||||||
|
<h2 style="margin-bottom:10px">Metadata</h2>
|
||||||
|
<div class="field-grid-2">
|
||||||
|
<div class="kv-item"><p class="kv-label">Title</p><p class="kv-value">{article()!.title || '—'}</p></div>
|
||||||
|
<div class="kv-item"><p class="kv-label">Status</p><p class="kv-value">{article()!.status || '—'}</p></div>
|
||||||
|
<div class="kv-item"><p class="kv-label">Slug</p><p class="kv-value">{article()!.slug || '—'}</p></div>
|
||||||
|
<div class="kv-item"><p class="kv-label">Category</p><p class="kv-value">{article()!.category || article()!.category_id || '—'}</p></div>
|
||||||
|
<div class="kv-item"><p class="kv-label">Created At</p><p class="kv-value">{article()!.created_at ? new Date(article()!.created_at!).toLocaleString() : '—'}</p></div>
|
||||||
|
<div class="kv-item"><p class="kv-label">Updated At</p><p class="kv-value">{article()!.updated_at ? new Date(article()!.updated_at!).toLocaleString() : '—'}</p></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="card">
|
||||||
|
<h2 style="margin-bottom:10px">Content</h2>
|
||||||
|
<pre class="json" style="max-height:720px;white-space:pre-wrap">{article()!.content || article()!.body || 'No content'}</pre>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
</AdminShell>
|
</AdminShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,138 @@
|
||||||
import { onMount } from 'solid-js';
|
|
||||||
import { A, useNavigate, useParams } from '@solidjs/router';
|
import { A, useNavigate, useParams } from '@solidjs/router';
|
||||||
|
import { createEffect, createResource, createSignal, Show } from 'solid-js';
|
||||||
import AdminShell from '~/components/AdminShell';
|
import AdminShell from '~/components/AdminShell';
|
||||||
|
|
||||||
export default function KbArticleEditAliasPage() {
|
const API = '/api/gateway';
|
||||||
|
|
||||||
|
type KbArticle = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
slug?: string;
|
||||||
|
content?: string;
|
||||||
|
body?: string;
|
||||||
|
status?: string;
|
||||||
|
category_id?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function loadArticle(id: string): Promise<KbArticle | null> {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API}/api/admin/kb/articles/${id}`);
|
||||||
|
if (!res.ok) return null;
|
||||||
|
return res.json();
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function KbArticleEditPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
onMount(() => navigate('/admin/kb', { replace: true }));
|
const [article] = createResource(() => params.id, loadArticle);
|
||||||
|
const [title, setTitle] = createSignal('');
|
||||||
|
const [slug, setSlug] = createSignal('');
|
||||||
|
const [categoryId, setCategoryId] = createSignal('');
|
||||||
|
const [status, setStatus] = createSignal('DRAFT');
|
||||||
|
const [content, setContent] = createSignal('');
|
||||||
|
const [saving, setSaving] = createSignal(false);
|
||||||
|
const [error, setError] = createSignal('');
|
||||||
|
const [loaded, setLoaded] = createSignal(false);
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const value = article();
|
||||||
|
if (!value || loaded()) return;
|
||||||
|
setTitle(value.title || '');
|
||||||
|
setSlug(value.slug || '');
|
||||||
|
setCategoryId(value.category_id || '');
|
||||||
|
setStatus(value.status || 'DRAFT');
|
||||||
|
setContent(value.content || value.body || '');
|
||||||
|
setLoaded(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
const save = async (e: Event) => {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
setSaving(true);
|
||||||
|
setError('');
|
||||||
|
const res = await fetch(`${API}/api/admin/kb/articles/${params.id}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
title: title(),
|
||||||
|
slug: slug(),
|
||||||
|
category_id: categoryId() || null,
|
||||||
|
status: status(),
|
||||||
|
content: content(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error('Failed to save article');
|
||||||
|
navigate(`/admin/kb/articles/${params.id}`);
|
||||||
|
} catch (err: any) {
|
||||||
|
setError(err.message || 'Failed to save article');
|
||||||
|
} finally {
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AdminShell>
|
<AdminShell>
|
||||||
<div class="card">
|
<div class="page-hero-card page-actions">
|
||||||
<p class="notice">Redirecting article edit <strong>{params.id}</strong> to knowledge base module...</p>
|
<div>
|
||||||
<div class="actions"><A class="btn" href="/admin/kb">Open Knowledge Base</A></div>
|
<h1 class="page-title">Edit KB Article</h1>
|
||||||
|
<p class="page-subtitle">Update article metadata, status, and content.</p>
|
||||||
|
</div>
|
||||||
|
<div class="page-actions-right">
|
||||||
|
<A class="btn" href={`/admin/kb/articles/${params.id}`}>Back to Detail</A>
|
||||||
|
<A class="btn" href="/admin/kb/articles">Back to Articles</A>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Show when={article.loading}>
|
||||||
|
<section class="card"><p class="notice">Loading article...</p></section>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={!article.loading && !article()}>
|
||||||
|
<section class="card"><p class="notice">Article not found.</p></section>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={article() && loaded()}>
|
||||||
|
<form class="card" onSubmit={save}>
|
||||||
|
<div class="field-grid-2">
|
||||||
|
<div class="field">
|
||||||
|
<label>Title</label>
|
||||||
|
<input value={title()} onInput={(e) => setTitle(e.currentTarget.value)} required />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Slug</label>
|
||||||
|
<input value={slug()} onInput={(e) => setSlug(e.currentTarget.value)} />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Category ID</label>
|
||||||
|
<input value={categoryId()} onInput={(e) => setCategoryId(e.currentTarget.value)} />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Status</label>
|
||||||
|
<select value={status()} onChange={(e) => setStatus(e.currentTarget.value)}>
|
||||||
|
<option value="DRAFT">DRAFT</option>
|
||||||
|
<option value="PUBLISHED">PUBLISHED</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="field" style="grid-column:1 / -1">
|
||||||
|
<label>Content</label>
|
||||||
|
<textarea rows="16" value={content()} onInput={(e) => setContent(e.currentTarget.value)} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Show when={error()}>
|
||||||
|
<p class="error-note">{error()}</p>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<div class="actions" style="justify-content:flex-end">
|
||||||
|
<button class="btn primary" type="submit" disabled={saving()}>
|
||||||
|
{saving() ? 'Saving...' : 'Save Article'}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Show>
|
||||||
</AdminShell>
|
</AdminShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
import { onMount } from 'solid-js';
|
import KbPage from '~/routes/admin/kb';
|
||||||
import { useNavigate } from '@solidjs/router';
|
|
||||||
import AdminShell from '~/components/AdminShell';
|
|
||||||
|
|
||||||
export default function KbArticleNewAliasPage() {
|
export default function KbArticleNewPage() {
|
||||||
const navigate = useNavigate();
|
return <KbPage initialTab="create-article" />;
|
||||||
onMount(() => navigate('/admin/kb', { replace: true }));
|
|
||||||
return <AdminShell><div class="card"><p class="notice">Redirecting to knowledge base editor...</p></div></AdminShell>;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
import { onMount } from 'solid-js';
|
import KbPage from '~/routes/admin/kb';
|
||||||
import { useNavigate } from '@solidjs/router';
|
|
||||||
import AdminShell from '~/components/AdminShell';
|
|
||||||
|
|
||||||
export default function KbCategoriesAliasPage() {
|
export default function KbCategoriesPage() {
|
||||||
const navigate = useNavigate();
|
return <KbPage initialTab="categories" />;
|
||||||
onMount(() => navigate('/admin/kb', { replace: true }));
|
|
||||||
return <AdminShell><div class="card"><p class="notice">Redirecting to knowledge base categories...</p></div></AdminShell>;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue