printedparts stage 13: pick alert recipients from shopdb users
Contract 0.13.0 puts the User model on the plugin surface. The settings page gains a checkbox picker over the user list; selected users receive low-stock alerts at their account email, merged and deduped with the free-text address list, inactive accounts skipped, site alert_recipients still the fallback when both are empty.
This commit is contained in:
@@ -34,7 +34,23 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Low-stock alert recipients</label>
|
||||
<label>Alert shopdb users</label>
|
||||
<div class="user-picker">
|
||||
<label v-for="candidate in users" :key="candidate.userid" class="user-row">
|
||||
<input type="checkbox" :value="String(candidate.userid)"
|
||||
v-model="selectedUserids" />
|
||||
<span>{{ candidate.username }}</span>
|
||||
<span class="user-email">{{ candidate.email }}</span>
|
||||
</label>
|
||||
<p v-if="users.length === 0" class="field-hint">No users loaded</p>
|
||||
</div>
|
||||
<p class="field-hint">
|
||||
Selected users receive low-stock alerts at their account email.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Additional alert emails</label>
|
||||
<input v-model="values.printedparts_alert_email" type="text"
|
||||
class="form-control" placeholder="parts-team@example.com, lead@example.com" />
|
||||
<p class="field-hint">
|
||||
@@ -53,21 +69,25 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { settingsApi } from '@/api'
|
||||
import { settingsApi, usersApi } from '@/api'
|
||||
|
||||
const KEYS = [
|
||||
'printedparts_code_prefix',
|
||||
'printedparts_default_threshold',
|
||||
'printedparts_unknown_badge',
|
||||
'printedparts_alert_email'
|
||||
'printedparts_alert_email',
|
||||
'printedparts_alert_userids'
|
||||
]
|
||||
|
||||
const values = ref({
|
||||
printedparts_code_prefix: '3DP',
|
||||
printedparts_default_threshold: 5,
|
||||
printedparts_unknown_badge: 'deny',
|
||||
printedparts_alert_email: ''
|
||||
printedparts_alert_email: '',
|
||||
printedparts_alert_userids: ''
|
||||
})
|
||||
const users = ref([])
|
||||
const selectedUserids = ref([])
|
||||
const saving = ref(false)
|
||||
const message = ref('')
|
||||
const error = ref('')
|
||||
@@ -81,6 +101,11 @@ onMounted(async () => {
|
||||
}
|
||||
values.value.printedparts_default_threshold =
|
||||
parseInt(values.value.printedparts_default_threshold, 10) || 0
|
||||
selectedUserids.value = (values.value.printedparts_alert_userids || '')
|
||||
.split(',').map(id => id.trim()).filter(Boolean)
|
||||
const usersResponse = await usersApi.list()
|
||||
users.value = (usersResponse.data.data || []).filter(
|
||||
candidate => candidate.isactive && candidate.email)
|
||||
} catch (loadError) {
|
||||
error.value = 'Could not load settings'
|
||||
console.error(loadError)
|
||||
@@ -92,6 +117,7 @@ async function save() {
|
||||
message.value = ''
|
||||
error.value = ''
|
||||
try {
|
||||
values.value.printedparts_alert_userids = selectedUserids.value.join(',')
|
||||
for (const key of KEYS) {
|
||||
await settingsApi.update(key, String(values.value[key] ?? ''))
|
||||
}
|
||||
@@ -106,4 +132,21 @@ async function save() {
|
||||
|
||||
<style scoped>
|
||||
.field-hint { color: var(--text-light); font-size: 0.85rem; margin-top: 0.25rem; }
|
||||
.user-picker {
|
||||
max-height: 12rem;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 0.35rem;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
.user-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.user-email { color: var(--text-light); font-size: 0.85rem; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user