feat : mise en place de composant UI pour les select, checkbox, date, text

This commit is contained in:
2026-01-29 17:45:52 +01:00
parent 07be7e8d14
commit bb8fbe4907
8 changed files with 456 additions and 282 deletions

27
.idea/workspace.xml generated
View File

@@ -6,15 +6,10 @@
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="7c107abe-5995-4428-8429-b146aaca8386" name="Changes" comment="feat : ajout de la partie reception des marchandises (étape 3) et modification du bon de réception"> <list default="true" id="7c107abe-5995-4428-8429-b146aaca8386" name="Changes" comment="feat : ajout de la partie reception des marchandises (étape 3) et modification du bon de réception">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/AGENTS.md" beforeDir="false" afterPath="$PROJECT_DIR$/AGENTS.md" afterDir="false" />
<change beforePath="$PROJECT_DIR$/config/reference.php" beforeDir="false" afterPath="$PROJECT_DIR$/config/reference.php" 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-unloading.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/reception/reception-product-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/composables/usePdfPrinter.ts" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/composables/usePdfPrinter.ts" 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/i18n/locales/fr.json" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/i18n/locales/fr.json" afterDir="false" /> <change beforePath="$PROJECT_DIR$/frontend/components/ui/license-plate-input.vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/components/ui/license-plate-input.vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/pages/reception/[[id]].vue" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/pages/reception/[[id]].vue" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/services/dto/reception-data.ts" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/services/dto/reception-data.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$/templates/reception_voucher.html.twig" beforeDir="false" afterPath="$PROJECT_DIR$/templates/reception_voucher.html.twig" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -279,7 +274,7 @@
<workItem from="1769413136483" duration="58000" /> <workItem from="1769413136483" duration="58000" />
<workItem from="1769413279223" duration="40490000" /> <workItem from="1769413279223" duration="40490000" />
<workItem from="1769612160652" duration="23952000" /> <workItem from="1769612160652" duration="23952000" />
<workItem from="1769696465294" duration="4048000" /> <workItem from="1769696465294" duration="7587000" />
</task> </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)"> <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" /> <option name="closed" value="true" />
@@ -585,7 +580,15 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1769676223697</updated> <updated>1769676223697</updated>
</task> </task>
<option name="localTasksCounter" value="39" /> <task id="LOCAL-00039" summary="feat : ajout de la partie reception des marchandises (étape 3) et modification du bon de réception">
<option name="closed" value="true" />
<created>1769700808988</created>
<option name="number" value="00039" />
<option name="presentableId" value="LOCAL-00039" />
<option name="project" value="LOCAL" />
<updated>1769700808988</updated>
</task>
<option name="localTasksCounter" value="40" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@@ -635,7 +638,6 @@
</option> </option>
</component> </component>
<component name="VcsManagerConfiguration"> <component name="VcsManagerConfiguration">
<MESSAGE value="fix : correction de l'accès au swagger en mode dev qui n'était plus accessible" />
<MESSAGE value="feat : ajout de la conf pour le déploiement en recette" /> <MESSAGE value="feat : ajout de la conf pour le déploiement en recette" />
<MESSAGE value="fix : fix de la conf pour le déploiement en recette" /> <MESSAGE value="fix : fix de la conf pour le déploiement en recette" />
<MESSAGE value="fix : migration apache vers nginx pour un déploiement plus simple" /> <MESSAGE value="fix : migration apache vers nginx pour un déploiement plus simple" />
@@ -660,7 +662,8 @@
<MESSAGE value="feat : update du CHANGELOG.md" /> <MESSAGE value="feat : update du CHANGELOG.md" />
<MESSAGE value="feat : finalisation de l'étape 1 &quot;Réception&quot; (formulaire)" /> <MESSAGE value="feat : finalisation de l'étape 1 &quot;Réception&quot; (formulaire)" />
<MESSAGE value="feat : ajout du numéro identification des receptions et ajustement du bon de reception" /> <MESSAGE value="feat : ajout du numéro identification des receptions et ajustement du bon de reception" />
<option name="LAST_COMMIT_MESSAGE" value="feat : ajout du numéro identification des receptions et ajustement du bon de reception" /> <MESSAGE value="feat : ajout de la partie reception des marchandises (étape 3) et modification du bon de réception" />
<option name="LAST_COMMIT_MESSAGE" value="feat : ajout de la partie reception des marchandises (étape 3) et modification du bon de réception" />
</component> </component>
<component name="XSLT-Support.FileAssociations.UIState"> <component name="XSLT-Support.FileAssociations.UIState">
<expand /> <expand />

View File

@@ -3,181 +3,96 @@
<div class="grid grid-cols-2 items-start gap-y-8 gap-x-40 mb-16"> <div class="grid grid-cols-2 items-start gap-y-8 gap-x-40 mb-16">
<h1 class="font-bold text-5xl uppercase col-start-1 row-start-1">Réception</h1> <h1 class="font-bold text-5xl uppercase col-start-1 row-start-1">Réception</h1>
<!-- Nom de l'utilisateur --> <!-- Nom de l'utilisateur -->
<div class="flex flex-col col-start-1 row-start-2"> <UiSelect
<label for="reception-user" class="font-bold uppercase text-xl mb-2">Nom de l'utilisateur</label> id="reception-user"
<select v-model="form.userId"
id="reception-user" label="Nom de l'utilisateur"
v-model="form.userId" :options="users.map((user) => ({
class="border-b border-black justify-self-start text-xl pb-[6px] bg-transparent" value: String(user.id),
:class="[ label: user.username
form.userId ? 'text-black' : 'text-neutral-400', }))"
isLoadingUsers ? 'cursor-not-allowed' : 'cursor-pointer' :loading="isLoadingUsers"
]" wrapper-class="col-start-1 row-start-2"
:disabled="isLoadingUsers" />
>
<option value="" disabled class="text-neutral-400">Sélectionner</option>
<option
v-for="user in users"
:key="user.id"
:value="String(user.id)"
class="text-black"
>
{{ user.username }}
</option>
</select>
</div>
<!-- Date de réception --> <!-- Date de réception -->
<div class="flex flex-col col-start-1 row-start-3"> <UiDateInput
<label for="reception-date" class="font-bold uppercase text-xl mb-2">Date de réception</label> id="reception-date"
<input v-model="form.receptionDate"
id="reception-date" label="Date de réception"
v-model="form.receptionDate" wrapper-class="col-start-1 row-start-3"
type="date" />
class="border-b border-black justify-self-start text-xl pb-[6px] uppercase cursor-pointer bg-transparent appearance-none h-[34px]"
/>
</div>
<!-- Type de réception --> <!-- Type de réception -->
<div class="flex flex-col col-start-1 row-start-4"> <UiSelect
<label for="reception-type" class="font-bold uppercase text-xl mb-2">Type de réception</label> id="reception-type"
<select v-model="form.receptionTypeId"
id="reception-type" label="Type de réception"
v-model="form.receptionTypeId" :options="receptionTypes.map((type) => ({
class="border-b border-black justify-self-start text-xl pb-[6px] bg-transparent cursor-pointer" value: String(type.id),
:class="form.receptionTypeId ? 'text-black' : 'text-neutral-400'" label: type.label
> }))"
<option value="" disabled class="text-neutral-400">Sélectionner</option> wrapper-class="col-start-1 row-start-4"
<option />
v-for="type in receptionTypes"
:key="type.id"
:value="String(type.id)"
class="text-black"
>
{{ type.label }}
</option>
</select>
</div>
<!-- Fournisseur --> <!-- Fournisseur -->
<div class="flex flex-col col-start-1 row-start-5"> <UiSelect
<label for="reception-supplier" class="font-bold uppercase text-xl mb-2">Fournisseur</label> id="reception-supplier"
<select v-model="form.supplierId"
id="reception-supplier" label="Fournisseur"
v-model="form.supplierId" :options="suppliers.map((supplier) => ({
class="border-b border-black justify-self-start text-xl pb-[6px] bg-transparent" value: String(supplier.id),
:class="[ label: supplier.name
form.supplierId ? 'text-black' : 'text-neutral-400', }))"
isLoadingSuppliers ? 'cursor-not-allowed' : 'cursor-pointer' :loading="isLoadingSuppliers"
]" wrapper-class="col-start-1 row-start-5"
:disabled="isLoadingSuppliers" />
>
<option value="" disabled class="text-neutral-400">Sélectionner</option>
<option
v-for="supplier in suppliers"
:key="supplier.id"
:value="String(supplier.id)"
class="text-black"
>
{{ supplier.name }}
</option>
</select>
</div>
<!-- Adresse fournisseur --> <!-- Adresse fournisseur -->
<div class="flex flex-col col-start-2 row-start-1"> <UiSelect
<label for="reception-address" class="font-bold uppercase text-xl mb-2">Adresse</label> id="reception-address"
<select v-model="form.addressId"
id="reception-address" label="Adresse"
v-model="form.addressId" :options="supplierAddresses.map((address) => ({
class="border-b border-black justify-self-start text-xl pb-[6px] bg-transparent" value: String(address.id),
:class="[ label: address.fullAddress
form.addressId ? 'text-black' : 'text-neutral-400', }))"
isLoadingSuppliers || supplierAddresses.length === 0 :disabled="isLoadingSuppliers || supplierAddresses.length === 0"
? 'cursor-not-allowed' wrapper-class="col-start-2 row-start-1"
: 'cursor-pointer' />
]"
:disabled="isLoadingSuppliers || supplierAddresses.length === 0"
>
<option value="" disabled class="text-neutral-400">Sélectionner</option>
<option
v-for="address in supplierAddresses"
:key="address.id"
:value="String(address.id)"
class="text-black"
>
{{ address.fullAddress }}
</option>
</select>
</div>
<!-- Camion --> <!-- Camion -->
<div class="flex flex-col col-start-2 row-start-2"> <UiSelect
<label for="reception-truck" class="font-bold uppercase text-xl mb-2">Camion</label> id="reception-truck"
<select v-model="form.truckId"
id="reception-truck" label="Camion"
v-model="form.truckId" :options="trucks.map((truck) => ({
class="border-b border-black justify-self-start text-xl pb-[6px] bg-transparent" value: String(truck.id),
:class="[ label: truck.name
form.truckId ? 'text-black' : 'text-neutral-400', }))"
isLoadingTrucks ? 'cursor-not-allowed' : 'cursor-pointer' :loading="isLoadingTrucks"
]" wrapper-class="col-start-2 row-start-2"
:disabled="isLoadingTrucks" />
>
<option value="" disabled class="text-neutral-400">Sélectionner</option>
<option
v-for="truck in trucks"
:key="truck.id"
:value="String(truck.id)"
class="text-black"
>
{{ truck.name }}
</option>
</select>
</div>
<!-- Transporteur --> <!-- Transporteur -->
<div class="flex flex-col col-start-2 row-start-3"> <UiSelect
<label for="reception-carrier" class="font-bold uppercase text-xl mb-2">Transporteur</label> id="reception-carrier"
<select v-model="form.carrierId"
id="reception-carrier" label="Transporteur"
v-model="form.carrierId" :options="carriers.map((carrier) => ({
class="border-b border-black justify-self-start text-xl pb-[6px] bg-transparent h-[34px]" value: String(carrier.id),
:class="[ label: carrier.name
form.carrierId ? 'text-black' : 'text-neutral-400', }))"
isLoadingCarriers ? 'cursor-not-allowed' : 'cursor-pointer' :loading="isLoadingCarriers"
]" select-class="h-[34px]"
:disabled="isLoadingCarriers" wrapper-class="col-start-2 row-start-3"
> />
<option value="" disabled class="text-neutral-400">Sélectionner</option>
<option
v-for="carrier in carriers"
:key="carrier.id"
:value="String(carrier.id)"
class="text-black"
>
{{ carrier.name }}
</option>
</select>
</div>
<!-- Chauffeur (LIOT) --> <!-- Chauffeur (LIOT) -->
<div class="flex flex-col col-start-2 row-start-4"> <UiSelect
<label for="reception-driver" class="font-bold uppercase text-xl mb-2">Nom du chauffeur si LIOT</label> id="reception-driver"
<select v-model="form.driverId"
id="reception-driver" label="Nom du chauffeur si LIOT"
v-model="form.driverId" :options="filteredDrivers.map((driver) => ({
class="border-b border-black justify-self-start text-xl pb-[6px] bg-transparent" value: String(driver.id),
:class="[ label: driver.name
form.driverId ? 'text-black' : 'text-neutral-400', }))"
isLoadingDrivers ? 'cursor-not-allowed' : 'cursor-pointer' :loading="isLoadingDrivers"
]" wrapper-class="col-start-2 row-start-4"
:disabled="isLoadingDrivers" />
>
<option value="" disabled class="text-neutral-400">Sélectionner</option>
<option
v-for="driver in filteredDrivers"
:key="driver.id"
:value="String(driver.id)"
class="text-black"
>
{{ driver.name }}
</option>
</select>
</div>
<!-- Plaque d'immatriculation --> <!-- Plaque d'immatriculation -->
<div v-if="!isLiotCarrier" class="col-start-2 row-start-5"> <div v-if="!isLiotCarrier" class="col-start-2 row-start-5">
<UiLicensePlateInput <UiLicensePlateInput
@@ -186,29 +101,19 @@
/> />
</div> </div>
<!-- Immatriculation (LIOT) --> <!-- Immatriculation (LIOT) -->
<div v-if="isLiotCarrier" class="flex flex-col col-start-2 row-start-5"> <UiSelect
<label for="reception-vehicle" class="font-bold uppercase text-xl mb-2">Immatriculation</label> v-if="isLiotCarrier"
<select id="reception-vehicle"
id="reception-vehicle" v-model="form.vehicleId"
v-model="form.vehicleId" label="Immatriculation"
class="border-b border-black justify-self-start text-xl pb-[6px] bg-transparent" :options="filteredVehicles.map((vehicle) => ({
:class="[ value: String(vehicle.id),
form.vehicleId ? 'text-black' : 'text-neutral-400', label: vehicle.plate
isLoadingVehicles ? 'cursor-not-allowed' : 'cursor-pointer' }))"
]" :loading="isLoadingVehicles"
:disabled="isLoadingVehicles || filteredVehicles.length === 0" :disabled="isLoadingVehicles || filteredVehicles.length === 0"
> wrapper-class="col-start-2 row-start-5"
<option value="" disabled class="text-neutral-400">Sélectionner</option> />
<option
v-for="vehicle in filteredVehicles"
:key="vehicle.id"
:value="String(vehicle.id)"
class="text-black"
>
{{ vehicle.plate }}
</option>
</select>
</div>
</div> </div>
<div class="flex justify-center"> <div class="flex justify-center">
<button <button
@@ -221,22 +126,22 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useReceptionStore } from '~/stores/reception' import {useReceptionStore} from '~/stores/reception'
import type { ReceptionTypeData } from '~/services/dto/reception-type-data' import type {ReceptionTypeData} from '~/services/dto/reception-type-data'
import { getReceptionTypeList } from '~/services/reception-type' import {getReceptionTypeList} from '~/services/reception-type'
import type { UserData } from '~/services/dto/user-data' import type {UserData} from '~/services/dto/user-data'
import { getUsers } from '~/services/auth' import {getUsers} from '~/services/auth'
import { useAuthStore } from '~/stores/auth' import {useAuthStore} from '~/stores/auth'
import type { SupplierData } from '~/services/dto/supplier-data' import type {SupplierData} from '~/services/dto/supplier-data'
import { getSupplierList } from '~/services/supplier' import {getSupplierList} from '~/services/supplier'
import type { TruckData } from '~/services/dto/truck-data' import type {TruckData} from '~/services/dto/truck-data'
import { getTruckList } from '~/services/truck' import {getTruckList} from '~/services/truck'
import type { CarrierData } from '~/services/dto/carrier-data' import type {CarrierData} from '~/services/dto/carrier-data'
import { getCarrierList } from '~/services/carrier' import {getCarrierList} from '~/services/carrier'
import type { DriverData } from '~/services/dto/driver-data' import type {DriverData} from '~/services/dto/driver-data'
import { getDriverList } from '~/services/driver' import {getDriverList} from '~/services/driver'
import type { VehicleData } from '~/services/dto/vehicle-data' import type {VehicleData} from '~/services/dto/vehicle-data'
import { getVehicleList } from '~/services/vehicle' import {getVehicleList} from '~/services/vehicle'
type ReceptionFormData = { type ReceptionFormData = {
licensePlate: string licensePlate: string
@@ -339,7 +244,7 @@ watch(
: '' : ''
isHydrating.value = false isHydrating.value = false
}, },
{ immediate: true } {immediate: true}
) )
const loadUsers = async () => { const loadUsers = async () => {
@@ -438,7 +343,7 @@ watch(
form.addressId = '' form.addressId = ''
} }
}, },
{ immediate: true } {immediate: true}
) )
watch( watch(
@@ -464,7 +369,7 @@ watch(
form.vehicleId = String(filteredVehicles.value[0].id) form.vehicleId = String(filteredVehicles.value[0].id)
} }
}, },
{ immediate: true } {immediate: true}
) )
watch( watch(
@@ -487,7 +392,7 @@ watch(
form.vehicleId = '' form.vehicleId = ''
} }
}, },
{ immediate: true } {immediate: true}
) )
watch( watch(
@@ -568,7 +473,7 @@ async function validate() {
const payload = { const payload = {
...basePayload, ...basePayload,
...(isLiotCarrier.value && driverIri ? { driver: driverIri } : {}) ...(isLiotCarrier.value && driverIri ? {driver: driverIri} : {})
} }
if (!receptionStore.current) { if (!receptionStore.current) {

View File

@@ -5,39 +5,25 @@
v-if="receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.MERCHANDISES" v-if="receptionStore.current?.receptionType?.code === RECEPTION_TYPE_CODES.MERCHANDISES"
class="flex flex-col gap-8 items-center w-full"> class="flex flex-col gap-8 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électionner des marchandises réceptionnnées</h1>
<div class="flex flex-col w-[550px]"> <UiSelect
<label for="merchandise-type" class="font-bold uppercase text-xl mb-2">Type de marchandises</label> id="merchandise-type"
<select v-model="selectedMerchandiseTypeId"
id="merchandise-type" label="Type de marchandises"
v-model="selectedMerchandiseTypeId" :options="merchandiseTypes.map((type) => ({ value: String(type.id), label: type.label }))"
class="border-b border-black justify-self-start text-xl pb-[6px] bg-transparent cursor-pointer" wrapper-class="w-[550px]"
:class="selectedMerchandiseTypeId ? 'text-black' : 'text-neutral-400'" />
>
<option value="" disabled class="text-neutral-400">Sélectionner</option>
<option
v-for="type in merchandiseTypes"
:key="type.id"
:value="String(type.id)"
class="text-black"
>
{{ type.label }}
</option>
</select>
</div>
<div <div
v-if="selectedMerchandiseTypeId && isAutres" v-if="selectedMerchandiseTypeId && isAutres"
class="flex flex-col w-full max-w-[550px]" class="flex flex-col w-full max-w-[550px]"
> >
<label for="merchandise-detail" class="font-bold uppercase text-xl mb-2">Préciser</label> <UiTextInput
<input
id="merchandise-detail" id="merchandise-detail"
v-model="merchandiseDetail" v-model="merchandiseDetail"
label="Préciser"
placeholder="Précisions complémentaires" placeholder="Précisions complémentaires"
type="text" :maxlength="255"
maxlength="255" />
class="border-b border-black text-xl pb-[6px] bg-transparent"
>
</div> </div>
<div <div
@@ -48,16 +34,12 @@
v-for="building in buildings" v-for="building in buildings"
:key="building.id" :key="building.id"
> >
<label <UiCheckbox
class="flex items-center gap-2 text-xl" v-model="selectedBuildingIds"
> :value="String(building.id)"
<input :label="building.label"
v-model="selectedBuildingIds" label-class="text-xl"
type="checkbox" />
:value="String(building.id)"
>
<span>{{ building.label }}</span>
</label>
</div> </div>
</div> </div>
@@ -68,18 +50,18 @@
<div class="grid grid-cols-1 gap-10 md:grid-cols-4"> <div class="grid grid-cols-1 gap-10 md:grid-cols-4">
<div v-for="type in pelletTypes" :key="type.id" class="flex flex-col gap-4"> <div v-for="type in pelletTypes" :key="type.id" class="flex flex-col gap-4">
<p class="font-bold uppercase">{{ type.label }}</p> <p class="font-bold uppercase">{{ type.label }}</p>
<label <div
v-for="building in buildings" v-for="building in buildings"
:key="building.id" :key="building.id"
class="flex items-center gap-2 text-lg" class="flex items-center gap-2 text-lg"
> >
<input <UiCheckbox
v-model="selectedPelletBuildingIds[String(type.id)]" v-model="selectedPelletBuildingIds[String(type.id)]"
type="checkbox"
:value="String(building.id)" :value="String(building.id)"
> :label="building.label"
<span>{{ building.label }}</span> label-class="text-lg"
</label> />
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,76 @@
<template>
<div :class="wrapperClass">
<label
class="flex items-center gap-2"
:class="labelClass"
>
<input
type="checkbox"
:checked="checked"
:disabled="disabled"
:class="inputClass"
@change="onChange"
>
<span v-if="label">{{ label }}</span>
</label>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
type CheckboxValue = string | number
const props = withDefaults(
defineProps<{
modelValue: boolean | CheckboxValue[]
value?: CheckboxValue
label?: string
disabled?: boolean
wrapperClass?: string
labelClass?: string
inputClass?: string
}>(),
{
value: undefined,
label: '',
disabled: false,
wrapperClass: '',
labelClass: '',
inputClass: ''
}
)
const emit = defineEmits<{
(event: 'update:modelValue', value: boolean | CheckboxValue[]): void
}>()
const checked = computed(() => {
if (Array.isArray(props.modelValue)) {
if (props.value === undefined) {
return false
}
return props.modelValue.includes(props.value)
}
return Boolean(props.modelValue)
})
const onChange = (event: Event) => {
const target = event.target as HTMLInputElement
if (Array.isArray(props.modelValue)) {
if (props.value === undefined) {
emit('update:modelValue', props.modelValue)
return
}
const next = new Set(props.modelValue)
if (target.checked) {
next.add(props.value)
} else {
next.delete(props.value)
}
emit('update:modelValue', Array.from(next))
return
}
emit('update:modelValue', target.checked)
}
</script>

View File

@@ -0,0 +1,62 @@
<template>
<div :class="['flex flex-col', wrapperClass]">
<label
v-if="label"
:for="id"
class="font-bold uppercase text-xl mb-2"
:class="labelClass"
>
{{ label }}
</label>
<input
:id="id"
type="date"
:value="modelValue ?? ''"
:disabled="disabled"
v-bind="attrs"
class="border-b border-black justify-self-start text-xl pb-[6px] uppercase bg-transparent appearance-none h-[34px]"
:class="[
isEmpty ? 'text-neutral-400' : 'text-black',
disabled ? 'cursor-not-allowed' : 'cursor-pointer',
inputClass
]"
@input="onInput"
/>
</div>
</template>
<script setup lang="ts">
import { computed, useAttrs } from 'vue'
defineOptions({ inheritAttrs: false })
const props = withDefaults(
defineProps<{
id?: string
label?: string
modelValue: string | null | undefined
disabled?: boolean
wrapperClass?: string
labelClass?: string
inputClass?: string
}>(),
{
disabled: false,
wrapperClass: '',
labelClass: '',
inputClass: ''
}
)
const emit = defineEmits<{
(event: 'update:modelValue', value: string): void
}>()
const attrs = useAttrs()
const isEmpty = computed(() => !props.modelValue)
const onInput = (event: Event) => {
const target = event.target as HTMLInputElement
emit('update:modelValue', target.value)
}
</script>

View File

@@ -0,0 +1,85 @@
<template>
<div :class="['flex flex-col', wrapperClass]">
<label
v-if="label"
:for="id"
class="font-bold uppercase text-xl mb-2"
:class="labelClass"
>
{{ label }}
</label>
<select
:id="id"
:value="modelValue ?? ''"
:disabled="disabled || loading"
v-bind="attrs"
class="border-b border-black justify-self-start text-xl pb-[6px] bg-transparent"
:class="[
isEmpty ? 'text-neutral-400' : 'text-black',
disabled || loading ? 'cursor-not-allowed' : 'cursor-pointer',
selectClass
]"
@change="onChange"
>
<option value="" disabled class="text-neutral-400">
{{ placeholderText }}
</option>
<option
v-for="option in options"
:key="option.value"
:value="option.value"
class="text-black"
>
{{ option.label }}
</option>
</select>
</div>
</template>
<script setup lang="ts">
import { computed, useAttrs } from 'vue'
type SelectOption = {
value: string | number
label: string
}
defineOptions({ inheritAttrs: false })
const props = withDefaults(
defineProps<{
id?: string
label?: string
placeholder?: string
modelValue: string | number | null | undefined
options: SelectOption[]
disabled?: boolean
loading?: boolean
wrapperClass?: string
labelClass?: string
selectClass?: string
}>(),
{
placeholder: 'Sélectionner',
disabled: false,
loading: false,
wrapperClass: '',
labelClass: '',
selectClass: ''
}
)
const emit = defineEmits<{
(event: 'update:modelValue', value: string): void
}>()
const attrs = useAttrs()
const isEmpty = computed(() => props.modelValue === '' || props.modelValue === null || props.modelValue === undefined)
const placeholderText = computed(() => props.placeholder || 'Sélectionner')
const onChange = (event: Event) => {
const target = event.target as HTMLSelectElement
emit('update:modelValue', target.value)
}
</script>

View File

@@ -0,0 +1,68 @@
<template>
<div :class="['flex flex-col', wrapperClass]">
<label
v-if="label"
:for="id"
class="font-bold uppercase text-xl mb-2"
:class="labelClass"
>
{{ label }}
</label>
<input
:id="id"
type="text"
:value="modelValue ?? ''"
:placeholder="placeholder"
:maxlength="maxlength"
:disabled="disabled"
v-bind="attrs"
class="border-b border-black text-xl pb-[6px] bg-transparent"
:class="[
isEmpty ? 'text-neutral-400' : 'text-black',
disabled ? 'cursor-not-allowed' : 'cursor-text',
inputClass
]"
@input="onInput"
>
</div>
</template>
<script setup lang="ts">
import { computed, useAttrs } from 'vue'
defineOptions({ inheritAttrs: false })
const props = withDefaults(
defineProps<{
id?: string
label?: string
modelValue: string | null | undefined
placeholder?: string
maxlength?: number | string
disabled?: boolean
wrapperClass?: string
labelClass?: string
inputClass?: string
}>(),
{
placeholder: '',
maxlength: undefined,
disabled: false,
wrapperClass: '',
labelClass: '',
inputClass: ''
}
)
const emit = defineEmits<{
(event: 'update:modelValue', value: string): void
}>()
const attrs = useAttrs()
const isEmpty = computed(() => !props.modelValue)
const onInput = (event: Event) => {
const target = event.target as HTMLInputElement
emit('update:modelValue', target.value)
}
</script>

View File

@@ -12,16 +12,15 @@
class="border-b border-black flex-1 min-w-0 text-xl uppercase h-[30px]" class="border-b border-black flex-1 min-w-0 text-xl uppercase h-[30px]"
@input="handleInput" @input="handleInput"
/> />
<label :for="checkboxId" class="ml-auto flex items-center gap-3 whitespace-nowrap text-sm"> <UiCheckbox
<input :id="checkboxId"
:id="checkboxId" :model-value="allowAny"
:checked="allowAny" label="Autoriser un format libre"
type="checkbox" wrapper-class="ml-auto"
class="h-4 w-4 accent-primary-500" label-class="gap-3 whitespace-nowrap text-sm"
@change="toggleAllowAny" input-class="h-4 w-4 accent-primary-500"
/> @update:modelValue="handleAllowAnyChange"
Autoriser un format libre />
</label>
</div> </div>
</div> </div>
</template> </template>
@@ -80,13 +79,7 @@ const handleInput = (event: Event) => {
emit('update:modelValue', target.value) emit('update:modelValue', target.value)
} }
const toggleAllowAny = (event: Event) => { const handleAllowAnyChange = (nextValue: boolean) => {
const target = event.target as HTMLInputElement | null
if (!target) {
return
}
const nextValue = target.checked
emit('update:allowAny', nextValue) emit('update:allowAny', nextValue)
if (!nextValue) { if (!nextValue) {
emit('update:modelValue', props.modelValue) emit('update:modelValue', props.modelValue)