chore: update frontend configuration
This commit is contained in:
@@ -5,17 +5,29 @@
|
||||
<!-- Header with Stats -->
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-gray-800">Vue d'ensemble</h2>
|
||||
<p class="text-gray-600">Machines organisées par site</p>
|
||||
<h2 class="text-2xl font-bold text-gray-800">
|
||||
Vue d'ensemble
|
||||
</h2>
|
||||
<p class="text-gray-600">
|
||||
Machines organisées par site
|
||||
</p>
|
||||
</div>
|
||||
<div class="stats shadow">
|
||||
<div class="stat">
|
||||
<div class="stat-title">Sites</div>
|
||||
<div class="stat-value text-primary">{{ sites.length }}</div>
|
||||
<div class="stat-title">
|
||||
Sites
|
||||
</div>
|
||||
<div class="stat-value text-primary">
|
||||
{{ sites.length }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-title">Machines</div>
|
||||
<div class="stat-value text-secondary">{{ totalMachines }}</div>
|
||||
<div class="stat-title">
|
||||
Machines
|
||||
</div>
|
||||
<div class="stat-value text-secondary">
|
||||
{{ totalMachines }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -33,14 +45,16 @@
|
||||
type="text"
|
||||
placeholder="Nom de machine ou site..."
|
||||
class="input input-bordered"
|
||||
/>
|
||||
>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Type de machine</span>
|
||||
</label>
|
||||
<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"
|
||||
@@ -55,7 +69,9 @@
|
||||
<span class="label-text">Catégorie</span>
|
||||
</label>
|
||||
<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"
|
||||
@@ -71,7 +87,7 @@
|
||||
|
||||
<!-- Loading State -->
|
||||
<div v-if="loading" class="flex justify-center items-center py-12">
|
||||
<span class="loading loading-spinner loading-lg"></span>
|
||||
<span class="loading loading-spinner loading-lg" />
|
||||
</div>
|
||||
|
||||
<!-- Hierarchical Machines View -->
|
||||
@@ -88,12 +104,12 @@
|
||||
Commencez par ajouter des sites et des machines.
|
||||
</p>
|
||||
<div class="flex gap-2 justify-center">
|
||||
<button @click="showAddSiteModal = true" class="btn btn-primary">
|
||||
<button class="btn btn-primary" @click="showAddSiteModal = true">
|
||||
Ajouter un site
|
||||
</button>
|
||||
<button
|
||||
@click="showAddMachineModal = true"
|
||||
class="btn btn-secondary"
|
||||
@click="showAddMachineModal = true"
|
||||
>
|
||||
Ajouter une machine
|
||||
</button>
|
||||
@@ -119,7 +135,9 @@
|
||||
</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="flex items-center gap-2">
|
||||
<IconLucideUser
|
||||
@@ -141,7 +159,7 @@
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span>
|
||||
{{ site.contactAddress }}<br />
|
||||
{{ site.contactAddress }}<br>
|
||||
{{ site.contactPostalCode }} {{ site.contactCity }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -153,8 +171,8 @@
|
||||
{{ site.machines?.length || 0 }} machines
|
||||
</div>
|
||||
<button
|
||||
@click="toggleSiteCollapse(site.id)"
|
||||
class="btn btn-ghost btn-sm"
|
||||
@click="toggleSiteCollapse(site.id)"
|
||||
>
|
||||
<IconLucideChevronDown
|
||||
class="w-5 h-5 transition-transform"
|
||||
@@ -171,12 +189,12 @@
|
||||
<div
|
||||
v-if="
|
||||
!collapsedSites.includes(site.id) &&
|
||||
site.machines &&
|
||||
site.machines.length > 0
|
||||
site.machines &&
|
||||
site.machines.length > 0
|
||||
"
|
||||
class="space-y-3"
|
||||
>
|
||||
<div class="divider"></div>
|
||||
<div class="divider" />
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<div
|
||||
v-for="machine in site.machines"
|
||||
@@ -186,7 +204,9 @@
|
||||
>
|
||||
<div class="card-body p-4">
|
||||
<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="
|
||||
@@ -246,7 +266,7 @@
|
||||
<div
|
||||
v-else-if="
|
||||
!collapsedSites.includes(site.id) &&
|
||||
(!site.machines || site.machines.length === 0)
|
||||
(!site.machines || site.machines.length === 0)
|
||||
"
|
||||
class="text-center py-6"
|
||||
>
|
||||
@@ -257,8 +277,8 @@
|
||||
Aucune machine dans ce site
|
||||
</p>
|
||||
<button
|
||||
@click="addMachineToSite(site)"
|
||||
class="btn btn-sm btn-primary"
|
||||
@click="addMachineToSite(site)"
|
||||
>
|
||||
Ajouter une machine
|
||||
</button>
|
||||
@@ -271,8 +291,10 @@
|
||||
<!-- Add Site Modal -->
|
||||
<div v-if="showAddSiteModal" class="modal modal-open">
|
||||
<div class="modal-box">
|
||||
<h3 class="font-bold text-lg mb-4">Ajouter un nouveau site</h3>
|
||||
<form @submit.prevent="handleCreateSite" class="space-y-4">
|
||||
<h3 class="font-bold text-lg mb-4">
|
||||
Ajouter un nouveau site
|
||||
</h3>
|
||||
<form class="space-y-4" @submit.prevent="handleCreateSite">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Nom du site</span>
|
||||
@@ -283,7 +305,7 @@
|
||||
placeholder="Ex: Usine de production"
|
||||
class="input input-bordered"
|
||||
required
|
||||
/>
|
||||
>
|
||||
</div>
|
||||
|
||||
<SiteContactFormFields :form="newSite" />
|
||||
@@ -291,12 +313,14 @@
|
||||
<div class="modal-action">
|
||||
<button
|
||||
type="button"
|
||||
@click="showAddSiteModal = false"
|
||||
class="btn btn-outline"
|
||||
@click="showAddSiteModal = false"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">Créer le site</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
Créer le site
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -305,7 +329,9 @@
|
||||
<!-- Add Machine Modal -->
|
||||
<div v-if="showAddMachineModal" class="modal modal-open">
|
||||
<div class="modal-box max-w-2xl">
|
||||
<h3 class="font-bold text-lg mb-4">Ajouter une nouvelle machine</h3>
|
||||
<h3 class="font-bold text-lg mb-4">
|
||||
Ajouter une nouvelle machine
|
||||
</h3>
|
||||
<form @submit.prevent="handleCreateMachine">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<div class="form-control">
|
||||
@@ -318,7 +344,7 @@
|
||||
placeholder="Ex: Presse hydraulique #1"
|
||||
class="input input-bordered"
|
||||
required
|
||||
/>
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
@@ -330,7 +356,9 @@
|
||||
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">
|
||||
{{ site.name }}
|
||||
</option>
|
||||
@@ -348,7 +376,9 @@
|
||||
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"
|
||||
@@ -368,7 +398,7 @@
|
||||
type="text"
|
||||
placeholder="Ex: PRESS-001"
|
||||
class="input input-bordered"
|
||||
/>
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -405,8 +435,8 @@
|
||||
<div class="modal-action">
|
||||
<button
|
||||
type="button"
|
||||
@click="showAddMachineModal = false"
|
||||
class="btn btn-outline"
|
||||
@click="showAddMachineModal = false"
|
||||
>
|
||||
Annuler
|
||||
</button>
|
||||
@@ -421,129 +451,129 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed } from "vue";
|
||||
import SiteContactFormFields from "~/components/sites/SiteContactFormFields.vue";
|
||||
import { useSites } from "~/composables/useSites";
|
||||
import { useMachineTypesApi } from "~/composables/useMachineTypesApi";
|
||||
import { useMachines } from "~/composables/useMachines";
|
||||
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 { ref, reactive, onMounted, computed } from 'vue'
|
||||
import SiteContactFormFields from '~/components/sites/SiteContactFormFields.vue'
|
||||
import { useSites } from '~/composables/useSites'
|
||||
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
|
||||
import { useMachines } from '~/composables/useMachines'
|
||||
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'
|
||||
|
||||
const { sites, loading, loadSites, createSite } = useSites();
|
||||
const { machineTypes, loadMachineTypes } = useMachineTypesApi();
|
||||
const { createMachineFromType, deleteMachine } = useMachines();
|
||||
const { sites, loading, loadSites, createSite } = useSites()
|
||||
const { machineTypes, loadMachineTypes } = useMachineTypesApi()
|
||||
const { createMachineFromType, deleteMachine } = useMachines()
|
||||
|
||||
// Data
|
||||
const showAddSiteModal = ref(false);
|
||||
const showAddMachineModal = ref(false);
|
||||
const searchTerm = ref("");
|
||||
const selectedType = ref("");
|
||||
const selectedCategory = ref("");
|
||||
const collapsedSites = ref([]);
|
||||
const showAddSiteModal = ref(false)
|
||||
const showAddMachineModal = ref(false)
|
||||
const searchTerm = ref('')
|
||||
const selectedType = ref('')
|
||||
const selectedCategory = ref('')
|
||||
const collapsedSites = ref([])
|
||||
|
||||
const newSite = reactive({
|
||||
name: "",
|
||||
contactName: "",
|
||||
contactPhone: "",
|
||||
contactAddress: "",
|
||||
contactPostalCode: "",
|
||||
contactCity: "",
|
||||
});
|
||||
name: '',
|
||||
contactName: '',
|
||||
contactPhone: '',
|
||||
contactAddress: '',
|
||||
contactPostalCode: '',
|
||||
contactCity: ''
|
||||
})
|
||||
|
||||
const newMachine = reactive({
|
||||
name: "",
|
||||
siteId: "",
|
||||
typeMachineId: "",
|
||||
reference: "",
|
||||
});
|
||||
name: '',
|
||||
siteId: '',
|
||||
typeMachineId: '',
|
||||
reference: ''
|
||||
})
|
||||
|
||||
// Computed
|
||||
const selectedMachineType = computed(() => {
|
||||
if (!newMachine.typeMachineId) return null;
|
||||
if (!newMachine.typeMachineId) { return null }
|
||||
return machineTypes.value.find(
|
||||
(type) => type.id === newMachine.typeMachineId
|
||||
);
|
||||
});
|
||||
type => type.id === newMachine.typeMachineId
|
||||
)
|
||||
})
|
||||
|
||||
const categories = computed(() => {
|
||||
const cats = new Set();
|
||||
const cats = new Set()
|
||||
machineTypes.value.forEach((type) => {
|
||||
if (type.category) cats.add(type.category);
|
||||
});
|
||||
return Array.from(cats);
|
||||
});
|
||||
if (type.category) { cats.add(type.category) }
|
||||
})
|
||||
return Array.from(cats)
|
||||
})
|
||||
|
||||
const totalMachines = computed(() => {
|
||||
return sites.value.reduce((total, site) => {
|
||||
return total + (site.machines?.length || 0);
|
||||
}, 0);
|
||||
});
|
||||
return total + (site.machines?.length || 0)
|
||||
}, 0)
|
||||
})
|
||||
|
||||
const filteredSites = computed(() => {
|
||||
let filtered = sites.value;
|
||||
let filtered = sites.value
|
||||
|
||||
// Filtrer par terme de recherche
|
||||
if (searchTerm.value) {
|
||||
filtered = filtered.filter((site) => {
|
||||
const lowerTerm = searchTerm.value.toLowerCase();
|
||||
const lowerTerm = searchTerm.value.toLowerCase()
|
||||
const siteMatches = [
|
||||
site.name,
|
||||
site.contactName,
|
||||
site.contactPhone,
|
||||
site.contactAddress,
|
||||
site.contactPostalCode,
|
||||
site.contactCity,
|
||||
site.contactCity
|
||||
].some((field) => {
|
||||
if (!field) return false;
|
||||
return field.toLowerCase().includes(lowerTerm);
|
||||
});
|
||||
if (!field) { return false }
|
||||
return field.toLowerCase().includes(lowerTerm)
|
||||
})
|
||||
|
||||
const machineMatches = site.machines?.some(
|
||||
(machine) =>
|
||||
machine =>
|
||||
machine.name.toLowerCase().includes(lowerTerm) ||
|
||||
machine.reference?.toLowerCase().includes(lowerTerm)
|
||||
);
|
||||
)
|
||||
|
||||
return siteMatches || machineMatches;
|
||||
});
|
||||
return siteMatches || machineMatches
|
||||
})
|
||||
}
|
||||
|
||||
// Filtrer par type de machine
|
||||
if (selectedType.value) {
|
||||
filtered = filtered
|
||||
.map((site) => ({
|
||||
.map(site => ({
|
||||
...site,
|
||||
machines:
|
||||
site.machines?.filter(
|
||||
(machine) => machine.typeMachineId === selectedType.value
|
||||
) || [],
|
||||
machine => machine.typeMachineId === selectedType.value
|
||||
) || []
|
||||
}))
|
||||
.filter((site) => site.machines.length > 0);
|
||||
.filter(site => site.machines.length > 0)
|
||||
}
|
||||
|
||||
// Filtrer par catégorie
|
||||
if (selectedCategory.value) {
|
||||
filtered = filtered
|
||||
.map((site) => ({
|
||||
.map(site => ({
|
||||
...site,
|
||||
machines:
|
||||
site.machines?.filter(
|
||||
(machine) =>
|
||||
machine =>
|
||||
machine.typeMachine?.category === selectedCategory.value
|
||||
) || [],
|
||||
) || []
|
||||
}))
|
||||
.filter((site) => site.machines.length > 0);
|
||||
.filter(site => site.machines.length > 0)
|
||||
}
|
||||
|
||||
return filtered;
|
||||
});
|
||||
return filtered
|
||||
})
|
||||
|
||||
// Methods
|
||||
const handleCreateSite = async () => {
|
||||
@@ -553,69 +583,69 @@ const handleCreateSite = async () => {
|
||||
contactPhone: newSite.contactPhone,
|
||||
contactAddress: newSite.contactAddress,
|
||||
contactPostalCode: newSite.contactPostalCode,
|
||||
contactCity: newSite.contactCity,
|
||||
});
|
||||
contactCity: newSite.contactCity
|
||||
})
|
||||
if (result.success) {
|
||||
showAddSiteModal.value = false;
|
||||
showAddSiteModal.value = false
|
||||
|
||||
// Reset form
|
||||
newSite.name = "";
|
||||
newSite.contactName = "";
|
||||
newSite.contactPhone = "";
|
||||
newSite.contactAddress = "";
|
||||
newSite.contactPostalCode = "";
|
||||
newSite.contactCity = "";
|
||||
newSite.name = ''
|
||||
newSite.contactName = ''
|
||||
newSite.contactPhone = ''
|
||||
newSite.contactAddress = ''
|
||||
newSite.contactPostalCode = ''
|
||||
newSite.contactCity = ''
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleCreateMachine = async () => {
|
||||
if (!selectedMachineType.value) {
|
||||
console.error("Aucun type de machine sélectionné");
|
||||
return;
|
||||
console.error('Aucun type de machine sélectionné')
|
||||
return
|
||||
}
|
||||
|
||||
const machineData = {
|
||||
name: newMachine.name,
|
||||
siteId: newMachine.siteId,
|
||||
reference: newMachine.reference,
|
||||
};
|
||||
reference: newMachine.reference
|
||||
}
|
||||
|
||||
const result = await createMachineFromType(
|
||||
machineData,
|
||||
selectedMachineType.value
|
||||
);
|
||||
)
|
||||
|
||||
if (result.success) {
|
||||
// Reset form
|
||||
newMachine.name = "";
|
||||
newMachine.siteId = "";
|
||||
newMachine.typeMachineId = "";
|
||||
newMachine.reference = "";
|
||||
showAddMachineModal.value = false;
|
||||
newMachine.name = ''
|
||||
newMachine.siteId = ''
|
||||
newMachine.typeMachineId = ''
|
||||
newMachine.reference = ''
|
||||
showAddMachineModal.value = false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const toggleSiteCollapse = (siteId) => {
|
||||
const index = collapsedSites.value.indexOf(siteId);
|
||||
const index = collapsedSites.value.indexOf(siteId)
|
||||
if (index > -1) {
|
||||
collapsedSites.value.splice(index, 1);
|
||||
collapsedSites.value.splice(index, 1)
|
||||
} else {
|
||||
collapsedSites.value.push(siteId);
|
||||
collapsedSites.value.push(siteId)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const viewMachineDetails = (machine) => {
|
||||
// Navigation vers la page de détails de la machine
|
||||
navigateTo(`/machine/${machine.id}`);
|
||||
};
|
||||
navigateTo(`/machine/${machine.id}`)
|
||||
}
|
||||
|
||||
const editMachine = (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 { showError, showSuccess } = useToast();
|
||||
const { showError, showSuccess } = useToast()
|
||||
|
||||
if (
|
||||
confirm(
|
||||
@@ -623,36 +653,36 @@ const confirmDeleteMachine = async (machine) => {
|
||||
)
|
||||
) {
|
||||
try {
|
||||
const result = await deleteMachine(machine.id);
|
||||
const result = await deleteMachine(machine.id)
|
||||
if (result.success) {
|
||||
showSuccess(`Machine "${machine.name}" supprimée avec succès`);
|
||||
showSuccess(`Machine "${machine.name}" supprimée avec succès`)
|
||||
} else {
|
||||
showError(`Erreur lors de la suppression: ${result.error}`);
|
||||
showError(`Erreur lors de la suppression: ${result.error}`)
|
||||
}
|
||||
} catch (error) {
|
||||
showError(`Erreur lors de la suppression: ${error.message}`);
|
||||
showError(`Erreur lors de la suppression: ${error.message}`)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const addMachineToSite = (site) => {
|
||||
newMachine.siteId = site.id;
|
||||
showAddMachineModal.value = true;
|
||||
};
|
||||
newMachine.siteId = site.id
|
||||
showAddMachineModal.value = true
|
||||
}
|
||||
|
||||
const getCategoryBadgeClass = (category) => {
|
||||
const classes = {
|
||||
Production: "badge-primary",
|
||||
Transformation: "badge-secondary",
|
||||
Manutention: "badge-accent",
|
||||
Traitement: "badge-info",
|
||||
Contrôle: "badge-warning",
|
||||
};
|
||||
return classes[category] || "badge-neutral";
|
||||
};
|
||||
Production: 'badge-primary',
|
||||
Transformation: 'badge-secondary',
|
||||
Manutention: 'badge-accent',
|
||||
Traitement: 'badge-info',
|
||||
Contrôle: 'badge-warning'
|
||||
}
|
||||
return classes[category] || 'badge-neutral'
|
||||
}
|
||||
|
||||
// Lifecycle
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadSites(), loadMachineTypes()]);
|
||||
});
|
||||
await Promise.all([loadSites(), loadMachineTypes()])
|
||||
})
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user