feat(rich-text) : migrer vers MalioInputRichText (layer-ui 1.4.7)

Remplace les éditeurs markdown locaux et les textareas
description par <MalioInputRichText> (TipTap v3 + StarterKit +
tiptap-markdown) du paquet @malio/layer-ui.

Sites migrés :
- TaskModal (description tâche)
- TaskGroupDrawer (description groupe de tâches)
- TimeEntryDrawer (description time entry)
- ClientTicketDetailModal (édition + lecture seule)
- ProjectClientTickets (panneau admin lecture seule)
- new-ticket (formulaire portail client)
- client-tickets (vue admin lecture seule)

Stockage en BDD inchangé : le markdown existant est parsé à
l'ouverture, le composant émet du HTML par défaut sur les
sauvegardes (migration lazy au fil des éditions).

Bumpe @malio/layer-ui de ^1.2.3 à ^1.4.7 et ajoute les
dépendances TipTap utilisées par le composant.

Co-Authored-By: RuFlo <ruv@ruv.net>
This commit is contained in:
2026-05-04 19:54:57 +02:00
parent 2898b22440
commit 2a68d2f9c6
9 changed files with 1376 additions and 116 deletions

View File

@@ -66,14 +66,10 @@
</div>
<div class="mt-4">
<label class="mb-1 block text-sm font-medium text-neutral-700">
{{ $t('clientTicket.description') }}
</label>
<textarea
<MalioInputRichText
v-model="editForm.description"
rows="5"
class="w-full rounded-lg border border-neutral-300 px-3 py-2 text-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
style="resize: vertical; min-height: 140px; max-height: 500px"
:label="$t('clientTicket.description')"
min-height="180px"
/>
</div>
@@ -129,7 +125,13 @@
<!-- Description -->
<div class="mt-4">
<p class="text-sm font-medium text-neutral-700">{{ $t('clientTicket.description') }}</p>
<p class="mt-1 whitespace-pre-wrap text-sm text-neutral-600">{{ ticket.description }}</p>
<MalioInputRichText
v-if="ticket.description"
:model-value="ticket.description"
:editable="false"
group-class="mt-1"
/>
<p v-else class="mt-1 text-sm italic text-neutral-400"></p>
</div>
<!-- URL (if bug) -->

View File

@@ -116,7 +116,12 @@
<!-- Expanded details -->
<div v-if="expandedId === ticket.id" class="border-t border-neutral-100 px-3 py-3">
<p class="text-sm text-neutral-600 whitespace-pre-wrap">{{ ticket.description }}</p>
<MalioInputRichText
v-if="ticket.description"
:model-value="ticket.description"
:editable="false"
/>
<p v-else class="text-sm italic text-neutral-400"></p>
<div v-if="ticket.url" class="mt-2">
<a
:href="ticket.url"

View File

@@ -8,10 +8,10 @@
:error="touched.title && !form.title.trim() ? 'Le titre est requis' : ''"
@blur="touched.title = true"
/>
<MalioInputTextArea
<MalioInputRichText
v-model="form.description"
label="Description"
:size="3"
min-height="120px"
/>
<div class="mt-4">
<ColorPicker v-model="form.color" />

View File

@@ -196,33 +196,13 @@
<!-- Description -->
<div class="mt-5">
<div class="mb-1 flex items-center justify-between">
<label class="text-sm font-medium text-slate-700">Description</label>
<button
v-if="form.description"
type="button"
class="flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium text-slate-500 transition-colors hover:bg-slate-100 hover:text-slate-700"
@click="showMarkdownPreview = true"
>
<Icon name="heroicons:eye" class="size-3.5" />
Aperçu MD
</button>
</div>
<MalioInputTextArea
<MalioInputRichText
v-model="form.description"
:size="5"
resize="vertical"
:min-resize-height="140"
:max-resize-height="500"
label="Description"
min-height="180px"
/>
</div>
<MarkdownPreviewModal
v-model="showMarkdownPreview"
:content="form.description"
title="Aperçu de la description"
/>
<!-- Documents -->
<TaskDocumentUpload
v-if="isEditing && task && isAdmin"
@@ -558,7 +538,7 @@ const isOpen = computed({
})
function close() {
if (confirmDeleteDocOpen.value || confirmDeleteOpen.value || showMarkdownPreview.value) return
if (confirmDeleteDocOpen.value || confirmDeleteOpen.value) return
isOpen.value = false
}
@@ -566,7 +546,6 @@ const isEditing = computed(() => !!props.task)
const isSubmitting = ref(false)
const confirmDeleteOpen = ref(false)
const activeTab = ref<'details' | 'planning'>('details')
const showMarkdownPreview = ref(false)
const giteaUrl = ref('')
const { getSettings: getGiteaSettings } = useGiteaService()

View File

@@ -11,14 +11,11 @@
/>
</div>
<div>
<label class="mb-1 block text-sm font-semibold text-neutral-700">Description</label>
<textarea
v-model="form.description"
rows="3"
class="w-full rounded-md border border-neutral-300 px-3 py-2 text-sm focus:border-primary-500 focus:outline-none"
/>
</div>
<MalioInputRichText
v-model="form.description"
label="Description"
min-height="120px"
/>
<div>
<label class="mb-1 block text-sm font-semibold text-neutral-700">Date</label>

File diff suppressed because it is too large Load Diff

View File

@@ -11,18 +11,24 @@
"build:dist": "nuxt generate && rm -rf dist && cp -R .output/public dist"
},
"dependencies": {
"@malio/layer-ui": "^1.2.3",
"@malio/layer-ui": "^1.4.7",
"@nuxt/icon": "^2.2.1",
"@nuxtjs/i18n": "^10.2.3",
"@nuxtjs/tailwindcss": "^6.14.0",
"@pinia/nuxt": "^0.11.3",
"@tailwindcss/typography": "^0.5.19",
"@tiptap/extension-link": "^2.10.3",
"@tiptap/extension-placeholder": "^2.10.3",
"@tiptap/pm": "^2.10.3",
"@tiptap/starter-kit": "^2.10.3",
"@tiptap/vue-3": "^2.10.3",
"@vuepic/vue-datepicker": "^12.1.0",
"chart.js": "^4.5.1",
"marked": "^18.0.0",
"nuxt": "^4.3.1",
"nuxt-toast": "^1.4.0",
"pinia": "^3.0.4",
"tiptap-markdown": "^0.8.10",
"vue": "^3.5.29",
"vue-advanced-cropper": "^2.8.9",
"vue-chartjs": "^5.3.3",

View File

@@ -37,15 +37,10 @@
<!-- Description -->
<div class="mt-4">
<MalioInputTextArea
<MalioInputRichText
v-model="form.description"
:label="$t('clientTicket.description')"
:size="5"
resize="vertical"
:min-resize-height="140"
:max-resize-height="500"
min-resize-width="100%"
max-resize-width="100%"
min-height="180px"
/>
</div>

View File

@@ -84,7 +84,12 @@
<!-- Expanded details -->
<div v-if="expandedId === ticket.id" class="border-t border-neutral-100 px-4 py-3">
<p class="text-sm text-neutral-600 whitespace-pre-wrap">{{ ticket.description }}</p>
<MalioInputRichText
v-if="ticket.description"
:model-value="ticket.description"
:editable="false"
/>
<p v-else class="text-sm italic text-neutral-400"></p>
<div v-if="ticket.url" class="mt-2">
<a
:href="ticket.url"