Files
Ferme/frontend/pages/entry-exit/entry/[id].vue
2026-04-29 10:11:24 +02:00

237 lines
7.2 KiB
Vue

<template>
<div class="px-[86px]">
<div class="flex items-center justify-start gap-6 relative mb-8">
<Icon
@click="router.push('/entry-exit')"
name="gg:arrow-left-o"
size="44"
class="cursor-pointer text-primary-500 absolute -left-[60px]"
/>
<div>
<h1 class="font-bold text-3xl uppercase text-primary-500">
Entrée bovins {{ reception?.identificationNumber ?? `#${receptionId}` }}
</h1>
<p class="text-sm text-slate-600 mt-1">
{{ reception?.supplier?.name ?? '—' }} · Bovins déclarés : {{ declaredCount }} · Bovins saisis : {{ savedBovines.length }}
</p>
</div>
</div>
<form
class="grid grid-cols-4 gap-4 mb-6 items-end"
@submit.prevent="addBovine"
>
<UiTextInput
v-model="form.nationalNumber"
label="Numéro national"
required
/>
<UiNumberInput
v-model="form.receivedWeight"
label="Poids à l'arrivée (kg)"
:min="1"
required
/>
<UiDateMaskedInput
v-model="form.arrivalDate"
label="Date d'arrivée"
required
/>
<UiSelect
v-model="form.supplierId"
label="Vendeur"
:options="supplierOptions"
required
/>
<UiNumberInput
v-model="form.pricePerKg"
label="Prix au kilo (€)"
:min="0"
:step="0.01"
required
/>
<UiSelect
v-model="form.buildingId"
label="Bâtiment"
:options="buildingOptions"
required
/>
<UiSelect
v-model="form.caseId"
label="Case"
:options="caseOptions"
:disabled="!form.buildingId"
required
/>
<UiButton
type="submit"
class="text-md font-bold uppercase bg-primary-500 text-white h-[50px]"
:disabled="!isFormValid || isAdding"
:loading="isAdding"
>
Ajouter
</UiButton>
</form>
<div class="text-slate-400 italic mt-6">Tableau récap à venir</div>
<div class="flex justify-end mt-8 mb-16">
<UiButton
type="button"
class="text-md font-bold uppercase bg-primary-500 text-white h-[50px] px-8"
disabled
>
Valider l'entrée
</UiButton>
</div>
</div>
</template>
<script setup lang="ts">
import type { ReceptionData } from '~/services/dto/reception-data'
import type { BovineData } from '~/services/dto/bovine-data'
import type { SupplierData } from '~/services/dto/supplier-data'
import type { BuildingData } from '~/services/dto/building-data'
import { getSupplierList } from '~/services/supplier'
import { getBuildingList } from '~/services/building'
const route = useRoute()
const router = useRouter()
const api = useApi()
const receptionId = computed(() => Number(route.params.id))
const reception = ref<ReceptionData | null>(null)
const suppliers = ref<SupplierData[]>([])
const buildings = ref<BuildingData[]>([])
const savedBovines = ref<BovineData[]>([])
const isAdding = ref(false)
interface FormState {
nationalNumber: string
receivedWeight: number | null
arrivalDate: string
supplierId: string | number | null
pricePerKg: number | null
buildingId: string | number | null
caseId: string | number | null
}
const initialForm = (): FormState => ({
nationalNumber: '',
receivedWeight: null,
arrivalDate: reception.value?.receptionDate?.slice(0, 10) ?? '',
supplierId: reception.value?.supplier?.id ?? null,
pricePerKg: null,
buildingId: reception.value?.buildings?.[0]?.id ?? null,
caseId: null
})
const form = reactive<FormState>(initialForm())
const supplierOptions = computed(() =>
suppliers.value.map(s => ({ value: s.id, label: s.name }))
)
const buildingOptions = computed(() =>
buildings.value.map(b => ({ value: b.id, label: b.label }))
)
const caseOptions = computed(() => {
const building = buildings.value.find(b => b.id === Number(form.buildingId))
if (!building?.buildingCases) return []
return [...building.buildingCases]
.sort((a, b) => (a.caseNumber ?? 0) - (b.caseNumber ?? 0))
.map(c => ({
value: c.id,
label: `Case ${c.caseNumber ?? c.code ?? c.id}`
}))
})
watch(() => form.buildingId, (newVal, oldVal) => {
if (newVal !== oldVal) form.caseId = null
})
const declaredCount = computed(() => {
if (!reception.value) return 0
const fromTypes = (reception.value.bovinesTypes ?? []).reduce((sum: number, bt: any) => {
return sum + (typeof bt.quantity === 'number' ? bt.quantity : 0)
}, 0)
const fromOther = parseInt(reception.value.bovineDetail ?? '0', 10) || 0
return fromTypes + fromOther
})
const isFormValid = computed(() =>
form.nationalNumber.trim() !== ''
&& (form.receivedWeight ?? 0) > 0
&& (form.pricePerKg ?? 0) > 0
&& form.arrivalDate !== ''
&& form.supplierId !== null
&& form.buildingId !== null
&& form.caseId !== null
)
const resetForm = () => {
Object.assign(form, initialForm())
}
const loadReception = async () => {
reception.value = await api.get<ReceptionData>(`receptions/${receptionId.value}`)
resetForm()
}
const loadSavedBovines = async () => {
const response = await api.get<{ 'hydra:member'?: BovineData[] } | BovineData[]>(
`bovines?reception=${receptionId.value}`,
{},
{ toast: false }
)
savedBovines.value = Array.isArray(response)
? response
: (response['hydra:member'] ?? [])
}
const focusFirstField = () => {
const el = document.querySelector<HTMLInputElement>('form input[type="text"]')
el?.focus()
}
const addBovine = async () => {
if (!isFormValid.value || isAdding.value) return
isAdding.value = true
try {
const payload = {
nationalNumber: form.nationalNumber.trim(),
receivedWeight: form.receivedWeight,
pricePerKg: form.pricePerKg,
arrivalDate: form.arrivalDate,
supplier: `/api/suppliers/${form.supplierId}`,
buildingCase: `/api/building_cases/${form.caseId}`,
reception: `/api/receptions/${receptionId.value}`
}
await api.post<BovineData>('bovines', payload, {
headers: { 'Content-Type': 'application/ld+json' }
})
await loadSavedBovines()
resetForm()
await nextTick()
focusFirstField()
} finally {
isAdding.value = false
}
}
onMounted(async () => {
[suppliers.value, buildings.value] = await Promise.all([
getSupplierList(),
getBuildingList()
])
await loadReception()
await loadSavedBovines()
})
</script>