Somewhere to actually turn the USB kiosk on
The kiosk page tells an administrator to enable it in Settings under USB. That place did not exist - the setting was reachable only through the API or a shell, which is the same trap the backups plugin shipped with. A settings page and a catalogue card, so the instruction leads somewhere.
This commit is contained in:
@@ -25,6 +25,12 @@ export default [
|
||||
name: 'usb-edit',
|
||||
component: () => import('./views/USBForm.vue'),
|
||||
meta: { requiresAuth: true, plugin: 'usb' }
|
||||
},
|
||||
{
|
||||
path: 'settings/usb',
|
||||
name: 'settings-usb',
|
||||
component: () => import('./views/USBSettings.vue'),
|
||||
meta: { requiresAuth: true, requiresAdmin: true, plugin: 'usb' }
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
113
plugins/usb/frontend/views/USBSettings.vue
Normal file
113
plugins/usb/frontend/views/USBSettings.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="page-header">
|
||||
<h2>USB Devices</h2>
|
||||
</div>
|
||||
|
||||
<div class="card form-card">
|
||||
<div v-if="message" class="settings-success">{{ message }}</div>
|
||||
<div v-if="error" class="error-message">{{ error }}</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="check-row">
|
||||
<input type="checkbox" v-model="values.usb_kiosk_enabled" />
|
||||
<span>Allow the logged-out USB kiosk</span>
|
||||
</label>
|
||||
<p class="field-hint">
|
||||
Turns on <code>/usb-kiosk</code>, a full-screen touch page where somebody
|
||||
at the drive cabinet takes and returns a drive without signing in.
|
||||
It is off by default: this is a write nobody has logged in for, so it is
|
||||
a decision a site makes deliberately. While it is off, every kiosk
|
||||
request is refused and the page says so.
|
||||
</p>
|
||||
<p class="field-hint">
|
||||
The kiosk can only move a drive between checked out and checked in. It
|
||||
cannot add, edit or retire one. Each action is attributed to the SSO
|
||||
entered on the screen, and an SSO that matches no employee is refused -
|
||||
a checkout nobody can trace to a person defeats the point of tracking
|
||||
it.
|
||||
</p>
|
||||
<p v-if="values.usb_kiosk_enabled" class="field-hint">
|
||||
<a :href="withBase('/usb-kiosk')" target="_blank">Open the kiosk</a>
|
||||
- also in the sidebar under Displays.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="check-row">
|
||||
<input type="checkbox" v-model="values.usb_kiosk_allow_return_for_others"
|
||||
:disabled="!values.usb_kiosk_enabled" />
|
||||
<span>Let somebody return a drive checked out to another person</span>
|
||||
</label>
|
||||
<p class="field-hint">
|
||||
The kiosk asks first and names the holder, then records the SSO that
|
||||
actually handed the drive back. Turn this off to refuse a mismatch
|
||||
outright - but then a drive left behind by somebody on leave cannot be
|
||||
returned at the kiosk at all, and needs a signed-in user to sort out.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button class="btn btn-primary" :disabled="saving" @click="save">
|
||||
{{ saving ? 'Saving...' : 'Save' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { settingsApi } from '@/api'
|
||||
import { withBase } from '@/utils/basePath'
|
||||
|
||||
const KEYS = ['usb_kiosk_enabled', 'usb_kiosk_allow_return_for_others']
|
||||
|
||||
const values = ref({
|
||||
usb_kiosk_enabled: false,
|
||||
usb_kiosk_allow_return_for_others: true,
|
||||
})
|
||||
const saving = ref(false)
|
||||
const message = ref('')
|
||||
const error = ref('')
|
||||
|
||||
function asBool (value) {
|
||||
return String(value).toLowerCase() === 'true' || value === true || value === 1
|
||||
}
|
||||
|
||||
async function load () {
|
||||
try {
|
||||
const response = await settingsApi.list({ category: 'usb' })
|
||||
for (const row of (response.data.data || [])) {
|
||||
if (KEYS.includes(row.key)) values.value[row.key] = asBool(row.value)
|
||||
}
|
||||
} catch (loadError) {
|
||||
error.value = 'Could not load USB settings.'
|
||||
}
|
||||
}
|
||||
|
||||
async function save () {
|
||||
saving.value = true
|
||||
message.value = ''
|
||||
error.value = ''
|
||||
try {
|
||||
for (const key of KEYS) {
|
||||
await settingsApi.update(key, values.value[key] ? 'true' : 'false')
|
||||
}
|
||||
message.value = 'Settings saved'
|
||||
} catch (saveError) {
|
||||
error.value = saveError.response?.data?.data?.error?.message || 'Save failed'
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.field-hint { color: var(--text-light); font-size: 0.85rem; margin-top: 0.25rem; }
|
||||
.check-row { display: flex; align-items: center; gap: 0.5rem; font-weight: 600; }
|
||||
.check-row input { width: auto; }
|
||||
.form-actions { margin-top: 1rem; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user