// Settings pages the admin may manage (D-21), server-filtered like the // navigation. The rail shows "Ustawienia" only when this list is non-empty. // An entry is either a singleton settings form (/settings/), a link // that opens an admin controller (its `controller` is set) or one of the // admin's own screens (its `path` is set, for example /system/plugins). import { computed, readonly, ref } from 'vue' import { api } from '../api/client' import { controllerPath } from '../app/controllerRoutes' import type { SettingsEntry } from '../api/types' const entries = ref([]) export const settings = readonly(entries) export const hasSettings = computed(() => entries.value.length > 0) export async function loadSettings(): Promise { const { data, response } = await api.GET('/settings') entries.value = response.ok && data ? data.data : [] return entries.value } export function setSettings(next: SettingsEntry[]): void { entries.value = next } export function settingsEntry(code: string): SettingsEntry | null { return entries.value.find((entry) => entry.code === code) ?? null } /** * Route a settings entry opens: its own SPA path for an admin screen, its * controller for a link entry (the index when the ID is malformed), * otherwise its singleton form. */ export function settingsPath(entry: SettingsEntry): string { if (entry.path) { return entry.path } if (entry.controller) { return controllerPath(entry.controller) ?? '/settings' } return `/settings/${encodeURIComponent(entry.code)}` } /** The link entry that opens the given controller, if any. */ export function settingsLinkFor(controllerId: string | null): SettingsEntry | null { if (!controllerId) { return null } return entries.value.find((entry) => entry.controller !== '' && entry.controller === controllerId) ?? null } /** The entry that opens the admin screen at path (entry.path), if any. */ export function settingsEntryForPath(path: string): SettingsEntry | null { return entries.value.find((entry) => !!entry.path && entry.path === path) ?? null } /** Entries grouped by category, categories in first-seen order, by order. */ export const settingsGroups = computed(() => { const sorted = entries.value .map((entry, index) => ({ entry, index })) .sort((a, b) => a.entry.order - b.entry.order || a.index - b.index) .map(({ entry }) => entry) const groups: Array<{ category: string; entries: SettingsEntry[] }> = [] for (const entry of sorted) { let group = groups.find((item) => item.category === entry.category) if (!group) { group = { category: entry.category, entries: [] } groups.push(group) } group.entries.push(entry) } return groups }) /** The settings pages of one category, with the page on screen. */ export interface SettingsSection { category: string current: SettingsEntry entries: SettingsEntry[] } /** * The category group of the given entry for the shell's settings panel, in * settingsGroups order and without entries of any other category. Null when * there is no current entry or its category holds a single page: there is * nothing to switch to. */ export function settingsSectionOf(current: SettingsEntry | null): SettingsSection | null { if (!current) { return null } const group = settingsGroups.value.find((item) => item.category === current.category) if (!group || group.entries.length < 2) { return null } return { category: group.category, current, entries: group.entries } } export function useSettings() { return { settings, hasSettings, settingsGroups, loadSettings, settingsEntry, settingsPath, settingsLinkFor, settingsEntryForPath, settingsSectionOf, } }