This commit is contained in:
Matthieu
2026-03-31 17:53:30 +02:00
parent e0f761da2b
commit 958a00c8fc
21 changed files with 281 additions and 144 deletions

View File

@@ -166,6 +166,7 @@ const table = useDataTable(
status: { default: 'open' },
entityType: { default: '' },
},
columnFilterKeys: ['entity'],
},
)

View File

@@ -144,7 +144,7 @@ const { componentTypes, loadComponentTypes } = useComponentTypes()
const table = useDataTable(
{ fetchData: fetchComposants },
{ defaultSort: 'name', defaultDirection: 'asc', defaultPerPage: 20, persistToUrl: true },
{ defaultSort: 'name', defaultDirection: 'asc', defaultPerPage: 20, persistToUrl: true, columnFilterKeys: ['typeComposant'] },
)
const columns = [

View File

@@ -64,6 +64,7 @@
:machine-constructeur-ids="d.machineConstructeurIds.value"
:machine-constructeurs-display="d.machineConstructeursDisplay.value"
:has-machine-constructeur="d.hasMachineConstructeur.value"
:constructeur-links="d.constructeurLinks.value"
:visible-custom-fields="d.visibleMachineCustomFields.value"
:get-machine-field-id="d.getMachineFieldId"
:machine-id="machineId"
@@ -72,6 +73,8 @@
@update:machine-reference="d.machineReference.value = $event"
@update:machine-site-id="d.machineSiteId.value = $event"
@update:constructeur-ids="d.handleMachineConstructeurChange"
@update:constructeur-links="d.constructeurLinks.value = $event"
@remove-constructeur-link="handleRemoveConstructeurLink"
@set-custom-field-value="d.setMachineCustomFieldValue"
@custom-fields-saved="() => { d.loadMachineData(); refreshVersions() }"
/>
@@ -196,9 +199,9 @@
</div>
<h3 class="text-lg font-semibold text-base-content mb-1">Machine non trouvée</h3>
<p class="text-sm text-base-content/50 mb-6">La machine avec l'ID "{{ machineId }}" n'existe pas ou a été supprimée.</p>
<NuxtLink to="/machines" class="btn btn-primary">
<button type="button" class="btn btn-primary" @click="$router.back()">
Retour aux machines
</NuxtLink>
</button>
</div>
</div>
</main>
@@ -280,6 +283,11 @@ const openAddModal = (kind) => {
addModalOpen.value = true
}
const handleRemoveConstructeurLink = (constructeurId) => {
const ids = d.machineConstructeurIds.value.filter(id => id !== constructeurId)
d.handleMachineConstructeurChange(ids)
}
const handleAddEntity = async (entityId) => {
if (addModalKind.value === 'component') {
await d.addComponentLink(entityId)

View File

@@ -120,11 +120,12 @@
</template>
<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { useMachines } from '~/composables/useMachines'
import { useSites } from '~/composables/useSites'
import { useToast } from '~/composables/useToast'
import { humanizeError } from '~/shared/utils/errorMessages'
import { useUrlState } from '~/composables/useUrlState'
import IconLucidePlus from '~icons/lucide/plus'
import IconLucideFactory from '~icons/lucide/factory'
import IconLucideMapPin from '~icons/lucide/map-pin'
@@ -135,8 +136,28 @@ const { machines, loading, loadMachines, deleteMachine } = useMachines()
const { sites, loadSites } = useSites()
const toast = useToast()
const urlState = useUrlState({
q: { default: '', debounce: 300 },
sites: { default: '' },
})
const searchQuery = urlState.q
const selectedSites = reactive(new Set())
const searchQuery = ref('')
// Sync URL → selectedSites on load and back/forward
watch(urlState.sites, (val) => {
selectedSites.clear()
if (val) {
for (const id of String(val).split(',')) {
if (id) selectedSites.add(id)
}
}
}, { immediate: true })
// Sync selectedSites → URL
watch(() => [...selectedSites], (ids) => {
urlState.sites.value = ids.join(',')
})
// Enrichir les machines avec les objets site complets
const enrichedMachines = computed(() => {

View File

@@ -112,6 +112,8 @@ const loadCategory = async () => {
category: response.category,
notes: response.notes ?? response.description ?? '',
structure: (response.structure as PieceModelStructure | null) ?? undefined,
referenceFormula: response.referenceFormula ?? null,
requiredFieldsForReference: response.requiredFieldsForReference ?? null,
}
} catch (error) {

View File

@@ -171,7 +171,7 @@ const { pieceTypes, loadPieceTypes } = usePieceTypes()
const table = useDataTable(
{ fetchData: fetchPieces },
{ defaultSort: 'name', defaultDirection: 'asc', defaultPerPage: 20, persistToUrl: true },
{ defaultSort: 'name', defaultDirection: 'asc', defaultPerPage: 20, persistToUrl: true, columnFilterKeys: ['typePiece'] },
)
const columns = [

View File

@@ -91,6 +91,10 @@
/>
</div>
</div>
<ConstructeurLinksTable
v-if="constructeurLinks.length"
v-model="constructeurLinks"
/>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
<div class="form-control">
@@ -213,6 +217,7 @@
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from '#imports'
import ConstructeurSelect from '~/components/ConstructeurSelect.vue'
import ConstructeurLinksTable from '~/components/ConstructeurLinksTable.vue'
import DocumentUpload from '~/components/DocumentUpload.vue'
import ProductSelect from '~/components/ProductSelect.vue'
import SearchSelect from '~/components/common/SearchSelect.vue'
@@ -222,8 +227,11 @@ import { useToast } from '~/composables/useToast'
import { humanizeError } from '~/shared/utils/errorMessages'
import { useCustomFields } from '~/composables/useCustomFields'
import { useDocuments } from '~/composables/useDocuments'
import { useConstructeurLinks } from '~/composables/useConstructeurLinks'
import { useConstructeurs } from '~/composables/useConstructeurs'
import { formatPieceStructurePreview } from '~/shared/modelUtils'
import { uniqueConstructeurIds } from '~/shared/constructeurUtils'
import { constructeurIdsFromLinks } from '~/shared/constructeurUtils'
import type { ConstructeurLinkEntry } from '~/shared/constructeurUtils'
import type { PieceModelStructure } from '~/shared/types/inventory'
import type { ModelType } from '~/services/modelTypes'
import {
@@ -255,6 +263,8 @@ const { createPiece } = usePieces()
const toast = useToast()
const { upsertCustomFieldValue, updateCustomFieldValue } = useCustomFields()
const { uploadDocuments } = useDocuments()
const { syncLinks } = useConstructeurLinks()
const { getConstructeurById } = useConstructeurs()
const { canEdit } = usePermissions()
const initialTypeId = ref<string>(typeof route.query.typeId === 'string' ? route.query.typeId : '')
@@ -267,6 +277,7 @@ const creationForm = reactive({
constructeurIds: [] as string[],
prix: '' as string,
})
const constructeurLinks = ref<ConstructeurLinkEntry[]>([])
const productSelections = ref<(string | null)[]>([])
const lastSuggestedName = ref('')
@@ -380,6 +391,7 @@ const clearCreationForm = () => {
creationForm.description = ''
creationForm.reference = ''
creationForm.constructeurIds = []
constructeurLinks.value = []
creationForm.prix = ''
productSelections.value = []
lastSuggestedName.value = ''
@@ -411,8 +423,6 @@ const submitCreation = async () => {
payload.reference = reference
}
payload.constructeurIds = uniqueConstructeurIds(creationForm.constructeurIds)
const normalizedProductIds = collectNormalizedProductIds(
productRequirementEntries.value,
productSelections.value,
@@ -448,6 +458,10 @@ const submitCreation = async () => {
],
{ customFieldInputs, upsertCustomFieldValue, updateCustomFieldValue, toast },
)
// Sync constructeur links after creation
if (constructeurLinks.value.length) {
await syncLinks('piece', createdPiece.id, [], constructeurLinks.value)
}
if (selectedDocuments.value.length && createdPiece.id) {
uploadingDocuments.value = true
const uploadResult = await uploadDocuments(
@@ -478,6 +492,26 @@ const submitCreation = async () => {
}
}
// Sync constructeurIds → constructeurLinks when IDs are added via ConstructeurSelect
watch(
() => creationForm.constructeurIds,
(ids) => {
const currentIds = new Set(constructeurLinks.value.map(l => l.constructeurId))
for (const id of ids) {
if (!currentIds.has(id)) {
const resolved = getConstructeurById(id)
constructeurLinks.value.push({
constructeurId: id,
constructeur: resolved ? { id: resolved.id, name: resolved.name } : null,
supplierReference: null,
})
}
}
constructeurLinks.value = constructeurLinks.value.filter(l => ids.includes(l.constructeurId))
},
{ deep: true },
)
onMounted(async () => {
await loadPieceTypes()
})

View File

@@ -175,7 +175,7 @@ const toast = useToast()
const table = useDataTable(
{ fetchData: fetchProducts },
{ defaultSort: 'name', defaultDirection: 'asc', defaultPerPage: 20, persistToUrl: true },
{ defaultSort: 'name', defaultDirection: 'asc', defaultPerPage: 20, persistToUrl: true, columnFilterKeys: ['typeProduct'] },
)
const errorMessage = computed(() => (typeof error.value === 'string' && error.value.length ? error.value : null))