feat(ui) : add tabs to machine page, compact header with site/reference badges

This commit is contained in:
2026-04-04 17:00:02 +02:00
parent a8a3facec8
commit 2b96d20d56
2 changed files with 182 additions and 187 deletions

View File

@@ -1,40 +1,46 @@
<template> <template>
<div class="space-y-3">
<div class="flex flex-col gap-4 md:flex-row md:items-start md:justify-between"> <div class="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-1">
<h1 class="text-3xl font-bold"> <div class="flex items-center gap-3 flex-wrap">
{{ title }} <h1 class="text-2xl font-bold">{{ title }}</h1>
</h1> <div
v-if="siteName"
class="badge badge-outline font-semibold"
:style="siteStyle"
>
{{ siteName }}
</div> </div>
<div class="flex items-center gap-2 print:hidden" data-print-hide> <div v-if="reference" class="badge badge-outline">{{ reference }}</div>
</div>
<p v-if="description" class="text-sm text-base-content/60">{{ description }}</p>
</div>
<div class="flex items-center gap-2 print:hidden">
<button <button
@click="$emit('toggle-edit')" v-if="canEdit"
class="btn btn-primary"
:class="{ 'btn-outline': isEditMode }"
>
<IconLucideSquarePen
v-if="!isEditMode"
class="w-5 h-5 mr-2"
aria-hidden="true"
/>
<IconLucideEye
v-else
class="w-5 h-5 mr-2"
aria-hidden="true"
/>
{{ isEditMode ? 'Voir détails' : 'Modifier' }}
</button>
<button
v-if="!isEditMode"
@click="$emit('open-print')"
type="button" type="button"
class="btn btn-outline btn-secondary" class="btn btn-primary btn-sm md:btn-md"
:class="{ 'btn-outline': isEditMode }"
@click="$emit('toggle-edit')"
> >
<IconLucidePrinter class="w-5 h-5 mr-2" aria-hidden="true" /> <IconLucideSquarePen v-if="!isEditMode" class="w-4 h-4 mr-1" aria-hidden="true" />
Imprimer <IconLucideEye v-else class="w-4 h-4 mr-1" aria-hidden="true" />
{{ isEditMode ? 'Voir d\u00e9tails' : 'Modifier' }}
</button> </button>
<button type="button" class="btn btn-ghost btn-sm md:btn-md" @click="goBack"> <button
Retour aux machines v-if="!isEditMode"
type="button"
class="btn btn-ghost btn-sm md:btn-md"
title="Imprimer"
@click="$emit('open-print')"
>
<IconLucidePrinter class="w-4 h-4" aria-hidden="true" />
</button> </button>
<NuxtLink to="/machines" class="btn btn-ghost btn-sm md:btn-md">
<IconLucideArrowLeft class="w-4 h-4 mr-1" aria-hidden="true" />
Parc machines
</NuxtLink>
</div>
</div> </div>
</div> </div>
</template> </template>
@@ -43,11 +49,16 @@
import IconLucideSquarePen from '~icons/lucide/square-pen' import IconLucideSquarePen from '~icons/lucide/square-pen'
import IconLucideEye from '~icons/lucide/eye' import IconLucideEye from '~icons/lucide/eye'
import IconLucidePrinter from '~icons/lucide/printer' import IconLucidePrinter from '~icons/lucide/printer'
import IconLucideArrowLeft from '~icons/lucide/arrow-left'
const router = useRouter() const { canEdit } = usePermissions()
defineProps<{ const props = defineProps<{
title: string title: string
description?: string
siteName?: string
siteColor?: string
reference?: string
isEditMode: boolean isEditMode: boolean
}>() }>()
@@ -56,12 +67,12 @@ defineEmits<{
'open-print': [] 'open-print': []
}>() }>()
function goBack() { const siteStyle = computed(() => {
if (window.history.length > 1) { if (!props.siteColor) return {}
router.back() return {
borderColor: props.siteColor + '60',
backgroundColor: props.siteColor + '25',
color: props.siteColor,
} }
else { })
navigateTo('/machines')
}
}
</script> </script>

View File

@@ -17,42 +17,20 @@
<!-- Header with actions --> <!-- Header with actions -->
<MachineDetailHeader <MachineDetailHeader
:title="machineViewTitle" :title="d.machine.value.name"
:description="d.machine.value.description"
:site-name="d.machine.value.site?.name"
:site-color="d.machine.value.site?.color"
:reference="d.machine.value.reference"
:is-edit-mode="d.isEditMode.value" :is-edit-mode="d.isEditMode.value"
@toggle-edit="d.toggleEditMode" @toggle-edit="d.toggleEditMode"
@open-print="d.openPrintModal" @open-print="d.openPrintModal"
/> />
<!-- Debug info --> <!-- Tabbed content -->
<div v-if="d.debug.value" class="bg-yellow-100 p-4 rounded-lg"> <EntityTabs v-model="activeTab" :tabs="machineTabs" aria-label="Sections machine">
<p>Debug: Machine trouvée - {{ d.machine.value.name }}</p> <template #tab-general>
<p>Components count: {{ d.components.value.length }}</p> <div class="space-y-8">
<p>Pieces count: {{ d.pieces.value.length }}</p>
</div>
<!-- Hero -->
<PageHero
:title="d.machine.value.name"
:subtitle="d.machine.value.description"
min-height="min-h-[20vh]"
max-width="max-w-md"
rounded
>
<div class="flex justify-center gap-4">
<div
v-if="d.machine.value.site?.name"
class="badge badge-outline font-semibold"
:style="d.machine.value.site?.color ? { borderColor: d.machine.value.site.color + '60', backgroundColor: d.machine.value.site.color + '25', color: d.machine.value.site.color } : {}"
>
{{ d.machine.value.site?.name }}
</div>
<div v-if="d.machine.value.reference" class="badge badge-outline">
{{ d.machine.value.reference }}
</div>
</div>
</PageHero>
<!-- Machine Info Card -->
<MachineInfoCard <MachineInfoCard
ref="machineInfoCardRef" ref="machineInfoCardRef"
:is-edit-mode="d.isEditMode.value" :is-edit-mode="d.isEditMode.value"
@@ -78,22 +56,6 @@
@set-custom-field-value="d.setMachineCustomFieldValue" @set-custom-field-value="d.setMachineCustomFieldValue"
@custom-fields-saved="() => { d.loadMachineData(); refreshVersions() }" @custom-fields-saved="() => { d.loadMachineData(); refreshVersions() }"
/> />
<!-- Documents -->
<MachineDocumentsCard
v-if="d.isEditMode.value || d.machineDocumentsList.value.length > 0"
:documents="d.machineDocumentsList.value"
:is-edit-mode="d.isEditMode.value"
:uploading="d.machineDocumentsUploading.value"
:files="d.machineDocumentFiles.value"
@update:files="d.machineDocumentFiles.value = $event"
@files-added="d.handleMachineFilesAdded"
@preview="d.openPreview"
@download="d.downloadDocument"
@remove="confirmRemoveDocument"
/>
<!-- Produits associés -->
<MachineProductsCard <MachineProductsCard
v-if="d.isEditMode.value || d.machineDirectProducts.value.length > 0" v-if="d.isEditMode.value || d.machineDirectProducts.value.length > 0"
:products="d.machineDirectProducts.value" :products="d.machineDirectProducts.value"
@@ -102,8 +64,11 @@
@remove-product="confirmRemoveProduct" @remove-product="confirmRemoveProduct"
@fill-entity="(linkId, typeId) => handleFillEntity(linkId, 'product', typeId)" @fill-entity="(linkId, typeId) => handleFillEntity(linkId, 'product', typeId)"
/> />
</div>
</template>
<!-- Components Section --> <template #tab-structure>
<div class="space-y-8">
<MachineComponentsCard <MachineComponentsCard
v-if="d.isEditMode.value || d.components.value.length > 0" v-if="d.isEditMode.value || d.components.value.length > 0"
:components="d.components.value" :components="d.components.value"
@@ -118,8 +83,6 @@
@remove-component="confirmRemoveComponent" @remove-component="confirmRemoveComponent"
@fill-entity="(linkId, typeId) => handleFillEntity(linkId, 'component', typeId)" @fill-entity="(linkId, typeId) => handleFillEntity(linkId, 'component', typeId)"
/> />
<!-- Machine Pieces Section -->
<MachinePiecesCard <MachinePiecesCard
v-if="d.isEditMode.value || d.machinePieces.value.length > 0" v-if="d.isEditMode.value || d.machinePieces.value.length > 0"
:pieces="d.machinePieces.value" :pieces="d.machinePieces.value"
@@ -134,6 +97,49 @@
@fill-entity="(linkId, typeId) => handleFillEntity(linkId, 'piece', typeId)" @fill-entity="(linkId, typeId) => handleFillEntity(linkId, 'piece', typeId)"
@toggle-collapse="d.toggleAllPieces" @toggle-collapse="d.toggleAllPieces"
/> />
</div>
</template>
<template #tab-documents>
<MachineDocumentsCard
v-if="d.isEditMode.value || d.machineDocumentsList.value.length > 0"
:documents="d.machineDocumentsList.value"
:is-edit-mode="d.isEditMode.value"
:uploading="d.machineDocumentsUploading.value"
:files="d.machineDocumentFiles.value"
@update:files="d.machineDocumentFiles.value = $event"
@files-added="d.handleMachineFilesAdded"
@preview="d.openPreview"
@download="d.downloadDocument"
@remove="confirmRemoveDocument"
/>
</template>
<template #tab-history>
<div class="space-y-8">
<EntityHistorySection
:entries="history"
:loading="historyLoading"
:error="historyError"
:field-labels="historyFieldLabels"
/>
<EntityVersionList
ref="versionListRef"
entity-type="machine"
:entity-id="String(machineId)"
:field-labels="historyFieldLabels"
:refresh-key="versionRefreshKey"
@restored="d.loadMachineData()"
/>
<CommentSection
entity-type="machine"
:entity-id="String(machineId)"
:entity-name="d.machine.value?.name"
show-resolved
/>
</div>
</template>
</EntityTabs>
<!-- Add Entity Modal --> <!-- Add Entity Modal -->
<AddEntityToMachineModal <AddEntityToMachineModal
@@ -164,35 +170,6 @@
Enregistrer les modifications Enregistrer les modifications
</button> </button>
</div> </div>
<!-- Historique -->
<EntityHistorySection
:entries="history"
:loading="historyLoading"
:error="historyError"
:field-labels="historyFieldLabels"
/>
<!-- Versions -->
<EntityVersionList
ref="versionListRef"
entity-type="machine"
:entity-id="String(machineId)"
:field-labels="historyFieldLabels"
:refresh-key="versionRefreshKey"
@restored="d.loadMachineData()"
/>
<!-- Comments -->
<div class="mt-4">
<CommentSection
entity-type="machine"
:entity-id="String(machineId)"
:entity-name="d.machine.value?.name"
show-resolved
/>
</div>
</div> </div>
<!-- Error State --> <!-- Error State -->
@@ -224,12 +201,11 @@
</template> </template>
<script setup> <script setup>
import { computed, ref, onMounted } from 'vue' import { computed, ref, watch, onMounted } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { useMachineDetailData } from '~/composables/useMachineDetailData' import { useMachineDetailData } from '~/composables/useMachineDetailData'
import { useEntityHistory } from '~/composables/useEntityHistory' import { useEntityHistory } from '~/composables/useEntityHistory'
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue' import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
import PageHero from '~/components/PageHero.vue'
import MachinePrintSelectionModal from '~/components/MachinePrintSelectionModal.vue' import MachinePrintSelectionModal from '~/components/MachinePrintSelectionModal.vue'
import MachineDetailHeader from '~/components/machine/MachineDetailHeader.vue' import MachineDetailHeader from '~/components/machine/MachineDetailHeader.vue'
import MachineInfoCard from '~/components/machine/MachineInfoCard.vue' import MachineInfoCard from '~/components/machine/MachineInfoCard.vue'
@@ -256,6 +232,18 @@ const versionRefreshKey = ref(0)
const refreshVersions = () => { versionRefreshKey.value++ } const refreshVersions = () => { versionRefreshKey.value++ }
const { confirm: confirmDialog } = useConfirm() const { confirm: confirmDialog } = useConfirm()
const activeTab = ref(route.query.tab || 'general')
watch(activeTab, (val) => {
navigateTo({ query: { ...route.query, tab: val } }, { replace: true })
})
const machineTabs = computed(() => [
{ key: 'general', label: 'Général' },
{ key: 'structure', label: 'Structure', count: d.components.value.length + d.machinePieces.value.length },
{ key: 'documents', label: 'Documents', count: d.machineDocumentsList.value.length },
{ key: 'history', label: 'Historique' },
])
const { const {
history, history,
loading: historyLoading, loading: historyLoading,
@@ -329,10 +317,6 @@ const handleFillEntity = (linkId, entityKind, modelTypeId) => {
addModalOpen.value = true addModalOpen.value = true
} }
const machineViewTitle = computed(() => {
return d.isEditMode.value ? 'Modification de la machine' : 'Détails de la machine'
})
const submitMachineEdition = async () => { const submitMachineEdition = async () => {
if (machineInfoCardRef.value?.saveFieldDefinitions) { if (machineInfoCardRef.value?.saveFieldDefinitions) {
await machineInfoCardRef.value.saveFieldDefinitions() await machineInfoCardRef.value.saveFieldDefinitions()