Files
Lesstime/frontend/components/user/UserDrawer.vue
Matthieu 2a0b202d32 feat(absences) : avancement module absences + suppression du portail client
Deux lots regroupés sur la branche feat/absence-management.

Suppression complète du portail client :
- retire ROLE_CLIENT (security.yaml) ; User::getRoles() ajoute toujours ROLE_USER
- supprime l'entité ClientTicket (+ repo, states, relations), User.client et
  User.allowedProjects, NotificationService, ProjectAllowedExtension, le bloc
  ROLE_CLIENT de MailAccessChecker
- front : pages /portal, layout portal, composants client-ticket/,
  AdminClientTicketTab, services/dto/i18n/docs associés
- fixtures : retire les users client-liot / client-acme
- migration Version20260522110000 (drop client_ticket, user_allowed_projects,
  colonnes liées ; task_document.task_id -> NOT NULL)
- tests : retire les cas obsolètes testant le blocage des clients sur le mail

Module gestion des absences (WIP) :
- entités / migrations (Version20260521160000, Version20260522090000)
- pages absences.vue / team-absences.vue, composants frontend/components/absence/
- services front, AccrueLeaveCommand, PublicHolidayController

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 11:31:31 +02:00

147 lines
4.5 KiB
Vue

<template>
<MalioDrawer v-model="isOpen">
<template #header>
<h2 class="text-xl font-bold">{{ isEditing ? $t('users.editUser') : $t('users.addUser') }}</h2>
</template>
<form class="flex flex-col gap-2" @submit.prevent="handleSubmit">
<MalioInputText
v-model="form.username"
label="Nom d'utilisateur"
input-class="w-full"
:error="touched.username && !form.username.trim() ? 'Le nom est requis' : ''"
@blur="touched.username = true"
/>
<MalioInputPassword
v-model="form.password"
label="Mot de passe"
input-class="w-full"
:hint="isEditing ? 'Laisser vide pour ne pas changer' : ''"
:error="touched.password && !isEditing && !form.password ? 'Le mot de passe est requis' : ''"
@blur="touched.password = true"
/>
<div class="mt-4">
<label class="text-sm font-semibold text-neutral-700">Rôles</label>
<div class="mt-2 flex flex-col gap-2">
<label
v-for="role in availableRoles"
:key="role"
class="flex items-center gap-2 text-sm text-neutral-700"
>
<input
v-model="form.roles"
type="checkbox"
:value="role"
class="rounded border-neutral-300"
/>
{{ role }}
</label>
</div>
</div>
<!-- RH / Absences -->
<div class="mt-6 border-t border-neutral-200 pt-4">
<MalioCheckbox v-model="form.isEmployee" label="Employé (soumis à la gestion des absences)" />
<p v-if="form.isEmployee" class="mt-2 text-xs text-neutral-500">
Les informations RH (contrat, dates, CP) se gèrent dans Absences équipe onglet Employés.
</p>
</div>
<div class="mt-6 flex justify-end">
<MalioButton
label="Enregistrer"
button-class="w-auto px-6"
:disabled="isSubmitting"
@click="handleSubmit"
/>
</div>
</form>
</MalioDrawer>
</template>
<script setup lang="ts">
import type { UserData, UserWrite } from '~/services/dto/user-data'
import { useUserService } from '~/services/users'
const props = defineProps<{
modelValue: boolean
item: UserData | null
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
(e: 'saved'): void
}>()
const isOpen = computed({
get: () => props.modelValue,
set: (v) => emit('update:modelValue', v),
})
const availableRoles = ['ROLE_ADMIN', 'ROLE_USER']
const isEditing = computed(() => !!props.item)
const isSubmitting = ref(false)
const form = reactive({
username: '',
password: '',
roles: [] as string[],
isEmployee: false,
})
const touched = reactive({
username: false,
password: false,
})
watch(() => props.modelValue, (open) => {
if (open) {
if (props.item) {
form.username = props.item.username ?? ''
form.password = ''
form.roles = [...props.item.roles]
form.isEmployee = props.item.isEmployee ?? false
} else {
form.username = ''
form.password = ''
form.roles = ['ROLE_USER']
form.isEmployee = false
}
touched.username = false
touched.password = false
}
})
const { create, update } = useUserService()
async function handleSubmit() {
touched.username = true
touched.password = true
if (!form.username.trim()) return
if (!isEditing.value && !form.password) return
isSubmitting.value = true
try {
const payload: UserWrite = {
username: form.username.trim(),
roles: form.roles,
isEmployee: form.isEmployee,
}
if (form.password) {
payload.plainPassword = form.password
}
if (isEditing.value && props.item) {
await update(props.item.id, payload)
} else {
await create(payload)
}
emit('saved')
isOpen.value = false
} finally {
isSubmitting.value = false
}
}
</script>