92a5c48e5e
| Numéro du ticket | Titre du ticket | |------------------|-----------------| | #332 | Refonte écran réception terminée | ## Description de la PR ## Modification du .env ## Check list - [x] Pas de régression - [ ] TU/TI/TF rédigée - [x] TU/TI/TF OK - [x] CHANGELOG modifié Co-authored-by: tristan <tristan@yuno.malio.fr> Reviewed-on: https://gitea.malio.fr/MALIO-DEV/Ferme/pulls/31 Reviewed-by: Autin <tristan@yuno.malio.fr> Co-authored-by: sroy <sebastien@yuno.malio.fr> Co-committed-by: sroy <sebastien@yuno.malio.fr>
217 lines
7.6 KiB
Vue
217 lines
7.6 KiB
Vue
<template>
|
|
<form>
|
|
<div class="flex flex-col">
|
|
<div class="w-full col-start-1 row-start-1">
|
|
<UiRadioGroup
|
|
id="merchandise-type"
|
|
v-model="selectedMerchandiseTypeId"
|
|
label="Type de marchandises"
|
|
:options="merchandiseTypes.map((type) => ({
|
|
value: String(type.id),
|
|
label: type.label
|
|
}))"
|
|
input-class="accent-primary-700 focus:ring-primary-700"
|
|
option-label-class="uppercase"
|
|
wrapper-class="w-full uppercase"
|
|
group-class="grid grid-cols-[336px_336px_355px_200px] w-[160px_160px_200px_180px] mt-9 mb-7"
|
|
:disabled="!isAdmin"
|
|
/>
|
|
</div>
|
|
|
|
<div class="w-full grid grid-cols-[3fr_1fr] gap-12 col-start-2 row-start-1">
|
|
<div
|
|
v-if="selectedMerchandiseTypeId && !isGranule"
|
|
class="flex gap-[218px]"
|
|
>
|
|
<div
|
|
v-for="building in buildings"
|
|
:key="building.id"
|
|
>
|
|
<UiCheckbox
|
|
v-model="selectedBuildingIds"
|
|
:value="String(building.id)"
|
|
:label="building.label"
|
|
:disabled="!isAdmin"
|
|
input-class="accent-primary-700 focus:ring-primary-700"
|
|
label-class="text-xl uppercase"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-if="selectedMerchandiseTypeId && isAutres"
|
|
class="flex flex-col justify-self-end max-w-[182px]"
|
|
>
|
|
<UiTextInput
|
|
id="merchandise-detail"
|
|
:disabled="!isAdmin"
|
|
v-model="merchandiseDetail"
|
|
placeholder="Préciser"
|
|
:maxlength="255"
|
|
class="h-6"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
v-if="selectedMerchandiseTypeId && isGranule"
|
|
class="flex flex-col gap-10 w-full col-start-2 row-start-1"
|
|
>
|
|
<div class="grid grid-cols-1 md:grid-cols-[max-content_max-content_max-content_max-content] justify-between">
|
|
<div v-for="type in pelletTypes" :key="type.id" class="flex flex-col gap-4">
|
|
<p class="mb-1 font-medium uppercase">{{ type.label }}</p>
|
|
<div
|
|
v-for="building in buildings"
|
|
:key="building.id"
|
|
class="flex text-lg"
|
|
>
|
|
<UiCheckbox
|
|
v-model="selectedPelletBuildingIds[String(type.id)]"
|
|
:value="String(building.id)"
|
|
:label="building.label"
|
|
:disabled="!isAdmin"
|
|
input-class="accent-primary-700 focus:ring-primary-700"
|
|
label-class="text-lg"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, onMounted, ref, watch } from 'vue'
|
|
import type { BuildingData } from '~/services/dto/building-data'
|
|
import type { MerchandiseTypeData } from '~/services/dto/merchandise-type-data'
|
|
import type { PelletTypeData } from '~/services/dto/pellet-type-data'
|
|
import type { MerchandiseEntryData } from '~/services/dto/reception-data'
|
|
import { getBuildingList } from '~/services/building'
|
|
import { getMerchandiseTypeList } from '~/services/merchandise-type'
|
|
import { getPelletTypeList } from '~/services/pellet-type'
|
|
import { MERCHANDISE_TYPE_CODES } from '~/utils/constants'
|
|
|
|
const props = defineProps<{
|
|
modelValue: MerchandiseEntryData
|
|
isAdmin: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(event: 'update:modelValue', value: MerchandiseEntryData): void
|
|
}>()
|
|
|
|
const merchandiseTypes = ref<MerchandiseTypeData[]>([])
|
|
const buildings = ref<BuildingData[]>([])
|
|
const pelletTypes = ref<PelletTypeData[]>([])
|
|
|
|
const selectedMerchandiseTypeId = ref('')
|
|
const selectedBuildingIds = ref<string[]>([])
|
|
const selectedPelletBuildingIds = ref<Record<string, string[]>>({})
|
|
const merchandiseDetail = ref('')
|
|
const isHydrating = ref(false)
|
|
const isReady = ref(false)
|
|
|
|
const selectedMerchandiseType = computed(() =>
|
|
merchandiseTypes.value.find((type) => String(type.id) === selectedMerchandiseTypeId.value) ?? null
|
|
)
|
|
const isGranule = computed(
|
|
() => selectedMerchandiseType.value?.code === MERCHANDISE_TYPE_CODES.GRANULE
|
|
)
|
|
const isAutres = computed(
|
|
() => selectedMerchandiseType.value?.code === MERCHANDISE_TYPE_CODES.AUTRES
|
|
)
|
|
|
|
function clonePelletSelections(value: Record<string, string[]>) {
|
|
const clone: Record<string, string[]> = {}
|
|
for (const [key, buildingIds] of Object.entries(value)) {
|
|
clone[key] = [...buildingIds]
|
|
}
|
|
return clone
|
|
}
|
|
|
|
function ensurePelletKeys() {
|
|
for (const pelletType of pelletTypes.value) {
|
|
const key = String(pelletType.id)
|
|
if (!selectedPelletBuildingIds.value[key]) {
|
|
selectedPelletBuildingIds.value[key] = []
|
|
}
|
|
}
|
|
}
|
|
|
|
function hydrateFromModelValue(value: MerchandiseEntryData) {
|
|
isHydrating.value = true
|
|
try {
|
|
selectedMerchandiseTypeId.value = value.merchandiseTypeId ?? ''
|
|
merchandiseDetail.value = value.merchandiseDetail ?? ''
|
|
selectedBuildingIds.value = [...(value.selectedBuildingIds ?? [])]
|
|
selectedPelletBuildingIds.value = clonePelletSelections(
|
|
value.selectedPelletBuildingIds ?? {}
|
|
)
|
|
ensurePelletKeys()
|
|
} finally {
|
|
isHydrating.value = false
|
|
}
|
|
}
|
|
|
|
function emitModelValue() {
|
|
emit('update:modelValue', {
|
|
merchandiseTypeId: selectedMerchandiseTypeId.value,
|
|
merchandiseDetail: merchandiseDetail.value,
|
|
selectedBuildingIds: [...selectedBuildingIds.value],
|
|
selectedPelletBuildingIds: clonePelletSelections(selectedPelletBuildingIds.value)
|
|
})
|
|
}
|
|
|
|
watch(
|
|
() => props.modelValue,
|
|
(value) => {
|
|
hydrateFromModelValue(value)
|
|
},
|
|
{ deep: true }
|
|
)
|
|
|
|
watch(
|
|
[selectedMerchandiseTypeId, selectedBuildingIds, selectedPelletBuildingIds, merchandiseDetail],
|
|
() => {
|
|
if (isHydrating.value || !isReady.value) {
|
|
return
|
|
}
|
|
|
|
if (isGranule.value) {
|
|
if (selectedBuildingIds.value.length > 0) {
|
|
selectedBuildingIds.value = []
|
|
}
|
|
} else {
|
|
for (const key of Object.keys(selectedPelletBuildingIds.value)) {
|
|
if (selectedPelletBuildingIds.value[key].length > 0) {
|
|
selectedPelletBuildingIds.value[key] = []
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isAutres.value && merchandiseDetail.value !== '') {
|
|
merchandiseDetail.value = ''
|
|
}
|
|
|
|
emitModelValue()
|
|
},
|
|
{ deep: true }
|
|
)
|
|
|
|
onMounted(async () => {
|
|
const [merchandiseTypeList, buildingList, pelletTypeList] = await Promise.all([
|
|
getMerchandiseTypeList(),
|
|
getBuildingList(),
|
|
getPelletTypeList()
|
|
])
|
|
merchandiseTypes.value = merchandiseTypeList
|
|
buildings.value = buildingList
|
|
pelletTypes.value = pelletTypeList
|
|
|
|
hydrateFromModelValue(props.modelValue)
|
|
isReady.value = true
|
|
emitModelValue()
|
|
})
|
|
</script>
|