Add email sending (service + 3 flows) and a general asset label generator
All checks were successful
CI / backend (push) Successful in 1m23s
CI / naming (push) Successful in 1s
CI / frontend (push) Successful in 7s

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>
This commit is contained in:
cproudlock
2026-07-12 11:58:30 -04:00
parent 7d309aabeb
commit a846587f39
34 changed files with 1819 additions and 12 deletions

View File

@@ -162,6 +162,7 @@
import { onMounted, ref, computed } from 'vue'
import { useSystemSettings } from '../../composables/systemSettings'
import { apiError } from '../../utils/apiError'
import { settingsApi } from '../../api'
const {
settings, saving, error, success,
@@ -189,18 +190,25 @@ const canTestEmail = computed(() => {
})
async function testEmail() {
// TODO: Implement test email endpoint
testingEmail.value = true
error.value = ''
success.value = ''
try {
// await settingsApi.testEmail()
success.value = 'Test email feature coming soon'
// Send to the configured Alert Recipients (the endpoint splits the list).
const response = await settingsApi.testEmail(settings.alert_recipients)
const result = response.data?.data || {}
if (result.sent) {
success.value = `Test email sent to ${settings.alert_recipients}`
} else {
error.value = result.error
? `Test email failed: ${result.error}`
: (response.data?.message || 'Email is not configured')
}
} catch (e) {
error.value = apiError(e, 'Failed to send test email')
} finally {
testingEmail.value = false
setTimeout(() => { success.value = '' }, 3000)
setTimeout(() => { success.value = '' }, 4000)
}
}