feat(front) : page saisie information bovin (accordéons)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-04 11:33:55 +02:00
parent 92c8c6a704
commit 2166fe2685

View File

@@ -0,0 +1,128 @@
<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]"
/>
<h1 class="font-bold text-3xl uppercase text-primary-500">
Saisie information bovin {{ reception?.identificationNumber ?? '' }}
</h1>
</div>
<div v-if="loading" class="text-center text-slate-500">Chargement</div>
<div v-else class="space-y-3">
<UiAccordion
v-for="bovine in sortedBovines"
:key="bovine.id"
:model-value="openId === bovine.id"
@update:model-value="onToggle(bovine.id, $event)"
>
<template #header>
<span class="flex items-center gap-3 normal-case">
<span class="font-bold text-base">{{ bovine.nationalNumber }}</span>
<span
v-if="isSaisi(bovine)"
class="inline-block rounded px-2 py-0.5 text-xs font-semibold bg-green-100 text-green-700"
>
Saisie
</span>
<span
v-else
class="inline-block rounded px-2 py-0.5 text-xs font-semibold bg-yellow-100 text-yellow-700"
>
Attente saisie
</span>
</span>
</template>
<BovineInfoForm
:bovine="bovine"
:buildings="buildings"
@saved="onSaved"
/>
</UiAccordion>
</div>
</div>
</template>
<script setup lang="ts">
import type { BovineData } from '~/services/dto/bovine-data'
import type { BuildingData } from '~/services/dto/building-data'
import type { ReceptionData } from '~/services/dto/reception-data'
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 bovines = ref<BovineData[]>([])
const buildings = ref<BuildingData[]>([])
const loading = ref(true)
const openId = ref<number | null>(null)
useHead({
title: () => `Saisie information bovin ${reception.value?.identificationNumber ?? ''}`.trim()
})
const isSaisi = (bovine: BovineData) =>
bovine.receivedWeight !== null
&& bovine.pricePerKg !== null
&& bovine.buildingCase !== null
const sortedBovines = computed(() => {
const pending = bovines.value.filter(b => !isSaisi(b))
const done = bovines.value.filter(b => isSaisi(b))
return [...pending, ...done]
})
const onToggle = (bovineId: number, value: boolean) => {
openId.value = value ? bovineId : null
}
const onSaved = (updated: BovineData) => {
const idx = bovines.value.findIndex(b => b.id === updated.id)
if (idx === -1) return
bovines.value[idx] = updated
const next = sortedBovines.value.find(b => !isSaisi(b) && b.id !== updated.id)
openId.value = next?.id ?? null
}
const loadBovines = async () => {
type Hydra = { 'hydra:member'?: BovineData[] }
const response = await api.get<BovineData[] | Hydra>(
'bovines',
{ reception: receptionId.value, itemsPerPage: 200 }
)
if (Array.isArray(response)) {
bovines.value = response
} else if (response && typeof response === 'object' && Array.isArray(response['hydra:member'])) {
bovines.value = response['hydra:member']
} else {
bovines.value = []
}
}
onMounted(async () => {
try {
const [r, , b] = await Promise.all([
api.get<ReceptionData>(`receptions/${receptionId.value}`),
loadBovines(),
getBuildingList()
])
reception.value = r
buildings.value = b
const firstPending = sortedBovines.value.find(bv => !isSaisi(bv))
openId.value = firstPending?.id ?? null
} finally {
loading.value = false
}
})
</script>