Files
Ferme/frontend/components/reception/update-bovin.vue

205 lines
6.2 KiB
Vue

<template>
<form>
<div class="flex flex-row justify-between gap-x-12 font-bold uppercase mb-8">
<div
v-for="type in bovineType"
:key="type.id"
>
<UiNumberInput
:label="type.label"
:code="type.code"
v-model="bovineQuantities[String(type.id)]"
:disabled="!auth.isAdmin"
:placeholder="0"
:min="0"
:max="10"
wrapperClass="w-44 flex-col"
/>
</div>
<UiNumberInput
label="Autres"
v-model="otherQuantity"
:disabled="!auth.isAdmin"
wrapperClass="w-44 flex-col"
/>
</div>
</form>
</template>
<script setup lang="ts">
import type {BovineTypeData} from "~/services/dto/bovine-type-data";
import {getBovineTypeList} from "~/services/bovine-type";
import {createReceptionBovine, deleteReceptionBovine, getReceptionBovineList, updateReceptionBovine} from "~/services/reception-bovine";
import {computed, onMounted, reactive, ref, watch} from "vue";
import {getReception, updateReception} from "~/services/reception";
const toast = useToast()
const isLoadingBovineType = ref(false)
const bovineType = ref<BovineTypeData[]>([])
const bovineQuantities = reactive<Record<string, number | null>>({})
const otherQuantity = ref<number | null>(0)
const initialBovineQuantities = ref<Record<string, number | null>>({})
const initialOtherQuantity = ref<number | null>(0)
const auth = useAuthStore()
const props = defineProps<{
idReception: number
isValidate: boolean
}>()
const receptionId = props.idReception
const reception = await getReception(receptionId)
const receptionIri = computed(() =>
receptionId ? `/api/receptions/${receptionId}` : null
)
const totalBovines = computed(() => {
const base = Object.values(bovineQuantities).reduce((sum, value) => {
return sum + (value ?? 0)
}, 0)
return base + (otherQuantity.value ?? 0)
})
const hasBovineChanged = () => {
if ((initialOtherQuantity.value ?? 0) !== (otherQuantity.value ?? 0)) {
return true
}
for (const [key, value] of Object.entries(bovineQuantities)) {
if ((initialBovineQuantities.value[key] ?? 0) !== (value ?? 0)) {
return true
}
}
return false
}
const loadBovineType = async () => {
isLoadingBovineType.value = true
try {
bovineType.value = await getBovineTypeList()
} finally {
isLoadingBovineType.value = false
}
}
onMounted(async () => {
await loadBovineType()
})
watch(
[() => receptionId, () => bovineType.value],
async ([id, types]) => {
if (!id || !receptionIri.value || types.length === 0) {
return
}
const selectionMap: Record<string, number | null> = {}
for (const type of types) {
selectionMap[String(type.id)] = 0
}
const existing = await getReceptionBovineList(receptionIri.value)
for (const selection of existing) {
const bovineTypeId = String(selection.bovineType.id)
selectionMap[bovineTypeId] = selection.quantity ?? 0
}
for (const key of Object.keys(bovineQuantities)) {
delete bovineQuantities[key]
}
Object.assign(bovineQuantities, selectionMap)
const existingOther = reception.bovineDetail
const parsedOther =
typeof existingOther === 'string' && existingOther.trim() !== ''
? Number(existingOther)
: 0
otherQuantity.value = Number.isFinite(parsedOther) ? parsedOther : 0
initialBovineQuantities.value = {...selectionMap}
initialOtherQuantity.value = otherQuantity.value ?? 0
},
{immediate: true}
)
async function syncBovineSelections(receptionIri: string) {
const existing = await getReceptionBovineList(receptionIri)
const existingMap = new Map<string, { id: number; quantity: number | null }>()
for (const selection of existing) {
const bovineTypeId = String(selection.bovineType.id)
existingMap.set(bovineTypeId, {
id: selection.id,
quantity: selection.quantity ?? 0
})
}
// Supprime les entrées supprimées ou modifiées
for (const [bovineTypeId, entry] of existingMap.entries()) {
const selectedQuantity = bovineQuantities[bovineTypeId] ?? 0
if (!selectedQuantity) {
await deleteReceptionBovine(entry.id)
existingMap.delete(bovineTypeId)
continue
}
if (selectedQuantity !== entry.quantity) {
await updateReceptionBovine(entry.id, {quantity: selectedQuantity})
existingMap.set(bovineTypeId, {
id: entry.id,
quantity: selectedQuantity
})
}
}
// Crée les entrées manquantes
for (const [bovineTypeId, quantity] of Object.entries(bovineQuantities)) {
if (!quantity) {
continue
}
if (existingMap.has(bovineTypeId)) {
continue
}
await createReceptionBovine({
reception: receptionIri,
bovineType: `/api/bovine_types/${bovineTypeId}`,
quantity
})
}
}
watch(
() => props.isValidate,
async (val) => {
if (!val) return
await runValidate()
}
)
const runValidate = async () => {
if (!hasBovineChanged()) {
return
}
const receptionIri = `/api/receptions/${reception.id}`
// @TODO Ajouter un composable pour le toaster qui gère les key i18n
if (totalBovines.value > 52) {
toast.error({
title: 'Erreur',
message: ('Le total des bovins ne peut pas dépasser 52.')
})
return
}
await syncBovineSelections(receptionIri)
await updateReception(receptionId, {
merchandiseType: null,
merchandiseDetail: null,
bovineDetail: otherQuantity.value ? String(otherQuantity.value) : null,
})
initialBovineQuantities.value = {...bovineQuantities}
initialOtherQuantity.value = otherQuantity.value ?? 0
}
</script>