Files
MangaCircus/public/settings-nav.js
T

61 lines
3.5 KiB
JavaScript

'use strict';
// Two navigation levels; scraper tabs stay within their section, not another menu.
const SETTINGS_SECTIONS = [
{ id: 'user/reading', group: 'User', title: 'Reading', hint: 'Reader modes, page spacing and zoom.' },
{ id: 'user/reading-time', group: 'User', title: 'Reading time & privacy', hint: 'Your clock, Reading Story and personal data.' },
{ id: 'user/notifications', group: 'User', title: 'Notifications', hint: 'Chapter alerts and your optional webhook.' },
{ id: 'user/account', group: 'User', title: 'Account', hint: 'Your account and password.' },
{ id: 'admin/scraper', group: 'Admin', title: 'Scraper', hint: 'Live activity, downloads, update checks and sources.' },
{ id: 'admin/library', group: 'Admin', title: 'Server & library', hint: 'Library location, scan status and maintenance.' },
{ id: 'admin/users', group: 'Admin', title: 'Users & access', hint: 'Manage accounts, passwords and administrators.' },
{ id: 'admin/collections', group: 'Admin', title: 'Collections & metadata', hint: 'Group manga and review their relationships.', href: 'collections.html' },
{ id: 'admin/health', group: 'Admin', title: 'Service health', hint: 'Service status, incidents and recovery.', href: 'health.html' },
];
const SCRAPER_TABS = ['activity', 'queue', 'updates', 'sources'];
function settingsRoute(hash, admin) {
const parts = String(hash || '').replace(/^#/, '').split('/');
const candidate = parts.slice(0, 2).join('/'), tab = parts[2];
const section = SETTINGS_SECTIONS.find(item => item.id === candidate && (admin || item.group === 'User'));
return { id: section?.id || 'user/reading', tab: SCRAPER_TABS.includes(tab) ? tab : tab === 'hosters' ? 'sources' : 'activity' };
}
function settingsShell(me, active = 'user/reading') {
const main = $('main'); main.textContent = '';
const nav = h('nav', { class: 'settings-menu', 'aria-label': 'Settings sections' });
const links = new Map();
for (const group of me.admin ? ['User', 'Admin'] : ['User']) {
nav.append(h('div', { class: 'settings-menu-group' }, h('h2', {}, group),
SETTINGS_SECTIONS.filter(section => section.group === group).map(section => {
const link = h('a', { href: section.href || 'settings.html#' + section.id }, section.title);
links.set(section.id, link);
return link;
})));
}
const crumb = h('p', { class: 'settings-breadcrumb muted small' });
const title = h('h1', { tabindex: '-1' });
const titleRow = h('div', { class: 'settings-title-row' }, title);
const hint = h('p', { class: 'muted settings-hint' });
const content = h('div', { class: 'settings-content' });
main.append(h('div', { class: 'settings-layout' }, nav,
h('div', { class: 'settings-workspace' }, crumb, titleRow, hint, content)));
function select(id, focus = false) {
const section = SETTINGS_SECTIONS.find(item => item.id === id);
if (!section || (section.group === 'Admin' && !me.admin)) return;
for (const [key, link] of links) {
if (key === id) link.setAttribute('aria-current', 'page');
else link.removeAttribute('aria-current');
}
crumb.textContent = 'Settings / ' + section.group;
title.textContent = section.title;
hint.textContent = section.hint;
document.title = section.title + ' · Settings · MangaCircus';
if (focus) title.focus({ preventScroll: true });
}
select(active);
return { content, titleRow, select };
}
if (typeof module !== 'undefined') module.exports = { SETTINGS_SECTIONS, SCRAPER_TABS, settingsRoute };