From 7bf43e137039967ad60d7f05a18848a2ae557908 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 1 Jul 2026 10:42:59 -0400 Subject: [PATCH] =?UTF-8?q?feat(guide):=20tabbed=20markdown=20reader=20?= =?UTF-8?q?=E2=80=94=20retire=20CRUD=20GuidesView=20(Phase=202,=20Refs=20#?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New GuideReader.svelte: nav tree (apps→modules) + tabbed markdown (marked), audience-gated server-side (admins see internal tabs, badged). Replaces the old DB-CRUD GuidesView. Consumes GET /api/v1/guide/nav + /modules/{module}. Deployed to guide.bcos.dev. Co-Authored-By: Claude Opus 4.8 --- frontend/index.html | 2 +- frontend/package-lock.json | 15 +- frontend/package.json | 3 +- frontend/src/App.svelte | 6 +- frontend/src/components/GuideReader.svelte | 235 ++++++++++++ .../src/components/Guides/GuidesView.svelte | 347 ------------------ frontend/src/lib/api.ts | 19 + 7 files changed, 274 insertions(+), 353 deletions(-) create mode 100644 frontend/src/components/GuideReader.svelte delete mode 100644 frontend/src/components/Guides/GuidesView.svelte diff --git a/frontend/index.html b/frontend/index.html index fd3da85..d35ca67 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - HiveOps Guide Admin + HiveIQ Guide diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 497881b..a4bcade 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,7 +8,8 @@ "name": "hiveops-guide-frontend", "version": "1.0.0", "dependencies": { - "axios": "^1.6.0" + "axios": "^1.6.0", + "marked": "^12.0.0" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.0", @@ -1578,6 +1579,18 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index d41e449..9df30ee 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "vite": "^5.0.0" }, "dependencies": { - "axios": "^1.6.0" + "axios": "^1.6.0", + "marked": "^12.0.0" } } diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 5200e59..739a640 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -2,7 +2,7 @@ import { onMount } from 'svelte'; import { authApi, setToken, getToken, clearToken } from './lib/api'; import { toasts } from './lib/stores'; - import GuidesView from './components/Guides/GuidesView.svelte'; + import GuideReader from './components/GuideReader.svelte'; let authenticated = false; let checking = true; @@ -53,7 +53,7 @@ -

Guide Admin

+

HiveIQ Guide

{#if loginError} @@ -74,7 +74,7 @@ {:else} - + {/if} diff --git a/frontend/src/components/GuideReader.svelte b/frontend/src/components/GuideReader.svelte new file mode 100644 index 0000000..7664b64 --- /dev/null +++ b/frontend/src/components/GuideReader.svelte @@ -0,0 +1,235 @@ + + +
+ + + + +
+ {#if loadingModule} +
+ {:else if !module} +
Select a guide from the left.
+ {:else} +
+
{module.title}
+
{module.module}
+
+ + {#if module.tabs.length > 1} +
+ {#each module.tabs as t, i} + + {/each} +
+ {/if} + +
+ {#if module.tabs[activeTab]} + {#if module.tabs[activeTab].audience === 'internal'} +
Internal — visible to admins only
+ {/if} +
{@html render(module.tabs[activeTab].body)}
+ {/if} +
+ {/if} +
+
+ + diff --git a/frontend/src/components/Guides/GuidesView.svelte b/frontend/src/components/Guides/GuidesView.svelte deleted file mode 100644 index 74db5b8..0000000 --- a/frontend/src/components/Guides/GuidesView.svelte +++ /dev/null @@ -1,347 +0,0 @@ - - -
-
-
-

Guide Admin

-

Manage in-app help guides by page key — no code deploy needed to update content.

-
-
- -
-
- -
-
-
- {#if !filterSidebarCollapsed} - Filters -
- {filtered.length} - -
- {:else} - - {/if} -
- {#if !filterSidebarCollapsed} -
- {#if hasActiveFilters} - - {/if} -
- - -
-
- {/if} -
- -
- {#if hasActiveFilters} -
- Filters: - {#if search} - - - "{search}" - - - {/if} -
- {/if} - -
-
-
- {filtered.length} guide{filtered.length !== 1 ? 's' : ''} -
- -
-
- - {#if loading} -
Loading guides…
- {:else if filtered.length === 0} -
{guides.length === 0 ? 'No guides yet. Click "+ New Guide" to create one.' : 'No guides match the current filters.'}
- {:else} -
-
- - - - - - - - - - - - {#each filtered as g (g.id)} - - - - - - - - {/each} - -
Page KeyStatusLast UpdatedUpdated By
{g.pageKey} - - {formatDate(g.updatedAt ?? g.createdAt)}{g.updatedBy ?? '—'} - -
-
-
- {/if} -
-
-
-
-
- -{#if panelOpen} - -
panelOpen = false}>
- -{/if} - - diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 5497d79..2143ae4 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -31,3 +31,22 @@ export const authApi = axios.create({ }); export default api; + +// ── Guide content API ────────────────────────────────────── +export interface NavTab { tab: string; order: number; audience: string; } +export interface NavModule { module: string; title: string; tabs: NavTab[]; } +export interface NavApp { app: string; modules: NavModule[]; } +export interface ModuleTab { tab: string; order: number; audience: string; body: string; } +export interface ModuleView { module: string; title: string; tabs: ModuleTab[]; } + +/** apps → modules → tabs (audience-filtered server-side by the caller's role). */ +export async function fetchNav(): Promise { + const res = await api.get('/api/v1/guide/nav'); + return res.data; +} + +/** one module with its tabs (markdown bodies). */ +export async function fetchModule(module: string): Promise { + const res = await api.get(`/api/v1/guide/modules/${encodeURIComponent(module)}`); + return res.data; +}