feat: enrich piece assignment labels and document previews
This commit is contained in:
@@ -208,24 +208,33 @@
|
||||
/>
|
||||
|
||||
<div v-if="componentDocuments.length" class="space-y-2">
|
||||
<div
|
||||
v-for="document in componentDocuments"
|
||||
:key="document.id"
|
||||
class="flex items-center justify-between rounded border border-base-200 bg-base-100 px-3 py-2"
|
||||
>
|
||||
<div class="flex items-center gap-3 text-sm">
|
||||
<div class="h-14 w-14 flex-shrink-0 overflow-hidden rounded-md border border-base-200 bg-base-200/70 flex items-center justify-center">
|
||||
<img
|
||||
v-if="isImageDocument(document) && document.path"
|
||||
:src="document.path"
|
||||
class="h-full w-full object-cover"
|
||||
:alt="`Aperçu de ${document.name}`"
|
||||
>
|
||||
<component
|
||||
v-else
|
||||
:is="documentIcon(document).component"
|
||||
class="h-6 w-6"
|
||||
:class="documentIcon(document).colorClass"
|
||||
<div
|
||||
v-for="document in componentDocuments"
|
||||
:key="document.id"
|
||||
class="flex items-center justify-between rounded border border-base-200 bg-base-100 px-3 py-2"
|
||||
>
|
||||
<div class="flex items-center gap-3 text-sm">
|
||||
<div
|
||||
class="flex-shrink-0 overflow-hidden rounded-md border border-base-200 bg-base-200/70 flex items-center justify-center"
|
||||
:class="documentThumbnailClass(document)"
|
||||
>
|
||||
<img
|
||||
v-if="isImageDocument(document) && document.path"
|
||||
:src="document.path"
|
||||
class="h-full w-full object-cover"
|
||||
:alt="`Aperçu de ${document.name}`"
|
||||
>
|
||||
<iframe
|
||||
v-else-if="shouldInlinePdf(document)"
|
||||
:src="documentPreviewSrc(document)"
|
||||
class="h-full w-full border-0 bg-white"
|
||||
title="Aperçu PDF"
|
||||
/>
|
||||
<component
|
||||
v-else
|
||||
:is="documentIcon(document).component"
|
||||
class="h-6 w-6"
|
||||
:class="documentIcon(document).colorClass"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
@@ -317,7 +326,7 @@ import DocumentUpload from './DocumentUpload.vue'
|
||||
import ConstructeurSelect from './ConstructeurSelect.vue'
|
||||
import { useDocuments } from '~/composables/useDocuments'
|
||||
import { getFileIcon } from '~/utils/fileIcons'
|
||||
import { canPreviewDocument, isImageDocument } from '~/utils/documentPreview'
|
||||
import { canPreviewDocument, isImageDocument, isPdfDocument } from '~/utils/documentPreview'
|
||||
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
||||
import IconLucideChevronRight from '~icons/lucide/chevron-right'
|
||||
import { useCustomFields } from '~/composables/useCustomFields'
|
||||
@@ -357,6 +366,40 @@ const componentDocuments = computed(() => props.component.documents || [])
|
||||
const documentIcon = doc => getFileIcon({ name: doc.filename || doc.name, mime: doc.mimeType })
|
||||
const previewDocument = ref(null)
|
||||
const previewVisible = ref(false)
|
||||
const PDF_PREVIEW_MAX_BYTES = 5 * 1024 * 1024
|
||||
const shouldInlinePdf = (document) => {
|
||||
if (!document || !isPdfDocument(document) || !document.path) {
|
||||
return false
|
||||
}
|
||||
if (typeof document.size === 'number' && document.size > PDF_PREVIEW_MAX_BYTES) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
const appendPdfViewerParams = (src) => {
|
||||
if (!src || src.startsWith('data:')) {
|
||||
return src || ''
|
||||
}
|
||||
if (src.includes('#')) {
|
||||
return `${src}&toolbar=0&navpanes=0`
|
||||
}
|
||||
return `${src}#toolbar=0&navpanes=0`
|
||||
}
|
||||
const documentPreviewSrc = (document) => {
|
||||
if (!document?.path) {
|
||||
return ''
|
||||
}
|
||||
if (isPdfDocument(document)) {
|
||||
return appendPdfViewerParams(document.path)
|
||||
}
|
||||
return document.path
|
||||
}
|
||||
const documentThumbnailClass = (document) => {
|
||||
if (shouldInlinePdf(document) || (isImageDocument(document) && document?.path)) {
|
||||
return 'h-24 w-20'
|
||||
}
|
||||
return 'h-16 w-16'
|
||||
}
|
||||
|
||||
const childComponents = computed(() => {
|
||||
const list = props.component.subcomponents || props.component.subComponents || []
|
||||
|
||||
@@ -228,16 +228,44 @@ watch(
|
||||
|
||||
const describePieceRequirement = (definition: ComponentModelPiece) => {
|
||||
const parts: string[] = [];
|
||||
if (definition.role) {
|
||||
parts.push(definition.role);
|
||||
const addPart = (value?: string | null) => {
|
||||
const trimmed = typeof value === 'string' ? value.trim() : '';
|
||||
if (trimmed && !parts.includes(trimmed)) {
|
||||
parts.push(trimmed);
|
||||
}
|
||||
};
|
||||
|
||||
const options = getPieceOptions(definition);
|
||||
const fallbackPiece = options[0] || null;
|
||||
const fallbackType = fallbackPiece?.typePiece || null;
|
||||
|
||||
addPart(definition.role);
|
||||
addPart(
|
||||
definition.typePieceLabel ||
|
||||
(definition as any).typePiece?.name ||
|
||||
fallbackType?.name,
|
||||
);
|
||||
|
||||
const family =
|
||||
definition.familyCode ||
|
||||
(definition as any).typePiece?.code ||
|
||||
fallbackType?.code ||
|
||||
null;
|
||||
if (family) {
|
||||
addPart(`Famille ${family}`);
|
||||
}
|
||||
if (definition.typePieceLabel) {
|
||||
parts.push(definition.typePieceLabel);
|
||||
} else if ((definition as any).typePiece?.name) {
|
||||
parts.push((definition as any).typePiece.name);
|
||||
} else if (definition.familyCode) {
|
||||
parts.push(definition.familyCode);
|
||||
|
||||
if (parts.length === 0) {
|
||||
addPart(fallbackType?.name);
|
||||
if (fallbackType?.code) {
|
||||
addPart(`Famille ${fallbackType.code}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (parts.length === 0 && definition.typePieceId) {
|
||||
addPart(`#${definition.typePieceId}`);
|
||||
}
|
||||
|
||||
return parts.length ? parts.join(' • ') : 'Pièce du squelette';
|
||||
};
|
||||
|
||||
|
||||
@@ -276,13 +276,22 @@
|
||||
class="flex items-center justify-between rounded border border-base-200 bg-base-100 px-3 py-2"
|
||||
>
|
||||
<div class="flex items-center gap-3 text-sm">
|
||||
<div class="h-14 w-14 flex-shrink-0 overflow-hidden rounded-md border border-base-200 bg-base-200/70 flex items-center justify-center">
|
||||
<div
|
||||
class="flex-shrink-0 overflow-hidden rounded-md border border-base-200 bg-base-200/70 flex items-center justify-center"
|
||||
:class="documentThumbnailClass(document)"
|
||||
>
|
||||
<img
|
||||
v-if="isImageDocument(document) && document.path"
|
||||
:src="document.path"
|
||||
class="h-full w-full object-cover"
|
||||
:alt="`Aperçu de ${document.name}`"
|
||||
>
|
||||
<iframe
|
||||
v-else-if="shouldInlinePdf(document)"
|
||||
:src="documentPreviewSrc(document)"
|
||||
class="h-full w-full border-0 bg-white"
|
||||
title="Aperçu PDF"
|
||||
/>
|
||||
<component
|
||||
v-else
|
||||
:is="documentIcon(document).component"
|
||||
@@ -348,7 +357,7 @@ import { useCustomFields } from "~/composables/useCustomFields";
|
||||
import { useToast } from "~/composables/useToast";
|
||||
import { useDocuments } from "~/composables/useDocuments";
|
||||
import { getFileIcon } from "~/utils/fileIcons";
|
||||
import { canPreviewDocument, isImageDocument } from "~/utils/documentPreview";
|
||||
import { canPreviewDocument, isImageDocument, isPdfDocument } from "~/utils/documentPreview";
|
||||
import DocumentUpload from "~/components/DocumentUpload.vue";
|
||||
import DocumentPreviewModal from "~/components/DocumentPreviewModal.vue";
|
||||
import IconLucidePackage from "~icons/lucide/package";
|
||||
@@ -388,6 +397,40 @@ const documentIcon = (doc) =>
|
||||
getFileIcon({ name: doc.filename || doc.name, mime: doc.mimeType });
|
||||
const previewDocument = ref(null);
|
||||
const previewVisible = ref(false);
|
||||
const PDF_PREVIEW_MAX_BYTES = 5 * 1024 * 1024;
|
||||
const shouldInlinePdf = (document) => {
|
||||
if (!document || !isPdfDocument(document) || !document.path) {
|
||||
return false;
|
||||
}
|
||||
if (typeof document.size === "number" && document.size > PDF_PREVIEW_MAX_BYTES) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
const appendPdfViewerParams = (src) => {
|
||||
if (!src || src.startsWith("data:")) {
|
||||
return src || "";
|
||||
}
|
||||
if (src.includes("#")) {
|
||||
return `${src}&toolbar=0&navpanes=0`;
|
||||
}
|
||||
return `${src}#toolbar=0&navpanes=0`;
|
||||
};
|
||||
const documentPreviewSrc = (document) => {
|
||||
if (!document?.path) {
|
||||
return "";
|
||||
}
|
||||
if (isPdfDocument(document)) {
|
||||
return appendPdfViewerParams(document.path);
|
||||
}
|
||||
return document.path;
|
||||
};
|
||||
const documentThumbnailClass = (document) => {
|
||||
if (shouldInlinePdf(document) || (isImageDocument(document) && document?.path)) {
|
||||
return "h-24 w-20";
|
||||
}
|
||||
return "h-16 w-16";
|
||||
};
|
||||
const extractStructureCustomFields = (structure) => {
|
||||
if (!structure || typeof structure !== "object") {
|
||||
return [];
|
||||
|
||||
@@ -495,6 +495,8 @@ const addPiece = () => {
|
||||
typePieceId: '',
|
||||
typePieceLabel: '',
|
||||
reference: '',
|
||||
familyCode: '',
|
||||
role: '',
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user