- Nouvelle page /bovine/[id] avec tabs Mouvement / Passeport bovin / Santé - Composant UiTabs partagé, réutilisé sur réception et expédition - Champs père/mère (numéro national + type de race) sur Bovine, alimentés via la sync EDNOTIF - Inventaire : ligne cliquable vers le passeport Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
136 lines
7.2 KiB
Vue
136 lines
7.2 KiB
Vue
<template>
|
|
<div class="px-[86px]">
|
|
<div class="flex items-center justify-between relative mb-10">
|
|
<div class="flex flex-row absolute -left-[60px]">
|
|
<Icon
|
|
@click="router.push('/inventory')"
|
|
name="gg:arrow-left-o"
|
|
size="44"
|
|
class="cursor-pointer text-primary-500"
|
|
/>
|
|
</div>
|
|
<h1 class="font-bold text-3xl uppercase text-primary-500">Vie du bovin</h1>
|
|
</div>
|
|
|
|
<UiTabs
|
|
v-model="activeTab"
|
|
:tabs="[
|
|
{ key: 'mouvement', label: 'Mouvement' },
|
|
{ key: 'passeport', label: 'Passeport bovin' },
|
|
{ key: 'sante', label: 'Santé' }
|
|
]"
|
|
/>
|
|
|
|
<div v-show="activeTab === 'passeport'">
|
|
<div class="mt-6">
|
|
<div class="grid grid-cols-[3rem_repeat(6,minmax(0,1fr))] grid-rows-2 border-2 border-black">
|
|
<div class="row-span-2 flex items-center justify-center border-r-2 border-black">
|
|
<span class="uppercase font-bold -rotate-90 whitespace-nowrap transform-gpu">Veau</span>
|
|
</div>
|
|
<div class="border-b border-r border-black px-2 py-1 text-center font-semibold text-sm">Numéro national</div>
|
|
<div class="border-b border-r border-black px-2 py-1 text-center font-semibold text-sm">N° de travail</div>
|
|
<div class="border-b border-r border-black px-2 py-1 text-center font-semibold text-sm">Sexe</div>
|
|
<div class="border-b border-r border-black px-2 py-1 text-center font-semibold text-sm">Code race</div>
|
|
<div class="border-b border-r border-black px-2 py-1 text-center font-semibold text-sm">Type de race</div>
|
|
<div class="border-b border-black px-2 py-1 text-center font-semibold text-sm">Date de naissance</div>
|
|
<div class="border-r border-black px-2 py-1 text-center">{{ display(bovine?.nationalNumber) }}</div>
|
|
<div class="border-r border-black px-2 py-1 text-center">{{ display(bovine?.workNumber) }}</div>
|
|
<div class="border-r border-black px-2 py-1 text-center">{{ display(bovine?.sex) }}</div>
|
|
<div class="border-r border-black px-2 py-1 text-center">{{ display(bovine?.bovineType?.code) }}</div>
|
|
<div class="border-r border-black px-2 py-1 text-center">{{ display(bovine?.bovineType?.label) }}</div>
|
|
<div class="px-2 py-1 text-center">{{ formatDate(bovine?.birthDate) }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-9">
|
|
<div class="grid grid-cols-[3rem_repeat(6,minmax(0,1fr))] grid-rows-2 border-2 border-black">
|
|
<div class="row-span-2 flex items-center justify-center border-r-2 border-black">
|
|
<span class="uppercase font-bold -rotate-90 whitespace-nowrap transform-gpu">Père</span>
|
|
</div>
|
|
<div class="border-b border-r border-black px-2 py-1 text-center font-semibold text-sm">Numéro national</div>
|
|
<div class="col-span-2 border-b border-r border-black px-2 py-1 text-center font-semibold text-sm">N° de travail</div>
|
|
<div class="border-b border-r border-black px-2 py-1 text-center font-semibold text-sm">Code race</div>
|
|
<div class="col-span-2 border-b border-black px-2 py-1 text-center font-semibold text-sm">Type de race</div>
|
|
<div class="border-r border-black px-2 py-1 text-center">{{ display(bovine?.fatherNationalNumber) }}</div>
|
|
<div class="col-span-2 border-r border-black px-2 py-1 text-center">{{ display(workNumberFromNational(bovine?.fatherNationalNumber)) }}</div>
|
|
<div class="border-r border-black px-2 py-1 text-center">{{ display(bovine?.fatherBovineType?.code) }}</div>
|
|
<div class="col-span-2 px-2 py-1 text-center">{{ display(bovine?.fatherBovineType?.label) }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-9">
|
|
<div class="grid grid-cols-[3rem_repeat(6,minmax(0,1fr))] grid-rows-2 border-2 border-black">
|
|
<div class="row-span-2 flex items-center justify-center border-r-2 border-black">
|
|
<span class="uppercase font-bold -rotate-90 whitespace-nowrap transform-gpu">Mère</span>
|
|
</div>
|
|
<div class="border-b border-r border-black px-2 py-1 text-center font-semibold text-sm">Numéro national</div>
|
|
<div class="col-span-2 border-b border-r border-black px-2 py-1 text-center font-semibold text-sm">N° de travail</div>
|
|
<div class="border-b border-r border-black px-2 py-1 text-center font-semibold text-sm">Code race</div>
|
|
<div class="col-span-2 border-b border-black px-2 py-1 text-center font-semibold text-sm">Type de race</div>
|
|
<div class="border-r border-black px-2 py-1 text-center">{{ display(bovine?.motherNationalNumber) }}</div>
|
|
<div class="col-span-2 border-r border-black px-2 py-1 text-center">{{ display(workNumberFromNational(bovine?.motherNationalNumber)) }}</div>
|
|
<div class="border-r border-black px-2 py-1 text-center">{{ display(bovine?.motherBovineType?.code) }}</div>
|
|
<div class="col-span-2 px-2 py-1 text-center">{{ display(bovine?.motherBovineType?.label) }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
useHead({ title: 'Vie du bovin' })
|
|
|
|
type BovineTab = 'mouvement' | 'passeport' | 'sante'
|
|
const activeTab = ref<BovineTab>('passeport')
|
|
|
|
interface BovineTypeRef {
|
|
id: number
|
|
label: string | null
|
|
code: string | null
|
|
}
|
|
|
|
interface BovinePassportData {
|
|
id: number
|
|
nationalNumber: string
|
|
workNumber: string | null
|
|
sex: string | null
|
|
birthDate: string | null
|
|
bovineType: BovineTypeRef | null
|
|
motherNationalNumber: string | null
|
|
motherBovineType: BovineTypeRef | null
|
|
fatherNationalNumber: string | null
|
|
fatherBovineType: BovineTypeRef | null
|
|
}
|
|
|
|
const router = useRouter()
|
|
const route = useRoute()
|
|
const api = useApi()
|
|
|
|
const bovine = ref<BovinePassportData | null>(null)
|
|
|
|
const bovineId = computed(() => {
|
|
const raw = Array.isArray(route.params.id) ? route.params.id[0] : route.params.id
|
|
const n = Number(raw)
|
|
return Number.isFinite(n) ? n : null
|
|
})
|
|
|
|
const display = (value: string | null | undefined) => (value && value !== '' ? value : '—')
|
|
|
|
const workNumberFromNational = (nationalNumber: string | null | undefined) => {
|
|
if (!nationalNumber) return null
|
|
return nationalNumber.slice(-4)
|
|
}
|
|
|
|
const formatDate = (date: string | null | undefined) => {
|
|
if (!date) return '—'
|
|
const d = new Date(date)
|
|
if (isNaN(d.getTime())) return date
|
|
return d.toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric' })
|
|
}
|
|
|
|
onMounted(async () => {
|
|
if (bovineId.value === null) return
|
|
bovine.value = await api.get<BovinePassportData>(`bovines/${bovineId.value}`)
|
|
})
|
|
</script>
|