fix: Mise a jour ui simple
- supp champs emplacement - augmenter espace en label et input sup btn compléter champs perso
This commit is contained in:
88
app/app.vue
88
app/app.vue
@@ -60,7 +60,7 @@
|
|||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="dropdown dropdown-hover"
|
class="dropdown"
|
||||||
:class="{ 'dropdown-open': openDropdown === 'pieces-mobile' }"
|
:class="{ 'dropdown-open': openDropdown === 'pieces-mobile' }"
|
||||||
@mouseenter="setDropdown('pieces-mobile')"
|
@mouseenter="setDropdown('pieces-mobile')"
|
||||||
@mouseleave="scheduleDropdownClose('pieces-mobile')"
|
@mouseleave="scheduleDropdownClose('pieces-mobile')"
|
||||||
@@ -71,6 +71,9 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
role="button"
|
role="button"
|
||||||
class="rounded-md px-2 py-1 transition-colors cursor-pointer"
|
class="rounded-md px-2 py-1 transition-colors cursor-pointer"
|
||||||
|
@click="toggleDropdown('pieces-mobile')"
|
||||||
|
@keydown.enter.prevent="toggleDropdown('pieces-mobile')"
|
||||||
|
@keydown.space.prevent="toggleDropdown('pieces-mobile')"
|
||||||
:class="
|
:class="
|
||||||
isActive('/pieces-catalog') || isActive('/piece-category')
|
isActive('/pieces-catalog') || isActive('/piece-category')
|
||||||
? 'bg-primary text-primary-content font-semibold shadow-sm'
|
? 'bg-primary text-primary-content font-semibold shadow-sm'
|
||||||
@@ -112,7 +115,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="dropdown dropdown-hover"
|
class="dropdown"
|
||||||
:class="{ 'dropdown-open': openDropdown === 'component-mobile' }"
|
:class="{ 'dropdown-open': openDropdown === 'component-mobile' }"
|
||||||
@mouseenter="setDropdown('component-mobile')"
|
@mouseenter="setDropdown('component-mobile')"
|
||||||
@mouseleave="scheduleDropdownClose('component-mobile')"
|
@mouseleave="scheduleDropdownClose('component-mobile')"
|
||||||
@@ -123,6 +126,9 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
role="button"
|
role="button"
|
||||||
class="rounded-md px-2 py-1 transition-colors cursor-pointer"
|
class="rounded-md px-2 py-1 transition-colors cursor-pointer"
|
||||||
|
@click="toggleDropdown('component-mobile')"
|
||||||
|
@keydown.enter.prevent="toggleDropdown('component-mobile')"
|
||||||
|
@keydown.space.prevent="toggleDropdown('component-mobile')"
|
||||||
:class="
|
:class="
|
||||||
isActive('/component-catalog') ||
|
isActive('/component-catalog') ||
|
||||||
isActive('/component-category')
|
isActive('/component-category')
|
||||||
@@ -165,7 +171,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="dropdown dropdown-hover"
|
class="dropdown"
|
||||||
:class="{ 'dropdown-open': openDropdown === 'resources-mobile' }"
|
:class="{ 'dropdown-open': openDropdown === 'resources-mobile' }"
|
||||||
@mouseenter="setDropdown('resources-mobile')"
|
@mouseenter="setDropdown('resources-mobile')"
|
||||||
@mouseleave="scheduleDropdownClose('resources-mobile')"
|
@mouseleave="scheduleDropdownClose('resources-mobile')"
|
||||||
@@ -176,6 +182,9 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
role="button"
|
role="button"
|
||||||
class="rounded-md px-2 py-1 transition-colors cursor-pointer"
|
class="rounded-md px-2 py-1 transition-colors cursor-pointer"
|
||||||
|
@click="toggleDropdown('resources-mobile')"
|
||||||
|
@keydown.enter.prevent="toggleDropdown('resources-mobile')"
|
||||||
|
@keydown.space.prevent="toggleDropdown('resources-mobile')"
|
||||||
:class="
|
:class="
|
||||||
isActive('/sites') ||
|
isActive('/sites') ||
|
||||||
isActive('/documents') ||
|
isActive('/documents') ||
|
||||||
@@ -288,20 +297,23 @@
|
|||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="dropdown dropdown-hover"
|
class="dropdown"
|
||||||
:class="{ 'dropdown-open': openDropdown === 'pieces-desktop' }"
|
:class="{ 'dropdown-open': openDropdown === 'pieces-desktop' }"
|
||||||
@mouseenter="setDropdown('pieces-desktop')"
|
@mouseenter="setDropdown('pieces-desktop')"
|
||||||
@mouseleave="scheduleDropdownClose('pieces-desktop')"
|
@mouseleave="scheduleDropdownClose('pieces-desktop')"
|
||||||
@focusin="setDropdown('pieces-desktop')"
|
@focusin="setDropdown('pieces-desktop')"
|
||||||
@focusout="scheduleDropdownClose('pieces-desktop')"
|
@focusout="scheduleDropdownClose('pieces-desktop')"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
role="button"
|
role="button"
|
||||||
class="transition-colors px-3 py-2 rounded-md inline-flex items-center gap-1 cursor-pointer"
|
class="transition-colors px-3 py-2 rounded-md inline-flex items-center gap-1 cursor-pointer"
|
||||||
:class="
|
@click="toggleDropdown('pieces-desktop')"
|
||||||
isActive('/pieces-catalog') || isActive('/piece-category')
|
@keydown.enter.prevent="toggleDropdown('pieces-desktop')"
|
||||||
? 'bg-primary text-primary-content font-semibold shadow-sm'
|
@keydown.space.prevent="toggleDropdown('pieces-desktop')"
|
||||||
|
:class="
|
||||||
|
isActive('/pieces-catalog') || isActive('/piece-category')
|
||||||
|
? 'bg-primary text-primary-content font-semibold shadow-sm'
|
||||||
: 'text-base-content hover:bg-primary/10 hover:text-primary'
|
: 'text-base-content hover:bg-primary/10 hover:text-primary'
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
@@ -340,20 +352,23 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="dropdown dropdown-hover"
|
class="dropdown"
|
||||||
:class="{ 'dropdown-open': openDropdown === 'component-desktop' }"
|
:class="{ 'dropdown-open': openDropdown === 'component-desktop' }"
|
||||||
@mouseenter="setDropdown('component-desktop')"
|
@mouseenter="setDropdown('component-desktop')"
|
||||||
@mouseleave="scheduleDropdownClose('component-desktop')"
|
@mouseleave="scheduleDropdownClose('component-desktop')"
|
||||||
@focusin="setDropdown('component-desktop')"
|
@focusin="setDropdown('component-desktop')"
|
||||||
@focusout="scheduleDropdownClose('component-desktop')"
|
@focusout="scheduleDropdownClose('component-desktop')"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
role="button"
|
role="button"
|
||||||
class="transition-colors px-3 py-2 rounded-md inline-flex items-center gap-1 cursor-pointer"
|
class="transition-colors px-3 py-2 rounded-md inline-flex items-center gap-1 cursor-pointer"
|
||||||
:class="
|
@click="toggleDropdown('component-desktop')"
|
||||||
isActive('/component-category') ||
|
@keydown.enter.prevent="toggleDropdown('component-desktop')"
|
||||||
isActive('/component-catalog')
|
@keydown.space.prevent="toggleDropdown('component-desktop')"
|
||||||
|
:class="
|
||||||
|
isActive('/component-category') ||
|
||||||
|
isActive('/component-catalog')
|
||||||
? 'bg-primary text-primary-content font-semibold shadow-sm'
|
? 'bg-primary text-primary-content font-semibold shadow-sm'
|
||||||
: 'text-base-content hover:bg-primary/10 hover:text-primary'
|
: 'text-base-content hover:bg-primary/10 hover:text-primary'
|
||||||
"
|
"
|
||||||
@@ -393,20 +408,23 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="dropdown dropdown-hover"
|
class="dropdown"
|
||||||
:class="{ 'dropdown-open': openDropdown === 'resources-desktop' }"
|
:class="{ 'dropdown-open': openDropdown === 'resources-desktop' }"
|
||||||
@mouseenter="setDropdown('resources-desktop')"
|
@mouseenter="setDropdown('resources-desktop')"
|
||||||
@mouseleave="scheduleDropdownClose('resources-desktop')"
|
@mouseleave="scheduleDropdownClose('resources-desktop')"
|
||||||
@focusin="setDropdown('resources-desktop')"
|
@focusin="setDropdown('resources-desktop')"
|
||||||
@focusout="scheduleDropdownClose('resources-desktop')"
|
@focusout="scheduleDropdownClose('resources-desktop')"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
role="button"
|
role="button"
|
||||||
class="transition-colors px-3 py-2 rounded-md inline-flex items-center gap-1 cursor-pointer"
|
class="transition-colors px-3 py-2 rounded-md inline-flex items-center gap-1 cursor-pointer"
|
||||||
:class="
|
@click="toggleDropdown('resources-desktop')"
|
||||||
isActive('/sites') ||
|
@keydown.enter.prevent="toggleDropdown('resources-desktop')"
|
||||||
isActive('/documents') ||
|
@keydown.space.prevent="toggleDropdown('resources-desktop')"
|
||||||
|
:class="
|
||||||
|
isActive('/sites') ||
|
||||||
|
isActive('/documents') ||
|
||||||
isActive('/constructeurs')
|
isActive('/constructeurs')
|
||||||
? 'bg-primary text-primary-content font-semibold shadow-sm'
|
? 'bg-primary text-primary-content font-semibold shadow-sm'
|
||||||
: 'text-base-content hover:bg-primary/10 hover:text-primary'
|
: 'text-base-content hover:bg-primary/10 hover:text-primary'
|
||||||
@@ -620,6 +638,22 @@ const scheduleDropdownClose = (name) => {
|
|||||||
}, 200);
|
}, 200);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeDropdownNow = () => {
|
||||||
|
if (dropdownCloseTimer) {
|
||||||
|
clearTimeout(dropdownCloseTimer);
|
||||||
|
dropdownCloseTimer = null;
|
||||||
|
}
|
||||||
|
openDropdown.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleDropdown = (name) => {
|
||||||
|
if (openDropdown.value === name) {
|
||||||
|
closeDropdownNow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setDropdown(name);
|
||||||
|
};
|
||||||
|
|
||||||
const activeProfileLabel = computed(() => {
|
const activeProfileLabel = computed(() => {
|
||||||
if (!activeProfile.value) {
|
if (!activeProfile.value) {
|
||||||
return "Profil inconnu";
|
return "Profil inconnu";
|
||||||
|
|||||||
@@ -278,20 +278,73 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Espacement adaptatif */
|
/* Espacement adaptatif */
|
||||||
.p-1 { padding: var(--spacing-xs); }
|
.p-1 {
|
||||||
.p-2 { padding: var(--spacing-sm); }
|
padding: var(--spacing-xs);
|
||||||
.p-3 { padding: var(--spacing-md); }
|
}
|
||||||
.p-4 { padding: var(--spacing-lg); }
|
.p-2 {
|
||||||
.p-5 { padding: var(--spacing-xl); }
|
padding: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
.p-3 {
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
}
|
||||||
|
.p-4 {
|
||||||
|
padding: var(--spacing-lg);
|
||||||
|
}
|
||||||
|
.p-5 {
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
.m-1 { margin: var(--spacing-xs); }
|
.m-1 {
|
||||||
.m-2 { margin: var(--spacing-sm); }
|
margin: var(--spacing-xs);
|
||||||
.m-3 { margin: var(--spacing-md); }
|
}
|
||||||
.m-4 { margin: var(--spacing-lg); }
|
.m-2 {
|
||||||
.m-5 { margin: var(--spacing-xl); }
|
margin: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
.m-3 {
|
||||||
|
margin: var(--spacing-md);
|
||||||
|
}
|
||||||
|
.m-4 {
|
||||||
|
margin: var(--spacing-lg);
|
||||||
|
}
|
||||||
|
.m-5 {
|
||||||
|
margin: var(--spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
.gap-1 { gap: var(--spacing-xs); }
|
.gap-1 {
|
||||||
.gap-2 { gap: var(--spacing-sm); }
|
gap: var(--spacing-xs);
|
||||||
.gap-3 { gap: var(--spacing-md); }
|
}
|
||||||
.gap-4 { gap: var(--spacing-lg); }
|
.gap-2 {
|
||||||
.gap-5 { gap: var(--spacing-xl); }
|
gap: var(--spacing-sm);
|
||||||
|
}
|
||||||
|
.gap-3 {
|
||||||
|
gap: var(--spacing-md);
|
||||||
|
}
|
||||||
|
.gap-4 {
|
||||||
|
gap: var(--spacing-lg);
|
||||||
|
}
|
||||||
|
.gap-5 {
|
||||||
|
gap: var(--spacing-xl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer components {
|
||||||
|
.form-control .label {
|
||||||
|
@apply mb-2;
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control .label + * {
|
||||||
|
margin-top: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
label + input,
|
||||||
|
label + select,
|
||||||
|
label + textarea,
|
||||||
|
label + .input,
|
||||||
|
label + .select,
|
||||||
|
label + .textarea {
|
||||||
|
margin-top: var(--spacing-xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,7 +15,9 @@
|
|||||||
|
|
||||||
<!-- Header with actions -->
|
<!-- Header with actions -->
|
||||||
<div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
<div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||||
<h1 class="text-3xl font-bold">Détails de la machine</h1>
|
<h1 class="text-3xl font-bold">
|
||||||
|
{{ machineViewTitle }}
|
||||||
|
</h1>
|
||||||
<div class="flex items-center gap-2 print:hidden" data-print-hide>
|
<div class="flex items-center gap-2 print:hidden" data-print-hide>
|
||||||
<button
|
<button
|
||||||
@click="toggleEditMode"
|
@click="toggleEditMode"
|
||||||
@@ -43,20 +45,6 @@
|
|||||||
>
|
>
|
||||||
Modifier les éléments du squelette
|
Modifier les éléments du squelette
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-outline"
|
|
||||||
:class="{ 'btn-primary': hasMissingRequiredCustomFields }"
|
|
||||||
:disabled="completingCustomFields"
|
|
||||||
@click="handleCompleteCustomFieldsClick"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
v-if="completingCustomFields"
|
|
||||||
class="loading loading-spinner loading-sm mr-2"
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
Compléter les champs personnalisés
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
v-if="!isEditMode"
|
v-if="!isEditMode"
|
||||||
@click="openPrintModal"
|
@click="openPrintModal"
|
||||||
@@ -98,7 +86,9 @@
|
|||||||
<div class="card-body space-y-6">
|
<div class="card-body space-y-6">
|
||||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="card-title">Modifier les éléments du squelette</h2>
|
<h2 class="card-title">
|
||||||
|
{{ skeletonEditorTitle }}
|
||||||
|
</h2>
|
||||||
<p class="text-sm text-gray-500">
|
<p class="text-sm text-gray-500">
|
||||||
Sélectionnez les composants et pièces à associer à cette machine selon les exigences du type.
|
Sélectionnez les composants et pièces à associer à cette machine selon les exigences du type.
|
||||||
</p>
|
</p>
|
||||||
@@ -874,7 +864,6 @@ if (!machineId) {
|
|||||||
const {
|
const {
|
||||||
updateMachine: updateMachineApi,
|
updateMachine: updateMachineApi,
|
||||||
reconfigureSkeleton: reconfigureMachineSkeleton,
|
reconfigureSkeleton: reconfigureMachineSkeleton,
|
||||||
addMissingCustomFields: addMissingCustomFieldsApi,
|
|
||||||
} = useMachines()
|
} = useMachines()
|
||||||
const {
|
const {
|
||||||
getComposantsByMachine,
|
getComposantsByMachine,
|
||||||
@@ -911,7 +900,6 @@ const loading = ref(true)
|
|||||||
const machine = ref(null)
|
const machine = ref(null)
|
||||||
const components = ref([])
|
const components = ref([])
|
||||||
const pieces = ref([])
|
const pieces = ref([])
|
||||||
const completingCustomFields = ref(false)
|
|
||||||
const printAreaRef = ref(null)
|
const printAreaRef = ref(null)
|
||||||
|
|
||||||
const { constructeurs, loadConstructeurs } = useConstructeurs()
|
const { constructeurs, loadConstructeurs } = useConstructeurs()
|
||||||
@@ -926,9 +914,6 @@ const machineConstructeurDisplay = computed(() => {
|
|||||||
return constructeurs.value.find(item => item.id === id) || machine.value?.constructeur || null
|
return constructeurs.value.find(item => item.id === id) || machine.value?.constructeur || null
|
||||||
})
|
})
|
||||||
|
|
||||||
// Valeurs des champs personnalisés de la machine
|
|
||||||
const machineCustomFieldValues = reactive({})
|
|
||||||
|
|
||||||
const machineDocumentFiles = ref([])
|
const machineDocumentFiles = ref([])
|
||||||
const machineDocumentsUploading = ref(false)
|
const machineDocumentsUploading = ref(false)
|
||||||
const machineDocumentsLoaded = ref(false)
|
const machineDocumentsLoaded = ref(false)
|
||||||
@@ -1284,7 +1269,6 @@ const applySkeletonReconfigurationResult = async (data) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await ensureModelsForExistingEntities()
|
await ensureModelsForExistingEntities()
|
||||||
await autoCompleteMissingCustomFieldsIfNeeded()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveSkeletonConfiguration = async () => {
|
const saveSkeletonConfiguration = async () => {
|
||||||
@@ -1369,6 +1353,16 @@ const closeSaveComponentModelModal = () => {
|
|||||||
const isEditMode = ref(false)
|
const isEditMode = ref(false)
|
||||||
const debug = ref(false) // Ajout de debug pour afficher les infos de debug
|
const debug = ref(false) // Ajout de debug pour afficher les infos de debug
|
||||||
|
|
||||||
|
const machineViewTitle = computed(() =>
|
||||||
|
isEditMode.value ? 'Modification de la machine' : 'Détails de la machine'
|
||||||
|
)
|
||||||
|
|
||||||
|
const skeletonEditorTitle = computed(() =>
|
||||||
|
skeletonEditor.open
|
||||||
|
? 'Modification des éléments du squelette'
|
||||||
|
: 'Éléments du squelette'
|
||||||
|
)
|
||||||
|
|
||||||
// Gestion du pliage global des composants
|
// Gestion du pliage global des composants
|
||||||
const componentsCollapsed = ref(true)
|
const componentsCollapsed = ref(true)
|
||||||
const collapseToggleToken = ref(0)
|
const collapseToggleToken = ref(0)
|
||||||
@@ -1423,26 +1417,6 @@ const flattenComponents = (list = []) => {
|
|||||||
|
|
||||||
const flattenedComponents = computed(() => flattenComponents(components.value))
|
const flattenedComponents = computed(() => flattenComponents(components.value))
|
||||||
|
|
||||||
const hasMissingRequiredCustomFields = computed(() => {
|
|
||||||
if (!machine.value) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasMissingRequiredCustomFieldsInMachine(machine.value)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (components.value.some(component => hasMissingRequiredCustomFieldsInComponent(component))) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pieces.value.some(piece => hasMissingRequiredCustomFieldsInPiece(piece))) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
|
|
||||||
const preloadModelsForTypeMachine = async (typeMachine) => {
|
const preloadModelsForTypeMachine = async (typeMachine) => {
|
||||||
if (!typeMachine) return
|
if (!typeMachine) return
|
||||||
const componentTypeIds = new Set(
|
const componentTypeIds = new Set(
|
||||||
@@ -1846,134 +1820,6 @@ const transformComponentCustomFields = (componentsData) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function getCustomFieldIdentifier(field) {
|
|
||||||
if (!field) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return field.customFieldId ?? field.customField?.id ?? field.id ?? null
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeCustomFieldEntries(entries = []) {
|
|
||||||
return entries
|
|
||||||
.map(entry => ({
|
|
||||||
id: getCustomFieldIdentifier(entry),
|
|
||||||
required: entry?.customField?.required ?? entry?.required ?? false,
|
|
||||||
value: entry?.value ?? null,
|
|
||||||
}))
|
|
||||||
.filter(entry => entry.id !== null)
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectCustomFieldDefinitions(...sources) {
|
|
||||||
return sources.flatMap(source => (Array.isArray(source) ? source : []))
|
|
||||||
}
|
|
||||||
|
|
||||||
function isEmptyCustomFieldValue(value) {
|
|
||||||
if (value === null || value === undefined) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
return value.trim() === ''
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasMissingCustomFieldValues(values = []) {
|
|
||||||
return values.some(entry => entry.required && isEmptyCustomFieldValue(entry.value))
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasMissingCustomFieldDefinitions(definitions = [], values = []) {
|
|
||||||
if (!definitions.length) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const valueIds = new Set(values.map(entry => entry.id))
|
|
||||||
return definitions.some(definition => {
|
|
||||||
if (!definition?.required) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
const id = getCustomFieldIdentifier(definition)
|
|
||||||
return id !== null && !valueIds.has(id)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasMissingRequiredCustomFieldsInMachine(machineData) {
|
|
||||||
if (!machineData) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const values = normalizeCustomFieldEntries(machineData.customFieldValues || [])
|
|
||||||
const definitions = collectCustomFieldDefinitions(machineData.typeMachine?.customFields)
|
|
||||||
|
|
||||||
return (
|
|
||||||
hasMissingCustomFieldDefinitions(definitions, values)
|
|
||||||
|| hasMissingCustomFieldValues(values)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasMissingRequiredCustomFieldsInComponent(component) {
|
|
||||||
if (!component) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const sourceValues = component.customFields?.length
|
|
||||||
? component.customFields
|
|
||||||
: component.customFieldValues || []
|
|
||||||
const values = normalizeCustomFieldEntries(sourceValues)
|
|
||||||
|
|
||||||
const definitions = collectCustomFieldDefinitions(
|
|
||||||
component.typeComposant?.customFields,
|
|
||||||
component.composantModel?.customFields,
|
|
||||||
component.typeMachineComponentRequirement?.customFields,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
|
||||||
hasMissingCustomFieldDefinitions(definitions, values)
|
|
||||||
|| hasMissingCustomFieldValues(values)
|
|
||||||
) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
Array.isArray(component.pieces)
|
|
||||||
&& component.pieces.some(piece => hasMissingRequiredCustomFieldsInPiece(piece))
|
|
||||||
) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
Array.isArray(component.subComponents)
|
|
||||||
&& component.subComponents.some(sub => hasMissingRequiredCustomFieldsInComponent(sub))
|
|
||||||
) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasMissingRequiredCustomFieldsInPiece(piece) {
|
|
||||||
if (!piece) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const sourceValues = piece.customFields?.length
|
|
||||||
? piece.customFields
|
|
||||||
: piece.customFieldValues || []
|
|
||||||
const values = normalizeCustomFieldEntries(sourceValues)
|
|
||||||
|
|
||||||
const definitions = collectCustomFieldDefinitions(
|
|
||||||
piece.typePiece?.customFields,
|
|
||||||
piece.pieceModel?.customFields,
|
|
||||||
piece.typeMachinePieceRequirement?.customFields,
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
hasMissingCustomFieldDefinitions(definitions, values)
|
|
||||||
|| hasMissingCustomFieldValues(values)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function mergePieceLists(existing = [], updates = []) {
|
function mergePieceLists(existing = [], updates = []) {
|
||||||
if (!existing.length) {
|
if (!existing.length) {
|
||||||
return updates
|
return updates
|
||||||
@@ -2036,91 +1882,6 @@ function mergeComponentTrees(existing = [], updates = []) {
|
|||||||
return merged
|
return merged
|
||||||
}
|
}
|
||||||
|
|
||||||
async function applyCustomFieldsCompletionResult(payload) {
|
|
||||||
if (!payload) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedMachine = payload.machine || payload
|
|
||||||
if (machine.value && updatedMachine?.customFieldValues) {
|
|
||||||
machine.value = {
|
|
||||||
...machine.value,
|
|
||||||
customFieldValues: updatedMachine.customFieldValues,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedComponentsRaw = payload.components ?? updatedMachine?.components ?? null
|
|
||||||
if (Array.isArray(updatedComponentsRaw)) {
|
|
||||||
const transformedComponents = transformComponentCustomFields(updatedComponentsRaw)
|
|
||||||
components.value = mergeComponentTrees(components.value, transformedComponents)
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedPiecesRaw = payload.pieces ?? updatedMachine?.pieces ?? null
|
|
||||||
if (Array.isArray(updatedPiecesRaw)) {
|
|
||||||
const transformedPieces = transformCustomFields(updatedPiecesRaw)
|
|
||||||
pieces.value = mergePieceLists(pieces.value, transformedPieces)
|
|
||||||
}
|
|
||||||
|
|
||||||
await ensureModelsForExistingEntities()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function completeMissingCustomFields({ silent = false, checkMissing = true } = {}) {
|
|
||||||
if (!machine.value?.id) {
|
|
||||||
const error = 'Machine introuvable'
|
|
||||||
if (!silent) {
|
|
||||||
toast.showError(error)
|
|
||||||
}
|
|
||||||
return { success: false, error }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkMissing && !hasMissingRequiredCustomFields.value) {
|
|
||||||
return { success: true, skipped: true }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (completingCustomFields.value) {
|
|
||||||
if (!silent) {
|
|
||||||
toast.showInfo('Complétion des champs personnalisés déjà en cours')
|
|
||||||
}
|
|
||||||
return { success: false, error: 'Complétion déjà en cours' }
|
|
||||||
}
|
|
||||||
|
|
||||||
completingCustomFields.value = true
|
|
||||||
try {
|
|
||||||
const result = await addMissingCustomFieldsApi(machine.value.id, { showToast: false })
|
|
||||||
if (result.success) {
|
|
||||||
await applyCustomFieldsCompletionResult(result.data)
|
|
||||||
if (!silent) {
|
|
||||||
toast.showSuccess('Champs personnalisés complétés avec succès')
|
|
||||||
}
|
|
||||||
} else if (!silent) {
|
|
||||||
toast.showError(result.error || 'Impossible de compléter les champs personnalisés')
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Erreur lors de la complétion des champs personnalisés:', error)
|
|
||||||
if (!silent) {
|
|
||||||
toast.showError('Impossible de compléter les champs personnalisés')
|
|
||||||
}
|
|
||||||
return { success: false, error: error?.message || 'Erreur inconnue' }
|
|
||||||
} finally {
|
|
||||||
completingCustomFields.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function autoCompleteMissingCustomFieldsIfNeeded() {
|
|
||||||
await nextTick()
|
|
||||||
if (completingCustomFields.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!machine.value?.id) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!hasMissingRequiredCustomFields.value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await completeMissingCustomFields({ silent: true, checkMissing: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
const loadMachineData = async () => {
|
const loadMachineData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -2196,8 +1957,6 @@ const loadMachineData = async () => {
|
|||||||
console.log('Aucune pièce trouvée dans la réponse de la machine')
|
console.log('Aucune pièce trouvée dans la réponse de la machine')
|
||||||
}
|
}
|
||||||
|
|
||||||
await autoCompleteMissingCustomFieldsIfNeeded()
|
|
||||||
|
|
||||||
if (!machineDocumentsLoaded.value) {
|
if (!machineDocumentsLoaded.value) {
|
||||||
await refreshMachineDocuments()
|
await refreshMachineDocuments()
|
||||||
}
|
}
|
||||||
@@ -2454,10 +2213,6 @@ const updatePieceCustomField = async (fieldUpdate) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCompleteCustomFieldsClick = async () => {
|
|
||||||
await completeMissingCustomFields({ silent: false, checkMissing: false })
|
|
||||||
}
|
|
||||||
|
|
||||||
const editComponent = (component) => {
|
const editComponent = (component) => {
|
||||||
// TODO: Implement edit modal
|
// TODO: Implement edit modal
|
||||||
console.log('Edit component:', component)
|
console.log('Edit component:', component)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="flex items-center justify-between mb-6">
|
<div class="flex items-center justify-between mb-6">
|
||||||
<h2 class="card-title text-2xl">
|
<h2 class="card-title text-2xl">
|
||||||
Modifier : {{ type.name }}
|
{{ typePageTitle }}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<NuxtLink :to="`/type/edit/${type.id}`" class="btn btn-secondary">
|
<NuxtLink :to="`/type/edit/${type.id}`" class="btn btn-secondary">
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
||||||
import { useToast } from '~/composables/useToast'
|
import { useToast } from '~/composables/useToast'
|
||||||
@@ -128,6 +128,17 @@ const { showError } = useToast()
|
|||||||
const type = ref(null)
|
const type = ref(null)
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
|
|
||||||
|
const isEditView = computed(() => {
|
||||||
|
const editQuery = Array.isArray(route.query.edit) ? route.query.edit[0] : route.query.edit
|
||||||
|
const modeQuery = Array.isArray(route.query.mode) ? route.query.mode[0] : route.query.mode
|
||||||
|
return editQuery === 'true' || editQuery === '1' || modeQuery === 'edit'
|
||||||
|
})
|
||||||
|
|
||||||
|
const typePageTitle = computed(() => {
|
||||||
|
const currentName = type.value?.name || 'Type de squelette'
|
||||||
|
return isEditView.value ? `Modification : ${currentName}` : currentName
|
||||||
|
})
|
||||||
|
|
||||||
const componentRequirementCount = computed(() => type.value?.componentRequirements?.length || 0)
|
const componentRequirementCount = computed(() => type.value?.componentRequirements?.length || 0)
|
||||||
const pieceRequirementCount = computed(() => type.value?.pieceRequirements?.length || 0)
|
const pieceRequirementCount = computed(() => type.value?.pieceRequirements?.length || 0)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user