Files
Lesstime/frontend/components/user/UserDrawer.vue
T
Matthieu a18e1f575f
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m11s
Pull Request — Quality gate / Frontend (build) (pull_request) Successful in 1m17s
refactor(client-portal) : remove client portal feature entirely
- drop ClientPortal module, ClientTicket entity, ROLE_CLIENT and all couplings (Task, TaskDocument, User, Notification) back to an internal-only model

- migration drops client_ticket / user_allowed_projects / related FK columns and removes leftover external client accounts (would otherwise be promoted to ROLE_USER)

- remove client-portal frontend module, admin tickets tab, user portal section, portal nav item and portal/clientTicket i18n keys

- fix directory nav icon (invalid mdi:contact-multiple-outline -> mdi:card-account-details-outline)

- add 'make sync-permissions' target, wire it into install/db-reset and the prod deploy script
2026-06-22 09:49:44 +02:00

178 lines
5.3 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"
/>
<MalioInputText
v-model="form.firstName"
label="Prénom"
input-class="w-full"
/>
<MalioInputText
v-model="form.lastName"
label="Nom"
input-class="w-full"
/>
<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: '',
firstName: '',
lastName: '',
password: '',
roles: [] as string[],
isEmployee: false,
})
const touched = reactive({
username: false,
password: false,
})
const { create, update, getById } = useUserService()
function applyUser(user: UserData) {
form.username = user.username ?? ''
form.firstName = user.firstName ?? ''
form.lastName = user.lastName ?? ''
form.password = ''
form.roles = [...user.roles]
form.isEmployee = user.isEmployee ?? false
}
watch(() => props.modelValue, async (open) => {
if (!open) {
return
}
touched.username = false
touched.password = false
if (props.item) {
applyUser(props.item)
try {
const full = await getById(props.item.id)
applyUser(full)
} catch {
// Keep the list data if the detailed fetch fails.
}
} else {
form.username = ''
form.firstName = ''
form.lastName = ''
form.password = ''
form.roles = ['ROLE_USER']
form.isEmployee = false
}
})
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(),
firstName: form.firstName.trim() || null,
lastName: form.lastName.trim() || null,
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>