Updated A LOT
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
<template>
|
||||
<section class="content-grid">
|
||||
<v-card class="span-5 panel-pad">
|
||||
<h2 class="section-title mb-4">Asset template</h2>
|
||||
<div class="toolbar-row mb-4">
|
||||
<h2 class="section-title">{{ localForm.id ? 'Edit template' : 'New asset template' }}</h2>
|
||||
<v-spacer />
|
||||
<v-btn v-if="localForm.id" variant="text" size="small" prepend-icon="mdi-plus" @click="startNew">New</v-btn>
|
||||
</div>
|
||||
<v-text-field v-model="localForm.name" label="Name" />
|
||||
<v-textarea v-model="localForm.description" label="Description" rows="2" />
|
||||
<v-select v-model="localForm.defaults.category" :items="categoryItems" label="Category" />
|
||||
@@ -23,41 +27,115 @@
|
||||
<v-switch v-model="field.required" label="Required" color="primary" density="compact" hide-details />
|
||||
<v-btn icon="mdi-delete" variant="text" color="error" @click="localForm.custom_fields.splice(index, 1)" />
|
||||
</div>
|
||||
<v-btn color="primary" prepend-icon="mdi-content-save" @click="$emit('save-template', localForm)">Save template</v-btn>
|
||||
<v-btn color="primary" prepend-icon="mdi-content-save" :disabled="!localForm.name" @click="$emit('save-template', localForm)">
|
||||
{{ localForm.id ? 'Update template' : 'Save template' }}
|
||||
</v-btn>
|
||||
</v-card>
|
||||
|
||||
<v-card class="span-7 panel-pad">
|
||||
<h2 class="section-title mb-4">Templates</h2>
|
||||
<v-list lines="two">
|
||||
<v-list-item v-for="template in templates" :key="template.id" prepend-icon="mdi-form-select">
|
||||
<v-list-item-title>{{ template.name }}</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ template.description }}</v-list-item-subtitle>
|
||||
<template #append>
|
||||
<v-chip size="small" variant="tonal">{{ template.custom_fields.length }} fields</v-chip>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<h2 class="section-title mb-3">Templates</h2>
|
||||
<DataTable
|
||||
:columns="columns"
|
||||
:rows="templateRows"
|
||||
row-key="id"
|
||||
:page-size="10"
|
||||
has-actions
|
||||
searchable
|
||||
search-placeholder="Filter templates"
|
||||
empty-text="No templates yet. Create one on the left."
|
||||
>
|
||||
<template #cell-name="{ row }">
|
||||
<span class="font-weight-bold">{{ row.name }}</span>
|
||||
</template>
|
||||
<template #cell-description="{ row }">{{ row.description || '—' }}</template>
|
||||
<template #actions="{ row }">
|
||||
<v-btn v-if="canDelete" icon="mdi-pencil" size="small" variant="text" @click="editTemplate(row)" />
|
||||
<v-btn v-if="canDelete" icon="mdi-delete-outline" size="small" variant="text" color="error" @click="confirmDelete(row)" />
|
||||
</template>
|
||||
</DataTable>
|
||||
</v-card>
|
||||
|
||||
<v-dialog v-model="deleteDialog" max-width="520">
|
||||
<v-card>
|
||||
<v-card-title class="text-error">Delete template</v-card-title>
|
||||
<v-card-text>
|
||||
<p class="mb-3">Delete the template <strong>“{{ deleteTarget?.name }}”</strong>?</p>
|
||||
<v-alert
|
||||
:type="deleteTarget && deleteTarget.asset_count ? 'warning' : 'info'"
|
||||
variant="tonal"
|
||||
density="comfortable"
|
||||
class="mb-3"
|
||||
>
|
||||
<template v-if="deleteTarget && deleteTarget.asset_count">
|
||||
This template is currently assigned to <strong>{{ deleteTarget.asset_count }}</strong>
|
||||
asset{{ deleteTarget.asset_count === 1 ? '' : 's' }}.
|
||||
</template>
|
||||
<template v-else>
|
||||
This template is not assigned to any assets.
|
||||
</template>
|
||||
</v-alert>
|
||||
<p class="mb-2">When you delete it:</p>
|
||||
<ul class="delete-impact">
|
||||
<li>Assigned assets <strong>keep all their data and custom-field values</strong> — nothing is removed from them.</li>
|
||||
<li>Those assets are simply <strong>unlinked</strong> from this template.</li>
|
||||
<li>The template will <strong>no longer be available</strong> when creating new assets.</li>
|
||||
</ul>
|
||||
<p class="quiet text-body-2 mt-3">This action cannot be undone.</p>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn variant="text" @click="deleteDialog = false">Cancel</v-btn>
|
||||
<v-btn color="error" prepend-icon="mdi-delete-outline" @click="performDelete">Delete template</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataTable from '../components/DataTable.vue';
|
||||
import { slugFieldKey } from '../utils/assets';
|
||||
import { clonePlain } from '../utils/clone';
|
||||
|
||||
function fieldId() {
|
||||
return crypto.randomUUID ? crypto.randomUUID() : String(Date.now() + Math.random());
|
||||
}
|
||||
|
||||
export default {
|
||||
components: { DataTable },
|
||||
props: {
|
||||
categoryItems: { type: Array, required: true },
|
||||
customFieldTypes: { type: Array, required: true },
|
||||
methodItems: { type: Array, required: true },
|
||||
templateForm: { type: Object, required: true },
|
||||
templates: { type: Array, required: true }
|
||||
templates: { type: Array, required: true },
|
||||
canDelete: { type: Boolean, default: false }
|
||||
},
|
||||
emits: ['save-template'],
|
||||
emits: ['save-template', 'delete-template'],
|
||||
data() {
|
||||
return {
|
||||
localForm: clonePlain(this.templateForm)
|
||||
localForm: clonePlain(this.templateForm),
|
||||
deleteDialog: false,
|
||||
deleteTarget: null,
|
||||
columns: [
|
||||
{ key: 'name', label: 'Template' },
|
||||
{ key: 'description', label: 'Description' },
|
||||
{ key: 'fields_count', label: 'Fields', format: 'number' },
|
||||
{ key: 'asset_count', label: 'Assets', format: 'number' }
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
templateRows() {
|
||||
return this.templates.map((template) => ({
|
||||
id: template.id,
|
||||
name: template.name,
|
||||
description: template.description,
|
||||
fields_count: (template.custom_fields || []).length,
|
||||
asset_count: template.asset_count || 0
|
||||
}));
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
templateForm: {
|
||||
handler(value) {
|
||||
@@ -69,7 +147,7 @@ export default {
|
||||
methods: {
|
||||
addField() {
|
||||
this.localForm.custom_fields.push({
|
||||
local_id: crypto.randomUUID ? crypto.randomUUID() : String(Date.now() + Math.random()),
|
||||
local_id: fieldId(),
|
||||
key: '',
|
||||
label: '',
|
||||
type: 'text',
|
||||
@@ -79,7 +157,59 @@ export default {
|
||||
},
|
||||
syncFieldKey(field) {
|
||||
if (!field.key) field.key = slugFieldKey(field.label);
|
||||
},
|
||||
startNew() {
|
||||
this.localForm = {
|
||||
id: null,
|
||||
name: '',
|
||||
description: '',
|
||||
defaults: { category: 'Computer Equipment', useful_life_months: 60, depreciation_method: 'straight_line' },
|
||||
custom_fields: []
|
||||
};
|
||||
},
|
||||
editTemplate(row) {
|
||||
const template = this.templates.find((item) => item.id === row.id);
|
||||
if (!template) return;
|
||||
this.localForm = {
|
||||
id: template.id,
|
||||
name: template.name,
|
||||
description: template.description || '',
|
||||
defaults: {
|
||||
category: 'Computer Equipment',
|
||||
useful_life_months: 60,
|
||||
depreciation_method: 'straight_line',
|
||||
...(template.defaults || {})
|
||||
},
|
||||
custom_fields: (template.custom_fields || []).map((f) => ({
|
||||
local_id: fieldId(),
|
||||
key: f.key || '',
|
||||
label: f.label || '',
|
||||
type: f.type || 'text',
|
||||
required: Boolean(f.required),
|
||||
defaultValue: f.defaultValue ?? f.default ?? ''
|
||||
}))
|
||||
};
|
||||
if (typeof window !== 'undefined') window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
},
|
||||
confirmDelete(row) {
|
||||
this.deleteTarget = row;
|
||||
this.deleteDialog = true;
|
||||
},
|
||||
performDelete() {
|
||||
if (this.deleteTarget) this.$emit('delete-template', this.deleteTarget.id);
|
||||
this.deleteDialog = false;
|
||||
this.deleteTarget = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.delete-impact {
|
||||
padding-left: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.delete-impact li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user