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>
This commit is contained in:
@@ -1,133 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="sticky top-8 z-20 bg-white pb-4 sm:top-12">
|
||||
<NuxtLink
|
||||
:to="`/portal/projects/${projectId}`"
|
||||
class="text-sm text-neutral-400 hover:text-primary-500"
|
||||
>
|
||||
{{ $t('portal.backToProject') }}
|
||||
</NuxtLink>
|
||||
<h1 class="mt-1 text-xl font-bold text-primary-500 sm:text-2xl">{{ $t('portal.newTicket') }}</h1>
|
||||
</div>
|
||||
|
||||
<form class="mt-4 max-w-2xl" @submit.prevent="handleSubmit">
|
||||
<!-- Type -->
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium text-neutral-700">{{ $t('clientTicket.selectType') }}</label>
|
||||
<select
|
||||
v-model="form.type"
|
||||
class="w-full rounded-lg border border-neutral-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||
>
|
||||
<option value="bug">{{ $t('clientTicket.type.bug') }}</option>
|
||||
<option value="improvement">{{ $t('clientTicket.type.improvement') }}</option>
|
||||
<option value="other">{{ $t('clientTicket.type.other') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Title -->
|
||||
<div class="mt-4">
|
||||
<MalioInputText
|
||||
v-model="form.title"
|
||||
:label="$t('clientTicket.title')"
|
||||
input-class="w-full"
|
||||
:error="touched.title && !form.title.trim() ? $t('clientTicket.title') + ' requis' : ''"
|
||||
@blur="touched.title = true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div class="mt-4">
|
||||
<MalioInputRichText
|
||||
v-model="form.description"
|
||||
:label="$t('clientTicket.description')"
|
||||
min-height="180px"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- URL (only for bug type) -->
|
||||
<div v-if="form.type === 'bug'" class="mt-4">
|
||||
<MalioInputText
|
||||
v-model="form.url"
|
||||
:label="$t('clientTicket.url')"
|
||||
input-class="w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Document upload (only after ticket is created) -->
|
||||
<div class="mt-4 rounded-lg border border-dashed border-neutral-300 p-4">
|
||||
<p class="text-sm text-neutral-500">
|
||||
<Icon name="heroicons:information-circle" class="mr-1 inline h-4 w-4" />
|
||||
Les documents pourront être ajoutés après la soumission du ticket.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Submit -->
|
||||
<div class="mt-6 flex items-center gap-3">
|
||||
<NuxtLink
|
||||
:to="`/portal/projects/${projectId}`"
|
||||
class="rounded-lg border border-neutral-300 px-4 py-2 text-sm font-semibold text-neutral-700 transition-colors hover:bg-neutral-50"
|
||||
>
|
||||
{{ $t('common.cancel') }}
|
||||
</NuxtLink>
|
||||
<MalioButton
|
||||
:label="$t('portal.submitTicket')"
|
||||
button-class="w-auto px-6"
|
||||
:disabled="isSubmitting"
|
||||
@click="handleSubmit"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ClientTicketType } from '~/services/dto/client-ticket'
|
||||
import { useClientTicketService } from '~/services/client-tickets'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'portal',
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const { t } = useI18n()
|
||||
const projectId = computed(() => Number(route.params.id))
|
||||
|
||||
useHead({ title: t('portal.newTicket') })
|
||||
|
||||
const clientTicketService = useClientTicketService()
|
||||
|
||||
const form = reactive({
|
||||
type: 'bug' as ClientTicketType | string,
|
||||
title: '',
|
||||
description: '',
|
||||
url: '',
|
||||
})
|
||||
|
||||
const touched = reactive({
|
||||
title: false,
|
||||
})
|
||||
|
||||
const isSubmitting = ref(false)
|
||||
|
||||
async function handleSubmit() {
|
||||
touched.title = true
|
||||
if (!form.title.trim()) return
|
||||
if (!form.description.trim()) return
|
||||
|
||||
isSubmitting.value = true
|
||||
try {
|
||||
await clientTicketService.create({
|
||||
type: form.type as ClientTicketType,
|
||||
title: form.title.trim(),
|
||||
description: form.description.trim(),
|
||||
url: form.type === 'bug' && form.url.trim() ? form.url.trim() : null,
|
||||
project: `/api/projects/${projectId.value}`,
|
||||
})
|
||||
await navigateTo(`/portal/projects/${projectId.value}`)
|
||||
} catch {
|
||||
// Toast already shown by useApi
|
||||
} finally {
|
||||
isSubmitting.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user