feat: ajout du prix au kilo et de l'age moyen bovin + feed bovin via xlsx (!50)
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
| Numéro du ticket | Titre du ticket | |------------------|-----------------| | | | ## Description de la PR ## Modification du .env ## Check list - [ ] Pas de régression - [x] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié Reviewed-on: #50 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #50.
This commit is contained in:
83
frontend/composables/useBovineColumns.ts
Normal file
83
frontend/composables/useBovineColumns.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { computed } from 'vue'
|
||||
import { useAuthStore } from '~/stores/auth'
|
||||
|
||||
export interface BovineColumn {
|
||||
key: string
|
||||
label: string
|
||||
width?: string
|
||||
}
|
||||
|
||||
export interface UseBovineColumnsOptions {
|
||||
/**
|
||||
* 'inventory' (par défaut) : colonnes complètes incluant Bâtiment + Case.
|
||||
* 'case' : pas de Bâtiment ni Case (déjà dans le titre de la page),
|
||||
* largeurs élargies pour combler l'espace.
|
||||
*/
|
||||
variant?: 'inventory' | 'case'
|
||||
}
|
||||
|
||||
/**
|
||||
* Définition partagée des colonnes des tableaux bovins (inventory + case).
|
||||
* Variants distincts pour chaque écran et chaque rôle (admin/user) afin de
|
||||
* pouvoir ajuster les largeurs indépendamment.
|
||||
*/
|
||||
export const useBovineColumns = (options: UseBovineColumnsOptions = {}) => {
|
||||
const auth = useAuthStore()
|
||||
|
||||
const adminColumnsInventory: BovineColumn[] = [
|
||||
{ key: 'nationalNumber', label: 'N° National', width: '80px' },
|
||||
{ key: 'workNumber', label: 'N° Travail', width: '60px' },
|
||||
{ key: 'sex', label: 'Sexe', width: '70px' },
|
||||
{ key: 'birthDate', label: 'Né le', width: '72px' },
|
||||
{ key: 'age', label: 'Age', width: '110px' },
|
||||
{ key: 'bovineType.label', label: 'Race', width: '90px' },
|
||||
{ key: 'buildingCase.building.label', label: 'Bâtiment', width: '1fr' },
|
||||
{ key: 'buildingCase.caseNumber', label: 'Case', width: '42px' },
|
||||
{ key: 'arrivalDate', label: 'Entrée le', width: '90px' },
|
||||
{ key: 'pricePerKg', label: 'Prix/kg', width: '65px' },
|
||||
{ key: 'finalPrice', label: 'Prix total', width: '80px' }
|
||||
]
|
||||
|
||||
const userColumnsInventory: BovineColumn[] = [
|
||||
{ key: 'nationalNumber', label: 'N° National', width: '80px' },
|
||||
{ key: 'workNumber', label: 'N° Travail', width: '60px' },
|
||||
{ key: 'sex', label: 'Sexe', width: '70px' },
|
||||
{ key: 'birthDate', label: 'Né le', width: '72px' },
|
||||
{ key: 'age', label: 'Age', width: '110px' },
|
||||
{ key: 'bovineType.label', label: 'Race', width: '1fr' },
|
||||
{ key: 'buildingCase.building.label', label: 'Bâtiment', width: '120px' },
|
||||
{ key: 'buildingCase.caseNumber', label: 'Case', width: '42px' },
|
||||
{ key: 'arrivalDate', label: 'Entrée le', width: '90px' }
|
||||
]
|
||||
|
||||
const adminColumnsCase: BovineColumn[] = [
|
||||
{ key: 'nationalNumber', label: 'N° National', width: '110px' },
|
||||
{ key: 'workNumber', label: 'N° Travail', width: '85px' },
|
||||
{ key: 'sex', label: 'Sexe', width: '90px' },
|
||||
{ key: 'birthDate', label: 'Né le', width: '100px' },
|
||||
{ key: 'age', label: 'Age', width: '90px' },
|
||||
{ key: 'bovineType.label', label: 'Race', width: '1fr' },
|
||||
{ key: 'arrivalDate', label: 'Entrée le', width: '110px' },
|
||||
{ key: 'pricePerKg', label: 'Prix/kg', width: '85px' },
|
||||
{ key: 'finalPrice', label: 'Prix total', width: '105px' }
|
||||
]
|
||||
|
||||
const userColumnsCase: BovineColumn[] = [
|
||||
{ key: 'nationalNumber', label: 'N° National', width: '130px' },
|
||||
{ key: 'workNumber', label: 'N° Travail', width: '100px' },
|
||||
{ key: 'sex', label: 'Sexe', width: '110px' },
|
||||
{ key: 'birthDate', label: 'Né le', width: '140px' },
|
||||
{ key: 'age', label: 'Age', width: '130px' },
|
||||
{ key: 'bovineType.label', label: 'Race', width: '1fr' },
|
||||
{ key: 'arrivalDate', label: 'Entrée le', width: '170px' }
|
||||
]
|
||||
|
||||
const columns = computed<BovineColumn[]>(() => {
|
||||
if (options.variant === 'case') {
|
||||
return auth.isAdmin ? adminColumnsCase : userColumnsCase
|
||||
}
|
||||
return auth.isAdmin ? adminColumnsInventory : userColumnsInventory
|
||||
})
|
||||
|
||||
return { columns }
|
||||
}
|
||||
@@ -33,7 +33,22 @@
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 mb-16">
|
||||
<div class="flex flex-wrap gap-3 mt-4">
|
||||
<div class="flex items-center gap-3 rounded-md bg-red-500 px-4 py-2">
|
||||
<span class="text-2xl font-bold text-white">{{ stats.over24 }}</span>
|
||||
<span class="text-sm uppercase tracking-wide text-white">≥ 24 mois</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 rounded-md bg-orange-500 px-4 py-2">
|
||||
<span class="text-2xl font-bold text-white">{{ stats.between22And24 }}</span>
|
||||
<span class="text-sm uppercase tracking-wide text-white">22 – 24 mois</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 rounded-md bg-yellow-500 px-4 py-2">
|
||||
<span class="text-2xl font-bold text-white">{{ stats.between20And22 }}</span>
|
||||
<span class="text-sm uppercase tracking-wide text-white">20 – 22 mois</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 mb-16">
|
||||
<UiDataTable
|
||||
v-model:page="page"
|
||||
v-model:per-page="perPage"
|
||||
@@ -41,7 +56,6 @@
|
||||
:items="items"
|
||||
:total-items="totalItems"
|
||||
:loading="loading"
|
||||
:row-class="rowClass"
|
||||
:row-clickable="auth.isAdmin"
|
||||
empty-message="Aucun bovin dans cette case."
|
||||
@row-click="goToBovine"
|
||||
@@ -74,19 +88,19 @@
|
||||
<template #header-age>
|
||||
<UiTextInput :model-value="''" placeholder="Age" size="compact" disabled />
|
||||
</template>
|
||||
<template #header-breedCode>
|
||||
<template #header-pricePerKg>
|
||||
<UiTextInput :model-value="''" placeholder="Prix/kg" size="compact" disabled />
|
||||
</template>
|
||||
<template #header-finalPrice>
|
||||
<UiTextInput :model-value="''" placeholder="Prix total" size="compact" disabled />
|
||||
</template>
|
||||
<template #header-bovineType.label>
|
||||
<UiTextInput
|
||||
v-model="filters.breedCode"
|
||||
v-model="filters['bovineType.label']"
|
||||
placeholder="Race"
|
||||
size="compact"
|
||||
/>
|
||||
</template>
|
||||
<template #header-buildingCase.building.label>
|
||||
<UiTextInput :model-value="''" placeholder="Bâtiment" size="compact" disabled />
|
||||
</template>
|
||||
<template #header-buildingCase.caseNumber>
|
||||
<UiTextInput :model-value="''" placeholder="Case" size="compact" disabled />
|
||||
</template>
|
||||
<template #header-arrivalDate>
|
||||
<UiDateMaskedInput v-model="arrivalDateFilter" size="compact" placeholder="Entrée le" />
|
||||
</template>
|
||||
@@ -94,16 +108,21 @@
|
||||
{{ formatDate(item.birthDate) }}
|
||||
</template>
|
||||
<template #cell-age="{ item }">
|
||||
{{ formatAgeLabel(item.ageMonths) }}
|
||||
<span
|
||||
class="inline-block rounded px-2 py-0.5 font-semibold"
|
||||
:class="ageBadgeClass(item.ageMonths)"
|
||||
>
|
||||
{{ formatAgeLabel(item.ageMonths) }}
|
||||
</span>
|
||||
</template>
|
||||
<template #cell-arrivalDate="{ item }">
|
||||
{{ formatDate(item.arrivalDate) }}
|
||||
</template>
|
||||
<template #cell-buildingCase.building.label="{ item }">
|
||||
{{ item.buildingCase?.building?.label ?? '—' }}
|
||||
<template #cell-pricePerKg="{ item }">
|
||||
{{ formatPrice(item.pricePerKg) }}
|
||||
</template>
|
||||
<template #cell-buildingCase.caseNumber="{ item }">
|
||||
{{ item.buildingCase?.caseNumber ?? '—' }}
|
||||
<template #cell-finalPrice="{ item }">
|
||||
{{ formatPrice(item.finalPrice) }}
|
||||
</template>
|
||||
</UiDataTable>
|
||||
</div>
|
||||
@@ -115,7 +134,8 @@ import type { BuildingCaseData } from '~/services/dto/building-case-data'
|
||||
import type { BovineData } from '~/services/dto/bovine-data'
|
||||
import { useAuthStore } from '~/stores/auth'
|
||||
import { useDataTableServerState } from '~/composables/useDataTableServerState'
|
||||
import { formatAgeLabel } from '~/utils/bovine-age'
|
||||
import { useBovineColumns } from '~/composables/useBovineColumns'
|
||||
import { formatAgeLabel, ageBadgeClass } from '~/utils/bovine-age'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -128,6 +148,34 @@ const hasCaseId = computed(() => Number.isFinite(caseId.value) && caseId.value >
|
||||
|
||||
const buildingCase = ref<BuildingCaseData | null>(null)
|
||||
|
||||
interface InventoryStats {
|
||||
total: number
|
||||
over24: number
|
||||
between22And24: number
|
||||
between20And22: number
|
||||
}
|
||||
|
||||
const stats = ref<InventoryStats>({
|
||||
total: 0,
|
||||
over24: 0,
|
||||
between22And24: 0,
|
||||
between20And22: 0
|
||||
})
|
||||
|
||||
const loadStats = async () => {
|
||||
if (!hasCaseId.value) {
|
||||
stats.value = { total: 0, over24: 0, between22And24: 0, between20And22: 0 }
|
||||
return
|
||||
}
|
||||
try {
|
||||
stats.value = await api.get<InventoryStats>('bovines/inventory-stats', {
|
||||
buildingCaseId: caseId.value
|
||||
}, { toast: false })
|
||||
} catch {
|
||||
// silencieux
|
||||
}
|
||||
}
|
||||
|
||||
const { items, totalItems, page, perPage, filters, loading, reload } =
|
||||
useDataTableServerState<BovineData>(
|
||||
'bovines',
|
||||
@@ -136,7 +184,7 @@ const { items, totalItems, page, perPage, filters, loading, reload } =
|
||||
buildingCase: '',
|
||||
nationalNumber: '',
|
||||
workNumber: '',
|
||||
breedCode: '',
|
||||
'bovineType.label': '',
|
||||
sex: '',
|
||||
'arrivalDate[after]': '',
|
||||
'arrivalDate[strictly_before]': '',
|
||||
@@ -174,17 +222,7 @@ const singleDateFilter = (afterKey: string, beforeKey: string) =>
|
||||
const arrivalDateFilter = singleDateFilter('arrivalDate[after]', 'arrivalDate[strictly_before]')
|
||||
const birthDateFilter = singleDateFilter('birthDate[after]', 'birthDate[strictly_before]')
|
||||
|
||||
const columns = [
|
||||
{ key: 'nationalNumber', label: 'N° National', width: '160px' },
|
||||
{ key: 'workNumber', label: 'N° Travail', width: '85px' },
|
||||
{ key: 'sex', label: 'Sexe', width: '70px' },
|
||||
{ key: 'birthDate', label: 'Né le', width: '120px' },
|
||||
{ key: 'age', label: 'Age', width: '110px' },
|
||||
{ key: 'breedCode', label: 'Race' },
|
||||
{ key: 'buildingCase.building.label', label: 'Bâtiment', width: '1.5fr' },
|
||||
{ key: 'buildingCase.caseNumber', label: 'Case', width: '80px' },
|
||||
{ key: 'arrivalDate', label: 'Entrée le', width: '120px' }
|
||||
]
|
||||
const { columns } = useBovineColumns({ variant: 'case' })
|
||||
|
||||
const title = computed(() => {
|
||||
if (!buildingCase.value) return ''
|
||||
@@ -209,14 +247,12 @@ const formatDate = (date: string | null) => {
|
||||
})
|
||||
}
|
||||
|
||||
const rowClass = (item: BovineData): string => {
|
||||
if (item.ageMonths === null || item.ageMonths === undefined) return ''
|
||||
if (item.ageMonths >= 24) return 'bg-violet-300 hover:bg-violet-400'
|
||||
if (item.ageMonths >= 22) return 'bg-red-300 hover:bg-red-400'
|
||||
if (item.ageMonths >= 20) return 'bg-orange-300 hover:bg-orange-400'
|
||||
return ''
|
||||
const formatPrice = (price: number | null) => {
|
||||
if (price === null || price === undefined) return '—'
|
||||
return `${price.toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} €`
|
||||
}
|
||||
|
||||
|
||||
const loadCase = async () => {
|
||||
if (!hasCaseId.value) {
|
||||
buildingCase.value = null
|
||||
@@ -247,6 +283,7 @@ watch(caseId, (id) => {
|
||||
}
|
||||
filters.value.buildingCase = `/api/building_cases/${id}`
|
||||
loadCase()
|
||||
loadStats()
|
||||
reload()
|
||||
}, { immediate: true })
|
||||
</script>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<h1 class="font-bold text-3xl uppercase text-primary-500">Inventaire bovins</h1>
|
||||
<span class="text-lg text-slate-500">({{ totalItems }} bovin{{ totalItems > 1 ? 's' : '' }})</span>
|
||||
<div
|
||||
v-if="auth.isAdmin"
|
||||
class="bg-primary-500 p-1 rounded-md flex items-center cursor-pointer hover:opacity-80"
|
||||
:class="exporting ? 'cursor-not-allowed opacity-60' : ''"
|
||||
title="Exporter en Excel"
|
||||
@@ -34,17 +35,17 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap gap-3 mt-4">
|
||||
<div class="flex items-center gap-3 rounded-md border-2 border-violet-300 px-4 py-2">
|
||||
<span class="text-2xl font-bold text-violet-700">{{ stats.over24 }}</span>
|
||||
<span class="text-sm uppercase tracking-wide text-violet-700">≥ 24 mois</span>
|
||||
<div class="flex items-center gap-3 rounded-md bg-red-500 px-4 py-2">
|
||||
<span class="text-2xl font-bold text-white">{{ stats.over24 }}</span>
|
||||
<span class="text-sm uppercase tracking-wide text-white">≥ 24 mois</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 rounded-md border-2 border-red-300 px-4 py-2">
|
||||
<span class="text-2xl font-bold text-red-700">{{ stats.between22And24 }}</span>
|
||||
<span class="text-sm uppercase tracking-wide text-red-700">22 – 24 mois</span>
|
||||
<div class="flex items-center gap-3 rounded-md bg-orange-500 px-4 py-2">
|
||||
<span class="text-2xl font-bold text-white">{{ stats.between22And24 }}</span>
|
||||
<span class="text-sm uppercase tracking-wide text-white">22 – 24 mois</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 rounded-md border-2 border-orange-300 px-4 py-2">
|
||||
<span class="text-2xl font-bold text-orange-700">{{ stats.between20And22 }}</span>
|
||||
<span class="text-sm uppercase tracking-wide text-orange-700">20 – 22 mois</span>
|
||||
<div class="flex items-center gap-3 rounded-md bg-yellow-500 px-4 py-2">
|
||||
<span class="text-2xl font-bold text-white">{{ stats.between20And22 }}</span>
|
||||
<span class="text-sm uppercase tracking-wide text-white">20 – 22 mois</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -56,7 +57,6 @@
|
||||
:items="items"
|
||||
:total-items="totalItems"
|
||||
:loading="loading"
|
||||
:row-class="rowClass"
|
||||
>
|
||||
<template #header-nationalNumber>
|
||||
<UiTextInput
|
||||
@@ -83,9 +83,9 @@
|
||||
<template #header-birthDate>
|
||||
<UiDateMaskedInput v-model="birthDateFilter" size="compact" placeholder="Né le" />
|
||||
</template>
|
||||
<template #header-breedCode>
|
||||
<template #header-bovineType.label>
|
||||
<UiTextInput
|
||||
v-model="filters.breedCode"
|
||||
v-model="filters['bovineType.label']"
|
||||
placeholder="Race"
|
||||
size="compact"
|
||||
/>
|
||||
@@ -102,21 +102,38 @@
|
||||
<template #header-age>
|
||||
<UiTextInput :model-value="''" placeholder="Age" size="compact" disabled />
|
||||
</template>
|
||||
<template #header-pricePerKg>
|
||||
<UiTextInput :model-value="''" placeholder="Prix/kg" size="compact" disabled />
|
||||
</template>
|
||||
<template #header-finalPrice>
|
||||
<UiTextInput :model-value="''" placeholder="Prix total" size="compact" disabled />
|
||||
</template>
|
||||
<template #cell-birthDate="{ item }">
|
||||
{{ formatDate(item.birthDate) }}
|
||||
</template>
|
||||
<template #cell-age="{ item }">
|
||||
{{ formatAgeLabel(item.ageMonths) }}
|
||||
<span
|
||||
class="inline-block rounded px-2 py-0.5 font-semibold"
|
||||
:class="ageBadgeClass(item.ageMonths)"
|
||||
>
|
||||
{{ formatAgeLabel(item.ageMonths) }}
|
||||
</span>
|
||||
</template>
|
||||
<template #cell-arrivalDate="{ item }">
|
||||
{{ formatDate(item.arrivalDate) }}
|
||||
</template>
|
||||
<template #cell-buildingCase.building.label="{ item }">
|
||||
{{ item.buildingCase?.building?.label ?? '—' }}
|
||||
{{ item.effectiveBuilding?.label ?? '—' }}
|
||||
</template>
|
||||
<template #cell-buildingCase.caseNumber="{ item }">
|
||||
{{ item.buildingCase?.caseNumber ?? '—' }}
|
||||
</template>
|
||||
<template #cell-pricePerKg="{ item }">
|
||||
{{ formatPrice(item.pricePerKg) }}
|
||||
</template>
|
||||
<template #cell-finalPrice="{ item }">
|
||||
{{ formatPrice(item.finalPrice) }}
|
||||
</template>
|
||||
</UiDataTable>
|
||||
</div>
|
||||
</div>
|
||||
@@ -127,7 +144,8 @@
|
||||
import type { BovineData } from '~/services/dto/bovine-data'
|
||||
import { useAuthStore } from '~/stores/auth'
|
||||
import { useDataTableServerState } from '~/composables/useDataTableServerState'
|
||||
import { formatAgeLabel } from '~/utils/bovine-age'
|
||||
import { useBovineColumns } from '~/composables/useBovineColumns'
|
||||
import { formatAgeLabel, ageBadgeClass } from '~/utils/bovine-age'
|
||||
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
@@ -218,7 +236,7 @@ const { items, totalItems, page, perPage, filters, loading, reload } =
|
||||
'exists[exitedAt]': 'false',
|
||||
nationalNumber: '',
|
||||
workNumber: '',
|
||||
breedCode: '',
|
||||
'bovineType.label': '',
|
||||
sex: '',
|
||||
'arrivalDate[after]': '',
|
||||
'arrivalDate[strictly_before]': '',
|
||||
@@ -255,17 +273,7 @@ const singleDateFilter = (afterKey: string, beforeKey: string) =>
|
||||
const arrivalDateFilter = singleDateFilter('arrivalDate[after]', 'arrivalDate[strictly_before]')
|
||||
const birthDateFilter = singleDateFilter('birthDate[after]', 'birthDate[strictly_before]')
|
||||
|
||||
const columns = [
|
||||
{ key: 'nationalNumber', label: 'N° National', width: '160px' },
|
||||
{ key: 'workNumber', label: 'N° Travail', width: '85px' },
|
||||
{ key: 'sex', label: 'Sexe', width: '70px' },
|
||||
{ key: 'birthDate', label: 'Né le', width: '120px' },
|
||||
{ key: 'age', label: 'Age', width: '110px' },
|
||||
{ key: 'breedCode', label: 'Race' },
|
||||
{ key: 'buildingCase.building.label', label: 'Bâtiment', width: '1.5fr' },
|
||||
{ key: 'buildingCase.caseNumber', label: 'Case', width: '80px' },
|
||||
{ key: 'arrivalDate', label: 'Entrée le', width: '120px' }
|
||||
]
|
||||
const { columns } = useBovineColumns()
|
||||
|
||||
const formatDate = (date: string | null) => {
|
||||
if (!date) return '—'
|
||||
@@ -278,14 +286,12 @@ const formatDate = (date: string | null) => {
|
||||
})
|
||||
}
|
||||
|
||||
const rowClass = (item: BovineData): string => {
|
||||
if (item.ageMonths === null || item.ageMonths === undefined) return ''
|
||||
if (item.ageMonths >= 24) return 'bg-violet-300 hover:bg-violet-400'
|
||||
if (item.ageMonths >= 22) return 'bg-red-300 hover:bg-red-400'
|
||||
if (item.ageMonths >= 20) return 'bg-orange-300 hover:bg-orange-400'
|
||||
return ''
|
||||
const formatPrice = (price: number | null) => {
|
||||
if (price === null || price === undefined) return '—'
|
||||
return `${price.toLocaleString('fr-FR', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} €`
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
reload()
|
||||
loadStats()
|
||||
|
||||
@@ -1,19 +1,27 @@
|
||||
export interface BovineBuildingRef {
|
||||
label: string
|
||||
}
|
||||
|
||||
export interface BovineBuildingCaseRef {
|
||||
caseNumber: number | null
|
||||
building: { label: string } | null
|
||||
building: BovineBuildingRef | null
|
||||
}
|
||||
|
||||
export interface BovineData {
|
||||
id: number
|
||||
nationalNumber: string
|
||||
receivedWeight: number | null
|
||||
pricePerKg: number | null
|
||||
finalPrice: number | null
|
||||
arrivalDate: string | null
|
||||
exitDate: string | null
|
||||
buildingCase: BovineBuildingCaseRef | null
|
||||
building: BovineBuildingRef | null
|
||||
effectiveBuilding: BovineBuildingRef | null
|
||||
supplier: string | null
|
||||
workNumber: string | null
|
||||
birthDate: string | null
|
||||
breedCode: string | null
|
||||
bovineType: { id: number; label: string; code: string } | null
|
||||
sex: string | null
|
||||
ageMonths: number | null
|
||||
exitedAt: string | null
|
||||
@@ -22,6 +30,7 @@ export interface BovineData {
|
||||
export type BovinePayload = {
|
||||
nationalNumber?: string
|
||||
receivedWeight?: number | null
|
||||
pricePerKg?: number | null
|
||||
arrivalDate?: string | null
|
||||
buildingCase?: string | null
|
||||
supplier?: string | null
|
||||
|
||||
@@ -8,3 +8,11 @@ export const formatAgeLabel = (months: number | null | undefined): string => {
|
||||
if (!label) label = '< 1 mois'
|
||||
return label
|
||||
}
|
||||
|
||||
export const ageBadgeClass = (months: number | null | undefined): string => {
|
||||
if (months === null || months === undefined) return ''
|
||||
if (months >= 24) return 'bg-red-500 text-white'
|
||||
if (months >= 22) return 'bg-orange-500 text-white'
|
||||
if (months >= 20) return 'bg-yellow-500 text-white'
|
||||
return ''
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user