feat(front) : écran saisie entrée — layout header + formulaire
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
195
frontend/pages/entry-exit/entry/[id].vue
Normal file
195
frontend/pages/entry-exit/entry/[id].vue
Normal file
@@ -0,0 +1,195 @@
|
||||
<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 addBovine = async () => {
|
||||
// implémenté en Task 9
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
[suppliers.value, buildings.value] = await Promise.all([
|
||||
getSupplierList(),
|
||||
getBuildingList()
|
||||
])
|
||||
await loadReception()
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user