flask plugin new output, minus the scaffold's AssetType seeding: printed parts are quantity-based consumables, not ADR-001 assets. on_install seeds the three plugin settings instead. Manifest pins core >=0.11.0, depends on employees (badge name resolution), ships disabled until a site opts in.
229 lines
5.4 KiB
Vue
229 lines
5.4 KiB
Vue
<template>
|
|
<div>
|
|
<div class="page-header">
|
|
<h2>{{ isEdit ? 'Edit Printedparts' : 'Add Printedparts' }}</h2>
|
|
</div>
|
|
|
|
<div class="card form-card">
|
|
<form @submit.prevent="submitForm">
|
|
<fieldset>
|
|
<legend>Asset Information</legend>
|
|
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="assetnumber">Asset Number *</label>
|
|
<input
|
|
id="assetnumber"
|
|
v-model="form.assetnumber"
|
|
type="text"
|
|
class="form-control"
|
|
required
|
|
:disabled="isEdit"
|
|
/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="name">Name</label>
|
|
<input
|
|
id="name"
|
|
v-model="form.name"
|
|
type="text"
|
|
class="form-control"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="serialnumber">Serial Number</label>
|
|
<input
|
|
id="serialnumber"
|
|
v-model="form.serialnumber"
|
|
type="text"
|
|
class="form-control"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
|
|
<fieldset>
|
|
<legend>Printedparts Details</legend>
|
|
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="examplefield">Example Field</label>
|
|
<input
|
|
id="examplefield"
|
|
v-model="form.examplefield"
|
|
type="text"
|
|
class="form-control"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
|
|
<div class="form-actions">
|
|
<button type="button" class="btn btn-secondary" @click="cancel">Cancel</button>
|
|
<button type="submit" class="btn btn-primary" :disabled="saving">
|
|
{{ saving ? 'Saving...' : (isEdit ? 'Save Changes' : 'Create') }}
|
|
</button>
|
|
</div>
|
|
|
|
<div v-if="error" class="error-message">{{ error }}</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, onMounted } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import api from '../../api'
|
|
|
|
// local api client. move into src/api/index.js (see
|
|
// plugins/printedparts/frontend-api-snippet.js) then swap for:
|
|
// import { printedpartsApi } from '../../api'
|
|
const printedpartsApi = {
|
|
list(params = {}) { return api.get('/printedparts', { params }) },
|
|
get(itemId) { return api.get(`/printedparts/${itemId}`) },
|
|
create(data) { return api.post('/printedparts', data) },
|
|
update(itemId, data) { return api.put(`/printedparts/${itemId}`, data) },
|
|
remove(itemId) { return api.delete(`/printedparts/${itemId}`) }
|
|
}
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
|
|
const itemId = route.params.id
|
|
const isEdit = computed(() => !!itemId)
|
|
|
|
const form = ref({
|
|
assetnumber: '',
|
|
name: '',
|
|
serialnumber: '',
|
|
examplefield: ''
|
|
})
|
|
const saving = ref(false)
|
|
const error = ref('')
|
|
|
|
onMounted(async () => {
|
|
if (isEdit.value) {
|
|
await loadItem()
|
|
}
|
|
})
|
|
|
|
async function loadItem() {
|
|
try {
|
|
const response = await printedpartsApi.get(itemId)
|
|
const data = response.data.data
|
|
form.value.assetnumber = data.assetnumber || ''
|
|
form.value.name = data.name || ''
|
|
form.value.serialnumber = data.serialnumber || ''
|
|
form.value.examplefield = data.examplefield || ''
|
|
} catch (loadError) {
|
|
console.error('Error loading printedparts:', loadError)
|
|
error.value = 'Failed to load record'
|
|
}
|
|
}
|
|
|
|
async function submitForm() {
|
|
saving.value = true
|
|
error.value = ''
|
|
|
|
try {
|
|
const payload = {
|
|
assetnumber: form.value.assetnumber,
|
|
name: form.value.name || null,
|
|
serialnumber: form.value.serialnumber || null,
|
|
examplefield: form.value.examplefield || null
|
|
}
|
|
|
|
let redirectId = itemId
|
|
if (isEdit.value) {
|
|
await printedpartsApi.update(itemId, payload)
|
|
} else {
|
|
const response = await printedpartsApi.create(payload)
|
|
redirectId = response.data.data?.assetid
|
|
}
|
|
|
|
router.push(redirectId ? `/printedparts/${redirectId}` : '/printedparts')
|
|
} catch (submitError) {
|
|
console.error('Error saving printedparts:', submitError)
|
|
error.value = 'Failed to save record'
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
function cancel() {
|
|
if (isEdit.value) {
|
|
router.push(`/printedparts/${itemId}`)
|
|
} else {
|
|
router.push('/printedparts')
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.form-card {
|
|
max-width: 800px;
|
|
}
|
|
|
|
fieldset {
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 1.5rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
legend {
|
|
font-weight: 600;
|
|
padding: 0 0.5rem;
|
|
color: var(--text);
|
|
}
|
|
|
|
.form-row {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 1rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.form-row:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.form-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.form-group label {
|
|
margin-bottom: 0.375rem;
|
|
font-weight: 500;
|
|
color: var(--text);
|
|
}
|
|
|
|
.form-actions {
|
|
display: flex;
|
|
gap: 1rem;
|
|
justify-content: flex-end;
|
|
margin-top: 1.5rem;
|
|
padding-top: 1.5rem;
|
|
border-top: 1px solid var(--border);
|
|
}
|
|
|
|
.error-message {
|
|
margin-top: 1rem;
|
|
padding: 0.75rem 1rem;
|
|
background: var(--danger);
|
|
color: white;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
@media (max-width: 600px) {
|
|
.form-row {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|