forked from mirrors/homebox
95ab14b866
* format readme * update logo * format html * add logo to docs * repository for document and document tokens * add attachments type and repository * autogenerate types via scripts * use autogenerated types * attachment type updates * add insured and quantity fields for items * implement HasID interface for entities * implement label updates for items * implement service update method * WIP item update client side actions * check err on attachment * finish types for basic items editor * remove unused var * house keeping
349 lines
11 KiB
Vue
349 lines
11 KiB
Vue
<script setup lang="ts">
|
|
import { ItemUpdate } from "~~/lib/api/types/data-contracts";
|
|
|
|
definePageMeta({
|
|
layout: "home",
|
|
});
|
|
|
|
const route = useRoute();
|
|
const api = useUserApi();
|
|
const toast = useNotifier();
|
|
const preferences = useViewPreferences();
|
|
|
|
const itemId = computed<string>(() => route.params.id as string);
|
|
|
|
const { data: locations } = useAsyncData(async () => {
|
|
const { data } = await api.locations.getAll();
|
|
return data.items;
|
|
});
|
|
|
|
const { data: labels } = useAsyncData(async () => {
|
|
const { data } = await api.labels.getAll();
|
|
return data.items;
|
|
});
|
|
|
|
const { data: item } = useAsyncData(async () => {
|
|
const { data, error } = await api.items.get(itemId.value);
|
|
if (error) {
|
|
toast.error("Failed to load item");
|
|
navigateTo("/home");
|
|
return;
|
|
}
|
|
|
|
return data;
|
|
});
|
|
|
|
async function saveItem() {
|
|
const payload: ItemUpdate = {
|
|
...item.value,
|
|
locationId: item.value.location?.id,
|
|
labelIds: item.value.labels.map(l => l.id),
|
|
};
|
|
|
|
const { error } = await api.items.update(itemId.value, payload);
|
|
|
|
if (error) {
|
|
toast.error("Failed to save item");
|
|
return;
|
|
}
|
|
|
|
toast.success("Item saved");
|
|
navigateTo("/item/" + itemId.value);
|
|
}
|
|
|
|
type FormField = {
|
|
type: "text" | "textarea" | "select" | "date" | "label" | "location" | "number" | "checkbox";
|
|
label: string;
|
|
ref: string;
|
|
};
|
|
|
|
const mainFields: FormField[] = [
|
|
{
|
|
type: "text",
|
|
label: "Name",
|
|
ref: "name",
|
|
},
|
|
{
|
|
type: "number",
|
|
label: "Quantity",
|
|
ref: "quantity",
|
|
},
|
|
{
|
|
type: "textarea",
|
|
label: "Description",
|
|
ref: "description",
|
|
},
|
|
{
|
|
type: "text",
|
|
label: "Serial Number",
|
|
ref: "serialNumber",
|
|
},
|
|
{
|
|
type: "text",
|
|
label: "Model Number",
|
|
ref: "modelNumber",
|
|
},
|
|
{
|
|
type: "text",
|
|
label: "Manufacturer",
|
|
ref: "manufacturer",
|
|
},
|
|
{
|
|
type: "textarea",
|
|
label: "Notes",
|
|
ref: "notes",
|
|
},
|
|
{
|
|
type: "checkbox",
|
|
label: "Insured",
|
|
ref: "insured",
|
|
},
|
|
];
|
|
|
|
const purchaseFields: FormField[] = [
|
|
{
|
|
type: "text",
|
|
label: "Purchased From",
|
|
ref: "purchaseFrom",
|
|
},
|
|
{
|
|
type: "text",
|
|
label: "Purchased Price",
|
|
ref: "purchasePrice",
|
|
},
|
|
{
|
|
type: "date",
|
|
label: "Purchased At",
|
|
ref: "purchaseTime",
|
|
},
|
|
];
|
|
|
|
const warrantyFields: FormField[] = [
|
|
{
|
|
type: "checkbox",
|
|
label: "Lifetime Warranty",
|
|
ref: "lifetimeWarranty",
|
|
},
|
|
{
|
|
type: "date",
|
|
label: "Warranty Expires",
|
|
ref: "warrantyExpires",
|
|
},
|
|
{
|
|
type: "textarea",
|
|
label: "Warranty Notes",
|
|
ref: "warrantyDetails",
|
|
},
|
|
];
|
|
|
|
const soldFields = [
|
|
{
|
|
type: "text",
|
|
label: "Sold To",
|
|
ref: "soldTo",
|
|
},
|
|
{
|
|
type: "text",
|
|
label: "Sold Price",
|
|
ref: "soldPrice",
|
|
},
|
|
{
|
|
type: "date",
|
|
label: "Sold At",
|
|
ref: "soldTime",
|
|
},
|
|
];
|
|
</script>
|
|
|
|
<template>
|
|
<BaseContainer v-if="item" class="pb-8">
|
|
<section class="px-3">
|
|
<div class="space-y-4">
|
|
<div class="overflow-hidden card bg-base-100 shadow-xl sm:rounded-lg">
|
|
<BaseSectionHeader v-if="item" class="p-5">
|
|
<Icon name="mdi-package-variant" class="-mt-1 mr-2 text-gray-600" />
|
|
<span class="text-gray-600">
|
|
{{ item.name }}
|
|
</span>
|
|
<p class="text-sm text-gray-600 font-bold pb-0 mb-0">Quantity {{ item.quantity }}</p>
|
|
<template #after>
|
|
<div class="modal-action mt-3">
|
|
<div class="mr-auto tooltip" data-tip="Hide the cruft! ">
|
|
<label class="label cursor-pointer mr-auto">
|
|
<input v-model="preferences.editorSimpleView" type="checkbox" class="toggle toggle-primary" />
|
|
<span class="label-text ml-4"> Simple View </span>
|
|
</label>
|
|
</div>
|
|
<BaseButton size="sm" @click="saveItem">
|
|
<template #icon>
|
|
<Icon name="mdi-content-save-outline" />
|
|
</template>
|
|
Save
|
|
</BaseButton>
|
|
</div>
|
|
</template>
|
|
</BaseSectionHeader>
|
|
<div class="px-5 mb-6 grid md:grid-cols-2 gap-4">
|
|
<FormSelect v-model="item.location" label="Location" :items="locations ?? []" select-first />
|
|
<FormMultiselect v-model="item.labels" label="Labels" :items="labels ?? []" />
|
|
</div>
|
|
|
|
<div class="border-t border-gray-300 sm:p-0">
|
|
<div v-for="field in mainFields" :key="field.ref" class="sm:divide-y sm:divide-gray-300 grid grid-cols-1">
|
|
<div class="pt-2 px-4 pb-4 sm:px-6 border-b border-gray-300">
|
|
<FormTextArea v-if="field.type === 'textarea'" v-model="item[field.ref]" :label="field.label" inline />
|
|
<FormTextField
|
|
v-else-if="field.type === 'text'"
|
|
v-model="item[field.ref]"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
<FormTextField
|
|
v-else-if="field.type === 'number'"
|
|
v-model.number="item[field.ref]"
|
|
type="number"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
<FormDatePicker
|
|
v-else-if="field.type === 'date'"
|
|
v-model="item[field.ref]"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
<FormCheckbox
|
|
v-else-if="field.type === 'checkbox'"
|
|
v-model="item[field.ref]"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="!preferences.editorSimpleView" class="overflow-visible card bg-base-100 shadow-xl sm:rounded-lg">
|
|
<div class="px-4 py-5 sm:px-6">
|
|
<h3 class="text-lg font-medium leading-6">Purchase Details</h3>
|
|
</div>
|
|
<div class="border-t border-gray-300 sm:p-0">
|
|
<div
|
|
v-for="field in purchaseFields"
|
|
:key="field.ref"
|
|
class="sm:divide-y sm:divide-gray-300 grid grid-cols-1"
|
|
>
|
|
<div class="pt-2 px-4 pb-4 sm:px-6 border-b border-gray-300">
|
|
<FormTextArea v-if="field.type === 'textarea'" v-model="item[field.ref]" :label="field.label" inline />
|
|
<FormTextField
|
|
v-else-if="field.type === 'text'"
|
|
v-model="item[field.ref]"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
<FormTextField
|
|
v-else-if="field.type === 'number'"
|
|
v-model.number="item[field.ref]"
|
|
type="number"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
<FormDatePicker
|
|
v-else-if="field.type === 'date'"
|
|
v-model="item[field.ref]"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
<FormCheckbox
|
|
v-else-if="field.type === 'checkbox'"
|
|
v-model="item[field.ref]"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="!preferences.editorSimpleView" class="overflow-visible card bg-base-100 shadow-xl sm:rounded-lg">
|
|
<div class="px-4 py-5 sm:px-6">
|
|
<h3 class="text-lg font-medium leading-6">Warranty Details</h3>
|
|
</div>
|
|
<div class="border-t border-gray-300 sm:p-0">
|
|
<div
|
|
v-for="field in warrantyFields"
|
|
:key="field.ref"
|
|
class="sm:divide-y sm:divide-gray-300 grid grid-cols-1"
|
|
>
|
|
<div class="pt-2 px-4 pb-4 sm:px-6 border-b border-gray-300">
|
|
<FormTextArea v-if="field.type === 'textarea'" v-model="item[field.ref]" :label="field.label" inline />
|
|
<FormTextField
|
|
v-else-if="field.type === 'text'"
|
|
v-model="item[field.ref]"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
<FormTextField
|
|
v-else-if="field.type === 'number'"
|
|
v-model.number="item[field.ref]"
|
|
type="number"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
<FormDatePicker
|
|
v-else-if="field.type === 'date'"
|
|
v-model="item[field.ref]"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
<FormCheckbox
|
|
v-else-if="field.type === 'checkbox'"
|
|
v-model="item[field.ref]"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="!preferences.editorSimpleView" class="overflow-visible card bg-base-100 shadow-xl sm:rounded-lg">
|
|
<div class="px-4 py-5 sm:px-6">
|
|
<h3 class="text-lg font-medium leading-6">Sold Details</h3>
|
|
</div>
|
|
<div class="border-t border-gray-300 sm:p-0">
|
|
<div v-for="field in soldFields" :key="field.ref" class="sm:divide-y sm:divide-gray-300 grid grid-cols-1">
|
|
<div class="pt-2 pb-4 px-4 sm:px-6 border-b border-gray-300">
|
|
<FormTextArea v-if="field.type === 'textarea'" v-model="item[field.ref]" :label="field.label" inline />
|
|
<FormTextField
|
|
v-else-if="field.type === 'text'"
|
|
v-model="item[field.ref]"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
<FormTextField
|
|
v-else-if="field.type === 'number'"
|
|
v-model.number="item[field.ref]"
|
|
type="number"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
<FormDatePicker
|
|
v-else-if="field.type === 'date'"
|
|
v-model="item[field.ref]"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
<FormCheckbox
|
|
v-else-if="field.type === 'checkbox'"
|
|
v-model="item[field.ref]"
|
|
:label="field.label"
|
|
inline
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</BaseContainer>
|
|
</template>
|