Refresh nav on auth changes and show session info

This commit is contained in:
2026-04-06 17:20:47 +02:00
parent a0da39b697
commit 76d8180f41
5 changed files with 58 additions and 9 deletions
+22
View File
@@ -6,6 +6,7 @@ import { APP_NAME } from './config.js';
import { createRouter, navigate } from './router.js'; import { createRouter, navigate } from './router.js';
import { createAppStore } from './store.js'; import { createAppStore } from './store.js';
import { appShell } from '../components/app-shell.js'; import { appShell } from '../components/app-shell.js';
import { navBar } from '../components/nav-bar.js';
import { registerFeatureData } from '../features/register.js'; import { registerFeatureData } from '../features/register.js';
async function installServiceWorker() { async function installServiceWorker() {
@@ -23,6 +24,20 @@ export function bootstrapApp() {
const appRoot = document.querySelector('#app'); const appRoot = document.querySelector('#app');
appRoot.innerHTML = appShell(APP_NAME); appRoot.innerHTML = appShell(APP_NAME);
Alpine.initTree(appRoot); Alpine.initTree(appRoot);
const navRoot = document.querySelector('#app-nav');
function renderNav() {
if (!navRoot) {
return;
}
if (typeof Alpine.destroyTree === 'function') {
Alpine.destroyTree(navRoot);
}
navRoot.innerHTML = navBar(APP_NAME);
Alpine.initTree(navRoot);
}
const router = createRouter({ const router = createRouter({
Alpine, Alpine,
@@ -35,6 +50,7 @@ export function bootstrapApp() {
if (!store.activeKitchen && kitchens.length) { if (!store.activeKitchen && kitchens.length) {
store.setActiveKitchen(kitchens[0]); store.setActiveKitchen(kitchens[0]);
} }
renderNav();
return kitchens; return kitchens;
} }
@@ -51,7 +67,9 @@ export function bootstrapApp() {
} else if (store.isConnected) { } else if (store.isConnected) {
await window.__loncApp.refreshKitchens(); await window.__loncApp.refreshKitchens();
} }
renderNav();
} catch (error) { } catch (error) {
renderNav();
if (window.location.hash !== '#/login') { if (window.location.hash !== '#/login') {
navigate('/login'); navigate('/login');
} }
@@ -64,10 +82,12 @@ export function bootstrapApp() {
} else if (store.isConnected) { } else if (store.isConnected) {
await window.__loncApp.refreshKitchens(); await window.__loncApp.refreshKitchens();
} }
renderNav();
return result; return result;
}, },
async logout() { async logout() {
await logout(store); await logout(store);
renderNav();
navigate('/login'); navigate('/login');
}, },
router, router,
@@ -89,6 +109,8 @@ export function bootstrapApp() {
.finally(() => router.start()) .finally(() => router.start())
.catch(() => router.start()); .catch(() => router.start());
renderNav();
installServiceWorker().catch(() => { installServiceWorker().catch(() => {
store.addAlert({ store.addAlert({
type: 'warning', type: 'warning',
+2
View File
@@ -3,7 +3,9 @@ import { navBar } from './nav-bar.js';
export function appShell(appName) { export function appShell(appName) {
return ` return `
<div class="app-shell d-flex flex-column min-vh-100"> <div class="app-shell d-flex flex-column min-vh-100">
<div id="app-nav">
${navBar(appName)} ${navBar(appName)}
</div>
<main id="route-view" class="flex-grow-1"></main> <main id="route-view" class="flex-grow-1"></main>
<div class="toast-stack" x-data="alertsData()"> <div class="toast-stack" x-data="alertsData()">
<template x-for="alert in alerts" :key="alert.id"> <template x-for="alert in alerts" :key="alert.id">
+13 -7
View File
@@ -1,6 +1,6 @@
export function navBar(appName) { export function navBar(appName) {
return ` return `
<nav class="navbar navbar-expand-lg bg-white border-bottom sticky-top shadow-sm"> <nav class="navbar navbar-expand-lg bg-white border-bottom sticky-top shadow-sm" x-data>
<div class="container-xxl"> <div class="container-xxl">
<a class="navbar-brand d-flex align-items-center gap-2 fw-semibold" href="#/"> <a class="navbar-brand d-flex align-items-center gap-2 fw-semibold" href="#/">
<span class="brand-mark">L</span> <span class="brand-mark">L</span>
@@ -11,19 +11,25 @@ export function navBar(appName) {
</button> </button>
<div id="main-nav" class="collapse navbar-collapse"> <div id="main-nav" class="collapse navbar-collapse">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"><a class="nav-link" href="#/">Dashboard</a></li> <li class="nav-item" x-show="$store.app.session?.state === 'connected'">
<li class="nav-item"><a class="nav-link" href="#/labels/new">New Label</a></li> <a class="nav-link" href="#/">Dashboard</a>
<li class="nav-item"><a class="nav-link" href="#/stock">Stock</a></li> </li>
<li class="nav-item" x-show="$store.app.session?.state === 'connected'">
<a class="nav-link" href="#/labels/new">New Label</a>
</li>
<li class="nav-item" x-show="$store.app.session?.state === 'connected'">
<a class="nav-link" href="#/stock">Stock</a>
</li>
<li class="nav-item"><a class="nav-link" href="#/settings">Settings</a></li> <li class="nav-item"><a class="nav-link" href="#/settings">Settings</a></li>
</ul> </ul>
<div class="d-flex flex-column flex-lg-row align-items-lg-center gap-2" x-data> <div class="d-flex flex-column flex-lg-row align-items-lg-center gap-2">
<template x-if="$store.app.activeKitchen"> <template x-if="$store.app.session?.state === 'connected' && $store.app.activeKitchen">
<div class="small text-body-secondary"> <div class="small text-body-secondary">
Kitchen: Kitchen:
<span class="fw-semibold text-body" x-text="$store.app.activeKitchen?.name"></span> <span class="fw-semibold text-body" x-text="$store.app.activeKitchen?.name"></span>
</div> </div>
</template> </template>
<template x-if="$store.app.isAuthenticated"> <template x-if="$store.app.session?.applicationKey">
<button class="btn btn-outline-secondary btn-sm" @click="window.__loncApp.logout()">Logout</button> <button class="btn btn-outline-secondary btn-sm" @click="window.__loncApp.logout()">Logout</button>
</template> </template>
</div> </div>
+2 -1
View File
@@ -173,8 +173,9 @@ export function loginPageData(store) {
return; return;
} }
const status = error?.cause?.status || error?.status; const status = error?.cause?.status || error?.status;
const isPendingValidation = !store.session?.hasValidated;
this.verifyState.error = this.verifyState.error =
status === 403 status === 403 || isPendingValidation
? 'Failed to verify connection. Please verify the application key in Tryton first.' ? 'Failed to verify connection. Please verify the application key in Tryton first.'
: (error?.message || this.verifyState.error || 'Failed to verify connection.'); : (error?.message || this.verifyState.error || 'Failed to verify connection.');
}); });
+18
View File
@@ -27,6 +27,17 @@ export function renderSettingsPage() {
<label class="form-label">Database name</label> <label class="form-label">Database name</label>
<input class="form-control" type="text" x-model="form.database" required /> <input class="form-control" type="text" x-model="form.database" required />
</div> </div>
<div>
<label class="form-label">User login</label>
<input class="form-control" type="text" :value="userLogin" readonly />
</div>
<div>
<label class="form-label">Application key</label>
<input class="form-control font-monospace" type="text" :value="maskedApplicationKey" readonly />
<div class="form-text">
Only the first 16 characters are shown for identification.
</div>
</div>
<button class="btn btn-primary align-self-start" type="submit">Save settings</button> <button class="btn btn-primary align-self-start" type="submit">Save settings</button>
</form> </form>
</div> </div>
@@ -57,6 +68,13 @@ export function settingsPageData(store) {
baseUrl: store.config.baseUrl || '', baseUrl: store.config.baseUrl || '',
database: store.config.database || '', database: store.config.database || '',
}, },
get userLogin() {
return store.session?.userLogin || '';
},
get maskedApplicationKey() {
const key = store.session?.applicationKey || '';
return key ? `${key.slice(0, 16)}...` : '';
},
save() { save() {
store.setConfig({ store.setConfig({
baseUrl: this.form.baseUrl.trim(), baseUrl: this.form.baseUrl.trim(),