feat(frontend): replace inline icons with lucide components
This commit is contained in:
54
app/app.vue
54
app/app.vue
@@ -5,9 +5,7 @@
|
|||||||
<div class="navbar-start">
|
<div class="navbar-start">
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
|
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideMenu class="w-5 h-5" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h8m-8 6h16"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<ul tabindex="0" class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
|
<ul tabindex="0" class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52">
|
||||||
<li class="pt-1 pb-2 lg:hidden">
|
<li class="pt-1 pb-2 lg:hidden">
|
||||||
@@ -15,10 +13,7 @@
|
|||||||
@click="openDisplaySettings"
|
@click="openDisplaySettings"
|
||||||
class="w-full flex items-center gap-2 rounded-md px-2 py-1 transition-colors text-base-content hover:bg-primary/10 hover:text-primary"
|
class="w-full flex items-center gap-2 rounded-md px-2 py-1 transition-colors text-base-content hover:bg-primary/10 hover:text-primary"
|
||||||
>
|
>
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideSettings class="w-4 h-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
||||||
</svg>
|
|
||||||
Paramètres d'affichage
|
Paramètres d'affichage
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
@@ -89,10 +84,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center space-x-3">
|
<div class="flex items-center space-x-3">
|
||||||
<div class="avatar placeholder">
|
<div class="avatar placeholder">
|
||||||
<div class="bg-primary text-primary-content rounded-lg w-10">
|
<div class="bg-primary text-primary-content rounded-lg w-10 grid place-items-center">
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideBoxes class="w-6 h-6" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<NuxtLink to="/" class="btn btn-ghost text-xl">Inventaire Pro</NuxtLink>
|
<NuxtLink to="/" class="btn btn-ghost text-xl">Inventaire Pro</NuxtLink>
|
||||||
@@ -173,43 +166,31 @@
|
|||||||
class="btn btn-ghost btn-circle hidden lg:inline-flex"
|
class="btn btn-ghost btn-circle hidden lg:inline-flex"
|
||||||
title="Paramètres d'affichage"
|
title="Paramètres d'affichage"
|
||||||
>
|
>
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideSettings class="w-5 h-5" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Menu Nouveau -->
|
<!-- Menu Nouveau -->
|
||||||
<div class="dropdown dropdown-end">
|
<div class="dropdown dropdown-end">
|
||||||
<div tabindex="0" role="button" class="btn btn-primary">
|
<div tabindex="0" role="button" class="btn btn-primary">
|
||||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePlus class="w-5 h-5 mr-2" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
|
||||||
</svg>
|
|
||||||
Nouveau
|
Nouveau
|
||||||
</div>
|
</div>
|
||||||
<ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52">
|
<ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52">
|
||||||
<li>
|
<li>
|
||||||
<NuxtLink to="/machines?add=true" class="flex items-center gap-2">
|
<NuxtLink to="/machines?add=true" class="flex items-center gap-2">
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideCpu class="w-4 h-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path>
|
|
||||||
</svg>
|
|
||||||
Nouvelle Machine
|
Nouvelle Machine
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<NuxtLink to="/generator" class="flex items-center gap-2">
|
<NuxtLink to="/generator" class="flex items-center gap-2">
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideFilePlus class="w-4 h-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
|
||||||
</svg>
|
|
||||||
Nouveau Type
|
Nouveau Type
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<NuxtLink to="/sites?add=true" class="flex items-center gap-2">
|
<NuxtLink to="/sites?add=true" class="flex items-center gap-2">
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideMapPin class="w-4 h-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
||||||
</svg>
|
|
||||||
Nouveau Site
|
Nouveau Site
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
@@ -236,17 +217,13 @@
|
|||||||
<li>
|
<li>
|
||||||
<NuxtLink to="/profiles/manage" class="justify-between">
|
<NuxtLink to="/profiles/manage" class="justify-between">
|
||||||
Gestion des profils
|
Gestion des profils
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideChevronRight class="w-4 h-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
||||||
</svg>
|
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<button type="button" class="text-error justify-between" @click="handleLogout">
|
<button type="button" class="text-error justify-between" @click="handleLogout">
|
||||||
Déconnexion
|
Déconnexion
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideLogOut class="w-4 h-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a2 2 0 01-2 2H7a2 2 0 01-2-2V7a2 2 0 012-2h4a2 2 0 012 2v1" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -282,6 +259,15 @@
|
|||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { useRoute, navigateTo } from '#imports'
|
import { useRoute, navigateTo } from '#imports'
|
||||||
import { useProfileSession } from '~/composables/useProfileSession'
|
import { useProfileSession } from '~/composables/useProfileSession'
|
||||||
|
import IconLucideMenu from '~icons/lucide/menu'
|
||||||
|
import IconLucideSettings from '~icons/lucide/settings'
|
||||||
|
import IconLucideBoxes from '~icons/lucide/boxes'
|
||||||
|
import IconLucidePlus from '~icons/lucide/plus'
|
||||||
|
import IconLucideCpu from '~icons/lucide/cpu'
|
||||||
|
import IconLucideFilePlus from '~icons/lucide/file-plus'
|
||||||
|
import IconLucideMapPin from '~icons/lucide/map-pin'
|
||||||
|
import IconLucideChevronRight from '~icons/lucide/chevron-right'
|
||||||
|
import IconLucideLogOut from '~icons/lucide/log-out'
|
||||||
|
|
||||||
// État du modal des paramètres d'affichage
|
// État du modal des paramètres d'affichage
|
||||||
const displaySettingsOpen = ref(false)
|
const displaySettingsOpen = ref(false)
|
||||||
|
|||||||
@@ -17,9 +17,7 @@
|
|||||||
:aria-expanded="!isCollapsed"
|
:aria-expanded="!isCollapsed"
|
||||||
:title="isCollapsed ? 'Déplier les détails du composant' : 'Replier les détails du composant'"
|
:title="isCollapsed ? 'Déplier les détails du composant' : 'Replier les détails du composant'"
|
||||||
>
|
>
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideChevronRight class="w-5 h-5 transition-transform" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
||||||
</svg>
|
|
||||||
<span class="sr-only">{{ isCollapsed ? 'Déplier' : 'Replier' }} le composant</span>
|
<span class="sr-only">{{ isCollapsed ? 'Déplier' : 'Replier' }} le composant</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
@@ -195,7 +193,11 @@
|
|||||||
>
|
>
|
||||||
<div class="flex items-center gap-3 text-sm">
|
<div class="flex items-center gap-3 text-sm">
|
||||||
<span class="text-xl" :class="documentIcon(document).colorClass">
|
<span class="text-xl" :class="documentIcon(document).colorClass">
|
||||||
{{ documentIcon(document).icon }}
|
<component
|
||||||
|
:is="documentIcon(document).component"
|
||||||
|
class="h-6 w-6"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<div class="font-medium">{{ document.name }}</div>
|
<div class="font-medium">{{ document.name }}</div>
|
||||||
@@ -277,6 +279,7 @@ import { useDocuments } from '~/composables/useDocuments'
|
|||||||
import { getFileIcon } from '~/utils/fileIcons'
|
import { getFileIcon } from '~/utils/fileIcons'
|
||||||
import { canPreviewDocument } from '~/utils/documentPreview'
|
import { canPreviewDocument } from '~/utils/documentPreview'
|
||||||
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
||||||
|
import IconLucideChevronRight from '~icons/lucide/chevron-right'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
component: {
|
component: {
|
||||||
|
|||||||
@@ -16,9 +16,7 @@
|
|||||||
class="absolute right-2 top-1/2 -translate-y-1/2 btn btn-ghost btn-xs"
|
class="absolute right-2 top-1/2 -translate-y-1/2 btn btn-ghost btn-xs"
|
||||||
@click="ensureOptionsLoaded(true)"
|
@click="ensureOptionsLoaded(true)"
|
||||||
>
|
>
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideChevronsUpDown class="w-4 h-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16l4-4 4 4m0-8l-4 4-4-4" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
v-if="openDropdown"
|
v-if="openDropdown"
|
||||||
@@ -90,6 +88,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch, computed, onMounted, onBeforeUnmount } from 'vue'
|
import { ref, watch, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||||
import { useConstructeurs } from '~/composables/useConstructeurs'
|
import { useConstructeurs } from '~/composables/useConstructeurs'
|
||||||
|
import IconLucideChevronsUpDown from '~icons/lucide/chevrons-up-down'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|||||||
@@ -7,9 +7,7 @@
|
|||||||
@drop.prevent="onDrop"
|
@drop.prevent="onDrop"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col items-center gap-3 text-center">
|
<div class="flex flex-col items-center gap-3 text-center">
|
||||||
<svg class="w-10 h-10 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideCloudUpload class="w-10 h-10 text-primary" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 00-.88 7.912L6 24h12a4 4 0 00.88-7.912L18 16H7zm5-14a4 4 0 014 4v4h1.586a1 1 0 01.707 1.707l-5.586 5.586a1 1 0 01-1.414 0L7.707 11.707A1 1 0 018.414 10H10V6a4 4 0 014-4z" />
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 class="font-semibold">{{ title }}</h3>
|
<h3 class="font-semibold">{{ title }}</h3>
|
||||||
@@ -35,7 +33,12 @@
|
|||||||
<ul v-if="selectedFiles.length" class="mt-4 w-full space-y-2 text-left">
|
<ul v-if="selectedFiles.length" class="mt-4 w-full space-y-2 text-left">
|
||||||
<li v-for="file in selectedFiles" :key="file.name" class="flex items-center justify-between text-sm">
|
<li v-for="file in selectedFiles" :key="file.name" class="flex items-center justify-between text-sm">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<span class="text-xl" :class="getIcon(file).colorClass">{{ getIcon(file).icon }}</span>
|
<component
|
||||||
|
:is="getIcon(file).component"
|
||||||
|
class="h-6 w-6"
|
||||||
|
:class="getIcon(file).colorClass"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span class="font-medium">{{ file.name }}</span>
|
<span class="font-medium">{{ file.name }}</span>
|
||||||
<span class="text-xs text-gray-500">{{ formatSize(file.size) }} • {{ file.type || 'Type inconnu' }}</span>
|
<span class="text-xs text-gray-500">{{ formatSize(file.size) }} • {{ file.type || 'Type inconnu' }}</span>
|
||||||
@@ -54,6 +57,7 @@
|
|||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { useToast } from '~/composables/useToast'
|
import { useToast } from '~/composables/useToast'
|
||||||
import { getFileIcon } from '~/utils/fileIcons'
|
import { getFileIcon } from '~/utils/fileIcons'
|
||||||
|
import IconLucideCloudUpload from '~icons/lucide/cloud-upload'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: {
|
title: {
|
||||||
|
|||||||
@@ -8,9 +8,10 @@
|
|||||||
|
|
||||||
<div class="flex items-center justify-between mb-3">
|
<div class="flex items-center justify-between mb-3">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<svg class="w-4 h-4 text-purple-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePackage
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
class="w-4 h-4 text-purple-500"
|
||||||
</svg>
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
<input
|
<input
|
||||||
v-if="isEditMode"
|
v-if="isEditMode"
|
||||||
:id="`piece-name-${piece.id}`"
|
:id="`piece-name-${piece.id}`"
|
||||||
@@ -201,7 +202,11 @@
|
|||||||
>
|
>
|
||||||
<div class="flex items-center gap-3 text-sm">
|
<div class="flex items-center gap-3 text-sm">
|
||||||
<span class="text-xl" :class="documentIcon(document).colorClass">
|
<span class="text-xl" :class="documentIcon(document).colorClass">
|
||||||
{{ documentIcon(document).icon }}
|
<component
|
||||||
|
:is="documentIcon(document).component"
|
||||||
|
class="h-6 w-6"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<div class="font-medium">{{ document.name }}</div>
|
<div class="font-medium">{{ document.name }}</div>
|
||||||
@@ -250,6 +255,7 @@ import { canPreviewDocument } from '~/utils/documentPreview'
|
|||||||
import DocumentUpload from '~/components/DocumentUpload.vue'
|
import DocumentUpload from '~/components/DocumentUpload.vue'
|
||||||
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
||||||
import ConstructeurSelect from './ConstructeurSelect.vue'
|
import ConstructeurSelect from './ConstructeurSelect.vue'
|
||||||
|
import IconLucidePackage from '~icons/lucide/package'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
piece: {
|
piece: {
|
||||||
|
|||||||
@@ -17,18 +17,26 @@
|
|||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<!-- Icon -->
|
<!-- Icon -->
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<svg v-if="toast.type === 'success'" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideCheck
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
v-if="toast.type === 'success'"
|
||||||
</svg>
|
class="w-5 h-5"
|
||||||
<svg v-else-if="toast.type === 'error'" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
aria-hidden="true"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
/>
|
||||||
</svg>
|
<IconLucideX
|
||||||
<svg v-else-if="toast.type === 'warning'" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
v-else-if="toast.type === 'error'"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z"></path>
|
class="w-5 h-5"
|
||||||
</svg>
|
aria-hidden="true"
|
||||||
<svg v-else class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
/>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
<IconLucideAlertTriangle
|
||||||
</svg>
|
v-else-if="toast.type === 'warning'"
|
||||||
|
class="w-5 h-5"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<IconLucideInfo
|
||||||
|
v-else
|
||||||
|
class="w-5 h-5"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Message -->
|
<!-- Message -->
|
||||||
@@ -41,9 +49,7 @@
|
|||||||
@click="removeToast(toast.id)"
|
@click="removeToast(toast.id)"
|
||||||
class="btn btn-ghost btn-xs"
|
class="btn btn-ghost btn-xs"
|
||||||
>
|
>
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideX class="w-4 h-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -54,6 +60,10 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { useToast } from '~/composables/useToast'
|
import { useToast } from '~/composables/useToast'
|
||||||
|
import IconLucideCheck from '~icons/lucide/check'
|
||||||
|
import IconLucideX from '~icons/lucide/x'
|
||||||
|
import IconLucideAlertTriangle from '~icons/lucide/alert-triangle'
|
||||||
|
import IconLucideInfo from '~icons/lucide/info'
|
||||||
|
|
||||||
const { toasts, removeToast } = useToast()
|
const { toasts, removeToast } = useToast()
|
||||||
|
|
||||||
|
|||||||
@@ -8,19 +8,13 @@
|
|||||||
@click="toggleComponent"
|
@click="toggleComponent"
|
||||||
title="Plier / déplier le composant"
|
title="Plier / déplier le composant"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-4 h-4 transition-transform duration-200"
|
class="w-4 h-4 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': expanded }"
|
:class="{ 'rotate-90': expanded }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<svg class="w-5 h-5 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideCpu class="w-5 h-5 text-blue-500" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path>
|
|
||||||
</svg>
|
|
||||||
<h4 class="text-lg font-medium">{{ component.name }}</h4>
|
<h4 class="text-lg font-medium">{{ component.name }}</h4>
|
||||||
<span v-if="!expanded && component.reference" class="text-xs text-gray-500">(Ref: {{ component.reference }})</span>
|
<span v-if="!expanded && component.reference" class="text-xs text-gray-500">(Ref: {{ component.reference }})</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -50,15 +44,11 @@
|
|||||||
class="btn btn-ghost btn-xs p-1"
|
class="btn btn-ghost btn-xs p-1"
|
||||||
@click="toggleSection('customFields')"
|
@click="toggleSection('customFields')"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-3 h-3 transition-transform duration-200"
|
class="w-3 h-3 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': expandedSections.customFields }"
|
:class="{ 'rotate-90': expandedSections.customFields }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="expandedSections.customFields" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2">
|
<div v-if="expandedSections.customFields" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2">
|
||||||
@@ -79,15 +69,11 @@
|
|||||||
class="btn btn-ghost btn-xs p-1"
|
class="btn btn-ghost btn-xs p-1"
|
||||||
@click="toggleSection('pieces')"
|
@click="toggleSection('pieces')"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-3 h-3 transition-transform duration-200"
|
class="w-3 h-3 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': expandedSections.pieces }"
|
:class="{ 'rotate-90': expandedSections.pieces }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="expandedSections.pieces" class="space-y-2">
|
<div v-if="expandedSections.pieces" class="space-y-2">
|
||||||
@@ -103,19 +89,13 @@
|
|||||||
class="btn btn-ghost btn-xs p-1"
|
class="btn btn-ghost btn-xs p-1"
|
||||||
@click="togglePieceDetails(pieceIndex)"
|
@click="togglePieceDetails(pieceIndex)"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-3 h-3 transition-transform duration-200"
|
class="w-3 h-3 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': isPieceExpanded(pieceIndex) }"
|
:class="{ 'rotate-90': isPieceExpanded(pieceIndex) }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<svg class="w-4 h-4 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePackage class="w-4 h-4 text-red-500" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
|
||||||
</svg>
|
|
||||||
<span class="font-medium">{{ piece.name }}</span>
|
<span class="font-medium">{{ piece.name }}</span>
|
||||||
<span v-if="!isPieceExpanded(pieceIndex) && piece.reference" class="text-xs text-gray-500">(Ref: {{ piece.reference }})</span>
|
<span v-if="!isPieceExpanded(pieceIndex) && piece.reference" class="text-xs text-gray-500">(Ref: {{ piece.reference }})</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -142,18 +122,14 @@
|
|||||||
<span class="text-xs font-medium text-gray-600">Champs personnalisés</span>
|
<span class="text-xs font-medium text-gray-600">Champs personnalisés</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-ghost btn-xs p-1"
|
class="btn btn-ghost btn-xs p-1"
|
||||||
@click="togglePieceCustomFields(pieceIndex)"
|
@click="togglePieceCustomFields(pieceIndex)"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-3 h-3 transition-transform duration-200"
|
class="w-3 h-3 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': isPieceCustomFieldSectionExpanded(pieceIndex) }"
|
:class="{ 'rotate-90': isPieceCustomFieldSectionExpanded(pieceIndex) }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isPieceCustomFieldSectionExpanded(pieceIndex)" class="grid grid-cols-1 md:grid-cols-2 gap-1">
|
<div v-if="isPieceCustomFieldSectionExpanded(pieceIndex)" class="grid grid-cols-1 md:grid-cols-2 gap-1">
|
||||||
@@ -178,15 +154,11 @@
|
|||||||
class="btn btn-ghost btn-xs p-1"
|
class="btn btn-ghost btn-xs p-1"
|
||||||
@click="toggleSection('subComponents')"
|
@click="toggleSection('subComponents')"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-3 h-3 transition-transform duration-200"
|
class="w-3 h-3 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': expandedSections.subComponents }"
|
:class="{ 'rotate-90': expandedSections.subComponents }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="expandedSections.subComponents" class="space-y-3">
|
<div v-if="expandedSections.subComponents" class="space-y-3">
|
||||||
@@ -196,25 +168,19 @@
|
|||||||
class="border border-gray-200 rounded p-3 bg-gray-50"
|
class="border border-gray-200 rounded p-3 bg-gray-50"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div class="flex items-center justify-between mb-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-ghost btn-xs p-1"
|
class="btn btn-ghost btn-xs p-1"
|
||||||
@click="toggleSubComponentDetails(subIndex)"
|
@click="toggleSubComponentDetails(subIndex)"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-3 h-3 transition-transform duration-200"
|
class="w-3 h-3 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': isSubComponentExpanded(subIndex) }"
|
:class="{ 'rotate-90': isSubComponentExpanded(subIndex) }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
</button>
|
||||||
>
|
<IconLucideCheckCircle2 class="w-4 h-4 text-green-500" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<svg class="w-4 h-4 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
||||||
</svg>
|
|
||||||
<span class="font-medium">{{ subComponent.name }}</span>
|
<span class="font-medium">{{ subComponent.name }}</span>
|
||||||
<span v-if="!isSubComponentExpanded(subIndex) && subComponent.reference" class="text-xs text-gray-500">(Ref: {{ subComponent.reference }})</span>
|
<span v-if="!isSubComponentExpanded(subIndex) && subComponent.reference" class="text-xs text-gray-500">(Ref: {{ subComponent.reference }})</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -244,15 +210,11 @@
|
|||||||
class="btn btn-ghost btn-xs p-1"
|
class="btn btn-ghost btn-xs p-1"
|
||||||
@click="toggleSubComponentCustomFieldSection(subIndex)"
|
@click="toggleSubComponentCustomFieldSection(subIndex)"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-3 h-3 transition-transform duration-200"
|
class="w-3 h-3 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': isSubComponentCustomFieldSectionExpanded(subIndex) }"
|
:class="{ 'rotate-90': isSubComponentCustomFieldSectionExpanded(subIndex) }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isSubComponentCustomFieldSectionExpanded(subIndex)" class="grid grid-cols-1 md:grid-cols-2 gap-1">
|
<div v-if="isSubComponentCustomFieldSectionExpanded(subIndex)" class="grid grid-cols-1 md:grid-cols-2 gap-1">
|
||||||
@@ -273,15 +235,11 @@
|
|||||||
class="btn btn-ghost btn-xs p-1"
|
class="btn btn-ghost btn-xs p-1"
|
||||||
@click="toggleSubComponentPieceSection(subIndex)"
|
@click="toggleSubComponentPieceSection(subIndex)"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-3 h-3 transition-transform duration-200"
|
class="w-3 h-3 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': isSubComponentPieceSectionExpanded(subIndex) }"
|
:class="{ 'rotate-90': isSubComponentPieceSectionExpanded(subIndex) }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isSubComponentPieceSectionExpanded(subIndex)" class="space-y-1">
|
<div v-if="isSubComponentPieceSectionExpanded(subIndex)" class="space-y-1">
|
||||||
@@ -297,19 +255,13 @@
|
|||||||
class="btn btn-ghost btn-xs p-1"
|
class="btn btn-ghost btn-xs p-1"
|
||||||
@click="toggleSubPieceDetails(subIndex, pieceIndex)"
|
@click="toggleSubPieceDetails(subIndex, pieceIndex)"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-3 h-3 transition-transform duration-200"
|
class="w-3 h-3 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': isSubPieceExpanded(subIndex, pieceIndex) }"
|
:class="{ 'rotate-90': isSubPieceExpanded(subIndex, pieceIndex) }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<svg class="w-3 h-3 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePackage class="w-3 h-3 text-red-500" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
|
||||||
</svg>
|
|
||||||
<span class="text-xs font-medium">{{ piece.name }}</span>
|
<span class="text-xs font-medium">{{ piece.name }}</span>
|
||||||
<span v-if="!isSubPieceExpanded(subIndex, pieceIndex) && piece.reference" class="text-[10px] text-gray-500">(Ref: {{ piece.reference }})</span>
|
<span v-if="!isSubPieceExpanded(subIndex, pieceIndex) && piece.reference" class="text-[10px] text-gray-500">(Ref: {{ piece.reference }})</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -339,15 +291,11 @@
|
|||||||
class="btn btn-ghost btn-xs p-1"
|
class="btn btn-ghost btn-xs p-1"
|
||||||
@click="toggleSubPieceCustomFields(subIndex, pieceIndex)"
|
@click="toggleSubPieceCustomFields(subIndex, pieceIndex)"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-3 h-3 transition-transform duration-200"
|
class="w-3 h-3 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': isSubPieceCustomFieldSectionExpanded(subIndex, pieceIndex) }"
|
:class="{ 'rotate-90': isSubPieceCustomFieldSectionExpanded(subIndex, pieceIndex) }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isSubPieceCustomFieldSectionExpanded(subIndex, pieceIndex)" class="grid grid-cols-1 md:grid-cols-2 gap-1">
|
<div v-if="isSubPieceCustomFieldSectionExpanded(subIndex, pieceIndex)" class="grid grid-cols-1 md:grid-cols-2 gap-1">
|
||||||
@@ -373,6 +321,10 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, computed, watch, onMounted } from 'vue'
|
import { ref, reactive, computed, watch, onMounted } from 'vue'
|
||||||
|
import IconLucideChevronRight from '~icons/lucide/chevron-right'
|
||||||
|
import IconLucideCpu from '~icons/lucide/cpu'
|
||||||
|
import IconLucidePackage from '~icons/lucide/package'
|
||||||
|
import IconLucideCheckCircle2 from '~icons/lucide/check-circle-2'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
component: {
|
component: {
|
||||||
|
|||||||
@@ -6,18 +6,12 @@
|
|||||||
Réinitialiser
|
Réinitialiser
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" class="btn btn-primary" :disabled="saving">
|
<button type="submit" class="btn btn-primary" :disabled="saving">
|
||||||
<svg
|
<IconLucideRefreshCw
|
||||||
v-if="saving"
|
v-if="saving"
|
||||||
class="w-5 h-5 mr-2 animate-spin"
|
class="w-5 h-5 mr-2 animate-spin"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
<IconLucideCheck v-else class="w-5 h-5 mr-2" aria-hidden="true" />
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
|
||||||
</svg>
|
|
||||||
<svg v-else class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
||||||
</svg>
|
|
||||||
{{ saving ? 'Sauvegarde...' : 'Sauvegarder les modifications' }}
|
{{ saving ? 'Sauvegarde...' : 'Sauvegarder les modifications' }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -26,6 +20,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import IconLucideRefreshCw from '~icons/lucide/refresh-cw'
|
||||||
|
import IconLucideCheck from '~icons/lucide/check'
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
saving: {
|
saving: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|||||||
@@ -8,15 +8,11 @@
|
|||||||
class="btn btn-ghost btn-sm p-1"
|
class="btn btn-ghost btn-sm p-1"
|
||||||
@click="toggleSection"
|
@click="toggleSection"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-4 h-4 transition-transform duration-200"
|
class="w-4 h-4 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': expanded }"
|
:class="{ 'rotate-90': expanded }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<h3 class="card-title text-lg">Composants</h3>
|
<h3 class="card-title text-lg">Composants</h3>
|
||||||
<span class="badge badge-accent">{{ components.length }}</span>
|
<span class="badge badge-accent">{{ components.length }}</span>
|
||||||
@@ -37,19 +33,13 @@
|
|||||||
@click="toggleComponent(index)"
|
@click="toggleComponent(index)"
|
||||||
title="Plier / déplier le composant"
|
title="Plier / déplier le composant"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-4 h-4 transition-transform duration-200"
|
class="w-4 h-4 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': isComponentExpanded(index) }"
|
:class="{ 'rotate-90': isComponentExpanded(index) }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<svg class="w-4 h-4 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideSettings2 class="w-4 h-4 text-green-500" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"></path>
|
|
||||||
</svg>
|
|
||||||
<h5 class="text-sm font-medium">Composant {{ index + 1 }}</h5>
|
<h5 class="text-sm font-medium">Composant {{ index + 1 }}</h5>
|
||||||
<span v-if="!isComponentExpanded(index)" class="text-xs text-gray-500 truncate max-w-[160px]">
|
<span v-if="!isComponentExpanded(index)" class="text-xs text-gray-500 truncate max-w-[160px]">
|
||||||
{{ component.name || 'Sans nom' }}
|
{{ component.name || 'Sans nom' }}
|
||||||
@@ -61,9 +51,7 @@
|
|||||||
class="btn btn-square btn-error btn-sm"
|
class="btn btn-square btn-error btn-sm"
|
||||||
title="Supprimer ce composant"
|
title="Supprimer ce composant"
|
||||||
>
|
>
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideX class="w-4 h-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -140,18 +128,14 @@
|
|||||||
|
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<button type="button" @click="addComponent" class="btn btn-primary btn-sm">
|
<button type="button" @click="addComponent" class="btn btn-primary btn-sm">
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePlus class="w-4 h-4 mr-2" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
|
||||||
</svg>
|
|
||||||
Ajouter un composant
|
Ajouter un composant
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="flex justify-end">
|
<div v-else class="flex justify-end">
|
||||||
<button type="button" @click="addComponent" class="btn btn-primary btn-sm">
|
<button type="button" @click="addComponent" class="btn btn-primary btn-sm">
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePlus class="w-4 h-4 mr-2" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
|
||||||
</svg>
|
|
||||||
Ajouter un composant
|
Ajouter un composant
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -165,6 +149,10 @@ import TypeEditCustomFieldsSection from '~/components/TypeEditCustomFieldsSectio
|
|||||||
import TypeMachinePieceForm from '~/components/TypeMachinePieceForm.vue'
|
import TypeMachinePieceForm from '~/components/TypeMachinePieceForm.vue'
|
||||||
import ConstructeurSelect from '~/components/ConstructeurSelect.vue'
|
import ConstructeurSelect from '~/components/ConstructeurSelect.vue'
|
||||||
import { useConstructeurs } from '~/composables/useConstructeurs'
|
import { useConstructeurs } from '~/composables/useConstructeurs'
|
||||||
|
import IconLucideChevronRight from '~icons/lucide/chevron-right'
|
||||||
|
import IconLucideSettings2 from '~icons/lucide/settings-2'
|
||||||
|
import IconLucideX from '~icons/lucide/x'
|
||||||
|
import IconLucidePlus from '~icons/lucide/plus'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|||||||
@@ -8,15 +8,11 @@
|
|||||||
class="btn btn-ghost btn-sm p-1"
|
class="btn btn-ghost btn-sm p-1"
|
||||||
@click="toggleSection"
|
@click="toggleSection"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-4 h-4 transition-transform duration-200"
|
class="w-4 h-4 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': expanded }"
|
:class="{ 'rotate-90': expanded }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<h3 class="card-title text-lg">Champs personnalisés du type</h3>
|
<h3 class="card-title text-lg">Champs personnalisés du type</h3>
|
||||||
<span class="badge badge-primary">{{ fields.length }}</span>
|
<span class="badge badge-primary">{{ fields.length }}</span>
|
||||||
@@ -37,19 +33,13 @@
|
|||||||
@click="toggleField(fieldIndex)"
|
@click="toggleField(fieldIndex)"
|
||||||
title="Plier / déplier le champ"
|
title="Plier / déplier le champ"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-4 h-4 transition-transform duration-200"
|
class="w-4 h-4 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': isFieldExpanded(fieldIndex) }"
|
:class="{ 'rotate-90': isFieldExpanded(fieldIndex) }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<svg class="w-4 h-4 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideListChecks class="w-4 h-4 text-blue-500" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
|
||||||
</svg>
|
|
||||||
<h5 class="text-sm font-medium">Champ personnalisé {{ fieldIndex + 1 }}</h5>
|
<h5 class="text-sm font-medium">Champ personnalisé {{ fieldIndex + 1 }}</h5>
|
||||||
<span v-if="!isFieldExpanded(fieldIndex)" class="text-xs text-gray-500 truncate max-w-[160px]">
|
<span v-if="!isFieldExpanded(fieldIndex)" class="text-xs text-gray-500 truncate max-w-[160px]">
|
||||||
{{ field.name || 'Sans nom' }}
|
{{ field.name || 'Sans nom' }}
|
||||||
@@ -61,9 +51,7 @@
|
|||||||
class="btn btn-square btn-error btn-sm"
|
class="btn btn-square btn-error btn-sm"
|
||||||
title="Supprimer ce champ"
|
title="Supprimer ce champ"
|
||||||
>
|
>
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideX class="w-4 h-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -148,18 +136,14 @@
|
|||||||
|
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<button type="button" @click="addField" class="btn btn-primary btn-sm">
|
<button type="button" @click="addField" class="btn btn-primary btn-sm">
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePlus class="w-4 h-4 mr-2" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
|
||||||
</svg>
|
|
||||||
Ajouter un champ
|
Ajouter un champ
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="flex justify-end">
|
<div v-else class="flex justify-end">
|
||||||
<button type="button" @click="addField" class="btn btn-primary btn-sm">
|
<button type="button" @click="addField" class="btn btn-primary btn-sm">
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePlus class="w-4 h-4 mr-2" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
|
||||||
</svg>
|
|
||||||
Ajouter un champ
|
Ajouter un champ
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -169,6 +153,10 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
|
import IconLucideChevronRight from '~icons/lucide/chevron-right'
|
||||||
|
import IconLucideListChecks from '~icons/lucide/list-checks'
|
||||||
|
import IconLucideX from '~icons/lucide/x'
|
||||||
|
import IconLucidePlus from '~icons/lucide/plus'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|||||||
@@ -8,15 +8,11 @@
|
|||||||
class="btn btn-ghost btn-sm p-1"
|
class="btn btn-ghost btn-sm p-1"
|
||||||
@click="toggleSection"
|
@click="toggleSection"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-4 h-4 transition-transform duration-200"
|
class="w-4 h-4 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': expanded }"
|
:class="{ 'rotate-90': expanded }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<h3 class="card-title text-lg">Pièces principales</h3>
|
<h3 class="card-title text-lg">Pièces principales</h3>
|
||||||
<span class="badge badge-secondary">{{ pieces.length }}</span>
|
<span class="badge badge-secondary">{{ pieces.length }}</span>
|
||||||
@@ -37,6 +33,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import TypeMachinePieceForm from '~/components/TypeMachinePieceForm.vue'
|
import TypeMachinePieceForm from '~/components/TypeMachinePieceForm.vue'
|
||||||
|
import IconLucideChevronRight from '~icons/lucide/chevron-right'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|||||||
@@ -8,19 +8,13 @@
|
|||||||
@click="toggleDetails"
|
@click="toggleDetails"
|
||||||
title="Plier / déplier la pièce"
|
title="Plier / déplier la pièce"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-4 h-4 transition-transform duration-200"
|
class="w-4 h-4 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': expanded }"
|
:class="{ 'rotate-90': expanded }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<svg class="w-5 h-5 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePackage class="w-5 h-5 text-red-500" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
|
||||||
</svg>
|
|
||||||
<h4 class="font-medium">{{ piece.name }}</h4>
|
<h4 class="font-medium">{{ piece.name }}</h4>
|
||||||
<span v-if="!expanded && piece.reference" class="text-xs text-gray-500">(Ref: {{ piece.reference }})</span>
|
<span v-if="!expanded && piece.reference" class="text-xs text-gray-500">(Ref: {{ piece.reference }})</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -62,6 +56,8 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
|
import IconLucideChevronRight from '~icons/lucide/chevron-right'
|
||||||
|
import IconLucidePackage from '~icons/lucide/package'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
piece: {
|
piece: {
|
||||||
|
|||||||
@@ -6,161 +6,145 @@
|
|||||||
class="btn btn-outline btn-sm"
|
class="btn btn-outline btn-sm"
|
||||||
@click="toggleAllPieces"
|
@click="toggleAllPieces"
|
||||||
>
|
>
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideChevronRight
|
||||||
<path
|
class="w-4 h-4 mr-2 transform transition-transform"
|
||||||
v-if="allExpanded"
|
:class="allExpanded ? 'rotate-90' : ''"
|
||||||
stroke-linecap="round"
|
aria-hidden="true"
|
||||||
stroke-linejoin="round"
|
/>
|
||||||
stroke-width="2"
|
<span>{{ allExpanded ? 'Tout plier' : 'Tout déplier' }}</span>
|
||||||
d="M18 12H6"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
v-else
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 6v12m6-6H6"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
{{ allExpanded ? 'Tout plier' : 'Tout déplier' }}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-for="(piece, index) in pieces" :key="index" class="border border-gray-200 rounded-lg p-4 bg-gray-50">
|
<div
|
||||||
<div class="flex items-center justify-between mb-3">
|
v-for="(piece, index) in pieces"
|
||||||
<div class="flex items-center gap-2">
|
:key="piece.id ?? index"
|
||||||
|
class="border border-base-200 rounded-lg bg-base-100 p-4 space-y-3"
|
||||||
|
>
|
||||||
|
<div class="flex items-start justify-between">
|
||||||
|
<div class="flex items-start gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-ghost btn-xs p-1"
|
class="btn btn-ghost btn-xs p-1"
|
||||||
@click="togglePieceDetails(index)"
|
@click="togglePieceDetails(index)"
|
||||||
title="Plier / déplier la pièce"
|
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-4 h-4 transition-transform duration-200"
|
class="w-4 h-4 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': isPieceExpanded(index) }"
|
:class="{ 'rotate-90': isPieceExpanded(index) }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<h5 class="text-sm font-medium">Nouvelle pièce {{ index + 1 }}</h5>
|
<div>
|
||||||
<span v-if="!isPieceExpanded(index)" class="text-xs text-gray-500 truncate max-w-[160px]">
|
<h5 class="text-sm font-medium">Pièce {{ index + 1 }}</h5>
|
||||||
{{ piece.name || 'Sans nom' }}
|
<span
|
||||||
</span>
|
v-if="!isPieceExpanded(index)"
|
||||||
|
class="text-xs text-gray-500 block max-w-[200px] truncate"
|
||||||
|
>
|
||||||
|
{{ piece.name || 'Sans nom' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
class="btn btn-square btn-error btn-xs"
|
||||||
@click="removePiece(index)"
|
@click="removePiece(index)"
|
||||||
class="btn btn-square btn-error btn-sm"
|
|
||||||
>
|
>
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideX class="w-3 h-3" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="isPieceExpanded(index)" class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div v-if="isPieceExpanded(index)" class="space-y-4">
|
||||||
<div class="form-control">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<label class="label">
|
<div class="form-control">
|
||||||
<span class="label-text">Nom de la pièce</span>
|
<label class="label"><span class="label-text">Nom</span></label>
|
||||||
</label>
|
<input
|
||||||
<input
|
v-model="piece.name"
|
||||||
v-model="piece.name"
|
type="text"
|
||||||
type="text"
|
class="input input-bordered input-sm"
|
||||||
placeholder="Nom de la pièce"
|
placeholder="Nom de la pièce"
|
||||||
class="input input-bordered input-sm"
|
/>
|
||||||
required
|
</div>
|
||||||
/>
|
<div class="form-control">
|
||||||
|
<label class="label"><span class="label-text">Référence</span></label>
|
||||||
|
<input
|
||||||
|
v-model="piece.reference"
|
||||||
|
type="text"
|
||||||
|
class="input input-bordered input-sm"
|
||||||
|
placeholder="Référence"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label"><span class="label-text">Constructeur</span></label>
|
||||||
|
<ConstructeurSelect
|
||||||
|
:model-value="piece.constructeurId || piece.constructeur?.id || null"
|
||||||
|
placeholder="Sélectionner un constructeur"
|
||||||
|
@update:modelValue="value => setPieceConstructeur(index, value)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label"><span class="label-text">Emplacement</span></label>
|
||||||
|
<input
|
||||||
|
v-model="piece.emplacement"
|
||||||
|
type="text"
|
||||||
|
class="input input-bordered input-sm"
|
||||||
|
placeholder="Emplacement"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label class="label"><span class="label-text">Prix</span></label>
|
||||||
|
<input
|
||||||
|
v-model="piece.prix"
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
class="input input-bordered input-sm"
|
||||||
|
placeholder="Prix (optionnel)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-control">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Référence</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
v-model="piece.reference"
|
|
||||||
type="text"
|
|
||||||
placeholder="Référence"
|
|
||||||
class="input input-bordered input-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Constructeur</span>
|
|
||||||
</label>
|
|
||||||
<ConstructeurSelect
|
|
||||||
:model-value="piece.constructeurId || piece.constructeur?.id || null"
|
|
||||||
placeholder="Rechercher un constructeur..."
|
|
||||||
@update:modelValue="(value) => setPieceConstructeur(index, value)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Emplacement</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
v-model="piece.emplacement"
|
|
||||||
type="text"
|
|
||||||
placeholder="Emplacement"
|
|
||||||
class="input input-bordered input-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Champs personnalisés de cette pièce -->
|
<div class="space-y-3">
|
||||||
<div v-if="isPieceExpanded(index) && piece.customFields && piece.customFields.length > 0" class="mt-4">
|
|
||||||
<h6 class="text-xs font-medium text-gray-600 mb-2">Champs personnalisés de cette pièce :</h6>
|
|
||||||
<div class="space-y-2">
|
|
||||||
<div
|
<div
|
||||||
v-for="(field, fieldIndex) in piece.customFields"
|
v-for="(field, fieldIndex) in piece.customFields || []"
|
||||||
:key="fieldIndex"
|
:key="field.id || `${index}-${fieldIndex}`"
|
||||||
class="border border-gray-200 rounded p-2 bg-white"
|
class="border border-base-200 rounded-lg bg-base-200/60 p-3 space-y-2"
|
||||||
>
|
>
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-ghost btn-xs p-1"
|
class="btn btn-ghost btn-xs p-1"
|
||||||
@click="togglePieceCustomFieldDetails(index, fieldIndex)"
|
@click="togglePieceCustomFieldDetails(index, fieldIndex)"
|
||||||
title="Plier / déplier le champ"
|
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideChevronRight
|
||||||
class="w-3 h-3 transition-transform duration-200"
|
class="w-3 h-3 transition-transform duration-200"
|
||||||
:class="{ 'rotate-90': isPieceCustomFieldExpanded(index, fieldIndex) }"
|
:class="{ 'rotate-90': isPieceCustomFieldExpanded(index, fieldIndex) }"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
<span class="text-xs font-medium">Champ {{ fieldIndex + 1 }}</span>
|
<span class="text-xs font-medium">Champ {{ fieldIndex + 1 }}</span>
|
||||||
<span v-if="!isPieceCustomFieldExpanded(index, fieldIndex)" class="text-[10px] text-gray-500 truncate max-w-[120px]">
|
<span
|
||||||
|
v-if="!isPieceCustomFieldExpanded(index, fieldIndex)"
|
||||||
|
class="text-[10px] text-gray-500 max-w-[160px] truncate"
|
||||||
|
>
|
||||||
{{ field.name || 'Sans nom' }}
|
{{ field.name || 'Sans nom' }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@click="removeCustomField(index, fieldIndex)"
|
|
||||||
class="btn btn-square btn-error btn-xs"
|
class="btn btn-square btn-error btn-xs"
|
||||||
|
@click="removeCustomField(index, fieldIndex)"
|
||||||
>
|
>
|
||||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideX class="w-3 h-3" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="isPieceCustomFieldExpanded(index, fieldIndex)" class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
<div v-if="isPieceCustomFieldExpanded(index, fieldIndex)" class="space-y-3">
|
||||||
<div>
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<input
|
<input
|
||||||
v-model="field.name"
|
v-model="field.name"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Nom du champ"
|
|
||||||
class="input input-bordered input-xs w-full"
|
class="input input-bordered input-xs w-full"
|
||||||
|
placeholder="Nom du champ"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<select v-model="field.type" class="select select-bordered select-xs w-full">
|
<select v-model="field.type" class="select select-bordered select-xs w-full">
|
||||||
<option value="">Type</option>
|
<option value="">Type</option>
|
||||||
<option value="text">Texte</option>
|
<option value="text">Texte</option>
|
||||||
@@ -170,63 +154,49 @@
|
|||||||
<option value="date">Date</option>
|
<option value="date">Date</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="isPieceCustomFieldExpanded(index, fieldIndex)" class="grid grid-cols-1 md:grid-cols-2 gap-2 mt-2">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
||||||
<div class="flex items-center gap-2">
|
<label class="flex items-center gap-2 text-xs">
|
||||||
<input
|
<input v-model="field.required" type="checkbox" class="checkbox checkbox-xs" />
|
||||||
v-model="field.required"
|
Obligatoire
|
||||||
type="checkbox"
|
</label>
|
||||||
class="checkbox checkbox-xs"
|
|
||||||
/>
|
|
||||||
<span class="text-xs">Obligatoire</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input
|
<input
|
||||||
v-model="field.defaultValue"
|
v-model="field.defaultValue"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Valeur par défaut"
|
|
||||||
class="input input-bordered input-xs w-full"
|
class="input input-bordered input-xs w-full"
|
||||||
|
placeholder="Valeur par défaut"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Options pour les champs de type SELECT -->
|
<div v-if="field.type === 'select'">
|
||||||
<div v-if="isPieceCustomFieldExpanded(index, fieldIndex) && field.type === 'select'" class="mt-2">
|
<textarea
|
||||||
<textarea
|
v-model="field.optionsText"
|
||||||
v-model="field.optionsText"
|
class="textarea textarea-bordered textarea-xs w-full h-16"
|
||||||
placeholder="Option 1 Option 2 Option 3"
|
placeholder="Option 1 Option 2 Option 3"
|
||||||
class="textarea textarea-bordered textarea-xs w-full h-16"
|
@input="updateFieldOptions(index, fieldIndex)"
|
||||||
@input="updateFieldOptions(index, fieldIndex)"
|
></textarea>
|
||||||
></textarea>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Bouton pour ajouter des champs personnalisés -->
|
<button
|
||||||
<div v-if="isPieceExpanded(index)" class="mt-3">
|
type="button"
|
||||||
<button
|
class="btn btn-outline btn-sm"
|
||||||
type="button"
|
@click="addCustomField(index)"
|
||||||
@click="addCustomField(index)"
|
>
|
||||||
class="btn btn-outline btn-sm"
|
<IconLucidePlus class="w-4 h-4 mr-2" aria-hidden="true" />
|
||||||
>
|
Ajouter un champ personnalisé
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
</button>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
</div>
|
||||||
</svg>
|
|
||||||
Ajouter champs personnalisés
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@click="addPiece"
|
|
||||||
class="btn btn-outline btn-sm"
|
class="btn btn-outline btn-sm"
|
||||||
|
@click="addPiece"
|
||||||
>
|
>
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePlus class="w-4 h-4 mr-2" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
|
||||||
</svg>
|
|
||||||
Ajouter une pièce de machine
|
Ajouter une pièce de machine
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -236,6 +206,9 @@
|
|||||||
import { ref, reactive, onMounted, watch } from 'vue'
|
import { ref, reactive, onMounted, watch } from 'vue'
|
||||||
import ConstructeurSelect from '~/components/ConstructeurSelect.vue'
|
import ConstructeurSelect from '~/components/ConstructeurSelect.vue'
|
||||||
import { useConstructeurs } from '~/composables/useConstructeurs'
|
import { useConstructeurs } from '~/composables/useConstructeurs'
|
||||||
|
import IconLucideChevronRight from '~icons/lucide/chevron-right'
|
||||||
|
import IconLucideX from '~icons/lucide/x'
|
||||||
|
import IconLucidePlus from '~icons/lucide/plus'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
|
|||||||
@@ -45,10 +45,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="filteredDocuments.length === 0" class="text-center py-16 text-sm text-gray-500">
|
<div v-else-if="filteredDocuments.length === 0" class="text-center py-16 text-sm text-gray-500">
|
||||||
<svg class="mx-auto mb-4 h-14 w-14 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideFileSearch class="mx-auto mb-4 h-14 w-14 text-gray-400" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15.172 7l-6.586 6.586a2 2 0 000 2.828L11.586 19a2 2 0 002.828 0L21 12.414V7H15.172z" />
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 17L3 20m0 0l3 3m-3-3h12" />
|
|
||||||
</svg>
|
|
||||||
Aucun document ne correspond à votre recherche pour l'instant.
|
Aucun document ne correspond à votre recherche pour l'instant.
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -69,7 +66,11 @@
|
|||||||
<td>
|
<td>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<span class="text-xl" :class="documentIcon(document).colorClass">
|
<span class="text-xl" :class="documentIcon(document).colorClass">
|
||||||
{{ documentIcon(document).icon }}
|
<component
|
||||||
|
:is="documentIcon(document).component"
|
||||||
|
class="h-6 w-6"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<div class="font-semibold">{{ document.name }}</div>
|
<div class="font-semibold">{{ document.name }}</div>
|
||||||
@@ -120,6 +121,7 @@ import { useDocuments } from '~/composables/useDocuments'
|
|||||||
import { getFileIcon } from '~/utils/fileIcons'
|
import { getFileIcon } from '~/utils/fileIcons'
|
||||||
import { canPreviewDocument } from '~/utils/documentPreview'
|
import { canPreviewDocument } from '~/utils/documentPreview'
|
||||||
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
||||||
|
import IconLucideFileSearch from '~icons/lucide/file-search'
|
||||||
|
|
||||||
|
|
||||||
const { documents, loading, loadDocuments } = useDocuments()
|
const { documents, loading, loadDocuments } = useDocuments()
|
||||||
|
|||||||
@@ -6,9 +6,10 @@
|
|||||||
<div class="card-body space-y-6">
|
<div class="card-body space-y-6">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<span class="inline-flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-primary">
|
<span class="inline-flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-primary">
|
||||||
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePlus
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
class="h-5 w-5"
|
||||||
</svg>
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="card-title text-2xl">Nouveau type de machine</h2>
|
<h2 class="card-title text-2xl">Nouveau type de machine</h2>
|
||||||
@@ -50,16 +51,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<p class="text-sm text-gray-600 line-clamp-3">{{ type.description || 'Aucune description' }}</p>
|
<p class="text-sm text-gray-600 line-clamp-3">{{ type.description || 'Aucune description' }}</p>
|
||||||
<div class="text-xs text-gray-500 flex items-center gap-2">
|
<div class="text-xs text-gray-500 flex items-center gap-2">
|
||||||
<span class="inline-flex items-center gap-1">
|
<span class="inline-flex items-center gap-1">
|
||||||
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideClipboardList class="h-4 w-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-3-3v6m-7 5h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
|
||||||
</svg>
|
|
||||||
{{ type.components?.length || 0 }} composant(s)
|
{{ type.components?.length || 0 }} composant(s)
|
||||||
</span>
|
</span>
|
||||||
<span class="inline-flex items-center gap-1">
|
<span class="inline-flex items-center gap-1">
|
||||||
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideList class="h-4 w-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 10h16M4 14h16M4 18h16" />
|
|
||||||
</svg>
|
|
||||||
{{ type.machinePieces?.length || 0 }} pièce(s) machine
|
{{ type.machinePieces?.length || 0 }} pièce(s) machine
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -79,6 +76,9 @@
|
|||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
||||||
import { useToast } from '~/composables/useToast'
|
import { useToast } from '~/composables/useToast'
|
||||||
|
import IconLucidePlus from '~icons/lucide/plus'
|
||||||
|
import IconLucideClipboardList from '~icons/lucide/clipboard-list'
|
||||||
|
import IconLucideList from '~icons/lucide/list'
|
||||||
|
|
||||||
const { machineTypes, loadMachineTypes, createMachineType } = useMachineTypesApi()
|
const { machineTypes, loadMachineTypes, createMachineType } = useMachineTypesApi()
|
||||||
const { showError } = useToast()
|
const { showError } = useToast()
|
||||||
|
|||||||
@@ -70,9 +70,7 @@
|
|||||||
<!-- Hierarchical Machines View -->
|
<!-- Hierarchical Machines View -->
|
||||||
<div v-else-if="filteredSites.length === 0" class="text-center py-12">
|
<div v-else-if="filteredSites.length === 0" class="text-center py-12">
|
||||||
<div class="max-w-md mx-auto">
|
<div class="max-w-md mx-auto">
|
||||||
<svg class="w-16 h-16 mx-auto text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideFactory class="w-16 h-16 mx-auto text-gray-400 mb-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"></path>
|
|
||||||
</svg>
|
|
||||||
<h3 class="text-lg font-medium text-gray-900 mb-2">Aucune machine trouvée</h3>
|
<h3 class="text-lg font-medium text-gray-900 mb-2">Aucune machine trouvée</h3>
|
||||||
<p class="text-gray-500 mb-4">Commencez par ajouter des sites et des machines.</p>
|
<p class="text-gray-500 mb-4">Commencez par ajouter des sites et des machines.</p>
|
||||||
<div class="flex gap-2 justify-center">
|
<div class="flex gap-2 justify-center">
|
||||||
@@ -97,32 +95,23 @@
|
|||||||
<div class="flex items-center justify-between mb-4">
|
<div class="flex items-center justify-between mb-4">
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<div class="avatar placeholder">
|
<div class="avatar placeholder">
|
||||||
<div class="bg-primary text-primary-content rounded-lg w-12">
|
<div class="bg-primary text-primary-content rounded-lg w-12 grid place-items-center">
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideMapPin class="w-6 h-6" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-xl font-bold">{{ site.name }}</h3>
|
<h3 class="text-xl font-bold">{{ site.name }}</h3>
|
||||||
<div class="text-sm text-gray-600 space-y-1">
|
<div class="text-sm text-gray-600 space-y-1">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<svg class="w-4 h-4 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideUser class="w-4 h-4 text-primary" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5.121 17.804A13.937 13.937 0 0112 15c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
||||||
</svg>
|
|
||||||
<span class="font-medium">{{ site.contactName }}</span>
|
<span class="font-medium">{{ site.contactName }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<svg class="w-4 h-4 text-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePhone class="w-4 h-4 text-secondary" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5a2 2 0 012-2h3l2 3h6l2-3h3a2 2 0 012 2v13a2 2 0 01-2 2H5a2 2 0 01-2-2V5z" />
|
|
||||||
</svg>
|
|
||||||
<span>{{ site.contactPhone }}</span>
|
<span>{{ site.contactPhone }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-start gap-2">
|
<div class="flex items-start gap-2">
|
||||||
<svg class="w-4 h-4 text-accent mt-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideMapPinned class="w-4 h-4 text-accent mt-1" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 11c1.38 0 2.5-1.12 2.5-2.5S13.38 6 12 6s-2.5 1.12-2.5 2.5S10.62 11 12 11zm0 0c-2.21 0-4 1.343-4 3v1.5h8V14c0-1.657-1.79-3-4-3z" />
|
|
||||||
</svg>
|
|
||||||
<span>
|
<span>
|
||||||
{{ site.contactAddress }}<br />
|
{{ site.contactAddress }}<br />
|
||||||
{{ site.contactPostalCode }} {{ site.contactCity }}
|
{{ site.contactPostalCode }} {{ site.contactCity }}
|
||||||
@@ -134,9 +123,11 @@
|
|||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<div class="badge badge-primary badge-lg">{{ site.machines?.length || 0 }} machines</div>
|
<div class="badge badge-primary badge-lg">{{ site.machines?.length || 0 }} machines</div>
|
||||||
<button @click="toggleSiteCollapse(site.id)" class="btn btn-ghost btn-sm">
|
<button @click="toggleSiteCollapse(site.id)" class="btn btn-ghost btn-sm">
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideChevronDown
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
|
class="w-5 h-5 transition-transform"
|
||||||
</svg>
|
:class="collapsedSites.includes(site.id) ? 'rotate-180' : ''"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -161,23 +152,17 @@
|
|||||||
|
|
||||||
<div class="space-y-1 text-xs text-gray-600">
|
<div class="space-y-1 text-xs text-gray-600">
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideSettings2 class="w-3 h-3" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>{{ machine.typeMachine?.name || 'Type inconnu' }}</span>
|
<span>{{ machine.typeMachine?.name || 'Type inconnu' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="machine.reference" class="flex items-center gap-1">
|
<div v-if="machine.reference" class="flex items-center gap-1">
|
||||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideTag class="w-3 h-3" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>{{ machine.reference }}</span>
|
<span>{{ machine.reference }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="machine.emplacement" class="flex items-center gap-1">
|
<div v-if="machine.emplacement" class="flex items-center gap-1">
|
||||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideBuilding class="w-3 h-3" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"></path>
|
|
||||||
</svg>
|
|
||||||
<span>{{ machine.emplacement }}</span>
|
<span>{{ machine.emplacement }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -201,9 +186,7 @@
|
|||||||
<!-- Empty Site State -->
|
<!-- Empty Site State -->
|
||||||
<div v-else-if="!collapsedSites.includes(site.id) && (!site.machines || site.machines.length === 0)" class="text-center py-6">
|
<div v-else-if="!collapsedSites.includes(site.id) && (!site.machines || site.machines.length === 0)" class="text-center py-6">
|
||||||
<div class="text-gray-400 mb-2">
|
<div class="text-gray-400 mb-2">
|
||||||
<svg class="w-8 h-8 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideFactory class="w-8 h-8 mx-auto" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="text-sm text-gray-500 mb-3">Aucune machine dans ce site</p>
|
<p class="text-sm text-gray-500 mb-3">Aucune machine dans ce site</p>
|
||||||
<button @click="addMachineToSite(site)" class="btn btn-sm btn-primary">
|
<button @click="addMachineToSite(site)" class="btn btn-sm btn-primary">
|
||||||
@@ -423,6 +406,15 @@ import { useSites } from '~/composables/useSites'
|
|||||||
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
||||||
import { useMachines } from '~/composables/useMachines'
|
import { useMachines } from '~/composables/useMachines'
|
||||||
import { useToast } from '~/composables/useToast'
|
import { useToast } from '~/composables/useToast'
|
||||||
|
import IconLucideFactory from '~icons/lucide/factory'
|
||||||
|
import IconLucideMapPin from '~icons/lucide/map-pin'
|
||||||
|
import IconLucideUser from '~icons/lucide/user'
|
||||||
|
import IconLucidePhone from '~icons/lucide/phone'
|
||||||
|
import IconLucideMapPinned from '~icons/lucide/map-pinned'
|
||||||
|
import IconLucideChevronDown from '~icons/lucide/chevron-down'
|
||||||
|
import IconLucideSettings2 from '~icons/lucide/settings-2'
|
||||||
|
import IconLucideTag from '~icons/lucide/tag'
|
||||||
|
import IconLucideBuilding from '~icons/lucide/building'
|
||||||
|
|
||||||
|
|
||||||
const { sites, loading, loadSites, createSite } = useSites()
|
const { sites, loading, loadSites, createSite } = useSites()
|
||||||
|
|||||||
@@ -19,331 +19,58 @@
|
|||||||
<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"
|
||||||
class="btn btn-primary"
|
class="btn btn-primary btn-sm"
|
||||||
:class="{ 'btn-outline': isEditMode }"
|
:class="{ 'btn-outline': isEditMode }"
|
||||||
>
|
>
|
||||||
<svg v-if="!isEditMode" class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideSquarePen
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
|
v-if="!isEditMode"
|
||||||
</svg>
|
class="w-4 h-4 mr-2"
|
||||||
<svg v-else class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
aria-hidden="true"
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
/>
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
|
<IconLucideCheck
|
||||||
</svg>
|
v-else
|
||||||
{{ isEditMode ? 'Voir détails' : 'Modifier' }}
|
class="w-4 h-4 mr-2"
|
||||||
</button>
|
aria-hidden="true"
|
||||||
<button
|
/>
|
||||||
v-if="!isEditMode"
|
<span class="text-sm">
|
||||||
@click="openPrintModal"
|
{{ isEditMode ? 'Quitter le mode édition' : 'Activer le mode édition' }}
|
||||||
type="button"
|
|
||||||
class="btn btn-outline btn-secondary"
|
|
||||||
>
|
|
||||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2V9a2 2 0 00-2-2h-2V4a1 1 0 00-1-1H8a1 1 0 00-1 1v3H5a2 2 0 00-2 2v6a2 2 0 002 2h2">
|
|
||||||
</path>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17H7v5h10v-5z"></path>
|
|
||||||
</svg>
|
|
||||||
Imprimer
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Debug info -->
|
|
||||||
<div v-if="debug" class="bg-yellow-100 p-4 rounded-lg">
|
|
||||||
<p>Debug: Machine trouvée - {{ machine.name }}</p>
|
|
||||||
<p>Components count: {{ components.length }}</p>
|
|
||||||
<p>Pieces count: {{ pieces.length }}</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Header -->
|
|
||||||
<PageHero
|
|
||||||
:title="machine.name"
|
|
||||||
:subtitle="machine.description || machine.typeMachine?.description"
|
|
||||||
min-height="min-h-[20vh]"
|
|
||||||
max-width="max-w-md"
|
|
||||||
rounded
|
|
||||||
>
|
|
||||||
<div class="flex justify-center gap-4">
|
|
||||||
<div class="badge badge-outline">{{ machine.typeMachine?.category || 'N/A' }}</div>
|
|
||||||
<div class="badge badge-outline">{{ machine.site?.name }}</div>
|
|
||||||
<div v-if="machine.reference" class="badge badge-outline">{{ machine.reference }}</div>
|
|
||||||
</div>
|
|
||||||
</PageHero>
|
|
||||||
|
|
||||||
<!-- Machine Info Card -->
|
|
||||||
<div class="card bg-base-100 shadow-lg">
|
|
||||||
<div class="card-body">
|
|
||||||
<h2 class="card-title">Informations de la machine</h2>
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Nom</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
v-if="isEditMode"
|
|
||||||
:id="getMachineFieldId('name')"
|
|
||||||
v-model="machineName"
|
|
||||||
type="text"
|
|
||||||
class="input input-bordered"
|
|
||||||
@blur="updateMachineInfo"
|
|
||||||
/>
|
|
||||||
<div v-else class="input input-bordered bg-base-200">
|
|
||||||
{{ machineName }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Référence</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
v-if="isEditMode"
|
|
||||||
:id="getMachineFieldId('reference')"
|
|
||||||
v-model="machineReference"
|
|
||||||
type="text"
|
|
||||||
class="input input-bordered"
|
|
||||||
@blur="updateMachineInfo"
|
|
||||||
/>
|
|
||||||
<div v-else class="input input-bordered bg-base-200">
|
|
||||||
{{ machineReference || 'Non définie' }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Emplacement</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
v-if="isEditMode"
|
|
||||||
:id="getMachineFieldId('emplacement')"
|
|
||||||
v-model="machineEmplacement"
|
|
||||||
type="text"
|
|
||||||
class="input input-bordered"
|
|
||||||
@blur="updateMachineInfo"
|
|
||||||
/>
|
|
||||||
<div v-else class="input input-bordered bg-base-200">
|
|
||||||
{{ machineEmplacement || 'Non défini' }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-control">
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text">Constructeur</span>
|
|
||||||
</label>
|
|
||||||
<ConstructeurSelect
|
|
||||||
v-if="isEditMode"
|
|
||||||
class="w-full"
|
|
||||||
:key="machine.value?.id"
|
|
||||||
:model-value="machineConstructeurId"
|
|
||||||
placeholder="Rechercher un constructeur..."
|
|
||||||
@update:modelValue="handleMachineConstructeurChange"
|
|
||||||
/>
|
|
||||||
<div v-else class="input input-bordered bg-base-200">
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<span class="font-medium">{{ machineConstructeurDisplay?.name || 'Non défini' }}</span>
|
|
||||||
<span class="text-xs text-gray-500">
|
|
||||||
{{ [machineConstructeurDisplay?.email, machineConstructeurDisplay?.phone].filter(Boolean).join(' • ') || '' }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Champs personnalisés de la machine -->
|
|
||||||
<div v-if="machine && machine.customFieldValues && machine.customFieldValues.length > 0" class="mt-6 pt-4 border-t border-gray-200">
|
|
||||||
<h4 class="font-semibold text-gray-700 mb-3">Champs personnalisés de la machine</h4>
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
||||||
<div
|
|
||||||
v-for="fieldValue in machine.customFieldValues"
|
|
||||||
:key="fieldValue.id"
|
|
||||||
class="form-control"
|
|
||||||
>
|
|
||||||
<label class="label">
|
|
||||||
<span class="label-text text-sm">{{ fieldValue.customField.name }}</span>
|
|
||||||
<span v-if="fieldValue.customField.required" class="label-text-alt text-error">*</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<!-- Mode édition -->
|
|
||||||
<template v-if="isEditMode">
|
|
||||||
<!-- Champ de type TEXT -->
|
|
||||||
<input
|
|
||||||
v-if="fieldValue.customField.type === 'text'"
|
|
||||||
:value="fieldValue.value"
|
|
||||||
@input="setMachineCustomFieldValue(fieldValue.id, $event.target.value)"
|
|
||||||
type="text"
|
|
||||||
:placeholder="fieldValue.customField.defaultValue || ''"
|
|
||||||
class="input input-bordered input-sm"
|
|
||||||
:required="fieldValue.customField.required"
|
|
||||||
@blur="updateMachineCustomField(fieldValue.id)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Champ de type NUMBER -->
|
|
||||||
<input
|
|
||||||
v-else-if="fieldValue.customField.type === 'number'"
|
|
||||||
:value="fieldValue.value"
|
|
||||||
@input="setMachineCustomFieldValue(fieldValue.id, $event.target.value)"
|
|
||||||
type="number"
|
|
||||||
:placeholder="fieldValue.customField.defaultValue || ''"
|
|
||||||
class="input input-bordered input-sm"
|
|
||||||
:required="fieldValue.customField.required"
|
|
||||||
@blur="updateMachineCustomField(fieldValue.id)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Champ de type SELECT -->
|
|
||||||
<select
|
|
||||||
v-else-if="fieldValue.customField.type === 'select'"
|
|
||||||
:value="fieldValue.value"
|
|
||||||
@change="setMachineCustomFieldValue(fieldValue.id, $event.target.value)"
|
|
||||||
class="select select-bordered select-sm"
|
|
||||||
:required="fieldValue.customField.required"
|
|
||||||
@blur="updateMachineCustomField(fieldValue.id)"
|
|
||||||
>
|
|
||||||
<option value="">{{ fieldValue.customField.defaultValue || 'Sélectionner...' }}</option>
|
|
||||||
<option
|
|
||||||
v-for="option in fieldValue.customField.options"
|
|
||||||
:key="option"
|
|
||||||
:value="option"
|
|
||||||
>
|
|
||||||
{{ option }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- Champ de type BOOLEAN -->
|
|
||||||
<div v-else-if="fieldValue.customField.type === 'boolean'" class="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
:value="fieldValue.value"
|
|
||||||
@change="setMachineCustomFieldValue(fieldValue.id, $event.target.checked ? 'true' : 'false')"
|
|
||||||
type="checkbox"
|
|
||||||
class="checkbox checkbox-sm"
|
|
||||||
:checked="fieldValue.value === 'true'"
|
|
||||||
@blur="updateMachineCustomField(fieldValue.id)"
|
|
||||||
/>
|
|
||||||
<span class="text-sm">{{ fieldValue.value === 'true' ? 'Oui' : 'Non' }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Champ de type DATE -->
|
|
||||||
<input
|
|
||||||
v-else-if="fieldValue.customField.type === 'date'"
|
|
||||||
:value="fieldValue.value"
|
|
||||||
@input="setMachineCustomFieldValue(fieldValue.id, $event.target.value)"
|
|
||||||
type="date"
|
|
||||||
:placeholder="fieldValue.customField.defaultValue || ''"
|
|
||||||
class="input input-bordered input-sm"
|
|
||||||
:required="fieldValue.customField.required"
|
|
||||||
@blur="updateMachineCustomField(fieldValue.id)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Mode lecture seule -->
|
|
||||||
<template v-else>
|
|
||||||
<div class="input input-bordered input-sm bg-base-200">
|
|
||||||
{{ fieldValue.value || fieldValue.customField.defaultValue || 'Non défini' }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card bg-base-100 shadow-lg mt-6">
|
|
||||||
<div class="card-body space-y-4">
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h2 class="card-title">Documents de la machine</h2>
|
|
||||||
<p class="text-xs text-gray-500">Ajoutez ou consultez les documents liés à cette machine.</p>
|
|
||||||
</div>
|
|
||||||
<span v-if="isEditMode && machineDocumentFiles.length" class="badge badge-outline">
|
|
||||||
{{ machineDocumentFiles.length }} fichier{{ machineDocumentFiles.length > 1 ? 's' : '' }} sélectionné{{ machineDocumentFiles.length > 1 ? 's' : '' }}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
<DocumentUpload
|
<button
|
||||||
v-if="isEditMode"
|
type="button"
|
||||||
v-model="machineDocumentFiles"
|
class="btn btn-ghost btn-sm"
|
||||||
title="Déposer des fichiers pour la machine"
|
@click="toggleAllComponents"
|
||||||
subtitle="Formats acceptés : PDF, images, documents..."
|
>
|
||||||
@files-added="handleMachineFilesAdded"
|
<IconLucideChevronDown
|
||||||
/>
|
class="w-4 h-4 mr-2 transform transition-transform"
|
||||||
|
:class="componentsCollapsed ? '-rotate-90' : 'rotate-0'"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<span class="text-sm">
|
||||||
|
{{ componentsCollapsed ? 'Tout déplier' : 'Tout replier' }}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<div v-if="machineDocumentsList.length" class="space-y-2">
|
<button
|
||||||
<div
|
type="button"
|
||||||
v-for="document in machineDocumentsList"
|
class="btn btn-outline btn-sm"
|
||||||
:key="document.id"
|
@click="openPrintModal"
|
||||||
class="flex items-center justify-between rounded border border-base-200 bg-base-100 px-3 py-2"
|
>
|
||||||
>
|
<IconLucidePrinter class="w-4 h-4 mr-2" aria-hidden="true" />
|
||||||
<div class="flex items-center gap-3 text-sm">
|
<span class="text-sm">Imprimer</span>
|
||||||
<span class="text-xl" :class="documentIcon(document).colorClass">
|
</button>
|
||||||
{{ documentIcon(document).icon }}
|
|
||||||
</span>
|
|
||||||
<div>
|
|
||||||
<div class="font-medium">{{ document.name }}</div>
|
|
||||||
<div class="text-xs text-gray-500">
|
|
||||||
{{ document.mimeType || 'Inconnu' }} • {{ formatSize(document.size) }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-ghost btn-xs"
|
|
||||||
:disabled="!canPreviewDocument(document)"
|
|
||||||
:title="canPreviewDocument(document) ? 'Consulter le document' : 'Aucun aperçu disponible pour ce type'"
|
|
||||||
@click="openPreview(document)"
|
|
||||||
>
|
|
||||||
Consulter
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-ghost btn-xs" @click="downloadDocument(document)">
|
|
||||||
Télécharger
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
v-if="isEditMode"
|
|
||||||
type="button"
|
|
||||||
class="btn btn-error btn-xs"
|
|
||||||
:disabled="machineDocumentsUploading"
|
|
||||||
@click="removeMachineDocument(document.id)"
|
|
||||||
>
|
|
||||||
Supprimer
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p v-else class="text-xs text-gray-500">Aucun document lié à cette machine.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Components Section -->
|
<!-- Components Tree -->
|
||||||
<div class="card bg-base-100 shadow-lg">
|
<ComponentHierarchy
|
||||||
<div class="card-body">
|
:components="components"
|
||||||
<div class="flex justify-between items-center mb-4">
|
:is-edit-mode="isEditMode"
|
||||||
<h2 class="card-title">Composants</h2>
|
:collapse-all="componentsCollapsed"
|
||||||
<button
|
:toggle-token="collapseToggleToken"
|
||||||
type="button"
|
@update="updateComponent"
|
||||||
class="btn btn-ghost btn-sm gap-2"
|
@edit-piece="updatePieceFromComponent"
|
||||||
@click="toggleAllComponents"
|
/>
|
||||||
:title="componentsCollapsed ? 'Déplier tous les composants' : 'Replier tous les composants'"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
class="w-5 h-5 transition-transform"
|
|
||||||
:class="componentsCollapsed ? 'rotate-0' : 'rotate-90'"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
||||||
</svg>
|
|
||||||
<span class="text-sm">
|
|
||||||
{{ componentsCollapsed ? 'Tout déplier' : 'Tout replier' }}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Components Tree -->
|
|
||||||
<ComponentHierarchy
|
|
||||||
:components="components"
|
|
||||||
:is-edit-mode="isEditMode"
|
|
||||||
:collapse-all="componentsCollapsed"
|
|
||||||
:toggle-token="collapseToggleToken"
|
|
||||||
@update="updateComponent"
|
|
||||||
@edit-piece="updatePieceFromComponent"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Machine Pieces Section -->
|
<!-- Machine Pieces Section -->
|
||||||
@@ -371,9 +98,7 @@
|
|||||||
<!-- Error State -->
|
<!-- Error State -->
|
||||||
<div v-else class="text-center py-12">
|
<div v-else class="text-center py-12">
|
||||||
<div class="max-w-md mx-auto">
|
<div class="max-w-md mx-auto">
|
||||||
<svg class="w-16 h-16 mx-auto text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideAlertTriangle class="w-16 h-16 mx-auto text-gray-400 mb-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6-4h6m2 5.291A7.962 7.962 0 0112 15c-2.34 0-4.47-.881-6.08-2.33"></path>
|
|
||||||
</svg>
|
|
||||||
<h3 class="text-lg font-medium text-gray-900 mb-2">Machine non trouvée</h3>
|
<h3 class="text-lg font-medium text-gray-900 mb-2">Machine non trouvée</h3>
|
||||||
<p class="text-gray-500 mb-4">La machine avec l'ID "{{ machineId }}" n'existe pas ou a été supprimée.</p>
|
<p class="text-gray-500 mb-4">La machine avec l'ID "{{ machineId }}" n'existe pas ou a été supprimée.</p>
|
||||||
<NuxtLink to="/machines" class="btn btn-primary">
|
<NuxtLink to="/machines" class="btn btn-primary">
|
||||||
@@ -415,6 +140,11 @@ import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
|||||||
import PageHero from '~/components/PageHero.vue'
|
import PageHero from '~/components/PageHero.vue'
|
||||||
import MachinePrintSelectionModal from '~/components/MachinePrintSelectionModal.vue'
|
import MachinePrintSelectionModal from '~/components/MachinePrintSelectionModal.vue'
|
||||||
import { buildMachinePrintContext, buildMachinePrintHtml } from '~/utils/printTemplates/machineReport'
|
import { buildMachinePrintContext, buildMachinePrintHtml } from '~/utils/printTemplates/machineReport'
|
||||||
|
import IconLucideAlertTriangle from '~icons/lucide/alert-triangle'
|
||||||
|
import IconLucideSquarePen from '~icons/lucide/square-pen'
|
||||||
|
import IconLucideCheck from '~icons/lucide/check'
|
||||||
|
import IconLucideChevronDown from '~icons/lucide/chevron-down'
|
||||||
|
import IconLucidePrinter from '~icons/lucide/printer'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const machineId = route.params.id
|
const machineId = route.params.id
|
||||||
|
|||||||
@@ -8,9 +8,7 @@
|
|||||||
<div class="flex justify-between items-center mb-6">
|
<div class="flex justify-between items-center mb-6">
|
||||||
<h2 class="text-2xl font-bold">Machines</h2>
|
<h2 class="text-2xl font-bold">Machines</h2>
|
||||||
<button @click="showAddMachineModal = true" class="btn btn-primary">
|
<button @click="showAddMachineModal = true" class="btn btn-primary">
|
||||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePlus class="w-5 h-5 mr-2" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
|
||||||
</svg>
|
|
||||||
Ajouter une machine
|
Ajouter une machine
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,9 +61,7 @@
|
|||||||
|
|
||||||
<div v-else-if="filteredMachines.length === 0" class="text-center py-12">
|
<div v-else-if="filteredMachines.length === 0" class="text-center py-12">
|
||||||
<div class="max-w-md mx-auto">
|
<div class="max-w-md mx-auto">
|
||||||
<svg class="w-16 h-16 mx-auto text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideFactory class="w-16 h-16 mx-auto text-gray-400 mb-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"></path>
|
|
||||||
</svg>
|
|
||||||
<h3 class="text-lg font-medium text-gray-900 mb-2">Aucune machine trouvée</h3>
|
<h3 class="text-lg font-medium text-gray-900 mb-2">Aucune machine trouvée</h3>
|
||||||
<p class="text-gray-500 mb-4">Commencez par ajouter votre première machine.</p>
|
<p class="text-gray-500 mb-4">Commencez par ajouter votre première machine.</p>
|
||||||
<button @click="showAddMachineModal = true" class="btn btn-primary">
|
<button @click="showAddMachineModal = true" class="btn btn-primary">
|
||||||
@@ -91,31 +87,22 @@
|
|||||||
|
|
||||||
<div class="space-y-2 text-sm">
|
<div class="space-y-2 text-sm">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<svg class="w-4 h-4 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideMapPin class="w-4 h-4 text-blue-500" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
||||||
</svg>
|
|
||||||
<span class="text-gray-600">{{ machine.site?.name || 'Site inconnu' }}</span>
|
<span class="text-gray-600">{{ machine.site?.name || 'Site inconnu' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<svg class="w-4 h-4 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideSettings2 class="w-4 h-4 text-green-500" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"></path>
|
|
||||||
</svg>
|
|
||||||
<span class="text-gray-600">{{ machine.typeMachine?.name || 'Type inconnu' }}</span>
|
<span class="text-gray-600">{{ machine.typeMachine?.name || 'Type inconnu' }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="machine.reference" class="flex items-center gap-2">
|
<div v-if="machine.reference" class="flex items-center gap-2">
|
||||||
<svg class="w-4 h-4 text-orange-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideTag class="w-4 h-4 text-orange-500" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
|
||||||
</svg>
|
|
||||||
<span class="text-gray-600">{{ machine.reference }}</span>
|
<span class="text-gray-600">{{ machine.reference }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="machine.emplacement" class="flex items-center gap-2">
|
<div v-if="machine.emplacement" class="flex items-center gap-2">
|
||||||
<svg class="w-4 h-4 text-purple-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideBuilding class="w-4 h-4 text-purple-500" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"></path>
|
|
||||||
</svg>
|
|
||||||
<span class="text-gray-600">{{ machine.emplacement }}</span>
|
<span class="text-gray-600">{{ machine.emplacement }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -245,6 +232,12 @@ import { useMachines } from '~/composables/useMachines'
|
|||||||
import { useSites } from '~/composables/useSites'
|
import { useSites } from '~/composables/useSites'
|
||||||
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
||||||
import { useToast } from '~/composables/useToast'
|
import { useToast } from '~/composables/useToast'
|
||||||
|
import IconLucidePlus from '~icons/lucide/plus'
|
||||||
|
import IconLucideFactory from '~icons/lucide/factory'
|
||||||
|
import IconLucideMapPin from '~icons/lucide/map-pin'
|
||||||
|
import IconLucideSettings2 from '~icons/lucide/settings-2'
|
||||||
|
import IconLucideTag from '~icons/lucide/tag'
|
||||||
|
import IconLucideBuilding from '~icons/lucide/building'
|
||||||
|
|
||||||
const { machines, loading, loadMachines, createMachineFromType, deleteMachine } = useMachines()
|
const { machines, loading, loadMachines, createMachineFromType, deleteMachine } = useMachines()
|
||||||
const { sites, loadSites } = useSites()
|
const { sites, loadSites } = useSites()
|
||||||
|
|||||||
@@ -12,9 +12,7 @@
|
|||||||
<div class="flex justify-between items-center mb-6">
|
<div class="flex justify-between items-center mb-6">
|
||||||
<h2 class="text-2xl font-bold">Sites</h2>
|
<h2 class="text-2xl font-bold">Sites</h2>
|
||||||
<button @click="showAddSiteModal = true" class="btn btn-primary">
|
<button @click="showAddSiteModal = true" class="btn btn-primary">
|
||||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePlus class="w-5 h-5 mr-2" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
|
||||||
</svg>
|
|
||||||
Ajouter un site
|
Ajouter un site
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -26,10 +24,7 @@
|
|||||||
|
|
||||||
<div v-else-if="sites.length === 0" class="text-center py-12">
|
<div v-else-if="sites.length === 0" class="text-center py-12">
|
||||||
<div class="max-w-md mx-auto">
|
<div class="max-w-md mx-auto">
|
||||||
<svg class="w-16 h-16 mx-auto text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideMapPin class="w-16 h-16 mx-auto text-gray-400 mb-4" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"></path>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
||||||
</svg>
|
|
||||||
<h3 class="text-lg font-medium text-gray-900 mb-2">Aucun site trouvé</h3>
|
<h3 class="text-lg font-medium text-gray-900 mb-2">Aucun site trouvé</h3>
|
||||||
<p class="text-gray-500 mb-4">Commencez par ajouter votre premier site.</p>
|
<p class="text-gray-500 mb-4">Commencez par ajouter votre premier site.</p>
|
||||||
<button @click="showAddSiteModal = true" class="btn btn-primary">
|
<button @click="showAddSiteModal = true" class="btn btn-primary">
|
||||||
@@ -52,23 +47,17 @@
|
|||||||
|
|
||||||
<div class="space-y-3 text-sm">
|
<div class="space-y-3 text-sm">
|
||||||
<div class="flex items-center gap-2 text-gray-700">
|
<div class="flex items-center gap-2 text-gray-700">
|
||||||
<svg class="w-4 h-4 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideUser class="w-4 h-4 text-primary" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5.121 17.804A13.937 13.937 0 0112 15c2.5 0 4.847.655 6.879 1.804M15 10a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
||||||
</svg>
|
|
||||||
<span class="font-medium">{{ site.contactName }}</span>
|
<span class="font-medium">{{ site.contactName }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-2 text-gray-600">
|
<div class="flex items-center gap-2 text-gray-600">
|
||||||
<svg class="w-4 h-4 text-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePhone class="w-4 h-4 text-secondary" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5a2 2 0 012-2h3l2 3h6l2-3h3a2 2 0 012 2v13a2 2 0 01-2 2H5a2 2 0 01-2-2V5z" />
|
|
||||||
</svg>
|
|
||||||
<span>{{ site.contactPhone }}</span>
|
<span>{{ site.contactPhone }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-start gap-2 text-gray-600">
|
<div class="flex items-start gap-2 text-gray-600">
|
||||||
<svg class="w-4 h-4 text-accent mt-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideMapPin class="w-4 h-4 text-accent mt-1" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 11c1.38 0 2.5-1.12 2.5-2.5S13.38 6 12 6s-2.5 1.12-2.5 2.5S10.62 11 12 11zm0 0c-2.21 0-4 1.343-4 3v1.5h8V14c0-1.657-1.79-3-4-3z" />
|
|
||||||
</svg>
|
|
||||||
<span>
|
<span>
|
||||||
{{ site.contactAddress }}<br />
|
{{ site.contactAddress }}<br />
|
||||||
{{ site.contactPostalCode }} {{ site.contactCity }}
|
{{ site.contactPostalCode }} {{ site.contactCity }}
|
||||||
@@ -76,9 +65,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-2 text-gray-600">
|
<div class="flex items-center gap-2 text-gray-600">
|
||||||
<svg class="w-4 h-4 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideFactory class="w-4 h-4 text-blue-500" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"></path>
|
|
||||||
</svg>
|
|
||||||
<span>{{ site.machines?.length || 0 }} machine(s)</span>
|
<span>{{ site.machines?.length || 0 }} machine(s)</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -312,7 +299,11 @@
|
|||||||
>
|
>
|
||||||
<div class="flex items-center gap-3 text-sm">
|
<div class="flex items-center gap-3 text-sm">
|
||||||
<span class="text-xl" :class="documentIcon(document).colorClass">
|
<span class="text-xl" :class="documentIcon(document).colorClass">
|
||||||
{{ documentIcon(document).icon }}
|
<component
|
||||||
|
:is="documentIcon(document).component"
|
||||||
|
class="h-6 w-6"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
<div class="font-medium">{{ document.name }}</div>
|
<div class="font-medium">{{ document.name }}</div>
|
||||||
@@ -371,6 +362,11 @@ import { getFileIcon } from '~/utils/fileIcons'
|
|||||||
import { canPreviewDocument } from '~/utils/documentPreview'
|
import { canPreviewDocument } from '~/utils/documentPreview'
|
||||||
import DocumentUpload from '~/components/DocumentUpload.vue'
|
import DocumentUpload from '~/components/DocumentUpload.vue'
|
||||||
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
import DocumentPreviewModal from '~/components/DocumentPreviewModal.vue'
|
||||||
|
import IconLucidePlus from '~icons/lucide/plus'
|
||||||
|
import IconLucideMapPin from '~icons/lucide/map-pin'
|
||||||
|
import IconLucideUser from '~icons/lucide/user'
|
||||||
|
import IconLucidePhone from '~icons/lucide/phone'
|
||||||
|
import IconLucideFactory from '~icons/lucide/factory'
|
||||||
|
|
||||||
const { sites, loading, loadSites, createSite, updateSite, deleteSite } = useSites()
|
const { sites, loading, loadSites, createSite, updateSite, deleteSite } = useSites()
|
||||||
const { uploadDocuments, deleteDocument, loadDocumentsBySite } = useDocuments()
|
const { uploadDocuments, deleteDocument, loadDocumentsBySite } = useDocuments()
|
||||||
|
|||||||
@@ -17,9 +17,7 @@
|
|||||||
</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">
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideSquarePen class="w-4 h-4 mr-2" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"></path>
|
|
||||||
</svg>
|
|
||||||
Éditer complètement
|
Éditer complètement
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink to="/types" class="btn btn-outline">
|
<NuxtLink to="/types" class="btn btn-outline">
|
||||||
@@ -37,27 +35,16 @@
|
|||||||
class="btn btn-outline btn-sm"
|
class="btn btn-outline btn-sm"
|
||||||
@click="toggleGlobalExpand"
|
@click="toggleGlobalExpand"
|
||||||
>
|
>
|
||||||
<svg
|
<IconLucideMinus
|
||||||
|
v-if="globalExpandState.expanded"
|
||||||
class="w-4 h-4 mr-2"
|
class="w-4 h-4 mr-2"
|
||||||
fill="none"
|
aria-hidden="true"
|
||||||
stroke="currentColor"
|
/>
|
||||||
viewBox="0 0 24 24"
|
<IconLucidePlus
|
||||||
>
|
v-else
|
||||||
<path
|
class="w-4 h-4 mr-2"
|
||||||
v-if="globalExpandState.expanded"
|
aria-hidden="true"
|
||||||
stroke-linecap="round"
|
/>
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M18 12H6"
|
|
||||||
></path>
|
|
||||||
<path
|
|
||||||
v-else
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M12 6v12m6-6H6"
|
|
||||||
></path>
|
|
||||||
</svg>
|
|
||||||
{{ globalExpandState.expanded ? 'Tout plier' : 'Tout déplier' }}
|
{{ globalExpandState.expanded ? 'Tout plier' : 'Tout déplier' }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -111,6 +98,9 @@ import { ref, reactive, 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'
|
||||||
|
import IconLucideSquarePen from '~icons/lucide/square-pen'
|
||||||
|
import IconLucideMinus from '~icons/lucide/minus'
|
||||||
|
import IconLucidePlus from '~icons/lucide/plus'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { getMachineTypeById } = useMachineTypesApi()
|
const { getMachineTypeById } = useMachineTypesApi()
|
||||||
|
|||||||
@@ -8,9 +8,10 @@
|
|||||||
<div class="flex justify-between items-center mb-6">
|
<div class="flex justify-between items-center mb-6">
|
||||||
<h2 class="text-2xl font-bold text-gray-800">Types de Machines</h2>
|
<h2 class="text-2xl font-bold text-gray-800">Types de Machines</h2>
|
||||||
<NuxtLink to="/generator" class="btn btn-primary">
|
<NuxtLink to="/generator" class="btn btn-primary">
|
||||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePlus
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
|
class="w-5 h-5 mr-2"
|
||||||
</svg>
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
Créer un type
|
Créer un type
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
@@ -43,21 +44,10 @@
|
|||||||
<p class="text-gray-600 mb-4">{{ type.description }}</p>
|
<p class="text-gray-600 mb-4">{{ type.description }}</p>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<div class="flex items-center text-sm text-gray-500">
|
<div class="flex items-center text-sm text-gray-500">
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucidePackage
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
class="w-4 h-4 mr-2"
|
||||||
</svg>
|
aria-hidden="true"
|
||||||
Maintenance: {{ type.maintenanceFrequency }}
|
/>
|
||||||
</div>
|
|
||||||
<div class="flex items-center text-sm text-gray-500">
|
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"></path>
|
|
||||||
</svg>
|
|
||||||
{{ type.components?.length || 0 }} composants principaux
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center text-sm text-gray-500">
|
|
||||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path>
|
|
||||||
</svg>
|
|
||||||
{{ type.machinePieces?.length || 0 }} pièces totales
|
{{ type.machinePieces?.length || 0 }} pièces totales
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,11 +64,9 @@
|
|||||||
|
|
||||||
<!-- Empty State -->
|
<!-- Empty State -->
|
||||||
<div v-if="filteredTypes.length === 0" class="text-center py-12">
|
<div v-if="filteredTypes.length === 0" class="text-center py-12">
|
||||||
<div class="avatar placeholder">
|
<div class="avatar placeholder">
|
||||||
<div class="bg-neutral text-neutral-content rounded-full w-16">
|
<div class="bg-neutral text-neutral-content rounded-full w-16">
|
||||||
<svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<IconLucideLayoutGrid class="w-8 h-8" aria-hidden="true" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="text-lg font-semibold text-gray-600 mt-4">Aucun type trouvé</h3>
|
<h3 class="text-lg font-semibold text-gray-600 mt-4">Aucun type trouvé</h3>
|
||||||
@@ -92,6 +80,9 @@
|
|||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
||||||
import { useToast } from '~/composables/useToast'
|
import { useToast } from '~/composables/useToast'
|
||||||
|
import IconLucidePlus from '~icons/lucide/plus'
|
||||||
|
import IconLucidePackage from '~icons/lucide/package'
|
||||||
|
import IconLucideLayoutGrid from '~icons/lucide/layout-grid'
|
||||||
|
|
||||||
const { machineTypes, loading, loadMachineTypes, deleteMachineType } = useMachineTypesApi()
|
const { machineTypes, loading, loadMachineTypes, deleteMachineType } = useMachineTypesApi()
|
||||||
|
|
||||||
|
|||||||
@@ -1,74 +1,85 @@
|
|||||||
|
import IconFile from '~icons/lucide/file'
|
||||||
|
import IconFileType from '~icons/lucide/file-type'
|
||||||
|
import IconFileText from '~icons/lucide/file-text'
|
||||||
|
import IconFileSpreadsheet from '~icons/lucide/file-spreadsheet'
|
||||||
|
import IconPresentation from '~icons/lucide/presentation'
|
||||||
|
import IconImage from '~icons/lucide/image'
|
||||||
|
import IconArchive from '~icons/lucide/archive'
|
||||||
|
import IconFileAudio2 from '~icons/lucide/file-audio-2'
|
||||||
|
import IconFileVideo2 from '~icons/lucide/file-video-2'
|
||||||
|
import IconFileCode from '~icons/lucide/file-code'
|
||||||
|
|
||||||
const iconMap = [
|
const iconMap = [
|
||||||
{
|
{
|
||||||
label: 'PDF',
|
label: 'PDF',
|
||||||
exts: ['pdf'],
|
exts: ['pdf'],
|
||||||
icon: '📕',
|
component: IconFileType,
|
||||||
colorClass: 'text-red-500'
|
colorClass: 'text-red-500'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Word',
|
label: 'Word',
|
||||||
exts: ['doc', 'docx'],
|
exts: ['doc', 'docx'],
|
||||||
icon: '📝',
|
component: IconFileText,
|
||||||
colorClass: 'text-blue-500'
|
colorClass: 'text-blue-500'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Excel',
|
label: 'Excel',
|
||||||
exts: ['xls', 'xlsx', 'csv'],
|
exts: ['xls', 'xlsx', 'csv'],
|
||||||
icon: '📊',
|
component: IconFileSpreadsheet,
|
||||||
colorClass: 'text-green-500'
|
colorClass: 'text-green-500'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'PowerPoint',
|
label: 'PowerPoint',
|
||||||
exts: ['ppt', 'pptx'],
|
exts: ['ppt', 'pptx'],
|
||||||
icon: '📈',
|
component: IconPresentation,
|
||||||
colorClass: 'text-orange-500'
|
colorClass: 'text-orange-500'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Image',
|
label: 'Image',
|
||||||
exts: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'heic'],
|
exts: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'heic'],
|
||||||
icon: '🖼️',
|
component: IconImage,
|
||||||
colorClass: 'text-purple-500'
|
colorClass: 'text-purple-500'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Archive',
|
label: 'Archive',
|
||||||
exts: ['zip', 'rar', '7z', 'tar', 'gz'],
|
exts: ['zip', 'rar', '7z', 'tar', 'gz'],
|
||||||
icon: '🗜️',
|
component: IconArchive,
|
||||||
colorClass: 'text-amber-500'
|
colorClass: 'text-amber-500'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Audio',
|
label: 'Audio',
|
||||||
exts: ['mp3', 'wav', 'ogg', 'flac', 'aac'],
|
exts: ['mp3', 'wav', 'ogg', 'flac', 'aac'],
|
||||||
icon: '🎵',
|
component: IconFileAudio2,
|
||||||
colorClass: 'text-pink-500'
|
colorClass: 'text-pink-500'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Vidéo',
|
label: 'Vidéo',
|
||||||
exts: ['mp4', 'mov', 'avi', 'mkv', 'webm'],
|
exts: ['mp4', 'mov', 'avi', 'mkv', 'webm'],
|
||||||
icon: '🎬',
|
component: IconFileVideo2,
|
||||||
colorClass: 'text-indigo-500'
|
colorClass: 'text-indigo-500'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Texte',
|
label: 'Texte',
|
||||||
exts: ['txt', 'md', 'rtf'],
|
exts: ['txt', 'md', 'rtf'],
|
||||||
icon: '📄',
|
component: IconFileText,
|
||||||
colorClass: 'text-gray-500'
|
colorClass: 'text-gray-500'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Code',
|
label: 'Code',
|
||||||
exts: ['json', 'xml', 'yml', 'yaml', 'js', 'ts', 'py', 'java', 'cs'],
|
exts: ['json', 'xml', 'yml', 'yaml', 'js', 'ts', 'py', 'java', 'cs'],
|
||||||
icon: '💻',
|
component: IconFileCode,
|
||||||
colorClass: 'text-sky-500'
|
colorClass: 'text-sky-500'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const mimeGroups = [
|
const mimeGroups = [
|
||||||
{ prefix: 'image/', icon: '🖼️', colorClass: 'text-purple-500' },
|
{ prefix: 'image/', component: IconImage, colorClass: 'text-purple-500', label: 'Image' },
|
||||||
{ prefix: 'video/', icon: '🎬', colorClass: 'text-indigo-500' },
|
{ prefix: 'video/', component: IconFileVideo2, colorClass: 'text-indigo-500', label: 'Vidéo' },
|
||||||
{ prefix: 'audio/', icon: '🎵', colorClass: 'text-pink-500' },
|
{ prefix: 'audio/', component: IconFileAudio2, colorClass: 'text-pink-500', label: 'Audio' },
|
||||||
{ prefix: 'text/', icon: '📄', colorClass: 'text-gray-500' },
|
{ prefix: 'text/', component: IconFileText, colorClass: 'text-gray-500', label: 'Texte' },
|
||||||
{ prefix: 'application/pdf', icon: '📕', colorClass: 'text-red-500' },
|
{ prefix: 'application/pdf', component: IconFileType, colorClass: 'text-red-500', label: 'PDF' },
|
||||||
{ prefix: 'application/zip', icon: '🗜️', colorClass: 'text-amber-500' },
|
{ prefix: 'application/zip', component: IconArchive, colorClass: 'text-amber-500', label: 'Archive' },
|
||||||
{ prefix: 'application/x-', icon: '🗜️', colorClass: 'text-amber-500' }
|
{ prefix: 'application/x-', component: IconArchive, colorClass: 'text-amber-500', label: 'Archive' }
|
||||||
]
|
]
|
||||||
|
|
||||||
export const getFileIcon = ({ name = '', mime = '' } = {}) => {
|
export const getFileIcon = ({ name = '', mime = '' } = {}) => {
|
||||||
@@ -78,7 +89,7 @@ export const getFileIcon = ({ name = '', mime = '' } = {}) => {
|
|||||||
const match = iconMap.find(entry => entry.exts.includes(extension))
|
const match = iconMap.find(entry => entry.exts.includes(extension))
|
||||||
if (match) {
|
if (match) {
|
||||||
return {
|
return {
|
||||||
icon: match.icon,
|
component: match.component,
|
||||||
colorClass: match.colorClass,
|
colorClass: match.colorClass,
|
||||||
label: match.label
|
label: match.label
|
||||||
}
|
}
|
||||||
@@ -89,15 +100,15 @@ export const getFileIcon = ({ name = '', mime = '' } = {}) => {
|
|||||||
const match = mimeGroups.find(entry => mime.startsWith(entry.prefix))
|
const match = mimeGroups.find(entry => mime.startsWith(entry.prefix))
|
||||||
if (match) {
|
if (match) {
|
||||||
return {
|
return {
|
||||||
icon: match.icon,
|
component: match.component,
|
||||||
colorClass: match.colorClass,
|
colorClass: match.colorClass,
|
||||||
label: mime
|
label: match.label || mime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
icon: '📁',
|
component: IconFile,
|
||||||
colorClass: 'text-primary',
|
colorClass: 'text-primary',
|
||||||
label: 'Document'
|
label: 'Document'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,18 @@ export default defineNuxtConfig({
|
|||||||
devServer: {
|
devServer: {
|
||||||
port: 3001,
|
port: 3001,
|
||||||
},
|
},
|
||||||
|
modules: [
|
||||||
|
[
|
||||||
|
'unplugin-icons/nuxt',
|
||||||
|
{
|
||||||
|
componentPrefix: 'Icon',
|
||||||
|
warn: process.env.NODE_ENV === 'development',
|
||||||
|
collections: {
|
||||||
|
lucide: () => import('@iconify-json/lucide/icons.json').then((i) => i.default),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
public: {
|
public: {
|
||||||
apiBaseUrl: process.env.NUXT_PUBLIC_API_BASE_URL || 'http://localhost:3000/api',
|
apiBaseUrl: process.env.NUXT_PUBLIC_API_BASE_URL || 'http://localhost:3000/api',
|
||||||
|
|||||||
@@ -20,5 +20,7 @@
|
|||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@iconify-json/lucide": "^1.2.68",
|
||||||
|
"unplugin-icons": "^0.19.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user