feat(ui) : add tabs to machine page, compact header with site/reference badges
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user