Polish the Site and Facility settings page
Booleans render as checkbox toggles, the employee/USB directory-mode settings as selfhosted/external dropdowns, and every field has a label and help text (raw keys and type-true/false text boxes are gone). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -94,6 +94,9 @@ ADR-007 and ADR-002.
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- Site & Facility settings page renders booleans as toggles and the
|
||||||
|
directory-mode settings as dropdowns, with labels and help text for every
|
||||||
|
field (no more raw keys or type-true/false boxes).
|
||||||
- System Settings tabs follow the URL: clicking a settings-rail link that
|
- System Settings tabs follow the URL: clicking a settings-rail link that
|
||||||
only changes the ?tab= query (Branding, Floor Map) now switches the right
|
only changes the ?tab= query (Branding, Floor Map) now switches the right
|
||||||
panel, tab clicks update the URL, and browser back/forward restore tabs.
|
panel, tab clicks update the URL, and browser back/forward restore tabs.
|
||||||
|
|||||||
@@ -10,7 +10,14 @@
|
|||||||
<div v-else class="form-grid">
|
<div v-else class="form-grid">
|
||||||
<label v-for="s in items" :key="s.key" class="field">
|
<label v-for="s in items" :key="s.key" class="field">
|
||||||
<span>{{ prettyLabel(s.key) }}</span>
|
<span>{{ prettyLabel(s.key) }}</span>
|
||||||
<input v-model="s.value" type="text" :placeholder="fieldHelp(s.key) ? '' : s.description" />
|
<span v-if="s.valuetype === 'boolean'" class="bool-row">
|
||||||
|
<input type="checkbox" :checked="isTrue(s)" @change="s.value = isTrue(s) ? 'false' : 'true'" />
|
||||||
|
<span class="muted">{{ isTrue(s) ? 'Enabled' : 'Disabled' }}</span>
|
||||||
|
</span>
|
||||||
|
<select v-else-if="OPTIONS[s.key]" v-model="s.value">
|
||||||
|
<option v-for="opt in OPTIONS[s.key]" :key="opt" :value="opt">{{ opt }}</option>
|
||||||
|
</select>
|
||||||
|
<input v-else v-model="s.value" type="text" :placeholder="fieldHelp(s.key) ? '' : s.description" />
|
||||||
<small class="muted">{{ fieldHelp(s.key) || s.description }}</small>
|
<small class="muted">{{ fieldHelp(s.key) || s.description }}</small>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
@@ -45,14 +52,30 @@ const LABELS = {
|
|||||||
pc_access_domain: 'PC Access Domain',
|
pc_access_domain: 'PC Access Domain',
|
||||||
employeeid_pattern: 'Employee ID Pattern',
|
employeeid_pattern: 'Employee ID Pattern',
|
||||||
printer_hostname_template: 'Printer Hostname Template',
|
printer_hostname_template: 'Printer Hostname Template',
|
||||||
dualpath_single_machine: 'Dualpath as Single Machine'
|
dualpath_single_machine: 'Dualpath as Single Machine',
|
||||||
|
employee_directory_mode: 'Employee Directory Mode',
|
||||||
|
usb_directory_mode: 'USB Directory Mode',
|
||||||
|
setup_complete: 'Setup Complete'
|
||||||
}
|
}
|
||||||
function prettyLabel(key) {
|
function prettyLabel(key) {
|
||||||
return LABELS[key] || key
|
return LABELS[key] || key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enumerated settings render as dropdowns to prevent typos.
|
||||||
|
const OPTIONS = {
|
||||||
|
employee_directory_mode: ['selfhosted', 'external'],
|
||||||
|
usb_directory_mode: ['selfhosted', 'external']
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTrue(setting) {
|
||||||
|
return String(setting.value).toLowerCase() === 'true'
|
||||||
|
}
|
||||||
|
|
||||||
// Inline help for site fields that need more than the stored description.
|
// Inline help for site fields that need more than the stored description.
|
||||||
const HELP = {
|
const HELP = {
|
||||||
|
employee_directory_mode: 'Where employee records live: selfhosted (managed in this app, with CSV import and photo uploads) or external (a read-only HR database configured via EMPLOYEE_DB_* environment variables).',
|
||||||
|
usb_directory_mode: 'Where USB checkout data lives: selfhosted (tables in this app) or external (a separate cmmc_usb database via CMMC_USB_DB_* environment variables).',
|
||||||
|
setup_complete: 'Set automatically when the first-run setup wizard finishes. Turning it off sends admins back to the /setup wizard on next login.',
|
||||||
employeeid_pattern: 'Regular expression that a scanned/typed employee ID must match to be recognized. Default: ^\\d{9}$ (9 digits). An invalid regex is ignored and the default is used.',
|
employeeid_pattern: 'Regular expression that a scanned/typed employee ID must match to be recognized. Default: ^\\d{9}$ (9 digits). An invalid regex is ignored and the default is used.',
|
||||||
printer_hostname_template: 'Template for generating printer hostnames from an IP. Use {ip} where the dash-separated IP goes. Example: Printer-{ip}.printer.geaerospace.net',
|
printer_hostname_template: 'Template for generating printer hostnames from an IP. Use {ip} where the dash-separated IP goes. Example: Printer-{ip}.printer.geaerospace.net',
|
||||||
dualpath_single_machine: 'Treat a Dualpath pair (a dual-bay machine with one controller) as a single machine in lists, counts, and the floor map. Both bay records are always kept; detail pages stay per-bay with a sibling banner. Enter true or false. Default: true.'
|
dualpath_single_machine: 'Treat a Dualpath pair (a dual-bay machine with one controller) as a single machine in lists, counts, and the floor map. Both bay records are always kept; detail pages stay per-bay with a sibling banner. Enter true or false. Default: true.'
|
||||||
@@ -109,13 +132,24 @@ onMounted(load)
|
|||||||
.field > span {
|
.field > span {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.field input {
|
.field input[type='text'],
|
||||||
|
.field select {
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
.bool-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.bool-row input[type='checkbox'] {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
accent-color: var(--primary);
|
||||||
|
}
|
||||||
.muted {
|
.muted {
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user