Rename dashboard

This commit is contained in:
Matthieu
2025-09-25 16:22:14 +02:00
parent d1ce074c6d
commit b7caa4f552
2 changed files with 580 additions and 298 deletions

View File

@@ -7,7 +7,10 @@
<div tabindex="0" role="button" class="btn btn-ghost lg:hidden"> <div tabindex="0" role="button" class="btn btn-ghost lg:hidden">
<IconLucideMenu class="w-5 h-5" aria-hidden="true" /> <IconLucideMenu class="w-5 h-5" aria-hidden="true" />
</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">
<button <button
@click="openDisplaySettings" @click="openDisplaySettings"
@@ -21,16 +24,24 @@
<NuxtLink <NuxtLink
to="/" to="/"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/') ? 'bg-primary text-primary-content font-semibold shadow-sm' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/')
? 'bg-primary text-primary-content font-semibold shadow-sm'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Dashboard Vue d'ensemble
</NuxtLink> </NuxtLink>
</li> </li>
<li> <li>
<NuxtLink <NuxtLink
to="/machines" to="/machines"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/machines') ? 'bg-primary text-primary-content font-semibold shadow-sm' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/machines')
? 'bg-primary text-primary-content font-semibold shadow-sm'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Parc Machines Parc Machines
</NuxtLink> </NuxtLink>
@@ -39,7 +50,11 @@
<NuxtLink <NuxtLink
to="/machine-skeleton" to="/machine-skeleton"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/machine-skeleton') ? 'bg-primary text-primary-content font-semibold shadow-sm' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/machine-skeleton')
? 'bg-primary text-primary-content font-semibold shadow-sm'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Squelettes de machine Squelettes de machine
</NuxtLink> </NuxtLink>
@@ -56,16 +71,27 @@
tabindex="0" tabindex="0"
role="button" role="button"
class="rounded-md px-2 py-1 transition-colors cursor-pointer" class="rounded-md px-2 py-1 transition-colors cursor-pointer"
:class="(isActive('/pieces-catalog') || isActive('/piece-category')) ? 'bg-primary text-primary-content font-semibold shadow-sm' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/pieces-catalog') || isActive('/piece-category')
? 'bg-primary text-primary-content font-semibold shadow-sm'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Pièces Pièces
</div> </div>
<ul tabindex="0" class="dropdown-content z-[1] menu menu-sm bg-base-100 rounded-box p-2 shadow space-y-1"> <ul
tabindex="0"
class="dropdown-content z-[1] menu menu-sm bg-base-100 rounded-box p-2 shadow space-y-1"
>
<li> <li>
<NuxtLink <NuxtLink
to="/piece-category" to="/piece-category"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/piece-category') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/piece-category')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Catégorie de pièce Catégorie de pièce
</NuxtLink> </NuxtLink>
@@ -74,7 +100,11 @@
<NuxtLink <NuxtLink
to="/pieces-catalog" to="/pieces-catalog"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/pieces-catalog') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/pieces-catalog')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Catalogue de pièce Catalogue de pièce
</NuxtLink> </NuxtLink>
@@ -93,16 +123,28 @@
tabindex="0" tabindex="0"
role="button" role="button"
class="rounded-md px-2 py-1 transition-colors cursor-pointer" class="rounded-md px-2 py-1 transition-colors cursor-pointer"
:class="(isActive('/component-catalog') || isActive('/component-category')) ? 'bg-primary text-primary-content font-semibold shadow-sm' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/component-catalog') ||
isActive('/component-category')
? 'bg-primary text-primary-content font-semibold shadow-sm'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Composant Composant
</div> </div>
<ul tabindex="0" class="dropdown-content z-[1] menu menu-sm bg-base-100 rounded-box p-2 shadow space-y-1"> <ul
tabindex="0"
class="dropdown-content z-[1] menu menu-sm bg-base-100 rounded-box p-2 shadow space-y-1"
>
<li> <li>
<NuxtLink <NuxtLink
to="/component-category" to="/component-category"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/component-category') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/component-category')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Catégorie de composant Catégorie de composant
</NuxtLink> </NuxtLink>
@@ -111,7 +153,11 @@
<NuxtLink <NuxtLink
to="/component-catalog" to="/component-catalog"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/component-catalog') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/component-catalog')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Catalogue de composant Catalogue de composant
</NuxtLink> </NuxtLink>
@@ -130,16 +176,29 @@
tabindex="0" tabindex="0"
role="button" role="button"
class="rounded-md px-2 py-1 transition-colors cursor-pointer" class="rounded-md px-2 py-1 transition-colors cursor-pointer"
:class="(isActive('/sites') || isActive('/documents') || isActive('/constructeurs')) ? 'bg-primary text-primary-content font-semibold shadow-sm' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/sites') ||
isActive('/documents') ||
isActive('/constructeurs')
? 'bg-primary text-primary-content font-semibold shadow-sm'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Ressources liées Ressources liées
</div> </div>
<ul tabindex="0" class="dropdown-content z-[1] menu menu-sm bg-base-100 rounded-box p-2 shadow space-y-1"> <ul
tabindex="0"
class="dropdown-content z-[1] menu menu-sm bg-base-100 rounded-box p-2 shadow space-y-1"
>
<li> <li>
<NuxtLink <NuxtLink
to="/sites" to="/sites"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/sites') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/sites')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Sites Sites
</NuxtLink> </NuxtLink>
@@ -148,7 +207,11 @@
<NuxtLink <NuxtLink
to="/documents" to="/documents"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/documents') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/documents')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Documents Documents
</NuxtLink> </NuxtLink>
@@ -157,7 +220,11 @@
<NuxtLink <NuxtLink
to="/constructeurs" to="/constructeurs"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/constructeurs') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/constructeurs')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Constructeurs Constructeurs
</NuxtLink> </NuxtLink>
@@ -168,11 +235,15 @@
</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 grid place-items-center"> <div
class="bg-primary text-primary-content rounded-lg w-10 grid place-items-center"
>
<IconLucideBoxes class="w-6 h-6" aria-hidden="true" /> <IconLucideBoxes class="w-6 h-6" aria-hidden="true" />
</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
>
</div> </div>
</div> </div>
<div class="navbar-center hidden lg:flex"> <div class="navbar-center hidden lg:flex">
@@ -181,16 +252,24 @@
<NuxtLink <NuxtLink
to="/" to="/"
class="transition-colors px-3 py-2 rounded-md" class="transition-colors px-3 py-2 rounded-md"
:class="isActive('/') ? 'bg-primary text-primary-content font-semibold shadow-sm' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/')
? 'bg-primary text-primary-content font-semibold shadow-sm'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Dashboard Vue d'ensemble
</NuxtLink> </NuxtLink>
</li> </li>
<li> <li>
<NuxtLink <NuxtLink
to="/machines" to="/machines"
class="transition-colors px-3 py-2 rounded-md" class="transition-colors px-3 py-2 rounded-md"
:class="isActive('/machines') ? 'bg-primary text-primary-content font-semibold shadow-sm' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/machines')
? 'bg-primary text-primary-content font-semibold shadow-sm'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Parc Machines Parc Machines
</NuxtLink> </NuxtLink>
@@ -199,7 +278,11 @@
<NuxtLink <NuxtLink
to="/machine-skeleton" to="/machine-skeleton"
class="transition-colors px-3 py-2 rounded-md" class="transition-colors px-3 py-2 rounded-md"
:class="isActive('/machine-skeleton') ? 'bg-primary text-primary-content font-semibold shadow-sm' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/machine-skeleton')
? 'bg-primary text-primary-content font-semibold shadow-sm'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Squelettes de machine Squelettes de machine
</NuxtLink> </NuxtLink>
@@ -216,16 +299,27 @@
tabindex="0" tabindex="0"
role="button" role="button"
class="transition-colors px-3 py-2 rounded-md inline-flex items-center gap-1 cursor-pointer" class="transition-colors px-3 py-2 rounded-md inline-flex items-center gap-1 cursor-pointer"
:class="(isActive('/pieces-catalog') || isActive('/piece-category')) ? 'bg-primary text-primary-content font-semibold shadow-sm' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/pieces-catalog') || isActive('/piece-category')
? 'bg-primary text-primary-content font-semibold shadow-sm'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Pièces Pièces
</div> </div>
<ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-60"> <ul
tabindex="0"
class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-60"
>
<li> <li>
<NuxtLink <NuxtLink
to="/piece-category" to="/piece-category"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/piece-category') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/piece-category')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Catégorie de pièce Catégorie de pièce
</NuxtLink> </NuxtLink>
@@ -234,7 +328,11 @@
<NuxtLink <NuxtLink
to="/pieces-catalog" to="/pieces-catalog"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/pieces-catalog') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/pieces-catalog')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Catalogue de pièce Catalogue de pièce
</NuxtLink> </NuxtLink>
@@ -253,16 +351,28 @@
tabindex="0" tabindex="0"
role="button" role="button"
class="transition-colors px-3 py-2 rounded-md inline-flex items-center gap-1 cursor-pointer" class="transition-colors px-3 py-2 rounded-md inline-flex items-center gap-1 cursor-pointer"
:class="(isActive('/component-category') || isActive('/component-catalog')) ? 'bg-primary text-primary-content font-semibold shadow-sm' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/component-category') ||
isActive('/component-catalog')
? 'bg-primary text-primary-content font-semibold shadow-sm'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Composant Composant
</div> </div>
<ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-64"> <ul
tabindex="0"
class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-64"
>
<li> <li>
<NuxtLink <NuxtLink
to="/component-category" to="/component-category"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/component-category') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/component-category')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Catégorie de composant Catégorie de composant
</NuxtLink> </NuxtLink>
@@ -271,7 +381,11 @@
<NuxtLink <NuxtLink
to="/component-catalog" to="/component-catalog"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/component-catalog') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/component-catalog')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Catalogue de composant Catalogue de composant
</NuxtLink> </NuxtLink>
@@ -290,16 +404,29 @@
tabindex="0" tabindex="0"
role="button" role="button"
class="transition-colors px-3 py-2 rounded-md inline-flex items-center gap-1 cursor-pointer" class="transition-colors px-3 py-2 rounded-md inline-flex items-center gap-1 cursor-pointer"
:class="(isActive('/sites') || isActive('/documents') || isActive('/constructeurs')) ? 'bg-primary text-primary-content font-semibold shadow-sm' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/sites') ||
isActive('/documents') ||
isActive('/constructeurs')
? 'bg-primary text-primary-content font-semibold shadow-sm'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Ressources liées Ressources liées
</div> </div>
<ul tabindex="0" class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-60"> <ul
tabindex="0"
class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-60"
>
<li> <li>
<NuxtLink <NuxtLink
to="/sites" to="/sites"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/sites') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/sites')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Sites Sites
</NuxtLink> </NuxtLink>
@@ -308,7 +435,11 @@
<NuxtLink <NuxtLink
to="/documents" to="/documents"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/documents') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/documents')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Documents Documents
</NuxtLink> </NuxtLink>
@@ -317,7 +448,11 @@
<NuxtLink <NuxtLink
to="/constructeurs" to="/constructeurs"
class="rounded-md px-2 py-1 transition-colors" class="rounded-md px-2 py-1 transition-colors"
:class="isActive('/constructeurs') ? 'bg-primary/10 text-primary font-semibold' : 'text-base-content hover:bg-primary/10 hover:text-primary'" :class="
isActive('/constructeurs')
? 'bg-primary/10 text-primary font-semibold'
: 'text-base-content hover:bg-primary/10 hover:text-primary'
"
> >
Constructeurs Constructeurs
</NuxtLink> </NuxtLink>
@@ -329,23 +464,29 @@
<div class="navbar-end"> <div class="navbar-end">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<!-- Bouton paramètres d'affichage --> <!-- Bouton paramètres d'affichage -->
<button <button
@click="openDisplaySettings" @click="openDisplaySettings"
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"
> >
<IconLucideSettings class="w-5 h-5" aria-hidden="true" /> <IconLucideSettings class="w-5 h-5" aria-hidden="true" />
</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">
<IconLucidePlus class="w-5 h-5 mr-2" aria-hidden="true" /> <IconLucidePlus class="w-5 h-5 mr-2" aria-hidden="true" />
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"
>
<IconLucideCpu class="w-4 h-4" aria-hidden="true" /> <IconLucideCpu class="w-4 h-4" aria-hidden="true" />
Nouvelle Machine Nouvelle Machine
</NuxtLink> </NuxtLink>
@@ -367,8 +508,14 @@
<ClientOnly> <ClientOnly>
<div class="dropdown dropdown-end" v-if="activeProfile"> <div class="dropdown dropdown-end" v-if="activeProfile">
<div tabindex="0" role="button" class="btn btn-ghost btn-circle avatar placeholder"> <div
<div class="bg-secondary text-secondary-content rounded-full w-10 h-10 grid place-items-center"> tabindex="0"
role="button"
class="btn btn-ghost btn-circle avatar placeholder"
>
<div
class="bg-secondary text-secondary-content rounded-full w-10 h-10 grid place-items-center"
>
<span <span
class="flex h-full w-full items-center justify-center text-sm font-semibold leading-none tracking-tight" class="flex h-full w-full items-center justify-center text-sm font-semibold leading-none tracking-tight"
> >
@@ -376,20 +523,32 @@
</span> </span>
</div> </div>
</div> </div>
<ul tabindex="0" class="menu dropdown-content mt-3 p-2 shadow bg-base-100 rounded-box w-64"> <ul
tabindex="0"
class="menu dropdown-content mt-3 p-2 shadow bg-base-100 rounded-box w-64"
>
<li class="px-2 py-1 text-sm text-base-content/70"> <li class="px-2 py-1 text-sm text-base-content/70">
Connecté en tant que<br /> Connecté en tant que<br />
<span class="font-semibold text-base-content">{{ activeProfileLabel }}</span> <span class="font-semibold text-base-content">{{
activeProfileLabel
}}</span>
</li> </li>
<li><hr class="my-1" /></li> <li><hr class="my-1" /></li>
<li> <li>
<NuxtLink to="/profiles/manage" class="justify-between"> <NuxtLink to="/profiles/manage" class="justify-between">
Gestion des profils Gestion des profils
<IconLucideChevronRight class="w-4 h-4" aria-hidden="true" /> <IconLucideChevronRight
class="w-4 h-4"
aria-hidden="true"
/>
</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
<IconLucideLogOut class="w-4 h-4" aria-hidden="true" /> <IconLucideLogOut class="w-4 h-4" aria-hidden="true" />
</button> </button>
@@ -408,8 +567,8 @@
<ToastContainer /> <ToastContainer />
<!-- Paramètres d'affichage --> <!-- Paramètres d'affichage -->
<DisplaySettings <DisplaySettings
:is-open="displaySettingsOpen" :is-open="displaySettingsOpen"
@close="closeDisplaySettings" @close="closeDisplaySettings"
@update-settings="handleSettingsUpdate" @update-settings="handleSettingsUpdate"
/> />
@@ -424,96 +583,99 @@
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue' import { ref, computed, onMounted, onUnmounted } 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 IconLucideMenu from "~icons/lucide/menu";
import IconLucideSettings from '~icons/lucide/settings' import IconLucideSettings from "~icons/lucide/settings";
import IconLucideBoxes from '~icons/lucide/boxes' import IconLucideBoxes from "~icons/lucide/boxes";
import IconLucidePlus from '~icons/lucide/plus' import IconLucidePlus from "~icons/lucide/plus";
import IconLucideCpu from '~icons/lucide/cpu' import IconLucideCpu from "~icons/lucide/cpu";
import IconLucideFilePlus from '~icons/lucide/file-plus' import IconLucideFilePlus from "~icons/lucide/file-plus";
import IconLucideMapPin from '~icons/lucide/map-pin' import IconLucideMapPin from "~icons/lucide/map-pin";
import IconLucideChevronRight from '~icons/lucide/chevron-right' import IconLucideChevronRight from "~icons/lucide/chevron-right";
import IconLucideLogOut from '~icons/lucide/log-out' 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);
const { activeProfile, ensureSession, logout } = useProfileSession() const { activeProfile, ensureSession, logout } = useProfileSession();
// Route active pour souligner l'onglet sélectionné dans la navbar // Route active pour souligner l'onglet sélectionné dans la navbar
const route = useRoute() const route = useRoute();
const isActive = (path) => { const isActive = (path) => {
if (path === '/') { if (path === "/") {
return route.path === '/' return route.path === "/";
} }
return route.path.startsWith(path) return route.path.startsWith(path);
} };
// Ouvrir les paramètres d'affichage // Ouvrir les paramètres d'affichage
const openDisplaySettings = () => { const openDisplaySettings = () => {
displaySettingsOpen.value = true displaySettingsOpen.value = true;
} };
// Fermer les paramètres d'affichage // Fermer les paramètres d'affichage
const closeDisplaySettings = () => { const closeDisplaySettings = () => {
displaySettingsOpen.value = false displaySettingsOpen.value = false;
} };
// Gérer les mises à jour des paramètres // Gérer les mises à jour des paramètres
const handleSettingsUpdate = (settings) => { const handleSettingsUpdate = (settings) => {
console.log('Paramètres d\'affichage mis à jour:', settings) console.log("Paramètres d'affichage mis à jour:", settings);
} };
const handleLogout = async () => { const handleLogout = async () => {
await logout() await logout();
await navigateTo('/profiles') await navigateTo("/profiles");
} };
const openDropdown = ref(null) const openDropdown = ref(null);
let dropdownCloseTimer = null let dropdownCloseTimer = null;
const setDropdown = (name) => { const setDropdown = (name) => {
if (dropdownCloseTimer) { if (dropdownCloseTimer) {
clearTimeout(dropdownCloseTimer) clearTimeout(dropdownCloseTimer);
dropdownCloseTimer = null dropdownCloseTimer = null;
} }
if (openDropdown.value !== name) { if (openDropdown.value !== name) {
openDropdown.value = name openDropdown.value = name;
} }
} };
const scheduleDropdownClose = (name) => { const scheduleDropdownClose = (name) => {
if (dropdownCloseTimer) { if (dropdownCloseTimer) {
clearTimeout(dropdownCloseTimer) clearTimeout(dropdownCloseTimer);
} }
dropdownCloseTimer = setTimeout(() => { dropdownCloseTimer = setTimeout(() => {
if (openDropdown.value === name) { if (openDropdown.value === name) {
openDropdown.value = null openDropdown.value = null;
} }
dropdownCloseTimer = null dropdownCloseTimer = null;
}, 200) }, 200);
} };
const activeProfileLabel = computed(() => { const activeProfileLabel = computed(() => {
if (!activeProfile.value) return 'Profil inconnu' if (!activeProfile.value) return "Profil inconnu";
return `${activeProfile.value.firstName} ${activeProfile.value.lastName}` return `${activeProfile.value.firstName} ${activeProfile.value.lastName}`;
}) });
const activeProfileInitials = computed(() => { const activeProfileInitials = computed(() => {
if (!activeProfile.value) return '??' if (!activeProfile.value) return "??";
const { firstName = '', lastName = '' } = activeProfile.value const { firstName = "", lastName = "" } = activeProfile.value;
return `${firstName.charAt(0) || ''}${lastName.charAt(0) || ''}`.toUpperCase() || '??' return (
}) `${firstName.charAt(0) || ""}${lastName.charAt(0) || ""}`.toUpperCase() ||
"??"
);
});
onMounted(async () => { onMounted(async () => {
await ensureSession() await ensureSession();
}) });
onUnmounted(() => { onUnmounted(() => {
if (dropdownCloseTimer) { if (dropdownCloseTimer) {
clearTimeout(dropdownCloseTimer) clearTimeout(dropdownCloseTimer);
dropdownCloseTimer = null dropdownCloseTimer = null;
} }
}) });
</script> </script>

View File

@@ -1,12 +1,11 @@
<template> <template>
<main class="container mx-auto px-6 py-8"> <main class="container mx-auto px-6 py-8">
<!-- Hierarchical View --> <!-- Hierarchical View -->
<div class="my-8"> <div class="my-8">
<!-- Header with Stats --> <!-- Header with Stats -->
<div class="flex justify-between items-center mb-6"> <div class="flex justify-between items-center mb-6">
<div> <div>
<h2 class="text-2xl font-bold text-gray-800">Vue Hiérarchique</h2> <h2 class="text-2xl font-bold text-gray-800">Vue d'ensemble</h2>
<p class="text-gray-600">Machines organisées par site</p> <p class="text-gray-600">Machines organisées par site</p>
</div> </div>
<div class="stats shadow"> <div class="stats shadow">
@@ -29,10 +28,10 @@
<label class="label"> <label class="label">
<span class="label-text">Rechercher</span> <span class="label-text">Rechercher</span>
</label> </label>
<input <input
v-model="searchTerm" v-model="searchTerm"
type="text" type="text"
placeholder="Nom de machine ou site..." placeholder="Nom de machine ou site..."
class="input input-bordered" class="input input-bordered"
/> />
</div> </div>
@@ -42,7 +41,11 @@
</label> </label>
<select v-model="selectedType" class="select select-bordered"> <select v-model="selectedType" class="select select-bordered">
<option value="">Tous les types</option> <option value="">Tous les types</option>
<option v-for="type in machineTypes" :key="type.id" :value="type.id"> <option
v-for="type in machineTypes"
:key="type.id"
:value="type.id"
>
{{ type.name }} {{ type.name }}
</option> </option>
</select> </select>
@@ -53,7 +56,11 @@
</label> </label>
<select v-model="selectedCategory" class="select select-bordered"> <select v-model="selectedCategory" class="select select-bordered">
<option value="">Toutes les catégories</option> <option value="">Toutes les catégories</option>
<option v-for="category in categories" :key="category" :value="category"> <option
v-for="category in categories"
:key="category"
:value="category"
>
{{ category }} {{ category }}
</option> </option>
</select> </select>
@@ -70,14 +77,24 @@
<!-- 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">
<IconLucideFactory class="w-16 h-16 mx-auto text-gray-400 mb-4" aria-hidden="true" /> <IconLucideFactory
<h3 class="text-lg font-medium text-gray-900 mb-2">Aucune machine trouvée</h3> class="w-16 h-16 mx-auto text-gray-400 mb-4"
<p class="text-gray-500 mb-4">Commencez par ajouter des sites et des machines.</p> aria-hidden="true"
/>
<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>
<div class="flex gap-2 justify-center"> <div class="flex gap-2 justify-center">
<button @click="showAddSiteModal = true" class="btn btn-primary"> <button @click="showAddSiteModal = true" class="btn btn-primary">
Ajouter un site Ajouter un site
</button> </button>
<button @click="showAddMachineModal = true" class="btn btn-secondary"> <button
@click="showAddMachineModal = true"
class="btn btn-secondary"
>
Ajouter une machine Ajouter une machine
</button> </button>
</div> </div>
@@ -85,8 +102,8 @@
</div> </div>
<div v-else class="space-y-6"> <div v-else class="space-y-6">
<div <div
v-for="site in filteredSites" v-for="site in filteredSites"
:key="site.id" :key="site.id"
class="card bg-base-100 shadow-lg" class="card bg-base-100 shadow-lg"
> >
@@ -95,7 +112,9 @@
<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 grid place-items-center"> <div
class="bg-primary text-primary-content rounded-lg w-12 grid place-items-center"
>
<IconLucideMapPin class="w-6 h-6" aria-hidden="true" /> <IconLucideMapPin class="w-6 h-6" aria-hidden="true" />
</div> </div>
</div> </div>
@@ -103,15 +122,24 @@
<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">
<IconLucideUser class="w-4 h-4 text-primary" aria-hidden="true" /> <IconLucideUser
class="w-4 h-4 text-primary"
aria-hidden="true"
/>
<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">
<IconLucidePhone class="w-4 h-4 text-secondary" aria-hidden="true" /> <IconLucidePhone
class="w-4 h-4 text-secondary"
aria-hidden="true"
/>
<span>{{ site.contactPhone }}</span> <span>{{ site.contactPhone }}</span>
</div> </div>
<div class="flex items-start gap-2"> <div class="flex items-start gap-2">
<IconLucideMapPinned class="w-4 h-4 text-accent mt-1" aria-hidden="true" /> <IconLucideMapPinned
class="w-4 h-4 text-accent mt-1"
aria-hidden="true"
/>
<span> <span>
{{ site.contactAddress }}<br /> {{ site.contactAddress }}<br />
{{ site.contactPostalCode }} {{ site.contactCity }} {{ site.contactPostalCode }} {{ site.contactCity }}
@@ -121,11 +149,18 @@
</div> </div>
</div> </div>
<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">
<button @click="toggleSiteCollapse(site.id)" class="btn btn-ghost btn-sm"> {{ site.machines?.length || 0 }} machines
</div>
<button
@click="toggleSiteCollapse(site.id)"
class="btn btn-ghost btn-sm"
>
<IconLucideChevronDown <IconLucideChevronDown
class="w-5 h-5 transition-transform" class="w-5 h-5 transition-transform"
:class="collapsedSites.includes(site.id) ? 'rotate-180' : ''" :class="
collapsedSites.includes(site.id) ? 'rotate-180' : ''
"
aria-hidden="true" aria-hidden="true"
/> />
</button> </button>
@@ -133,11 +168,18 @@
</div> </div>
<!-- Machines List --> <!-- Machines List -->
<div v-if="!collapsedSites.includes(site.id) && site.machines && site.machines.length > 0" class="space-y-3"> <div
v-if="
!collapsedSites.includes(site.id) &&
site.machines &&
site.machines.length > 0
"
class="space-y-3"
>
<div class="divider"></div> <div class="divider"></div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div <div
v-for="machine in site.machines" v-for="machine in site.machines"
:key="machine.id" :key="machine.id"
class="card bg-base-200 hover:bg-base-300 transition-colors cursor-pointer" class="card bg-base-200 hover:bg-base-300 transition-colors cursor-pointer"
@click="viewMachineDetails(machine)" @click="viewMachineDetails(machine)"
@@ -145,32 +187,53 @@
<div class="card-body p-4"> <div class="card-body p-4">
<div class="flex items-center justify-between mb-2"> <div class="flex items-center justify-between mb-2">
<h4 class="font-semibold text-sm">{{ machine.name }}</h4> <h4 class="font-semibold text-sm">{{ machine.name }}</h4>
<div class="badge badge-sm" :class="getCategoryBadgeClass(machine.typeMachine?.category)"> <div
{{ machine.typeMachine?.category || 'N/A' }} class="badge badge-sm"
:class="
getCategoryBadgeClass(machine.typeMachine?.category)
"
>
{{ machine.typeMachine?.category || "N/A" }}
</div> </div>
</div> </div>
<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">
<IconLucideSettings2 class="w-3 h-3" aria-hidden="true" /> <IconLucideSettings2
<span>{{ machine.typeMachine?.name || 'Type inconnu' }}</span> class="w-3 h-3"
aria-hidden="true"
/>
<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"
>
<IconLucideTag class="w-3 h-3" aria-hidden="true" /> <IconLucideTag class="w-3 h-3" aria-hidden="true" />
<span>{{ machine.reference }}</span> <span>{{ machine.reference }}</span>
</div> </div>
</div> </div>
<div class="card-actions justify-end mt-3"> <div class="card-actions justify-end mt-3">
<button class="btn btn-xs btn-outline" @click.stop="editMachine(machine)"> <button
class="btn btn-xs btn-outline"
@click.stop="editMachine(machine)"
>
Modifier Modifier
</button> </button>
<button class="btn btn-xs btn-error" @click.stop="confirmDeleteMachine(machine)"> <button
class="btn btn-xs btn-error"
@click.stop="confirmDeleteMachine(machine)"
>
Supprimer Supprimer
</button> </button>
<NuxtLink :to="`/machine/${machine.id}`" class="btn btn-xs btn-primary"> <NuxtLink
:to="`/machine/${machine.id}`"
class="btn btn-xs btn-primary"
>
Détails Détails
</NuxtLink> </NuxtLink>
</div> </div>
@@ -180,12 +243,23 @@
</div> </div>
<!-- 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">
<IconLucideFactory class="w-8 h-8 mx-auto" aria-hidden="true" /> <IconLucideFactory class="w-8 h-8 mx-auto" aria-hidden="true" />
</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">
<button @click="addMachineToSite(site)" class="btn btn-sm btn-primary"> Aucune machine dans ce site
</p>
<button
@click="addMachineToSite(site)"
class="btn btn-sm btn-primary"
>
Ajouter une machine Ajouter une machine
</button> </button>
</div> </div>
@@ -203,11 +277,11 @@
<label class="label"> <label class="label">
<span class="label-text">Nom du site</span> <span class="label-text">Nom du site</span>
</label> </label>
<input <input
v-model="newSite.name" v-model="newSite.name"
type="text" type="text"
placeholder="Ex: Usine de production" placeholder="Ex: Usine de production"
class="input input-bordered" class="input input-bordered"
required required
/> />
</div> </div>
@@ -215,12 +289,14 @@
<SiteContactFormFields :form="newSite" /> <SiteContactFormFields :form="newSite" />
<div class="modal-action"> <div class="modal-action">
<button type="button" @click="showAddSiteModal = false" class="btn btn-outline"> <button
type="button"
@click="showAddSiteModal = false"
class="btn btn-outline"
>
Annuler Annuler
</button> </button>
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">Créer le site</button>
Créer le site
</button>
</div> </div>
</form> </form>
</div> </div>
@@ -236,20 +312,24 @@
<label class="label"> <label class="label">
<span class="label-text">Nom de la machine</span> <span class="label-text">Nom de la machine</span>
</label> </label>
<input <input
v-model="newMachine.name" v-model="newMachine.name"
type="text" type="text"
placeholder="Ex: Presse hydraulique #1" placeholder="Ex: Presse hydraulique #1"
class="input input-bordered" class="input input-bordered"
required required
/> />
</div> </div>
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label">
<span class="label-text">Site</span> <span class="label-text">Site</span>
</label> </label>
<select v-model="newMachine.siteId" class="select select-bordered" required> <select
v-model="newMachine.siteId"
class="select select-bordered"
required
>
<option value="">Sélectionner un site</option> <option value="">Sélectionner un site</option>
<option v-for="site in sites" :key="site.id" :value="site.id"> <option v-for="site in sites" :key="site.id" :value="site.id">
{{ site.name }} {{ site.name }}
@@ -263,9 +343,17 @@
<label class="label"> <label class="label">
<span class="label-text">Type de machine</span> <span class="label-text">Type de machine</span>
</label> </label>
<select v-model="newMachine.typeMachineId" class="select select-bordered" required> <select
v-model="newMachine.typeMachineId"
class="select select-bordered"
required
>
<option value="">Sélectionner un type</option> <option value="">Sélectionner un type</option>
<option v-for="type in machineTypes" :key="type.id" :value="type.id"> <option
v-for="type in machineTypes"
:key="type.id"
:value="type.id"
>
{{ type.name }} ({{ type.category }}) {{ type.name }} ({{ type.category }})
</option> </option>
</select> </select>
@@ -275,36 +363,51 @@
<label class="label"> <label class="label">
<span class="label-text">Référence</span> <span class="label-text">Référence</span>
</label> </label>
<input <input
v-model="newMachine.reference" v-model="newMachine.reference"
type="text" type="text"
placeholder="Ex: PRESS-001" placeholder="Ex: PRESS-001"
class="input input-bordered" class="input input-bordered"
/> />
</div> </div>
</div> </div>
<!-- Type Preview --> <!-- Type Preview -->
<div v-if="selectedMachineType" class="mb-4 p-4 bg-gray-50 rounded-lg"> <div
<h4 class="font-semibold text-sm mb-2">Structure du type sélectionné :</h4> v-if="selectedMachineType"
class="mb-4 p-4 bg-gray-50 rounded-lg"
>
<h4 class="font-semibold text-sm mb-2">
Structure du type sélectionné :
</h4>
<div class="text-xs space-y-1"> <div class="text-xs space-y-1">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="font-medium">Familles de composants :</span> <span class="font-medium">Familles de composants :</span>
<span class="badge badge-sm">{{ selectedMachineType.componentRequirements?.length || 0 }}</span> <span class="badge badge-sm">{{
selectedMachineType.componentRequirements?.length || 0
}}</span>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="font-medium">Groupes de pièces :</span> <span class="font-medium">Groupes de pièces :</span>
<span class="badge badge-sm">{{ selectedMachineType.pieceRequirements?.length || 0 }}</span> <span class="badge badge-sm">{{
selectedMachineType.pieceRequirements?.length || 0
}}</span>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="font-medium">Catégorie :</span> <span class="font-medium">Catégorie :</span>
<span class="badge badge-outline badge-sm">{{ selectedMachineType.category }}</span> <span class="badge badge-outline badge-sm">{{
selectedMachineType.category
}}</span>
</div> </div>
</div> </div>
</div> </div>
<div class="modal-action"> <div class="modal-action">
<button type="button" @click="showAddMachineModal = false" class="btn btn-outline"> <button
type="button"
@click="showAddMachineModal = false"
class="btn btn-outline"
>
Annuler Annuler
</button> </button>
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">
@@ -318,116 +421,129 @@
</template> </template>
<script setup> <script setup>
import { ref, reactive, onMounted, computed } from 'vue' import { ref, reactive, onMounted, computed } from "vue";
import SiteContactFormFields from '~/components/sites/SiteContactFormFields.vue' import SiteContactFormFields from "~/components/sites/SiteContactFormFields.vue";
import { useSites } from '~/composables/useSites' 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 IconLucideFactory from "~icons/lucide/factory";
import IconLucideMapPin from '~icons/lucide/map-pin' import IconLucideMapPin from "~icons/lucide/map-pin";
import IconLucideUser from '~icons/lucide/user' import IconLucideUser from "~icons/lucide/user";
import IconLucidePhone from '~icons/lucide/phone' import IconLucidePhone from "~icons/lucide/phone";
import IconLucideMapPinned from '~icons/lucide/map-pinned' import IconLucideMapPinned from "~icons/lucide/map-pinned";
import IconLucideChevronDown from '~icons/lucide/chevron-down' import IconLucideChevronDown from "~icons/lucide/chevron-down";
import IconLucideSettings2 from '~icons/lucide/settings-2' import IconLucideSettings2 from "~icons/lucide/settings-2";
import IconLucideTag from '~icons/lucide/tag' import IconLucideTag from "~icons/lucide/tag";
const { sites, loading, loadSites, createSite } = useSites();
const { sites, loading, loadSites, createSite } = useSites() const { machineTypes, loadMachineTypes } = useMachineTypesApi();
const { machineTypes, loadMachineTypes } = useMachineTypesApi() const { createMachineFromType, deleteMachine } = useMachines();
const { createMachineFromType, deleteMachine } = useMachines()
// Data // Data
const showAddSiteModal = ref(false) const showAddSiteModal = ref(false);
const showAddMachineModal = ref(false) const showAddMachineModal = ref(false);
const searchTerm = ref('') const searchTerm = ref("");
const selectedType = ref('') const selectedType = ref("");
const selectedCategory = ref('') const selectedCategory = ref("");
const collapsedSites = ref([]) const collapsedSites = ref([]);
const newSite = reactive({ const newSite = reactive({
name: '', name: "",
contactName: '', contactName: "",
contactPhone: '', contactPhone: "",
contactAddress: '', contactAddress: "",
contactPostalCode: '', contactPostalCode: "",
contactCity: '' contactCity: "",
}) });
const newMachine = reactive({ const newMachine = reactive({
name: '', name: "",
siteId: '', siteId: "",
typeMachineId: '', typeMachineId: "",
reference: '' reference: "",
}) });
// Computed // Computed
const selectedMachineType = computed(() => { const selectedMachineType = computed(() => {
if (!newMachine.typeMachineId) return null if (!newMachine.typeMachineId) return null;
return machineTypes.value.find(type => type.id === newMachine.typeMachineId) return machineTypes.value.find(
}) (type) => type.id === newMachine.typeMachineId
);
});
const categories = computed(() => { const categories = computed(() => {
const cats = new Set() const cats = new Set();
machineTypes.value.forEach(type => { machineTypes.value.forEach((type) => {
if (type.category) cats.add(type.category) if (type.category) cats.add(type.category);
}) });
return Array.from(cats) return Array.from(cats);
}) });
const totalMachines = computed(() => { const totalMachines = computed(() => {
return sites.value.reduce((total, site) => { return sites.value.reduce((total, site) => {
return total + (site.machines?.length || 0) return total + (site.machines?.length || 0);
}, 0) }, 0);
}) });
const filteredSites = computed(() => { const filteredSites = computed(() => {
let filtered = sites.value let filtered = sites.value;
// Filtrer par terme de recherche // Filtrer par terme de recherche
if (searchTerm.value) { if (searchTerm.value) {
filtered = filtered.filter(site => { filtered = filtered.filter((site) => {
const lowerTerm = searchTerm.value.toLowerCase() const lowerTerm = searchTerm.value.toLowerCase();
const siteMatches = [ const siteMatches = [
site.name, site.name,
site.contactName, site.contactName,
site.contactPhone, site.contactPhone,
site.contactAddress, site.contactAddress,
site.contactPostalCode, site.contactPostalCode,
site.contactCity site.contactCity,
].some(field => { ].some((field) => {
if (!field) return false if (!field) return false;
return field.toLowerCase().includes(lowerTerm) return field.toLowerCase().includes(lowerTerm);
}) });
const machineMatches = site.machines?.some(machine => const machineMatches = site.machines?.some(
machine.name.toLowerCase().includes(lowerTerm) || (machine) =>
machine.reference?.toLowerCase().includes(lowerTerm) machine.name.toLowerCase().includes(lowerTerm) ||
) machine.reference?.toLowerCase().includes(lowerTerm)
);
return siteMatches || machineMatches
}) return siteMatches || machineMatches;
});
} }
// Filtrer par type de machine // Filtrer par type de machine
if (selectedType.value) { if (selectedType.value) {
filtered = filtered.map(site => ({ filtered = filtered
...site, .map((site) => ({
machines: site.machines?.filter(machine => machine.typeMachineId === selectedType.value) || [] ...site,
})).filter(site => site.machines.length > 0) machines:
site.machines?.filter(
(machine) => machine.typeMachineId === selectedType.value
) || [],
}))
.filter((site) => site.machines.length > 0);
} }
// Filtrer par catégorie // Filtrer par catégorie
if (selectedCategory.value) { if (selectedCategory.value) {
filtered = filtered.map(site => ({ filtered = filtered
...site, .map((site) => ({
machines: site.machines?.filter(machine => machine.typeMachine?.category === selectedCategory.value) || [] ...site,
})).filter(site => site.machines.length > 0) machines:
site.machines?.filter(
(machine) =>
machine.typeMachine?.category === selectedCategory.value
) || [],
}))
.filter((site) => site.machines.length > 0);
} }
return filtered return filtered;
}) });
// Methods // Methods
const handleCreateSite = async () => { const handleCreateSite = async () => {
@@ -437,102 +553,106 @@ const handleCreateSite = async () => {
contactPhone: newSite.contactPhone, contactPhone: newSite.contactPhone,
contactAddress: newSite.contactAddress, contactAddress: newSite.contactAddress,
contactPostalCode: newSite.contactPostalCode, contactPostalCode: newSite.contactPostalCode,
contactCity: newSite.contactCity contactCity: newSite.contactCity,
}) });
if (result.success) { if (result.success) {
showAddSiteModal.value = false showAddSiteModal.value = false;
// Reset form // Reset form
newSite.name = '' newSite.name = "";
newSite.contactName = '' newSite.contactName = "";
newSite.contactPhone = '' newSite.contactPhone = "";
newSite.contactAddress = '' newSite.contactAddress = "";
newSite.contactPostalCode = '' newSite.contactPostalCode = "";
newSite.contactCity = '' newSite.contactCity = "";
} }
} };
const handleCreateMachine = async () => { const handleCreateMachine = async () => {
if (!selectedMachineType.value) { if (!selectedMachineType.value) {
console.error('Aucun type de machine sélectionné') console.error("Aucun type de machine sélectionné");
return return;
} }
const machineData = { const machineData = {
name: newMachine.name, name: newMachine.name,
siteId: newMachine.siteId, siteId: newMachine.siteId,
reference: newMachine.reference reference: newMachine.reference,
} };
const result = await createMachineFromType(
machineData,
selectedMachineType.value
);
const result = await createMachineFromType(machineData, selectedMachineType.value)
if (result.success) { if (result.success) {
// Reset form // Reset form
newMachine.name = '' newMachine.name = "";
newMachine.siteId = '' newMachine.siteId = "";
newMachine.typeMachineId = '' newMachine.typeMachineId = "";
newMachine.reference = '' newMachine.reference = "";
showAddMachineModal.value = false showAddMachineModal.value = false;
} }
} };
const toggleSiteCollapse = (siteId) => { const toggleSiteCollapse = (siteId) => {
const index = collapsedSites.value.indexOf(siteId) const index = collapsedSites.value.indexOf(siteId);
if (index > -1) { if (index > -1) {
collapsedSites.value.splice(index, 1) collapsedSites.value.splice(index, 1);
} else { } else {
collapsedSites.value.push(siteId) collapsedSites.value.push(siteId);
} }
} };
const viewMachineDetails = (machine) => { const viewMachineDetails = (machine) => {
// Navigation vers la page de détails de la machine // Navigation vers la page de détails de la machine
navigateTo(`/machine/${machine.id}`) navigateTo(`/machine/${machine.id}`);
} };
const editMachine = (machine) => { const editMachine = (machine) => {
// Rediriger vers la page d'édition de la machine // Rediriger vers la page d'édition de la machine
navigateTo(`/machine/${machine.id}?edit=true`) navigateTo(`/machine/${machine.id}?edit=true`);
} };
const confirmDeleteMachine = async (machine) => { const confirmDeleteMachine = async (machine) => {
const { showError, showSuccess } = useToast() const { showError, showSuccess } = useToast();
if (confirm(`Êtes-vous sûr de vouloir supprimer la machine "${machine.name}" ? Cette action est irréversible.`)) { if (
confirm(
`Êtes-vous sûr de vouloir supprimer la machine "${machine.name}" ? Cette action est irréversible.`
)
) {
try { try {
const result = await deleteMachine(machine.id) const result = await deleteMachine(machine.id);
if (result.success) { if (result.success) {
showSuccess(`Machine "${machine.name}" supprimée avec succès`) showSuccess(`Machine "${machine.name}" supprimée avec succès`);
} else { } else {
showError(`Erreur lors de la suppression: ${result.error}`) showError(`Erreur lors de la suppression: ${result.error}`);
} }
} catch (error) { } catch (error) {
showError(`Erreur lors de la suppression: ${error.message}`) showError(`Erreur lors de la suppression: ${error.message}`);
} }
} }
} };
const addMachineToSite = (site) => { const addMachineToSite = (site) => {
newMachine.siteId = site.id newMachine.siteId = site.id;
showAddMachineModal.value = true showAddMachineModal.value = true;
} };
const getCategoryBadgeClass = (category) => { const getCategoryBadgeClass = (category) => {
const classes = { const classes = {
'Production': 'badge-primary', Production: "badge-primary",
'Transformation': 'badge-secondary', Transformation: "badge-secondary",
'Manutention': 'badge-accent', Manutention: "badge-accent",
'Traitement': 'badge-info', Traitement: "badge-info",
'Contrôle': 'badge-warning' Contrôle: "badge-warning",
} };
return classes[category] || 'badge-neutral' return classes[category] || "badge-neutral";
} };
// Lifecycle // Lifecycle
onMounted(async () => { onMounted(async () => {
await Promise.all([ await Promise.all([loadSites(), loadMachineTypes()]);
loadSites(), });
loadMachineTypes() </script>
])
})
</script>