Email: a stdlib SMTP mailer (settings-first config, graceful no-op when unconfigured), a test-email endpoint wired to the Email settings page, forced first-login password change (users.mustchangepassword, migration 7d23, /change-password flow), new-user welcome mail, and on-demand report/alert delivery (POST /api/reports/email + Email Report buttons) with an external-cron-with-a-scoped-PAT path documented for automation. All tests patch smtplib - no network. Labels: a shared /print/asset-label/<type>/<id> view any asset detail page opens - card or plain style, QR or barcode, configurable encoding. Per-type qr_target_* templates plus label_default_style/codetype/encodes settings on the Printing page. Measuring-tool labels default to encoding their inspection-operation code (derived from the location name, e.g. 0615), so every tool in an area shares the area code - verified by decoding the rendered QR. Machine labels default to the machine number; blank-serial handled gracefully. 808 tests pass; both features verified live. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
193 lines
7.1 KiB
Vue
193 lines
7.1 KiB
Vue
<template>
|
|
<div>
|
|
<div class="page-header">
|
|
<h2>Printing & Labels</h2>
|
|
</div>
|
|
|
|
<div class="section-card">
|
|
<div class="setting-group">
|
|
<p class="setting-description">
|
|
Where printed QR codes point. Leave a target blank to link to the
|
|
asset's own page on this instance, or enter a custom URL template
|
|
with {placeholder} substitution.
|
|
</p>
|
|
|
|
<div class="setting-row">
|
|
<label>
|
|
<span>Printer QR target</span>
|
|
<input
|
|
type="text"
|
|
v-model="settings.qr_target_printer"
|
|
placeholder="(blank = printer page)"
|
|
@blur="saveSetting('qr_target_printer', settings.qr_target_printer)"
|
|
:disabled="saving"
|
|
>
|
|
<small class="input-hint">Placeholders: {printerid}, {assetid}, {assetnumber}, {serialnumber}, {ip}, {hostname}</small>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<label>
|
|
<span>USB label QR target</span>
|
|
<input
|
|
type="text"
|
|
v-model="settings.qr_target_usb"
|
|
placeholder="(blank = USB device page)"
|
|
@blur="saveSetting('qr_target_usb', settings.qr_target_usb)"
|
|
:disabled="saving"
|
|
>
|
|
<small class="input-hint">Placeholders: {id}, {serialnumber}, {alias}</small>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<label>
|
|
<span>Machine label target</span>
|
|
<input
|
|
type="text"
|
|
v-model="settings.qr_target_machine"
|
|
placeholder="(blank = machine page)"
|
|
@blur="saveSetting('qr_target_machine', settings.qr_target_machine)"
|
|
:disabled="saving"
|
|
>
|
|
<small class="input-hint">Placeholders: {assetid}, {assetnumber}, {serialnumber}, {name}, {pluginid}</small>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<label>
|
|
<span>Computer label target</span>
|
|
<input
|
|
type="text"
|
|
v-model="settings.qr_target_computer"
|
|
placeholder="(blank = computer page)"
|
|
@blur="saveSetting('qr_target_computer', settings.qr_target_computer)"
|
|
:disabled="saving"
|
|
>
|
|
<small class="input-hint">Placeholders: {assetid}, {assetnumber}, {serialnumber}, {name}, {pluginid}</small>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<label>
|
|
<span>Network device label target</span>
|
|
<input
|
|
type="text"
|
|
v-model="settings.qr_target_network_device"
|
|
placeholder="(blank = network device page)"
|
|
@blur="saveSetting('qr_target_network_device', settings.qr_target_network_device)"
|
|
:disabled="saving"
|
|
>
|
|
<small class="input-hint">Placeholders: {assetid}, {assetnumber}, {serialnumber}, {name}, {pluginid}</small>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<label>
|
|
<span>Measuring tool label target</span>
|
|
<input
|
|
type="text"
|
|
v-model="settings.qr_target_measuring_tool"
|
|
placeholder="(blank = measuring tool page)"
|
|
@blur="saveSetting('qr_target_measuring_tool', settings.qr_target_measuring_tool)"
|
|
:disabled="saving"
|
|
>
|
|
<small class="input-hint">Placeholders: {assetid}, {assetnumber}, {serialnumber}, {name}, {pluginid}, {locationcode}, {locationname}</small>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<label>
|
|
<span>Default label style</span>
|
|
<select
|
|
v-model="settings.label_default_style"
|
|
@change="saveSetting('label_default_style', settings.label_default_style)"
|
|
:disabled="saving"
|
|
>
|
|
<option value="card">Card (badge with image and identity)</option>
|
|
<option value="plain">Plain (just the code and a caption)</option>
|
|
</select>
|
|
<small class="input-hint">House style used when an asset label first opens.</small>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<label>
|
|
<span>Default label code type</span>
|
|
<select
|
|
v-model="settings.label_default_codetype"
|
|
@change="saveSetting('label_default_codetype', settings.label_default_codetype)"
|
|
:disabled="saving"
|
|
>
|
|
<option value="qr">QR code</option>
|
|
<option value="barcode">Barcode (CODE128)</option>
|
|
</select>
|
|
<small class="input-hint">Code type used when an asset label first opens.</small>
|
|
</label>
|
|
</div>
|
|
|
|
<p class="setting-description">
|
|
What each asset type's label encodes by default (still overridable on
|
|
the label page itself).
|
|
</p>
|
|
|
|
<div class="setting-row" v-for="row in encodesRows" :key="row.key">
|
|
<label>
|
|
<span>{{ row.label }} label content</span>
|
|
<select
|
|
v-model="settings[row.key]"
|
|
@change="saveSetting(row.key, settings[row.key])"
|
|
:disabled="saving"
|
|
>
|
|
<option value="assetpage">Link to the asset page</option>
|
|
<option value="assetnumber">Asset / machine number</option>
|
|
<option value="serialnumber">Serial number</option>
|
|
<option v-if="row.hasLocation" value="location">Inspection location code</option>
|
|
<option value="custom">Custom target template</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="setting-row">
|
|
<label>
|
|
<span>USB label style</span>
|
|
<select
|
|
v-model="settings.usb_label_style"
|
|
@change="saveSetting('usb_label_style', settings.usb_label_style)"
|
|
:disabled="saving"
|
|
>
|
|
<option value="barcode">Barcode (CODE128 of the serial number)</option>
|
|
<option value="qr">QR code (links to the USB label QR target)</option>
|
|
</select>
|
|
<small class="input-hint">Applies to the batch USB mini-label print sheet.</small>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="error" class="error-message">{{ error }}</div>
|
|
<div v-if="success" class="settings-success">{{ success }}</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { onMounted } from 'vue'
|
|
import { useSystemSettings } from '../../composables/systemSettings'
|
|
|
|
const {
|
|
settings, saving, error, success,
|
|
loadSettings, saveSetting,
|
|
} = useSystemSettings()
|
|
|
|
// Per-asset-type "what does the code encode" defaults.
|
|
const encodesRows = [
|
|
{ key: 'label_default_encodes_machine', label: 'Machine' },
|
|
{ key: 'label_default_encodes_computer', label: 'Computer' },
|
|
{ key: 'label_default_encodes_printer', label: 'Printer' },
|
|
{ key: 'label_default_encodes_network_device', label: 'Network device' },
|
|
{ key: 'label_default_encodes_measuring_tool', label: 'Measuring tool', hasLocation: true },
|
|
]
|
|
|
|
onMounted(loadSettings)
|
|
</script>
|