Merge remote-tracking branch 'refs/remotes/origin/develop' into feat/271-expedition-etape-1

# Conflicts:
#	.idea/workspace.xml
This commit is contained in:
2026-02-05 10:47:05 +01:00
30 changed files with 1205 additions and 235 deletions

117
.idea/workspace.xml generated
View File

@@ -4,9 +4,19 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="7c107abe-5995-4428-8429-b146aaca8386" name="Changes" comment="feat : Expedition dev back-end WIP">
<list default="true" id="7c107abe-5995-4428-8429-b146aaca8386" name="Changes" comment="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)">
<change beforePath="$PROJECT_DIR$/.idea/dataSources.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/config/reference.php" beforeDir="false" afterPath="$PROJECT_DIR$/config/reference.php" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/components/reception/reception-bovine-received.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-bovine-received.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/components/reception/reception-form.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-form.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/components/reception/reception-product-received.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-product-received.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/components/ui/UiNumberInput.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/ui/UiNumberInput.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/services/reception.ts" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/services/reception.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/Entity/Reception.php" beforeDir="false" afterPath="$PROJECT_DIR$/src/Entity/Reception.php" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/Repository/.gitignore" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/src/Repository/BovineTypeRepository.php" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/src/Repository/ReceptionBovineRepository.php" beforeDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -19,17 +29,18 @@
</component>
<component name="CopilotPersistence">
<persistenceIdMap>
<entry key="_//wsl.localhost/Ubuntu-24.04/home/matte/Ferme" value="381AhnCm9yPeOiWgMObKHhtgv2C" />
<entry key="_//wsl.localhost/Ubuntu-24.04/home/kevin/Stage/Ferme" value="381AhnCm9yPeOiWgMObKHhtgv2C" />
</persistenceIdMap>
</component>
<component name="EmbeddingIndexingInfo">
<option name="cachedIndexableFilesCount" value="151" />
<option name="fileBasedEmbeddingIndicesEnabled" value="true" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="TypeScript File" />
<option value="Vue Composition API Component" />
<option value="TypeScript File" />
</list>
</option>
</component>
@@ -41,9 +52,6 @@
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="HighlightingSettingsPerFile">
<setting file="file://$PROJECT_DIR$/src/Entity/BovinType.php" root0="FORCE_HIGHLIGHTING" root1="FORCE_HIGHLIGHTING" />
</component>
<component name="McpProjectServerCommands">
<commands />
<urls />
@@ -57,7 +65,7 @@
</server>
</servers>
</component>
<component name="PhpWorkspaceProjectConfiguration" interpreter_name="C:/php/php.exe">
<component name="PhpWorkspaceProjectConfiguration" interpreter_name="C:/php-8.4.3/php.exe">
<include_path>
<path value="$PROJECT_DIR$/vendor/psr/log" />
<path value="$PROJECT_DIR$/vendor/psr/event-dispatcher" />
@@ -227,19 +235,14 @@
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;,
&quot;com.intellij.ml.llm.matterhorn.ej.ui.settings.DefaultModelSelectionForGA.v1&quot;: &quot;true&quot;,
&quot;database.data.extractors.current.export.id&quot;: &quot;Comma-separated (CSV)_id&quot;,
&quot;database.data.extractors.current.id&quot;: &quot;Comma-separated (CSV)_id&quot;,
&quot;git-widget-placeholder&quot;: &quot;feat/271-expedition-etape-1&quot;,
&quot;junie.onboarding.icon.badge.shown&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;//wsl.localhost/Ubuntu-24.04/home/matte/Ferme/frontend/services&quot;,
&quot;git-widget-placeholder&quot;: &quot;feat/256-reception-etape-3-bovin&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/sroy/Documents/test/Ferme&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.lookFeel&quot;,
&quot;to.speed.mode.migration.done&quot;: &quot;true&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;configurable.tailwindcss&quot;,
&quot;ts.external.directory.path&quot;: &quot;/opt/phpstorm/plugins/javascript-plugin/jsLanguageServicesImpl/external&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
},
@@ -256,13 +259,6 @@
}
}</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="\\wsl.localhost\Ubuntu-24.04\home\matte\Ferme\frontend\services" />
<recent name="\\wsl.localhost\Ubuntu-24.04\home\matte\Ferme\frontend\services\dto" />
<recent name="\\wsl.localhost\Ubuntu-24.04\home\matte\Ferme\frontend\stores" />
<recent name="\\wsl.localhost\Ubuntu-24.04\home\matte\Ferme\frontend\pages\shipment" />
<recent name="\\wsl.localhost\Ubuntu-24.04\home\matte\Ferme\frontend\components\shipment" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="\\wsl.localhost\Ubuntu-24.04\home\m-tristan\workspace\Ferme" />
<recent name="\\wsl.localhost\Ubuntu-24.04\home\tristan\workspace\ferme\templates" />
@@ -304,12 +300,26 @@
<workItem from="1770055690365" duration="370000" />
<workItem from="1770056515646" duration="21000" />
<workItem from="1770102495553" duration="2280000" />
<workItem from="1770125858721" duration="10606000" />
<workItem from="1770188542722" duration="1032000" />
<workItem from="1770189650316" duration="5784000" />
<workItem from="1770195538424" duration="17019000" />
<workItem from="1770220986512" duration="1007000" />
<workItem from="1770274952830" duration="1124000" />
<workItem from="1770195604082" duration="90000" />
<workItem from="1770195718952" duration="215000" />
<workItem from="1770195959162" duration="18915000" />
<workItem from="1770274844804" duration="3940000" />
</task>
<task id="LOCAL-00001" summary="feat : Ajout de pinia, création de la table weight et reception mise en place du système de step pour les receptions (WIP)">
<option name="closed" value="true" />
<created>1768237763998</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1768237763998</updated>
</task>
<task id="LOCAL-00002" summary="feat : Ajout de zod, création d'un composant de chargement loading-dots.vue et finalisation du flow d'une reception">
<option name="closed" value="true" />
<created>1768316052474</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1768316052474</updated>
</task>
<task id="LOCAL-00003" summary="feat : Ajout d'un composable pour la pesée qui sera réutilisable pour l'expédition, ajout de contrainte sur les entity de reception et weight pour plus de robustesse et correction de la class active des liens dans la nav">
<option name="closed" value="true" />
@@ -663,47 +673,31 @@
<option name="project" value="LOCAL" />
<updated>1769782099473</updated>
</task>
<task id="LOCAL-00047" summary="feat : mise à jour du bon de réception wip">
<task id="LOCAL-00047" summary="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)">
<option name="closed" value="true" />
<created>1770135384363</created>
<created>1770131226364</created>
<option name="number" value="00047" />
<option name="presentableId" value="LOCAL-00047" />
<option name="project" value="LOCAL" />
<updated>1770135384363</updated>
<updated>1770131226364</updated>
</task>
<task id="LOCAL-00048" summary="feat : mise à jour du bon de réception WIP">
<task id="LOCAL-00048" summary="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)">
<option name="closed" value="true" />
<created>1770135408267</created>
<created>1770206668867</created>
<option name="number" value="00048" />
<option name="presentableId" value="LOCAL-00048" />
<option name="project" value="LOCAL" />
<updated>1770135408267</updated>
<updated>1770206668867</updated>
</task>
<task id="LOCAL-00049" summary="feat : mise à jour du bon de réception WIP">
<task id="LOCAL-00049" summary="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)">
<option name="closed" value="true" />
<created>1770136359244</created>
<created>1770217875423</created>
<option name="number" value="00049" />
<option name="presentableId" value="LOCAL-00049" />
<option name="project" value="LOCAL" />
<updated>1770136359244</updated>
<updated>1770217875423</updated>
</task>
<task id="LOCAL-00050" summary="feat : mise à jour du bon de réception WIP">
<option name="closed" value="true" />
<created>1770136475283</created>
<option name="number" value="00050" />
<option name="presentableId" value="LOCAL-00050" />
<option name="project" value="LOCAL" />
<updated>1770136475283</updated>
</task>
<task id="LOCAL-00051" summary="feat : Expedition dev back-end WIP">
<option name="closed" value="true" />
<created>1770220736390</created>
<option name="number" value="00051" />
<option name="presentableId" value="LOCAL-00051" />
<option name="project" value="LOCAL" />
<updated>1770220736392</updated>
</task>
<option name="localTasksCounter" value="52" />
<option name="localTasksCounter" value="50" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -753,6 +747,8 @@
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="fix : correction du path URI pour la création d'un poids dans une réception" />
<MESSAGE value="feat : Ajout du bundle Monolog pour la gestion des logs" />
<MESSAGE value="fix : affiche plus détail dans les logs en recette/prod" />
<MESSAGE value="fix : modification du script de déploiement pour corriger le problème d'écriture des logs de prod" />
<MESSAGE value="fix : doc de déploiement" />
@@ -775,17 +771,16 @@
<MESSAGE value="feat : ajout de colonne pour les Supplier, Address et modification du numéro de réception" />
<MESSAGE value="feat : ajout de colonne pour les Supplier, Address. Modification du numéro de réception et ajout de fixtures" />
<MESSAGE value="feat : mise à jour du bon de réception" />
<MESSAGE value="feat : mise à jour du bon de réception wip" />
<MESSAGE value="feat : mise à jour du bon de réception WIP" />
<MESSAGE value="feat : Expedition dev back-end WIP" />
<option name="LAST_COMMIT_MESSAGE" value="feat : Expedition dev back-end WIP" />
<MESSAGE value="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)" />
<option name="LAST_COMMIT_MESSAGE" value="feat : Ajout de la sélection des bovins étape 3 d'une réception (WIP)" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" type="javascript">
<url>file://$PROJECT_DIR$/frontend/stores/reception.ts</url>
<option name="timeStamp" value="2" />
<line-breakpoint enabled="true" type="php">
<url>file://$PROJECT_DIR$/src/Entity/ReceptionPelletBuilding.php</url>
<line>6</line>
<option name="timeStamp" value="3" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>

View File

@@ -27,6 +27,8 @@ Ajouter dans le fichier .env du frontend
* Ajout du bundle malio/ednotif-bundle
* Ajout de composant UI
* Finalisation de la partie réception de marchandise
* [#267] Lister les réceptions en attente
* [#268] Lister les réceptions terminées
### Changed

View File

@@ -0,0 +1,30 @@
<template>
<NuxtLink :to="link">
<div class="w-[324px] h-[228px] border border-black rounded-md p-6 flex flex-col justify-between">
<div class="flex justify-between">
<div class="rounded-full w-[80px] h-[80px] bg-neutral-400 flex justify-center items-center">
<Icon :name="iconName" style="color: black" size="44" />
</div>
<div>
<Icon name="mdi:plus" style="color: black" size="44" />
</div>
</div>
<div class="uppercase font-bold">
<p class="text-3xl"> {{ label }} </p>
</div>
</div>
</NuxtLink>
</template>
<script setup lang="ts">
const props = defineProps<{
link: string
iconName: string
label: string
}>()
</script>

View File

@@ -0,0 +1,183 @@
<template>
<div
v-if="receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.BOVINS"
class="flex flex-col items-center gap-16">
<h1 class="text-4xl uppercase font-bold">Sélection des marchandises réceptionnnées</h1>
<div
class="flex flex-row gap-8 items-center">
<div
v-for="type in bovineType"
:key="type.id"
class="mt-8 flex flex-row mb-2 gap-6">
<UiNumberInput
:label="type.label"
:code="type.code"
v-model="bovineQuantities[String(type.id)]"
:placeholder="0"
:min="0"
:max="10"
/>
</div>
<div
class="mt-8 flex flex-row mb-2 gap-6">
<UiNumberInput
label="Autres"
v-model="otherQuantity"
/>
</div>
</div>
<button
class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px]"
@click="goNext"
>Peser
</button>
</div>
</template>
<script setup lang="ts">
import type {BovineTypeData} from "~/services/dto/bovine-type-data";
import {getBovineTypeList} from "~/services/bovine-type";
import {RECEPTION_TYPE_CODES} from "~/utils/constants";
import {useReceptionStore} from '~/stores/reception'
import {
createReceptionBovine,
deleteReceptionBovine,
getReceptionBovineList,
updateReceptionBovine
} from "~/services/reception-bovine";
import {computed, onMounted, reactive, ref, watch} from "vue";
const toast = useToast()
const isLoadingBovineType = ref(false)
const bovineType = ref<BovineTypeData[]>([])
const receptionStore = useReceptionStore()
const bovineQuantities = reactive<Record<string, number | null>>({})
const otherQuantity = ref<number | null>(0)
const receptionId = computed(() => receptionStore.current?.id ?? null)
const receptionIri = computed(() =>
receptionId.value ? `/api/receptions/${receptionId.value}` : null
)
const totalBovines = computed(() => {
const base = Object.values(bovineQuantities).reduce((sum, value) => {
return sum + (value ?? 0)
}, 0)
return base + (otherQuantity.value ?? 0)
})
const loadBovineType = async () => {
isLoadingBovineType.value = true
try {
bovineType.value = await getBovineTypeList()
} finally {
isLoadingBovineType.value = false
}
}
onMounted(async () => {
await loadBovineType()
})
watch(
() => receptionId.value,
async (id) => {
if (!id || !receptionIri.value) {
return
}
const selectionMap: Record<string, number | null> = {}
for (const type of bovineType.value) {
selectionMap[String(type.id)] = 0
}
const existing = await getReceptionBovineList(receptionIri.value)
for (const selection of existing) {
const bovineTypeId = String(selection.bovineType.id)
selectionMap[bovineTypeId] = selection.quantity ?? 0
}
for (const key of Object.keys(bovineQuantities)) {
delete bovineQuantities[key]
}
Object.assign(bovineQuantities, selectionMap)
const existingOther = receptionStore.current?.bovineDetail
const parsedOther =
typeof existingOther === 'string' && existingOther.trim() !== ''
? Number(existingOther)
: 0
otherQuantity.value = Number.isFinite(parsedOther) ? parsedOther : 0
},
{immediate: true}
)
async function syncBovineSelections(receptionIri: string) {
const existing = await getReceptionBovineList(receptionIri)
const existingMap = new Map<string, { id: number; quantity: number | null }>()
for (const selection of existing) {
const bovineTypeId = String(selection.bovineType.id)
existingMap.set(bovineTypeId, {
id: selection.id,
quantity: selection.quantity ?? 0
})
}
// Supprime les entrées supprimées ou modifiées
for (const [bovineTypeId, entry] of existingMap.entries()) {
const selectedQuantity = bovineQuantities[bovineTypeId] ?? 0
if (!selectedQuantity) {
await deleteReceptionBovine(entry.id)
existingMap.delete(bovineTypeId)
continue
}
if (selectedQuantity !== entry.quantity) {
await updateReceptionBovine(entry.id, {quantity: selectedQuantity})
existingMap.set(bovineTypeId, {
id: entry.id,
quantity: selectedQuantity
})
}
}
// Crée les entrées manquantes
for (const [bovineTypeId, quantity] of Object.entries(bovineQuantities)) {
if (!quantity) {
continue
}
if (existingMap.has(bovineTypeId)) {
// Déjà à jour
continue
}
await createReceptionBovine({
reception: receptionIri,
bovineType: `/api/bovine_types/${bovineTypeId}`,
quantity
})
}
}
async function goNext() {
if (!receptionStore.current || !receptionIri.value) {
return
}
// @TODO Ajouter un composable pour le toaster qui gère les key i18n
if (totalBovines.value > 52) {
toast.error({
title: 'Erreur',
message: ('Le total des bovins ne peut pas dépasser 52.')
})
return
}
const nextStep = receptionStore.current.currentStep + 1
await syncBovineSelections(receptionIri.value)
await receptionStore.updateReception(receptionStore.current.id, {
merchandiseType: null,
merchandiseDetail: null,
bovineDetail: otherQuantity.value ? String(otherQuantity.value) : null,
currentStep: nextStep
})
}
</script>

View File

@@ -142,7 +142,8 @@ import type {DriverData} from '~/services/dto/driver-data'
import {getDriverList} from '~/services/driver'
import type {VehicleData} from '~/services/dto/vehicle-data'
import {getVehicleList} from '~/services/vehicle'
import {SUPLLIER_CODE} from "~/utils/constants";
import {RECEPTION_TYPE_CODES, SUPLLIER_CODE} from "~/utils/constants";
import {deleteReceptionBovine, getReceptionBovineList} from "~/services/reception-bovine";
type ReceptionFormData = {
licensePlate: string
@@ -222,6 +223,18 @@ const filteredVehicles = computed<VehicleData[]>(() => {
)
})
const selectedReceptionType = computed(() =>
receptionTypes.value.find((type) => String(type.id) === form.receptionTypeId) ?? null
)
// Supprime les données bovines si on change de type de réception
const clearReceptionBovines = async (receptionIri: string) => {
const existing = await getReceptionBovineList(receptionIri)
for (const selection of existing) {
await deleteReceptionBovine(selection.id)
}
}
// Hydrate le formulaire depuis la réception en cours
watch(
() => receptionStore.current,
@@ -509,6 +522,16 @@ async function validate() {
return
}
const previousTypeCode = receptionStore.current.receptionType?.code ?? null
const nextTypeCode = selectedReceptionType.value?.code ?? null
const receptionIri = `/api/receptions/${receptionStore.current.id}`
if (
previousTypeCode === RECEPTION_TYPE_CODES.BOVINS &&
nextTypeCode === RECEPTION_TYPE_CODES.MERCHANDISES
) {
await clearReceptionBovines(receptionIri)
}
const nextStep = receptionStore.current.currentStep + 1
await receptionStore.updateReception(receptionStore.current.id, {
currentStep: nextStep,

View File

@@ -1,10 +1,9 @@
<template>
<div class="flex flex-col items-center gap-16">
<!-- @TODO voir pour séparer dans un composant au moment de l'implémentation des Bovins -->
<div
v-if="receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.MERCHANDISES"
class="flex flex-col gap-16 items-center w-full">
<h1 class="text-4xl uppercase font-bold">Sélectionner des marchandises réceptionnnées</h1>
<h1 class="text-4xl uppercase font-bold">Sélection des marchandises réceptionnnées</h1>
<UiSelect
id="merchandise-type"
v-model="selectedMerchandiseTypeId"
@@ -12,7 +11,6 @@
:options="merchandiseTypes.map((type) => ({ value: String(type.id), label: type.label }))"
wrapper-class="w-[550px]"
/>
<div
v-if="selectedMerchandiseTypeId && isAutres"
class="flex flex-col w-full max-w-[550px]"
@@ -69,25 +67,27 @@
<button
class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px]"
@click="goNext"
>Peser</button>
>Peser
</button>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { getBuildingList } from '~/services/building'
import { getMerchandiseTypeList } from '~/services/merchandise-type'
import type { MerchandiseTypeData } from '~/services/dto/merchandise-type-data'
import type { BuildingData } from '~/services/dto/building-data'
import type { PelletTypeData } from '~/services/dto/pellet-type-data'
import { getPelletTypeList } from '~/services/pellet-type'
import {computed, onMounted, ref} from 'vue'
import {getBuildingList} from '~/services/building'
import {getMerchandiseTypeList} from '~/services/merchandise-type'
import type {MerchandiseTypeData} from '~/services/dto/merchandise-type-data'
import type {BuildingData} from '~/services/dto/building-data'
import type {PelletTypeData} from '~/services/dto/pellet-type-data'
import {getPelletTypeList} from '~/services/pellet-type'
import {
createReceptionPelletBuilding,
deleteReceptionPelletBuilding,
getReceptionPelletBuildingList
} from '~/services/reception-pellet-building'
import { useReceptionStore } from '~/stores/reception'
import { MERCHANDISE_TYPE_CODES, RECEPTION_TYPE_CODES } from '~/utils/constants'
import {useReceptionStore} from '~/stores/reception'
import {MERCHANDISE_TYPE_CODES, RECEPTION_TYPE_CODES} from '~/utils/constants'
import ReceptionBovineReceived from "~/components/reception/reception-bovine-received.vue";
const receptionStore = useReceptionStore()
const merchandiseTypes = ref<MerchandiseTypeData[]>([])
@@ -173,7 +173,6 @@ onMounted(async () => {
}
selectedPelletBuildingIds.value = selectionMap
})
// Enregistre les sélections et passe à l'étape suivante
async function goNext() {
if (!receptionStore.current) {
@@ -191,6 +190,8 @@ async function goNext() {
buildings: isGranule.value
? []
: selectedBuildingIds.value.map((id) => `/api/buildings/${id}`),
bovineDetail: null,
bovinesTypes: null,
currentStep: nextStep
})
@@ -208,7 +209,6 @@ async function clearPelletSelections(receptionIri: string) {
await deleteReceptionPelletBuilding(selection.id)
}
}
// Synchronise les associations granulés/bâtiments avec l'état du formulaire
async function syncPelletSelections(receptionIri: string) {
const existing = await getReceptionPelletBuildingList(receptionIri)
@@ -227,7 +227,7 @@ async function syncPelletSelections(receptionIri: string) {
const desiredEntries: Array<{ pelletTypeId: string; buildingId: string }> = []
for (const [pelletTypeId, buildingIds] of Object.entries(selectedPelletBuildingIds.value)) {
for (const buildingId of buildingIds) {
desiredEntries.push({ pelletTypeId, buildingId })
desiredEntries.push({pelletTypeId, buildingId})
}
}

View File

@@ -0,0 +1,91 @@
<template>
<div :class="['flex flex-row items-center gap-2', wrapperClass]">
<label
v-if="label"
:for="id"
class="text-xl text-bold flex items-center gap-2"
:class="labelClass"
>
<span
v-if="label">
{{ label }}
</span>
<span
v-if="code" class="text-neutral-600">
({{ code }})
</span>
</label>
<input
:id="id"
type="number"
:value="modelValue ?? ''"
:min="min"
:max="max"
:step="step"
:disabled="disabled"
v-bind="attrs"
class="border-b border-black text-xl bg-transparent w-48"
:class="[
isEmpty ? 'text-neutral-400' : 'text-black',
disabled ? 'cursor-not-allowed' : 'cursor-text',
inputClass
]"
@keydown="onKeydown"
@input="onInput"
>
</div>
</template>
<script setup lang="ts">
import {computed, useAttrs} from 'vue'
defineOptions({inheritAttrs: false})
const props = withDefaults(
defineProps<{
id?: string
label?: string
code?: string
modelValue: number | string | null | undefined
min?: number | string
max?: number | string
step?: number | string
disabled?: boolean
wrapperClass?: string
labelClass?: string
inputClass?: string
}>(),
{
min: undefined,
max: undefined,
step: undefined,
disabled: false,
wrapperClass: '',
labelClass: '',
inputClass: ''
}
)
const emit = defineEmits<{
(event: 'update:modelValue', value: number | null): void
}>()
const attrs = useAttrs()
const isEmpty = computed(() => props.modelValue === null || props.modelValue === undefined || props.modelValue === '')
const onInput = (event: Event) => {
const target = event.target as HTMLInputElement
if (target.value === '') {
emit('update:modelValue', null)
return
}
const numeric = Math.max(0, Number(target.value))
emit('update:modelValue', Number.isNaN(numeric) ? null : numeric)
}
const onKeydown = (event: KeyboardEvent) => {
if (event.key === '-' || event.key === 'e' || event.key === 'E') {
event.preventDefault()
}
}
</script>

View File

@@ -1,64 +1,72 @@
{
"errors": {
"http": {
"get": "Impossible de récupérer les données.",
"post": "Impossible de créer la ressource.",
"put": "Impossible de mettre à jour la ressource.",
"patch": "Impossible de mettre à jour la ressource.",
"delete": "Impossible de supprimer la ressource."
"errors": {
"http": {
"get": "Impossible de récupérer les données.",
"post": "Impossible de créer la ressource.",
"put": "Impossible de mettre à jour la ressource.",
"patch": "Impossible de mettre à jour la ressource.",
"delete": "Impossible de supprimer la ressource."
},
"reception": {
"list": "Impossible de récupérer la liste des réceptions.",
"fetch": "Impossible de récupérer la réception.",
"create": "Impossible de créer la réception.",
"update": "Impossible de mettre à jour la réception.",
"weigh": "Impossible de récupérer la pesée."
},
"receptionType": {
"list": "Impossible de récupérer la liste des types de réception."
},
"merchandiseType": {
"list": "Impossible de récupérer la liste des types de marchandises."
},
"building": {
"list": "Impossible de récupérer la liste des bâtiments."
},
"pelletType": {
"list": "Impossible de récupérer la liste des types de granulés."
},
"receptionPelletBuilding": {
"list": "Impossible de récupérer la liste des dépôts de granulés.",
"create": "Impossible d'enregistrer le dépôt de granulés.",
"delete": "Impossible de supprimer le dépôt de granulés."
},
"receptionBovine": {
"list": "Impossible de récupérer la liste des bovins de la réception.",
"create": "Impossible d'enregistrer le bovin.",
"delete": "Impossible de supprimer le bovin."
},
"supplier": {
"list": "Impossible de récupérer la liste des fournisseurs."
},
"truck": {
"list": "Impossible de récupérer la liste des camions."
},
"bovin": {
"list": "Impossible de récupérer la liste des races de bovins."
},
"carrier": {
"list": "Impossible de récupérer la liste des transporteurs."
},
"driver": {
"list": "Impossible de récupérer la liste des chauffeurs."
},
"vehicle": {
"list": "Impossible de récupérer la liste des immatriculations."
},
"auth": {
"login": "Identifiants invalides.",
"users": "Impossible de récupérer les utilisateurs.",
"logout": "Impossible de se déconnecter."
}
},
"reception": {
"list": "Impossible de récupérer la liste des réceptions.",
"fetch": "Impossible de récupérer la réception.",
"create": "Impossible de créer la réception.",
"update": "Impossible de mettre à jour la réception.",
"weigh": "Impossible de récupérer la pesée."
},
"receptionType": {
"list": "Impossible de récupérer la liste des types de réception."
},
"merchandiseType": {
"list": "Impossible de récupérer la liste des types de marchandises."
},
"building": {
"list": "Impossible de récupérer la liste des bâtiments."
},
"pelletType": {
"list": "Impossible de récupérer la liste des types de granulés."
},
"receptionPelletBuilding": {
"list": "Impossible de récupérer la liste des dépôts de granulés.",
"create": "Impossible d'enregistrer le dépôt de granulés.",
"delete": "Impossible de supprimer le dépôt de granulés."
},
"supplier": {
"list": "Impossible de récupérer la liste des fournisseurs."
},
"truck": {
"list": "Impossible de récupérer la liste des camions."
},
"carrier": {
"list": "Impossible de récupérer la liste des transporteurs."
},
"driver": {
"list": "Impossible de récupérer la liste des chauffeurs."
},
"vehicle": {
"list": "Impossible de récupérer la liste des immatriculations."
},
"auth": {
"login": "Identifiants invalides.",
"users": "Impossible de récupérer les utilisateurs.",
"logout": "Impossible de se déconnecter."
"success": {
"reception": {
"update": "Réception mise à jour avec succès."
},
"auth": {
"login": "Connexion réussie.",
"logout": "Déconnexion réussie."
}
}
},
"success": {
"reception": {
"update": "Réception mise à jour avec succès."
},
"auth": {
"login": "Connexion réussie.",
"logout": "Déconnexion réussie."
}
}
}

View File

@@ -9,7 +9,8 @@ export default defineNuxtConfig({
'@nuxtjs/tailwindcss',
'@pinia/nuxt',
'nuxt-toast',
'@nuxtjs/i18n'
'@nuxtjs/i18n',
'@nuxt/icon'
],
css: ['~/assets/css/main.css', '~/assets/css/toast.css'],
runtimeConfig: {

View File

@@ -7,6 +7,7 @@
"name": "frontend",
"hasInstallScript": true,
"dependencies": {
"@nuxt/icon": "^2.2.1",
"@nuxtjs/i18n": "^10.2.1",
"@pinia/nuxt": "^0.11.3",
"izitoast": "^1.4.0",
@@ -35,6 +36,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@antfu/install-pkg": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
"integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==",
"license": "MIT",
"dependencies": {
"package-manager-detector": "^1.3.0",
"tinyexec": "^1.0.1"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
@@ -63,7 +77,6 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -1064,6 +1077,7 @@
"version": "4.12.2",
"resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
"integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
"peer": true,
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
@@ -1072,6 +1086,7 @@
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
"integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
"peer": true,
"dependencies": {
"@eslint/object-schema": "^2.1.7",
"debug": "^4.3.1",
@@ -1085,6 +1100,7 @@
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"peer": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -1094,6 +1110,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"peer": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -1105,6 +1122,7 @@
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
"integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
"peer": true,
"dependencies": {
"@eslint/core": "^0.17.0"
},
@@ -1116,6 +1134,7 @@
"version": "0.17.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
"integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
"peer": true,
"dependencies": {
"@types/json-schema": "^7.0.15"
},
@@ -1127,6 +1146,7 @@
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz",
"integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==",
"peer": true,
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
@@ -1149,6 +1169,7 @@
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"peer": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -1158,6 +1179,7 @@
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"peer": true,
"engines": {
"node": ">= 4"
}
@@ -1166,6 +1188,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"peer": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -1177,6 +1200,7 @@
"version": "9.39.2",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz",
"integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==",
"peer": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -1188,6 +1212,7 @@
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
"integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
"peer": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
@@ -1196,6 +1221,7 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
"integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
"peer": true,
"dependencies": {
"@eslint/core": "^0.17.0",
"levn": "^0.4.1"
@@ -1208,6 +1234,7 @@
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
"integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
"peer": true,
"engines": {
"node": ">=18.18.0"
}
@@ -1216,6 +1243,7 @@
"version": "0.16.7",
"resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
"integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
"peer": true,
"dependencies": {
"@humanfs/core": "^0.19.1",
"@humanwhocodes/retry": "^0.4.0"
@@ -1228,6 +1256,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
"integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
"peer": true,
"engines": {
"node": ">=12.22"
},
@@ -1240,6 +1269,7 @@
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
"integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
"peer": true,
"engines": {
"node": ">=18.18"
},
@@ -1248,6 +1278,47 @@
"url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@iconify/collections": {
"version": "1.0.646",
"resolved": "https://registry.npmjs.org/@iconify/collections/-/collections-1.0.646.tgz",
"integrity": "sha512-zA5Gr1MJm1SI0TjOUl7wu4kvBWXQ6Uh8ALEtqQ5ucXyUxP2M8m2bk2hfVtGykSdMlDB+Xs2AHbJ9pQqayz9WGQ==",
"license": "MIT",
"dependencies": {
"@iconify/types": "*"
}
},
"node_modules/@iconify/types": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
"integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
"license": "MIT"
},
"node_modules/@iconify/utils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz",
"integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==",
"license": "MIT",
"dependencies": {
"@antfu/install-pkg": "^1.1.0",
"@iconify/types": "^2.0.0",
"mlly": "^1.8.0"
}
},
"node_modules/@iconify/vue": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@iconify/vue/-/vue-5.0.0.tgz",
"integrity": "sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==",
"license": "MIT",
"dependencies": {
"@iconify/types": "^2.0.0"
},
"funding": {
"url": "https://github.com/sponsors/cyberalien"
},
"peerDependencies": {
"vue": ">=3"
}
},
"node_modules/@intlify/bundle-utils": {
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-11.0.3.tgz",
@@ -2268,6 +2339,28 @@
"devtools-wizard": "cli.mjs"
}
},
"node_modules/@nuxt/icon": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@nuxt/icon/-/icon-2.2.1.tgz",
"integrity": "sha512-GI840yYGuvHI0BGDQ63d6rAxGzG96jQcWrnaWIQKlyQo/7sx9PjXkSHckXUXyX1MCr9zY6U25Td6OatfY6Hklw==",
"license": "MIT",
"dependencies": {
"@iconify/collections": "^1.0.641",
"@iconify/types": "^2.0.0",
"@iconify/utils": "^3.1.0",
"@iconify/vue": "^5.0.0",
"@nuxt/devtools-kit": "^3.1.1",
"@nuxt/kit": "^4.2.2",
"consola": "^3.4.2",
"local-pkg": "^1.1.2",
"mlly": "^1.8.0",
"ohash": "^2.0.11",
"pathe": "^2.0.3",
"picomatch": "^4.0.3",
"std-env": "^3.10.0",
"tinyglobby": "^0.2.15"
}
},
"node_modules/@nuxt/kit": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.2.2.tgz",
@@ -2951,7 +3044,6 @@
"version": "0.95.0",
"resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.95.0.tgz",
"integrity": "sha512-Te8fE/SmiiKWIrwBwxz5Dod87uYvsbcZ9JAL5ylPg1DevyKgTkxCXnPEaewk1Su2qpfNmry5RHoN+NywWFCG+A==",
"peer": true,
"dependencies": {
"@oxc-project/types": "^0.95.0"
},
@@ -4856,7 +4948,8 @@
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"peer": true
},
"node_modules/@types/parse-path": {
"version": "7.0.3",
@@ -5206,7 +5299,6 @@
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.26.tgz",
"integrity": "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/parser": "^7.28.5",
"@vue/compiler-core": "3.5.26",
@@ -5404,7 +5496,6 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -5442,6 +5533,7 @@
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@@ -5814,7 +5906,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -5928,7 +6019,6 @@
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
"integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=8"
}
@@ -6005,6 +6095,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"peer": true,
"engines": {
"node": ">=6"
}
@@ -6123,7 +6214,6 @@
"resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz",
"integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"consola": "^3.2.3"
}
@@ -6734,7 +6824,8 @@
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"peer": true
},
"node_modules/deepmerge": {
"version": "4.3.1",
@@ -7245,6 +7336,7 @@
"version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
"peer": true,
"dependencies": {
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
@@ -7271,6 +7363,7 @@
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"peer": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -7280,6 +7373,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"peer": true,
"engines": {
"node": ">=10"
},
@@ -7291,6 +7385,7 @@
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"peer": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -7302,6 +7397,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"peer": true,
"dependencies": {
"is-glob": "^4.0.3"
},
@@ -7313,6 +7409,7 @@
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"peer": true,
"engines": {
"node": ">= 4"
}
@@ -7321,6 +7418,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"peer": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -7332,6 +7430,7 @@
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
"integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
"peer": true,
"dependencies": {
"acorn": "^8.15.0",
"acorn-jsx": "^5.3.2",
@@ -7348,6 +7447,7 @@
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"peer": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -7371,6 +7471,7 @@
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
"integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
"peer": true,
"dependencies": {
"estraverse": "^5.1.0"
},
@@ -7382,6 +7483,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"peer": true,
"dependencies": {
"estraverse": "^5.2.0"
},
@@ -7479,7 +7581,8 @@
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"peer": true
},
"node_modules/fast-fifo": {
"version": "1.3.2",
@@ -7506,12 +7609,14 @@
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"peer": true
},
"node_modules/fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"peer": true
},
"node_modules/fast-npm-meta": {
"version": "0.4.7",
@@ -7552,6 +7657,7 @@
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
"peer": true,
"dependencies": {
"flat-cache": "^4.0.0"
},
@@ -7581,6 +7687,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"peer": true,
"dependencies": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
@@ -7596,6 +7703,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
"peer": true,
"dependencies": {
"flatted": "^3.2.9",
"keyv": "^4.5.4"
@@ -7607,7 +7715,8 @@
"node_modules/flatted": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
"peer": true
},
"node_modules/foreground-child": {
"version": "3.3.1",
@@ -7874,6 +7983,7 @@
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
"integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"peer": true,
"engines": {
"node": ">=18"
},
@@ -8174,6 +8284,7 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"peer": true,
"dependencies": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@@ -8189,6 +8300,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"peer": true,
"engines": {
"node": ">=4"
}
@@ -8210,6 +8322,7 @@
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"peer": true,
"engines": {
"node": ">=0.8.19"
}
@@ -8537,8 +8650,7 @@
"node_modules/izitoast": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/izitoast/-/izitoast-1.4.0.tgz",
"integrity": "sha512-Oc1X2wiQtPp39i5VpIjf3GJf5sfCtHKXZ5szx7RareyEeFLUlcEW0FSfBni28+Ul6KNKZRKzhVuWzSP4Xngh0w==",
"peer": true
"integrity": "sha512-Oc1X2wiQtPp39i5VpIjf3GJf5sfCtHKXZ5szx7RareyEeFLUlcEW0FSfBni28+Ul6KNKZRKzhVuWzSP4Xngh0w=="
},
"node_modules/jackspeak": {
"version": "3.4.3",
@@ -8596,17 +8708,20 @@
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"peer": true
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"peer": true
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
"integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
"peer": true
},
"node_modules/json5": {
"version": "2.2.3",
@@ -8684,6 +8799,7 @@
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
"peer": true,
"dependencies": {
"json-buffer": "3.0.1"
}
@@ -8957,6 +9073,7 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
"integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
"peer": true,
"dependencies": {
"prelude-ls": "^1.2.1",
"type-check": "~0.4.0"
@@ -9041,6 +9158,7 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"peer": true,
"dependencies": {
"p-locate": "^5.0.0"
},
@@ -9078,7 +9196,8 @@
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"peer": true
},
"node_modules/lodash.uniq": {
"version": "4.5.0",
@@ -9424,7 +9543,8 @@
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"peer": true
},
"node_modules/negotiator": {
"version": "0.6.3",
@@ -10162,7 +10282,6 @@
"resolved": "https://registry.npmjs.org/nuxt/-/nuxt-4.2.2.tgz",
"integrity": "sha512-n6oYFikgLEb70J4+K19jAzfx4exZcRSRX7yZn09P5qlf2Z59VNOBqNmaZO5ObzvyGUZ308SZfL629/Q2v2FVjw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@dxup/nuxt": "^0.2.2",
"@nuxt/cli": "^3.31.1",
@@ -10430,6 +10549,7 @@
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
"integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
"peer": true,
"dependencies": {
"deep-is": "^0.1.3",
"fast-levenshtein": "^2.0.6",
@@ -10476,7 +10596,6 @@
"resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.102.0.tgz",
"integrity": "sha512-xMiyHgr2FZsphQ12ZCsXRvSYzmKXCm1ejmyG4GDZIiKOmhyt5iKtWq0klOfFsEQ6jcgbwrUdwcCVYzr1F+h5og==",
"license": "MIT",
"peer": true,
"dependencies": {
"@oxc-project/types": "^0.102.0"
},
@@ -10549,6 +10668,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"peer": true,
"dependencies": {
"yocto-queue": "^0.1.0"
},
@@ -10563,6 +10683,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"peer": true,
"dependencies": {
"p-limit": "^3.0.2"
},
@@ -10589,6 +10710,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"peer": true,
"dependencies": {
"callsites": "^3.0.0"
},
@@ -10637,6 +10759,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"peer": true,
"engines": {
"node": ">=8"
}
@@ -10751,7 +10874,6 @@
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
"integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
"peer": true,
"dependencies": {
"@vue/devtools-api": "^7.7.7"
},
@@ -10857,7 +10979,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
@@ -11407,7 +11528,6 @@
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz",
"integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==",
"license": "MIT",
"peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
@@ -11475,6 +11595,7 @@
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"peer": true,
"engines": {
"node": ">= 0.8.0"
}
@@ -11529,6 +11650,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"peer": true,
"engines": {
"node": ">=6"
}
@@ -11880,7 +12002,6 @@
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz",
"integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -12440,6 +12561,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"peer": true,
"engines": {
"node": ">=8"
},
@@ -12702,7 +12824,6 @@
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
@@ -13042,6 +13163,7 @@
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
"integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"peer": true,
"dependencies": {
"prelude-ls": "^1.2.1"
},
@@ -13111,7 +13233,6 @@
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -13553,6 +13674,7 @@
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
"peer": true,
"dependencies": {
"punycode": "^2.1.0"
}
@@ -13578,7 +13700,6 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.0.tgz",
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -13935,7 +14056,6 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.26.tgz",
"integrity": "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.26",
"@vue/compiler-sfc": "3.5.26",
@@ -13971,7 +14091,6 @@
"version": "11.2.8",
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.2.8.tgz",
"integrity": "sha512-vJ123v/PXCZntd6Qj5Jumy7UBmIuE92VrtdX+AXr+1WzdBHojiBxnAxdfctUFL+/JIN+VQH4BhsfTtiGsvVObg==",
"peer": true,
"dependencies": {
"@intlify/core-base": "11.2.8",
"@intlify/shared": "11.2.8",
@@ -13992,7 +14111,6 @@
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
"integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/devtools-api": "^6.6.4"
},
@@ -14044,6 +14162,7 @@
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -14309,6 +14428,7 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"peer": true,
"engines": {
"node": ">=10"
},

View File

@@ -11,6 +11,7 @@
"build:dist": "nuxt generate && rm -rf dist && cp -R .output/public dist"
},
"dependencies": {
"@nuxt/icon": "^2.2.1",
"@nuxtjs/i18n": "^10.2.1",
"@pinia/nuxt": "^0.11.3",
"izitoast": "^1.4.0",

View File

@@ -1,55 +1,16 @@
<script setup lang="ts">
</script>
<template>
<div>
<h1 class="text-3xl font-bold">Liste des receptions</h1>
<div class="mt-6 border border-slate-200">
<div class="grid grid-cols-6 gap-4 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide">
<div>ID</div>
<div>Immatriculation</div>
<div>Pesée plein</div>
<div>Pesée vide</div>
<div>Etape</div>
<div>Date</div>
</div>
<div
v-for="reception in receptionList"
:key="reception.id"
class="grid grid-cols-6 gap-4 px-4 py-3 text-sm hover:bg-slate-50 cursor-pointer border-t border-slate-200"
role="button"
tabindex="0"
@click="goToReception(reception.id)"
@keydown.enter="goToReception(reception.id)"
>
<div>{{ reception.id }}</div>
<div>{{ reception.licensePlate }}</div>
<div>{{ formatWeighing(reception, 'gross') }}</div>
<div>{{ formatWeighing(reception, 'tare') }}</div>
<div>{{ reception.currentStep }}</div>
<div>{{ reception.receptionDate }}</div>
</div>
</div>
<div class="grid grid-cols-3 gap-x-20 gap-y-10 ">
<card-link label="NOUVELLE RÉCEPTION" link="/reception" iconName="mdi:truck-outline" />
<card-link label="NOUVELLE EXPÉDITION" link="/" iconName="mdi:truck-fast-outline" />
<card-link label="PLAN DE SITE" link="/" iconName="mdi:warehouse" />
<card-link label="RÉCEPTIONS EN ATTENTE" link="/reception/waiting-reception" iconName="mdi:truck-remove-outline" />
<card-link label="EXPÉDITIONS EN ATTENTE" link="/" iconName="mdi:truck-cargo-container" />
<card-link label="CASES" link="/" iconName="mdi:cube-outline" />
<card-link label="RÉCEPTIONS FINIES" link="/reception/finish-reception" iconName="mdi:truck-check-outline" />
<card-link label="EXPÉDITIONS FINIES" link="/" iconName="mdi:truck-delivery-outline" />
<card-link label="PASSEPORT DU BOVIN" link="/" iconName="mdi:cow" />
</div>
</template>
<script setup lang="ts">
import type {ReceptionData} from "~/services/dto/reception-data";
import {getReceptionList} from "~/services/reception";
const receptionList = ref<ReceptionData[]>()
const router = useRouter()
const goToReception = (id: number) => {
router.push(`/reception/${id}`)
}
const formatWeighing = (reception: ReceptionData, type: 'gross' | 'tare') => {
const entry = reception.weights?.find((weight) => weight.type === type)
if (!entry || entry.weight == null || entry.dsd == null) {
return '—'
}
return `${entry.weight} kg / ${entry.dsd} dsd`
}
onMounted(async () => {
receptionList.value = await getReceptionList()
})
</script>

View File

@@ -16,7 +16,12 @@
</div>
<ReceptionForm v-if="!storeReception || storeReception.currentStep === 0"/>
<ReceptionWeight v-if="storeReception?.currentStep === 1" mode="gross"/>
<ReceptionProductReceived v-if="storeReception?.currentStep === 2"/>
<ReceptionProductReceived
v-if="storeReception?.currentStep === 2 &&
receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.MERCHANDISES"/>
<ReceptionBovineReceived
v-if="storeReception?.currentStep === 2 &&
receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.BOVINS"/>
<ReceptionWeight v-if="storeReception?.currentStep !== null && storeReception?.currentStep >= 3" mode="tare"/>
</div>
</template>
@@ -25,6 +30,7 @@
import {useReceptionStore} from '~/stores/reception'
import {storeToRefs} from 'pinia'
import {RECEPTION_STEP_LABELS} from '~/constants/steps'
import {RECEPTION_TYPE_CODES} from "~/utils/constants";
const route = useRoute()
const router = useRouter()

View File

@@ -0,0 +1,53 @@
<template>
<div class="flex items-center justify-start gap-10">
<Icon @click="router.push('/')" name="gg:arrow-left-o" style="color: black" size="44" />
<h1 class="text-3xl font-bold uppercase">listes des réceptions finie</h1>
</div>
<div class="ps-20 " >
<div class="mt-6 border border-slate-200 mb-16 ">
<div class="grid grid-cols-6 gap-4 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide">
<div>Numéro</div>
<div>Date</div>
<div>Fournisseur</div>
<div>Adresse</div>
<div>Type réception</div>
<div>Poids</div>
</div>
<div
v-for="reception in receptionList"
:key="reception.id"
class="grid grid-cols-6 gap-4 px-4 py-3 text-sm hover:bg-slate-50 cursor-pointer border-t border-slate-200"
role="button"
tabindex="0"
>
<div>{{ reception.identificationNumber}}</div>
<div>{{ reception.receptionDate}}</div>
<div>{{ reception.supplier?.name }}</div>
<div>{{ reception.address?.fullAddress }}</div>
<div>{{ reception.receptionType?.label }}</div>
<div>{{ formatWeighing(reception, 'gross') }} | {{ formatWeighing(reception, 'tare') }}</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type {ReceptionData} from "~/services/dto/reception-data";
import {getReceptionList} from "~/services/reception";
const receptionList = ref<ReceptionData[]>()
const router = useRouter()
const formatWeighing = (reception: ReceptionData, type: 'gross' | 'tare') => {
const entry = reception.weights?.find((weight) => weight.type === type)
if (!entry || entry.weight == null || entry.dsd == null) {
return '—'
}
return `${entry.weight} kg`
}
onMounted(async () => {
receptionList.value = await getReceptionList(true)
})
</script>

View File

@@ -0,0 +1,51 @@
<template>
<div class="flex items-center justify-between ">
<div class="flex items-center gap-10">
<Icon @click="router.push('/')" name="gg:arrow-left-o" style="color: black" size="44" />
<h1 class="text-3xl font-bold uppercase">listes des réceptions en attente</h1>
</div>
</div>
<div class="ps-20 " >
<div class="mt-6 border border-slate-200 mb-16 ">
<div class="grid grid-cols-5 gap-4 bg-slate-100 px-4 py-3 text-sm font-semibold uppercase tracking-wide">
<div>Fournisseur</div>
<div>Adresse</div>
<div>Type réception</div>
<div>Transporteur</div>
<div>Immatriculation</div>
</div>
<div
v-for="reception in receptionList"
:key="reception.id"
class="grid grid-cols-5 gap-4 px-4 py-3 text-sm hover:bg-slate-50 cursor-pointer border-t border-slate-200"
role="button"
tabindex="0"
@click="goToReception(reception.id)"
@keydown.enter="goToReception(reception.id)"
>
<div>{{ reception.supplier?.name }}</div>
<div>{{ reception.address?.fullAddress }}</div>
<div>{{ reception.receptionType?.label }}</div>
<div>{{ reception.carrier?.name }}</div>
<div>{{ reception.licensePlate }}</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type {ReceptionData} from "~/services/dto/reception-data";
import {getReceptionList} from "~/services/reception";
const receptionList = ref<ReceptionData[]>()
const router = useRouter()
const goToReception = (id: number) => {
router.push(`/reception/${id}`)
}
onMounted(async () => {
receptionList.value = await getReceptionList(false)
})
</script>

View File

@@ -0,0 +1,23 @@
import { useApi } from '~/composables/useApi'
import type {BovineTypeData} from "~/services/dto/bovine-type-data";
export type BovineTypeListResponse =
| BovineTypeData[]
| { 'hydra:member'?: BovineTypeData[] }
export async function getBovineTypeList(): Promise<BovineTypeData[]> {
const api = useApi()
const response = await api.get<BovineTypeListResponse>('bovine_types', {}, {
toastErrorKey: 'errors.bovin.list'
})
if (Array.isArray(response)) {
return response
}
if (response && typeof response === 'object' && Array.isArray(response['hydra:member'])) {
return response['hydra:member']
}
return []
}

View File

@@ -0,0 +1,5 @@
export interface BovineTypeData{
id: number
label: string
code: string
}

View File

@@ -0,0 +1,8 @@
import type {BovineTypeData} from "~/services/dto/bovine-type-data";
export interface ReceptionBovineTypeData{
id: number
quantity : number
reception?: string
bovineType: BovineTypeData
}

View File

@@ -8,6 +8,7 @@ import type { AddressData } from '~/services/dto/address-data'
import type { TruckData } from '~/services/dto/truck-data'
import type { CarrierData } from '~/services/dto/carrier-data'
import type { DriverData } from '~/services/dto/driver-data'
import type {BovineTypeData} from "~/services/dto/bovine-type-data";
export interface ReceptionData {
id: number
@@ -20,7 +21,9 @@ export interface ReceptionData {
receptionType?: ReceptionTypeData | null
merchandiseType?: MerchandiseTypeData | null
merchandiseDetail?: string | null
bovineDetail?: string | null
buildings?: BuildingData[] | null
bovinesTypes?: BovineTypeData[] | null
pelletBuildings?: ReceptionPelletBuildingData[] | null
user?: UserData | null
supplier?: SupplierData | null
@@ -46,7 +49,9 @@ export type ReceptionPayload = {
receptionType?: string | null
merchandiseType?: string | null
merchandiseDetail?: string | null
bovineDetail?: string | null
buildings?: string[] | null
bovinesTypes?: string[] | null
user?: string | null
supplier?: string | null
address?: string | null

View File

@@ -0,0 +1,59 @@
import { useApi } from '~/composables/useApi'
import type { ReceptionBovineTypeData } from '~/services/dto/reception-bovine-data'
export type ReceptionBovineListResponse =
| ReceptionBovineTypeData[]
| { 'hydra:member'?: ReceptionBovineTypeData[] }
export type ReceptionBovinePayload = {
quantity: number
reception: string
bovineType: string
}
export async function getReceptionBovineList(
receptionIri: string
): Promise<ReceptionBovineTypeData[]> {
const api = useApi()
const response = await api.get<ReceptionBovineListResponse>(
'reception_bovines',
{ reception: receptionIri },
{
toastErrorKey: 'errors.receptionBovine.list'
}
)
if (Array.isArray(response)) {
return response
}
if (response && typeof response === 'object' && Array.isArray(response['hydra:member'])) {
return response['hydra:member']
}
return []
}
export async function createReceptionBovine(
payload: ReceptionBovinePayload
): Promise<ReceptionBovineTypeData> {
const api = useApi()
return api.post<ReceptionBovineTypeData>('reception_bovines', payload, {
toastErrorKey: 'errors.receptionBovine.create'
})
}
export async function deleteReceptionBovine(id: number): Promise<void> {
const api = useApi()
await api.delete<void>(`reception_bovines/${id}`, {}, {
toastErrorKey: 'errors.receptionBovine.delete'
})
}
export async function updateReceptionBovine(
id: number,
payload: Partial<ReceptionBovinePayload>
): Promise<ReceptionBovineTypeData> {
const api = useApi()
return api.patch<ReceptionBovineTypeData>(`reception_bovines/${id}`, payload, {
toastErrorKey: 'errors.receptionBovine.update'
})
}

View File

@@ -2,13 +2,15 @@ import {useApi} from '~/composables/useApi'
import type {ReceptionData, ReceptionPayload} from '~/services/dto/reception-data'
import type {WeightData} from '~/services/dto/weight-data'
export async function getReceptionList() {
export async function getReceptionList(isValid: boolean|null = null) {
const api = useApi()
return api.get<ReceptionData>(`receptions`, {}, {
const query = isValid !== null ? { isValid: isValid} : {}
return api.get<ReceptionData[]>('receptions', query, {
toastErrorKey: 'errors.reception.list'
})
}
export async function getReception(id: number) {
const api = useApi()
return api.get<ReceptionData>(`receptions/${id}`, {}, {

View File

@@ -1,5 +1,6 @@
export const RECEPTION_TYPE_CODES = {
MERCHANDISES: 'MARCHANDISES'
MERCHANDISES: 'MARCHANDISES',
BOVINS: 'BOVINS'
} as const
export const MERCHANDISE_TYPE_CODES = {

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20260203123833 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE bovine_type (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, label VARCHAR(120) NOT NULL, code VARCHAR(50) NOT NULL, PRIMARY KEY (id))');
$this->addSql('CREATE TABLE reception_bovine (id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, quantity INT NOT NULL, reception_id INT DEFAULT NULL, bovine_type_id INT NOT NULL, PRIMARY KEY (id))');
$this->addSql('CREATE INDEX IDX_636B9DB97C14DF52 ON reception_bovine (reception_id)');
$this->addSql('CREATE INDEX IDX_636B9DB97899F32E ON reception_bovine (bovine_type_id)');
$this->addSql('ALTER TABLE reception_bovine ADD CONSTRAINT FK_636B9DB97C14DF52 FOREIGN KEY (reception_id) REFERENCES reception (id)');
$this->addSql('ALTER TABLE reception_bovine ADD CONSTRAINT FK_636B9DB97899F32E FOREIGN KEY (bovine_type_id) REFERENCES bovine_type (id) NOT DEFERRABLE');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE reception_bovine DROP CONSTRAINT FK_636B9DB97C14DF52');
$this->addSql('ALTER TABLE reception_bovine DROP CONSTRAINT FK_636B9DB97899F32E');
$this->addSql('DROP TABLE bovine_type');
$this->addSql('DROP TABLE reception_bovine');
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20260204141406 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE reception_bovine ALTER quantity SET DEFAULT 0');
$this->addSql('CREATE UNIQUE INDEX uniq_reception_bovine_type ON reception_bovine (reception_id, bovine_type_id)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP INDEX uniq_reception_bovine_type');
$this->addSql('ALTER TABLE reception_bovine ALTER quantity DROP DEFAULT');
}
}

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20260205070819 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE reception ADD bovine_detail VARCHAR(255) DEFAULT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE reception DROP bovine_detail');
}
}

70
src/Entity/BovineType.php Normal file
View File

@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Groups;
#[ORM\Entity]
#[ApiResource(
operations: [
new Get(
requirements: ['id' => '\d+'],
normalizationContext: ['groups' => ['bovine-type:read']],
),
new GetCollection(
normalizationContext: ['groups' => ['bovine-type:read']],
),
],
security: "is_granted('ROLE_USER')",
)]
class BovineType
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(['bovine-type:read', 'reception:read', 'reception-bovine:read'])]
private ?int $id = null;
#[ORM\Column(length: 120)]
#[Groups(['bovine-type:read', 'reception:read', 'reception-bovine:read'])]
private ?string $label = null;
#[ORM\Column(length: 50)]
#[Groups(['bovine-type:read', 'reception:read', 'reception-bovine:read'])]
private ?string $code = null;
public function getId(): ?int
{
return $this->id;
}
public function getLabel(): ?string
{
return $this->label;
}
public function setLabel(string $label): static
{
$this->label = $label;
return $this;
}
public function getCode(): ?string
{
return $this->code;
}
public function setCode(string $code): static
{
$this->code = $code;
return $this;
}
}

View File

@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace App\Entity;
use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Get;
@@ -26,6 +28,7 @@ use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
#[ORM\Entity]
#[ORM\HasLifecycleCallbacks]
#[ORM\Table(name: 'reception')]
#[ApiFilter(BooleanFilter::class, properties: ['isValid'])]
#[ApiResource(
operations: [
new Get(
@@ -72,27 +75,27 @@ class Reception
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(['reception:read'])]
#[Groups(['reception:read', 'reception-bovine:read'])]
private ?int $id = null;
#[ORM\Column(length: 20, nullable: true)]
#[Groups(['reception:read', 'reception:write'])]
#[Groups(['reception:read', 'reception:write', 'reception-bovine:read'])]
private ?string $licensePlate = null;
#[ORM\Column(length: 20, unique: true, nullable: true)]
#[Groups(['reception:read'])]
#[Groups(['reception:read', 'reception-bovine:read'])]
private ?string $identificationNumber = null;
#[ORM\Column(options: ['default' => 0])]
#[Groups(['reception:read', 'reception:write'])]
#[Groups(['reception:read', 'reception:write', 'reception-bovine:read'])]
private int $currentStep = 0;
#[ORM\Column(options: ['default' => false])]
#[Groups(['reception:read', 'reception:write'])]
#[Groups(['reception:read', 'reception:write', 'reception-bovine:read'])]
private bool $isValid = false;
#[ORM\Column(name: 'date_reception', type: 'datetime_immutable')]
#[Groups(['reception:read', 'reception:write'])]
#[Groups(['reception:read', 'reception:write', 'reception-bovine:read'])]
#[Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
private ?DateTimeImmutable $receptionDate = null;
@@ -168,6 +171,20 @@ class Reception
#[ApiProperty(readableLink: true)]
private ?Driver $driver = null;
/**
* @var Collection<int, ReceptionBovine>
*/
#[ORM\OneToMany(targetEntity: ReceptionBovine::class, mappedBy: 'reception', cascade: ['persist', 'remove'], orphanRemoval: true)]
#[Assert\Range(
min: 0
)]
#[Groups(['reception:read', 'reception:write'])]
private Collection $bovines_types;
#[ORM\Column(length: 255, nullable: true)]
#[Groups(['reception:read', 'reception:write'])]
private ?string $bovineDetail = null;
public function __construct(
?DateTimeImmutable $receptionDate = null,
) {
@@ -175,6 +192,7 @@ class Reception
$this->weights = new ArrayCollection();
$this->buildings = new ArrayCollection();
$this->pelletBuildings = new ArrayCollection();
$this->bovines_types = new ArrayCollection();
}
public function getId(): ?int
@@ -469,4 +487,46 @@ class Reception
)
;
}
/**
* @return Collection<int, ReceptionBovine>
*/
public function getBovinesTypes(): Collection
{
return $this->bovines_types;
}
public function addBovinesType(ReceptionBovine $bovinesType): static
{
if (!$this->bovines_types->contains($bovinesType)) {
$this->bovines_types->add($bovinesType);
$bovinesType->setReception($this);
}
return $this;
}
public function removeBovinesType(ReceptionBovine $bovinesType): static
{
if ($this->bovines_types->removeElement($bovinesType)) {
// set the owning side to null (unless already changed)
if ($bovinesType->getReception() === $this) {
$bovinesType->setReception(null);
}
}
return $this;
}
public function getBovineDetail(): ?string
{
return $this->bovineDetail;
}
public function setBovineDetail(?string $bovineDetail): static
{
$this->bovineDetail = $bovineDetail;
return $this;
}
}

View File

@@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace App\Entity;
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Attribute\Groups;
#[ORM\Entity]
#[ApiFilter(SearchFilter::class, properties: ['reception' => 'exact'])]
#[ORM\UniqueConstraint(name: 'uniq_reception_bovine_type', columns: ['reception_id', 'bovine_type_id'])]
#[ApiResource(
operations: [
new Get(
requirements: ['id' => '\d+'],
normalizationContext: ['groups' => ['reception-bovine:read']],
),
new GetCollection(
normalizationContext: ['groups' => ['reception-bovine:read']],
),
new Post(
normalizationContext: ['groups' => ['reception-bovine:read']],
denormalizationContext: ['groups' => ['reception-bovine:write']],
),
new Patch(
normalizationContext: ['groups' => ['reception-bovine:read']],
denormalizationContext: ['groups' => ['reception-bovine:write']],
),
new Delete(),
],
security: "is_granted('ROLE_USER')",
)]
class ReceptionBovine
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(['reception-bovine:read', 'reception:read'])]
private ?int $id = null;
#[ORM\ManyToOne(inversedBy: 'bovines_types')]
#[Groups(['reception-bovine:read', 'reception-bovine:write'])]
private ?Reception $reception = null;
#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
#[Groups(['reception-bovine:read', 'reception-bovine:write', 'reception:read'])]
#[ApiProperty(readableLink: true)]
private ?BovineType $bovineType = null;
#[ORM\Column(options: ['default' => 0])]
#[Assert\Range(
min: 0
)]
#[Groups(['reception-bovine:read', 'reception-bovine:write', 'reception:read'])]
private ?int $quantity = null;
public function getId(): ?int
{
return $this->id;
}
public function getReception(): ?Reception
{
return $this->reception;
}
public function setReception(?Reception $reception): static
{
$this->reception = $reception;
return $this;
}
public function getBovineType(): ?BovineType
{
return $this->bovineType;
}
public function setBovineType(?BovineType $bovineType): static
{
$this->bovineType = $bovineType;
return $this;
}
public function getQuantity(): ?int
{
return $this->quantity;
}
public function setQuantity(int $quantity): static
{
$this->quantity = $quantity;
return $this;
}
}

View File