move working code to exemple/navigation/

sources/, config.yml and build.sh relocated under exemple/navigation/.
Root now only contains template/ and exemple/.
README updated to reflect the new structure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
GnomeZworc 2026-06-07 13:16:42 +02:00
commit 6185d8ea48
Signed by: nicolas.boufideline
GPG key ID: 4406BBBF8845D632
18 changed files with 18 additions and 10 deletions

View file

@ -0,0 +1,4 @@
const response = await fetch('./config.json')
if (!response.ok) throw new Error('Failed to load config.json')
export const config = await response.json()

View file

@ -0,0 +1,37 @@
// Dynamic menu loader.
// Fetches data for groups declared with empty children in navigation.json
// and injects them into side-nav after it is ready.
//
// Imported non-blocking from index.html — does not delay routing.
// Replace MOCK_* with real api-client calls when the backend is ready.
// ── Mock data ────────────────────────────────────────────────────────────────
const MOCK_ACCOUNTS = [
{ id: 'tresorerie', name: 'Trésorerie' },
{ id: 'charges', name: 'Charges' },
{ id: 'produits', name: 'Produits' },
{ id: 'fournisseurs', name: 'Fournisseurs' },
]
async function fetchAccounts() {
// TODO: replace with real call
// const api = document.querySelector('api-client')
// return await api.finance.list('/accounts')
await new Promise(r => setTimeout(r, 500)) // simulate network latency
return MOCK_ACCOUNTS
}
// ── Injection ────────────────────────────────────────────────────────────────
const nav = document.querySelector('side-nav')
if (!nav) throw new Error('menu.js: side-nav not found in DOM')
await nav.ready
const accounts = await fetchAccounts()
nav.setGroupChildren('Comptes', accounts.map(a => ({
label: a.name,
href: `#/account?id=${a.id}`,
icon: 'account',
})))

View file

@ -0,0 +1,10 @@
// Loads and registers all components listed in components.json.
// To add a component: git clone into components/, add the path here.
const response = await fetch('./components.json')
if (!response.ok) throw new Error('Failed to load components.json')
const components = await response.json()
for (const path of components) {
await import(`../${path}`)
}

View file

@ -0,0 +1,55 @@
// Hash-based SPA router.
// Reads pages.json (generated by build.sh from config.yml).
// Each page is an HTML fragment (index.html) + optional CSS + optional JS module.
//
// Load order enforced by index.html:
// await import('./core/registry.js') ← defines all components
// await import('./core/router.js') ← this file
const response = await fetch('./pages.json')
if (!response.ok) throw new Error('router: failed to load pages.json — run build.sh')
const PAGES = await response.json()
// Cache of imported JS modules — modules are only fetched once per session.
const MODULE_CACHE = new Map()
// Currently active page CSS <link> — removed on navigation.
let activeCSS = null
function currentRoute() {
const hash = window.location.hash.replace(/^#\//, '') || PAGES[0]?.route || ''
return hash.split('?')[0]
}
async function render(routeName) {
const page = PAGES.find(p => p.route === routeName) ?? PAGES[0]
const main = document.querySelector('main')
if (!main || !page) return
// ── HTML ────────────────────────────────────────────────────────────────────
const htmlRes = await fetch(page.html)
if (!htmlRes.ok) throw new Error(`router: failed to load ${page.html}`)
main.innerHTML = await htmlRes.text()
// ── CSS ─────────────────────────────────────────────────────────────────────
activeCSS?.remove()
activeCSS = null
if (page.css) {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = page.css
document.head.appendChild(link)
activeCSS = link
}
// ── JS ──────────────────────────────────────────────────────────────────────
if (page.js) {
if (!MODULE_CACHE.has(page.js)) {
MODULE_CACHE.set(page.js, await import(`../${page.js}`))
}
MODULE_CACHE.get(page.js)?.init?.(main)
}
}
await render(currentRoute())
window.addEventListener('hashchange', () => render(currentRoute()))