homemanager-fe/src/components/item/NewItemModal.vue

297 lines
7.7 KiB
Vue

<template>
<Modal ref="modalRef" size="xl">
<template #title> Add a new item </template>
<template #default="{ closeModal }">
<FormAlert :message="error" />
<Form @submit="onSubmit" v-model="data" :validators="validators">
<FormAutocompleteField
v-if="typeof item !== 'string'"
name="item"
label="Item"
required
:search-fn="searchForItems"
>
<template #notfound="{ query }">
<button @click="startAddingNewItem(query)">Add a new item</button>
</template>
</FormAutocompleteField>
<template v-if="item && typeof item === 'string'">
<FormField name="displayName" label="Display Name" required />
<FormSelectField
:options="itemTypesOptions"
required
name="type"
label="Type"
/>
<FormField name="barcode" label="Barcode" />
<FormField name="url" label="URL" />
<div class="grid space-y-2 sm:grid-cols-3 sm:space-y-0 sm:space-x-2">
<FormField
name="weight"
type="number"
step="0.001"
label="Weight (g)"
/>
<FormField
name="consumable"
type="checkbox"
label="Consumable / Food item"
/>
<FormField name="public" type="checkbox" label="Public item" />
</div>
<FormTextareaField name="notes" label="Notes" />
</template>
<FormGroup name="transactionInfo">
<FormDateField
name="actionAt"
label="Action committed at"
:disabled="!item"
required
/>
<div class="grid grid-cols-2 space-x-2 sm:grid-cols-4">
<FormField
name="price"
label="Cost"
type="number"
step=".01"
class="sm:col-span-3"
:disabled="!item"
/>
<FormSelectField
:options="currencies"
:disabled="!item"
name="currency"
label="Currency"
/>
</div>
<FormTextareaField
:disabled="!item"
name="notes"
label="Additional notes about the transaction"
/>
</FormGroup>
<FormGroup name="additionalInfo">
<div class="grid space-y-2 sm:grid-cols-3 sm:space-y-0 sm:space-x-2">
<FormDateField
name="expiresAt"
label="Expires at"
:disabled="!item"
/>
<FormDateField
name="acquiredAt"
label="Acquired at"
:disabled="!item"
/>
<FormDateField
name="consumedAt"
label="Consumed at"
:disabled="!item"
/>
</div>
<FormTextareaField
name="notes"
label="Additional notes about the individual stored item"
:disabled="!item"
/>
</FormGroup>
<div class="flex justify-end space-x-1">
<Button button-type="submit" @click="submitlock = true"
>Submit and add another</Button
>
<Button button-type="submit">Submit</Button>
</div>
</Form>
</template>
</Modal>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { useAccessToken } from '../../composables/useAccessToken';
import { BACKEND_URL } from '../../constants';
import { BuildingListItem } from '../../interfaces/building.interfaces';
import {
StorageItem,
StorageListItem,
StoredItem,
} from '../../interfaces/storage.interfaces';
import jfetch from '../../utils/jfetch';
import takeError from '../../utils/take-error';
import { FormSubmit, SelectOption } from '../form/form.types';
import Form from '../form/Form.vue';
import FormAlert from '../form/FormAlert.vue';
import FormField from '../form/FormField.vue';
import FormSelectField from '../form/fields/FormSelectField.vue';
import { IsRequired, MinLength } from '../form/validators';
import Modal from '../Modal.vue';
import { ItemType, ItemTypeName } from '../../enums/item-type.enum';
import { TransactionType } from '../../enums/transaction-type.enum';
import FormGroup from '../form/FormGroup.vue';
import FormDateField from '../form/fields/FormDateField.vue';
import FormAutocompleteField from '../form/fields/FormAutocompleteField.vue';
import { FormValidator } from '../form/validator.types';
import deepUnref from '../../utils/deep-unref';
import FormTextareaField from '../form/fields/FormTextareaField.vue';
import Button from '../Button.vue';
const defaults: any = {
item: null,
transactionInfo: {
type: TransactionType.ACQUIRED,
actionAt: new Date(),
},
additionalInfo: {
acquiredAt: new Date(),
},
};
const { authHeader } = useAccessToken();
const modalRef = ref<InstanceType<typeof Modal>>();
const building = ref<BuildingListItem>();
const storage = ref<StorageListItem>();
const item = ref<StorageItem | string | null>(null);
const data: any = ref({
...deepUnref(defaults),
});
const error = ref('');
const submitlock = ref(false);
const emit = defineEmits<{
(e: 'added', storage: StoredItem): void;
}>();
const itemTypesOptions = computed(() => {
return Object.keys(ItemTypeName).reduce<SelectOption[]>(
(list, key) => [
...list,
{
value: key.toString(),
name: ItemTypeName[key as ItemType],
},
],
[]
);
});
const validators = ref<FormValidator[]>([
{
field: 'displayName',
validators: [MinLength(3), IsRequired()],
},
{
field: 'type',
validators: [IsRequired()],
},
{
field: 'transactionInfo.actionAt',
validators: [IsRequired()],
},
]);
const currencies = [
{
value: 'EUR',
name: 'EUR',
},
{
value: 'USD',
name: 'USD',
},
];
const searchForItems = async (search: string) => {
if (!search || search.length < 3) return [];
const query = new URLSearchParams();
if (search.startsWith('b:') && search.length > 3) {
query.append('barcode', search.substring(2));
} else {
query.append('searchTerm', search);
}
const { data: list } = await jfetch(
`${BACKEND_URL}/storage/item?${query.toString()}`,
{
headers: authHeader.value,
}
);
return list;
};
defineExpose({
openModal: (useBuilding: BuildingListItem, useStorage: StorageListItem) => {
building.value = useBuilding;
storage.value = useStorage;
item.value = null;
data.value = {
...deepUnref(defaults),
};
modalRef.value?.openModal();
},
});
watch(
() => data.value.item,
(selected) => {
if (selected && selected.id) {
item.value = selected;
return;
}
item.value = null;
}
);
const startAddingNewItem = (insert: string) => {
item.value = insert;
if (insert.startsWith('b:')) {
data.value['barcode'] = insert.substring(2);
} else {
data.value['displayName'] = insert;
}
};
const onSubmit = async (form: FormSubmit) => {
error.value = '';
if (!form.isValid || !storage.value) {
submitlock.value = false;
return;
}
const body = {
...form.formData,
};
delete body.item;
const requestUrl = `${BACKEND_URL}/storage/item/${storage.value.id}${
item.value && typeof item.value !== 'string' ? `/${item.value.id}` : ''
}`;
let createdStoredItem: StoredItem;
try {
const { data: response } = await jfetch(requestUrl, {
method: 'POST',
headers: authHeader.value,
body,
});
createdStoredItem = response;
} catch (e) {
error.value = takeError(e);
return;
}
emit('added', createdStoredItem);
if (submitlock.value) {
submitlock.value = false;
return;
}
modalRef.value?.closeModal();
};
</script>