Compare commits
No commits in common. "ae7574045eb6e5393c2f74349a69816f28226a65" and "34875321ac1df96447b2fbe1aadf3b2088e802ea" have entirely different histories.
ae7574045e
...
34875321ac
@ -86,28 +86,9 @@ class EmployeeRepository extends BaseRepository implements EmployeeRepositoryInt
|
|||||||
/**
|
/**
|
||||||
* Get employees with pagination.
|
* Get employees with pagination.
|
||||||
*/
|
*/
|
||||||
public function getPaginated(int $perPage = 10, array $filters = []): array
|
public function getPaginated(int $perPage = 10): array
|
||||||
{
|
{
|
||||||
$query = $this->model->newQuery()->with(['thanatopractitioner', 'user']);
|
$paginator = $this->model->newQuery()->with(['thanatopractitioner', 'user'])->paginate($perPage);
|
||||||
|
|
||||||
if (!empty($filters['search'])) {
|
|
||||||
$query->search($filters['search']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists('active', $filters)) {
|
|
||||||
if (filter_var($filters['active'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)) {
|
|
||||||
$query->active();
|
|
||||||
} else {
|
|
||||||
$query->inactive();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$sortField = $filters['sort_by'] ?? 'last_name';
|
|
||||||
$sortDirection = $filters['sort_direction'] ?? 'asc';
|
|
||||||
|
|
||||||
$paginator = $query
|
|
||||||
->orderBy($sortField, $sortDirection)
|
|
||||||
->paginate($perPage);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'employees' => $paginator->getCollection(),
|
'employees' => $paginator->getCollection(),
|
||||||
@ -116,8 +97,6 @@ class EmployeeRepository extends BaseRepository implements EmployeeRepositoryInt
|
|||||||
'last_page' => $paginator->lastPage(),
|
'last_page' => $paginator->lastPage(),
|
||||||
'per_page' => $paginator->perPage(),
|
'per_page' => $paginator->perPage(),
|
||||||
'total' => $paginator->total(),
|
'total' => $paginator->total(),
|
||||||
'from' => $paginator->firstItem(),
|
|
||||||
'to' => $paginator->lastItem(),
|
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,10 +62,9 @@ interface EmployeeRepositoryInterface
|
|||||||
* Get employees with pagination.
|
* Get employees with pagination.
|
||||||
*
|
*
|
||||||
* @param int $perPage
|
* @param int $perPage
|
||||||
* @param array<string, mixed> $filters
|
|
||||||
* @return array{employees: Collection<int, Employee>, pagination: array}
|
* @return array{employees: Collection<int, Employee>, pagination: array}
|
||||||
*/
|
*/
|
||||||
public function getPaginated(int $perPage = 10, array $filters = []): array;
|
public function getPaginated(int $perPage = 10): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get employees with their thanatopractitioner data.
|
* Get employees with their thanatopractitioner data.
|
||||||
|
|||||||
@ -14,11 +14,9 @@
|
|||||||
:data="employeeData"
|
:data="employeeData"
|
||||||
:loading="loadingData"
|
:loading="loadingData"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
:search="search"
|
|
||||||
@view="goToDetails"
|
@view="goToDetails"
|
||||||
@delete="deleteEmployee"
|
@delete="deleteEmployee"
|
||||||
@page-change="handleChangePage"
|
@changePage="handleChangePage"
|
||||||
@search-change="handleSearchChange"
|
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</employee-template>
|
</employee-template>
|
||||||
@ -34,12 +32,7 @@ import { useRouter } from "vue-router";
|
|||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits(["pushDetails", "deleteEmployee", "changePage"]);
|
||||||
"pushDetails",
|
|
||||||
"deleteEmployee",
|
|
||||||
"changePage",
|
|
||||||
"searchChange",
|
|
||||||
]);
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
employeeData: {
|
employeeData: {
|
||||||
@ -54,10 +47,6 @@ const props = defineProps({
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
search: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const goToEmployee = () => {
|
const goToEmployee = () => {
|
||||||
@ -88,8 +77,8 @@ const handleChangePage = (page) => {
|
|||||||
emit("changePage", page);
|
emit("changePage", page);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSearchChange = (query) => {
|
|
||||||
emit("searchChange", query);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* Component-specific styles */
|
||||||
|
</style>
|
||||||
|
|||||||
@ -66,7 +66,7 @@
|
|||||||
<strong>1.</strong> Définir nom et email.
|
<strong>1.</strong> Définir nom et email.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm mb-2">
|
<p class="text-sm mb-2">
|
||||||
<strong>2.</strong> Le mot de passe initial est optionnel.
|
<strong>2.</strong> Saisir un mot de passe initial.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-sm mb-0">
|
<p class="text-sm mb-0">
|
||||||
<strong>3.</strong> Assigner le rôle adapté au périmètre
|
<strong>3.</strong> Assigner le rôle adapté au périmètre
|
||||||
|
|||||||
@ -1,60 +1,72 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
|
<!-- Loading State -->
|
||||||
<div v-if="loading" class="loading-container">
|
<div v-if="loading" class="loading-container">
|
||||||
<div class="loading-spinner">
|
<div class="loading-spinner">
|
||||||
<div
|
<div class="spinner-border text-primary" role="status">
|
||||||
class="spinner-border text-success loading-spinner-circle"
|
|
||||||
role="status"
|
|
||||||
>
|
|
||||||
<span class="visually-hidden">Chargement...</span>
|
<span class="visually-hidden">Chargement...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="loading-content">
|
<div class="loading-content">
|
||||||
|
<!-- Skeleton Rows -->
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-flush">
|
<table class="table table-flush">
|
||||||
<thead class="thead-light">
|
<thead class="thead-light">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Employé</th>
|
<th>ID</th>
|
||||||
<th>Référence</th>
|
<th>Nom & Prénom</th>
|
||||||
|
<th>Email</th>
|
||||||
|
<th>Téléphone</th>
|
||||||
<th>Poste</th>
|
<th>Poste</th>
|
||||||
<th>Coordonnées</th>
|
|
||||||
<th>Date d'embauche</th>
|
<th>Date d'embauche</th>
|
||||||
<th>Compte lié</th>
|
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="i in skeletonRows" :key="i" class="skeleton-row">
|
<tr v-for="i in skeletonRows" :key="i" class="skeleton-row">
|
||||||
|
<!-- ID Column Skeleton -->
|
||||||
|
<td>
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div class="skeleton-checkbox"></div>
|
||||||
|
<div class="skeleton-text short ms-2"></div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Name Column Skeleton -->
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div class="skeleton-avatar"></div>
|
<div class="skeleton-avatar"></div>
|
||||||
<div class="skeleton-text medium ms-2"></div>
|
<div class="skeleton-text medium ms-2"></div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
<!-- Email Column Skeleton -->
|
||||||
<td>
|
<td>
|
||||||
<div class="skeleton-text short"></div>
|
<div class="skeleton-text long"></div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
<!-- Phone Column Skeleton -->
|
||||||
|
<td>
|
||||||
|
<div class="skeleton-text medium"></div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Position Column Skeleton -->
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div class="skeleton-icon"></div>
|
<div class="skeleton-icon"></div>
|
||||||
<div class="skeleton-text medium ms-2"></div>
|
<div class="skeleton-text medium ms-2"></div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
|
||||||
<div class="contact-info">
|
<!-- Hire Date Column Skeleton -->
|
||||||
<div class="skeleton-text long mb-1"></div>
|
|
||||||
<div class="skeleton-text medium"></div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div class="skeleton-text medium"></div>
|
|
||||||
</td>
|
|
||||||
<td>
|
<td>
|
||||||
<div class="skeleton-text medium"></div>
|
<div class="skeleton-text medium"></div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
<!-- Status Column Skeleton -->
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div class="skeleton-icon"></div>
|
<div class="skeleton-icon small"></div>
|
||||||
<div class="skeleton-text short ms-2"></div>
|
<div class="skeleton-text short ms-2"></div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -65,201 +77,196 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else class="table-responsive">
|
<!-- Data State -->
|
||||||
<table id="employee-list" class="table table-flush">
|
<div v-else>
|
||||||
<thead class="thead-light">
|
<div class="table-responsive">
|
||||||
<tr>
|
<table id="employee-list" class="table table-flush">
|
||||||
<th>Employé</th>
|
<thead class="thead-light">
|
||||||
<th>Référence</th>
|
<tr>
|
||||||
<th>Poste</th>
|
<th>ID</th>
|
||||||
<th>Coordonnées</th>
|
<th>Nom & Prénom</th>
|
||||||
<th>Date d'embauche</th>
|
<th>Email</th>
|
||||||
<th>Compte lié</th>
|
<th>Téléphone</th>
|
||||||
<th>Status</th>
|
<th>Poste</th>
|
||||||
<th>Action</th>
|
<th>Date d'embauche</th>
|
||||||
</tr>
|
<th>Status</th>
|
||||||
</thead>
|
<th>Action</th>
|
||||||
<tbody>
|
</tr>
|
||||||
<tr v-for="employee in data" :key="employee.id">
|
</thead>
|
||||||
<td class="font-weight-bold">
|
<tbody>
|
||||||
<div class="d-flex align-items-center">
|
<tr v-for="employee in data" :key="employee.id">
|
||||||
<soft-avatar
|
<!-- ID Column -->
|
||||||
:img="getRandomAvatar()"
|
<td>
|
||||||
size="xs"
|
<div class="d-flex align-items-center">
|
||||||
class="me-2"
|
<soft-checkbox />
|
||||||
alt="employee image"
|
<p class="text-xs font-weight-bold ms-2 mb-0">
|
||||||
circular
|
{{ employee.id }}
|
||||||
/>
|
</p>
|
||||||
<div>
|
</div>
|
||||||
<span
|
</td>
|
||||||
>{{ employee.last_name }} {{ employee.first_name }}</span
|
|
||||||
>
|
<!-- Name Column -->
|
||||||
<div
|
<td class="font-weight-bold">
|
||||||
v-if="employee.thanatopractitioner"
|
<div class="d-flex align-items-center">
|
||||||
class="text-xs text-info"
|
<soft-avatar
|
||||||
>
|
:img="getRandomAvatar()"
|
||||||
Thanatopractitioner
|
size="xs"
|
||||||
|
class="me-2"
|
||||||
|
alt="user image"
|
||||||
|
circular
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
>{{ employee.last_name }} {{ employee.first_name }}</span
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="employee.thanatopractitioner"
|
||||||
|
class="text-xs text-info"
|
||||||
|
>
|
||||||
|
Thanatopractitioner
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</td>
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="text-xs font-weight-bold">
|
<!-- Email Column -->
|
||||||
<span class="my-2 text-xs">EMP-{{ employee.id }}</span>
|
<td class="text-xs font-weight-bold">
|
||||||
</td>
|
<span class="text-xs">{{ employee.email || "N/A" }}</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
<td class="text-xs font-weight-bold">
|
<!-- Phone Column -->
|
||||||
<div class="d-flex align-items-center">
|
<td class="text-xs font-weight-bold">
|
||||||
<soft-button
|
<span class="text-xs">{{ employee.phone || "N/A" }}</span>
|
||||||
:color="getPositionColor(employee.job_title)"
|
</td>
|
||||||
variant="outline"
|
|
||||||
class="btn-icon-only btn-rounded mb-0 me-2 btn-sm d-flex align-items-center justify-content-center"
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
:class="getPositionIcon(employee.job_title)"
|
|
||||||
aria-hidden="true"
|
|
||||||
></i>
|
|
||||||
</soft-button>
|
|
||||||
<span>{{ employee.job_title || "Non renseigné" }}</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="text-xs font-weight-bold">
|
<!-- Position Column -->
|
||||||
<div class="contact-info">
|
<td class="text-xs font-weight-bold">
|
||||||
<div class="text-xs text-secondary">
|
<div class="d-flex align-items-center">
|
||||||
{{ employee.email || "N/A" }}
|
<soft-button
|
||||||
|
:color="getPositionColor(employee.job_title)"
|
||||||
|
variant="outline"
|
||||||
|
class="btn-icon-only btn-rounded mb-0 me-2 btn-sm d-flex align-items-center justify-content-center"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
:class="getPositionIcon(employee.job_title)"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</soft-button>
|
||||||
|
<span>{{ employee.job_title || "N/A" }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs">{{ employee.phone || "N/A" }}</div>
|
</td>
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td class="text-xs font-weight-bold">
|
<!-- Hire Date Column -->
|
||||||
{{ formatDate(employee.hire_date) }}
|
<td class="text-xs font-weight-bold">
|
||||||
</td>
|
<span class="text-xs">{{
|
||||||
|
formatDate(employee.hire_date)
|
||||||
|
}}</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
<td class="text-xs font-weight-bold">
|
<!-- Status Column -->
|
||||||
<div class="d-flex flex-column">
|
<td class="text-xs font-weight-bold">
|
||||||
<span>{{ employee.user?.name || "Aucun compte" }}</span>
|
<div class="d-flex align-items-center">
|
||||||
<span class="text-xs text-muted">
|
<soft-button
|
||||||
{{ employee.user?.email || "Non lié" }}
|
:color="employee.active ? 'success' : 'danger'"
|
||||||
</span>
|
variant="outline"
|
||||||
</div>
|
class="btn-icon-only btn-rounded mb-0 me-2 btn-sm d-flex align-items-center justify-content-center"
|
||||||
</td>
|
>
|
||||||
|
<i
|
||||||
|
:class="employee.active ? 'fas fa-check' : 'fas fa-times'"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
</soft-button>
|
||||||
|
<span>{{ employee.active ? "Actif" : "Inactif" }}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
<td class="text-xs font-weight-bold">
|
<td>
|
||||||
<div class="d-flex flex-column">
|
<div class="d-flex align-items-center gap-2">
|
||||||
<soft-button
|
<!-- View Button -->
|
||||||
v-if="employee.active"
|
<soft-button
|
||||||
color="success"
|
color="info"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="btn-sm"
|
title="Voir l'employé"
|
||||||
>
|
:data-employee-id="employee.id"
|
||||||
<i class="fas fa-check me-1"></i>
|
class="btn-icon-only btn-rounded mb-0 btn-sm d-flex align-items-center justify-content-center"
|
||||||
Actif
|
@click="handleView(employee.id)"
|
||||||
</soft-button>
|
>
|
||||||
<soft-button
|
<i class="fas fa-eye" aria-hidden="true"></i>
|
||||||
v-else
|
</soft-button>
|
||||||
color="danger"
|
|
||||||
variant="outline"
|
|
||||||
class="btn-sm"
|
|
||||||
>
|
|
||||||
<i class="fas fa-times me-1"></i>
|
|
||||||
Inactif
|
|
||||||
</soft-button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td>
|
<!-- Delete Button -->
|
||||||
<div class="d-flex align-items-center gap-2">
|
<soft-button
|
||||||
<soft-button
|
color="danger"
|
||||||
color="info"
|
variant="outline"
|
||||||
variant="outline"
|
title="Supprimer l'employé"
|
||||||
title="Voir l'employé"
|
:data-employee-id="employee.id"
|
||||||
:data-employee-id="employee.id"
|
class="btn-icon-only btn-rounded mb-0 btn-sm d-flex align-items-center justify-content-center"
|
||||||
class="btn-icon-only btn-rounded mb-0 btn-sm d-flex align-items-center justify-content-center"
|
@click="handleDelete(employee.id)"
|
||||||
>
|
>
|
||||||
<i class="fas fa-eye" aria-hidden="true"></i>
|
<i class="fas fa-trash" aria-hidden="true"></i>
|
||||||
</soft-button>
|
</soft-button>
|
||||||
|
</div>
|
||||||
<soft-button
|
</td>
|
||||||
color="danger"
|
</tr>
|
||||||
variant="outline"
|
</tbody>
|
||||||
title="Supprimer l'employé"
|
</table>
|
||||||
:data-employee-id="employee.id"
|
|
||||||
class="btn-icon-only btn-rounded mb-0 btn-sm d-flex align-items-center justify-content-center"
|
|
||||||
>
|
|
||||||
<i class="fas fa-trash" aria-hidden="true"></i>
|
|
||||||
</soft-button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
v-if="!loading && data.length > 0 && (pagination?.last_page || 1) > 1"
|
|
||||||
class="d-flex justify-content-between align-items-center mt-3 px-3 flex-wrap gap-3"
|
|
||||||
>
|
|
||||||
<div class="text-xs text-secondary font-weight-bold">
|
|
||||||
Affichage de {{ safeFrom }} à {{ safeTo }} sur
|
|
||||||
{{ pagination.total || data.length }} employés
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav aria-label="Pagination employés">
|
<!-- Custom Pagination Controls -->
|
||||||
<ul class="pagination pagination-sm pagination-success mb-0">
|
<div
|
||||||
<li
|
v-if="pagination.total > pagination.per_page"
|
||||||
class="page-item"
|
class="d-flex justify-content-between align-items-center mt-3"
|
||||||
:class="{ disabled: (pagination.current_page || 1) === 1 }"
|
>
|
||||||
|
<div class="text-sm text-muted">
|
||||||
|
Affichage de {{ pagination.from }} à {{ pagination.to }} sur
|
||||||
|
{{ pagination.total }} employés
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<!-- Previous Button -->
|
||||||
|
<soft-button
|
||||||
|
color="outline"
|
||||||
|
variant="outline"
|
||||||
|
class="btn-sm"
|
||||||
|
:disabled="pagination.current_page === 1 || loading"
|
||||||
|
@click="changePage(pagination.current_page - 1)"
|
||||||
>
|
>
|
||||||
<a
|
<i class="fas fa-chevron-left me-1"></i>
|
||||||
class="page-link"
|
Précédent
|
||||||
href="#"
|
</soft-button>
|
||||||
aria-label="Previous"
|
|
||||||
@click.prevent="changePage((pagination.current_page || 1) - 1)"
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">
|
|
||||||
<i class="fa fa-angle-left" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li
|
<!-- Page Numbers -->
|
||||||
v-for="page in displayedPages"
|
<div class="d-flex gap-1">
|
||||||
:key="`page-${page}`"
|
<soft-button
|
||||||
class="page-item"
|
v-for="page in getVisiblePages()"
|
||||||
:class="{
|
:key="page"
|
||||||
active: (pagination.current_page || 1) === page,
|
:color="page === pagination.current_page ? 'primary' : 'outline'"
|
||||||
disabled: page === '...',
|
variant="outline"
|
||||||
}"
|
class="btn-sm"
|
||||||
>
|
:disabled="loading"
|
||||||
<a class="page-link" href="#" @click.prevent="changePage(page)">
|
@click="changePage(page)"
|
||||||
|
>
|
||||||
{{ page }}
|
{{ page }}
|
||||||
</a>
|
</soft-button>
|
||||||
</li>
|
</div>
|
||||||
|
|
||||||
<li
|
<!-- Next Button -->
|
||||||
class="page-item"
|
<soft-button
|
||||||
:class="{
|
color="outline"
|
||||||
disabled:
|
variant="outline"
|
||||||
(pagination.current_page || 1) === (pagination.last_page || 1),
|
class="btn-sm"
|
||||||
}"
|
:disabled="
|
||||||
|
pagination.current_page === pagination.last_page || loading
|
||||||
|
"
|
||||||
|
@click="changePage(pagination.current_page + 1)"
|
||||||
>
|
>
|
||||||
<a
|
Suivant
|
||||||
class="page-link"
|
<i class="fas fa-chevron-right ms-1"></i>
|
||||||
href="#"
|
</soft-button>
|
||||||
aria-label="Next"
|
</div>
|
||||||
@click.prevent="changePage((pagination.current_page || 1) + 1)"
|
</div>
|
||||||
>
|
|
||||||
<span aria-hidden="true">
|
|
||||||
<i class="fa fa-angle-right" aria-hidden="true"></i>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Empty State -->
|
||||||
<div v-if="!loading && data.length === 0" class="empty-state">
|
<div v-if="!loading && data.length === 0" class="empty-state">
|
||||||
<div class="empty-icon">
|
<div class="empty-icon">
|
||||||
<i class="fas fa-users fa-3x text-muted"></i>
|
<i class="fas fa-users fa-3x text-muted"></i>
|
||||||
@ -273,21 +280,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import { ref, onMounted, watch, onUnmounted } from "vue";
|
||||||
computed,
|
// import { DataTable } from "simple-datatables"; // Disabled to avoid interference
|
||||||
defineEmits,
|
import SoftCheckbox from "@/components/SoftCheckbox.vue";
|
||||||
defineProps,
|
|
||||||
onMounted,
|
|
||||||
onUnmounted,
|
|
||||||
ref,
|
|
||||||
watch,
|
|
||||||
} from "vue";
|
|
||||||
import { DataTable } from "simple-datatables";
|
|
||||||
import SoftButton from "@/components/SoftButton.vue";
|
import SoftButton from "@/components/SoftButton.vue";
|
||||||
import SoftAvatar from "@/components/SoftAvatar.vue";
|
import SoftAvatar from "@/components/SoftAvatar.vue";
|
||||||
|
import { defineProps, defineEmits } from "vue";
|
||||||
|
|
||||||
const emit = defineEmits(["view", "delete", "page-change", "search-change"]);
|
const emit = defineEmits(["view", "delete", "changePage"]);
|
||||||
|
|
||||||
|
// Sample avatar images
|
||||||
import img1 from "@/assets/img/team-2.jpg";
|
import img1 from "@/assets/img/team-2.jpg";
|
||||||
import img2 from "@/assets/img/team-1.jpg";
|
import img2 from "@/assets/img/team-1.jpg";
|
||||||
import img3 from "@/assets/img/team-3.jpg";
|
import img3 from "@/assets/img/team-3.jpg";
|
||||||
@ -297,7 +299,8 @@ import img6 from "@/assets/img/ivana-squares.jpg";
|
|||||||
|
|
||||||
const avatarImages = [img1, img2, img3, img4, img5, img6];
|
const avatarImages = [img1, img2, img3, img4, img5, img6];
|
||||||
|
|
||||||
const dataTableInstance = ref(null);
|
// Reactive data - DataTable disabled
|
||||||
|
// const dataTableInstance = ref(null);
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
data: {
|
data: {
|
||||||
@ -323,85 +326,9 @@ const props = defineProps({
|
|||||||
to: 0,
|
to: 0,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
search: {
|
|
||||||
type: String,
|
|
||||||
default: "",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let searchInputHandler = null;
|
|
||||||
|
|
||||||
const displayedPages = computed(() => {
|
|
||||||
const total = Number(props.pagination?.last_page) || 1;
|
|
||||||
const current = Number(props.pagination?.current_page) || 1;
|
|
||||||
|
|
||||||
if (total <= 1) {
|
|
||||||
return [1];
|
|
||||||
}
|
|
||||||
|
|
||||||
const delta = 2;
|
|
||||||
const range = [];
|
|
||||||
|
|
||||||
for (
|
|
||||||
let page = Math.max(2, current - delta);
|
|
||||||
page <= Math.min(total - 1, current + delta);
|
|
||||||
page++
|
|
||||||
) {
|
|
||||||
range.push(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current - delta > 2) {
|
|
||||||
range.unshift("...");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current + delta < total - 1) {
|
|
||||||
range.push("...");
|
|
||||||
}
|
|
||||||
|
|
||||||
range.unshift(1);
|
|
||||||
|
|
||||||
if (total > 1) {
|
|
||||||
range.push(total);
|
|
||||||
}
|
|
||||||
|
|
||||||
return range.filter(
|
|
||||||
(value, index, self) =>
|
|
||||||
value !== "..." || (value === "..." && self[index - 1] !== "...")
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const safeFrom = computed(() => {
|
|
||||||
if (props.pagination?.from) {
|
|
||||||
return props.pagination.from;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!props.pagination?.total || props.data.length === 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
((Number(props.pagination.current_page) || 1) - 1) *
|
|
||||||
(Number(props.pagination.per_page) || 10) +
|
|
||||||
1
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const safeTo = computed(() => {
|
|
||||||
if (props.pagination?.to) {
|
|
||||||
return props.pagination.to;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!props.pagination?.total || props.data.length === 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.min(
|
|
||||||
(Number(props.pagination.current_page) || 1) *
|
|
||||||
(Number(props.pagination.per_page) || 10),
|
|
||||||
Number(props.pagination.total) || 0
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Methods
|
||||||
const getRandomAvatar = () => {
|
const getRandomAvatar = () => {
|
||||||
const randomIndex = Math.floor(Math.random() * avatarImages.length);
|
const randomIndex = Math.floor(Math.random() * avatarImages.length);
|
||||||
return avatarImages[randomIndex];
|
return avatarImages[randomIndex];
|
||||||
@ -435,27 +362,69 @@ const getPositionIcon = (position) => {
|
|||||||
return icons[position] || "fas fa-user";
|
return icons[position] || "fas fa-user";
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleTableClick = (event) => {
|
// Direct button handlers
|
||||||
const button = event.target.closest("button");
|
const handleView = (employeeId) => {
|
||||||
if (!button) return;
|
console.log("Direct view button clicked for ID:", employeeId);
|
||||||
|
emit("view", employeeId);
|
||||||
|
};
|
||||||
|
|
||||||
const employeeId = button.getAttribute("data-employee-id");
|
const handleDelete = (employeeId) => {
|
||||||
if (!employeeId) return;
|
console.log("Direct delete button clicked for ID:", employeeId);
|
||||||
|
emit("delete", employeeId);
|
||||||
|
};
|
||||||
|
|
||||||
if (
|
// Pagination methods
|
||||||
button.title === "Supprimer l'employé" ||
|
const changePage = (page) => {
|
||||||
button.querySelector(".fa-trash")
|
console.log("changePage called in EmployeeTable with page:", page);
|
||||||
) {
|
if (page >= 1 && page <= props.pagination.last_page) {
|
||||||
emit("delete", Number(employeeId));
|
console.log("Emitting changePage event from EmployeeTable:", page);
|
||||||
} else if (
|
emit("changePage", page);
|
||||||
button.title === "Voir l'employé" ||
|
|
||||||
button.querySelector(".fa-eye")
|
|
||||||
) {
|
|
||||||
emit("view", Number(employeeId));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getVisiblePages = () => {
|
||||||
|
const current = props.pagination.current_page;
|
||||||
|
const last = props.pagination.last_page;
|
||||||
|
const pages = [];
|
||||||
|
|
||||||
|
if (last <= 7) {
|
||||||
|
// Show all pages if 7 or fewer
|
||||||
|
for (let i = 1; i <= last; i++) {
|
||||||
|
pages.push(i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Show first page, current range, and last page
|
||||||
|
pages.push(1);
|
||||||
|
|
||||||
|
if (current > 3) {
|
||||||
|
pages.push("...");
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = Math.max(2, current - 1);
|
||||||
|
const end = Math.min(last - 1, current + 1);
|
||||||
|
|
||||||
|
for (let i = start; i <= end; i++) {
|
||||||
|
if (!pages.includes(i)) {
|
||||||
|
pages.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current < last - 2) {
|
||||||
|
pages.push("...");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pages.includes(last)) {
|
||||||
|
pages.push(last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pages;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Commented out DataTable initialization
|
||||||
|
/*
|
||||||
const initializeDataTable = () => {
|
const initializeDataTable = () => {
|
||||||
|
// Destroy existing instance if it exists
|
||||||
if (dataTableInstance.value) {
|
if (dataTableInstance.value) {
|
||||||
dataTableInstance.value.destroy();
|
dataTableInstance.value.destroy();
|
||||||
dataTableInstance.value = null;
|
dataTableInstance.value = null;
|
||||||
@ -463,84 +432,75 @@ const initializeDataTable = () => {
|
|||||||
|
|
||||||
const dataTableEl = document.getElementById("employee-list");
|
const dataTableEl = document.getElementById("employee-list");
|
||||||
if (dataTableEl) {
|
if (dataTableEl) {
|
||||||
|
// Initialize DataTable with search and default pagination
|
||||||
dataTableInstance.value = new DataTable(dataTableEl, {
|
dataTableInstance.value = new DataTable(dataTableEl, {
|
||||||
searchable: true,
|
searchable: true,
|
||||||
fixedHeight: true,
|
fixedHeight: true,
|
||||||
paging: false,
|
perPage: 10, // Default to 10 entries per page
|
||||||
perPage: Number(props.pagination?.per_page) || 10,
|
perPageSelect: false, // Disable per-page selector since we handle it server-side
|
||||||
perPageSelect: false,
|
pager: false, // Disable DataTable pagination since we handle it server-side
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add click listener for action buttons
|
||||||
dataTableEl.addEventListener("click", handleTableClick);
|
dataTableEl.addEventListener("click", handleTableClick);
|
||||||
|
|
||||||
const searchInput =
|
|
||||||
document.querySelector("#employee-list .dataTable-input") ||
|
|
||||||
document.querySelector(".dataTable-input");
|
|
||||||
|
|
||||||
if (searchInput instanceof HTMLInputElement) {
|
|
||||||
searchInput.placeholder = "Rechercher un employé par nom";
|
|
||||||
searchInput.value = props.search || "";
|
|
||||||
|
|
||||||
searchInputHandler = (event) => {
|
|
||||||
emit("search-change", event.target.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
searchInput.removeEventListener("input", searchInputHandler);
|
|
||||||
searchInput.addEventListener("input", searchInputHandler);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const changePage = (page) => {
|
const handleTableClick = (event) => {
|
||||||
|
console.log("Table click detected:", event.target);
|
||||||
|
const button = event.target.closest("button");
|
||||||
|
if (!button) return;
|
||||||
|
|
||||||
|
const employeeId = button.getAttribute("data-employee-id");
|
||||||
|
console.log("Employee ID:", employeeId);
|
||||||
|
console.log("Button title:", button.title);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
page !== "..." &&
|
button.title === "Supprimer l'employé" ||
|
||||||
page >= 1 &&
|
button.querySelector(".fa-trash")
|
||||||
page <= (props.pagination?.last_page || 1) &&
|
|
||||||
page !== Number(props.pagination?.current_page)
|
|
||||||
) {
|
) {
|
||||||
emit("page-change", page);
|
console.log("Delete button clicked!");
|
||||||
|
emit("delete", employeeId);
|
||||||
|
} else if (
|
||||||
|
button.title === "Voir l'employé" ||
|
||||||
|
button.querySelector(".fa-eye")
|
||||||
|
) {
|
||||||
|
console.log("View button clicked!");
|
||||||
|
emit("view", employeeId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
watch(
|
// Watch for data changes
|
||||||
() => props.search,
|
|
||||||
(value) => {
|
|
||||||
const searchInput = document.querySelector(".dataTable-input");
|
|
||||||
if (
|
|
||||||
searchInput instanceof HTMLInputElement &&
|
|
||||||
searchInput.value !== value
|
|
||||||
) {
|
|
||||||
searchInput.value = value || "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.data,
|
() => props.data,
|
||||||
() => {
|
() => {
|
||||||
if (!props.loading) {
|
if (!props.loading) {
|
||||||
setTimeout(() => {
|
console.log("EmployeeTable: Data changed");
|
||||||
initializeDataTable();
|
|
||||||
}, 100);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
const dataTableEl = document.getElementById("employee-list");
|
// Clean up any event listeners if needed
|
||||||
if (dataTableEl) {
|
// const dataTableEl = document.getElementById("employee-list");
|
||||||
dataTableEl.removeEventListener("click", handleTableClick);
|
// if (dataTableEl) {
|
||||||
}
|
// dataTableEl.removeEventListener("click", handleTableClick);
|
||||||
|
// }
|
||||||
if (dataTableInstance.value) {
|
// if (dataTableInstance.value) {
|
||||||
dataTableInstance.value.destroy();
|
// dataTableInstance.value.destroy();
|
||||||
}
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Initialize data
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!props.loading && props.data.length > 0) {
|
if (!props.loading && props.data.length > 0) {
|
||||||
initializeDataTable();
|
console.log(
|
||||||
|
"EmployeeTable: Component mounted with",
|
||||||
|
props.data.length,
|
||||||
|
"employees"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -553,30 +513,31 @@ onMounted(() => {
|
|||||||
|
|
||||||
.loading-container {
|
.loading-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
min-height: 260px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-spinner {
|
.loading-spinner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 20px;
|
||||||
left: 50%;
|
right: 20px;
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-spinner-circle {
|
|
||||||
width: 2.25rem;
|
|
||||||
height: 2.25rem;
|
|
||||||
border-width: 0.28em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-content {
|
.loading-content {
|
||||||
opacity: 0.55;
|
opacity: 0.7;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skeleton-row {
|
.skeleton-row {
|
||||||
animation: none;
|
animation: pulse 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-checkbox {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: shimmer 2s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.skeleton-avatar {
|
.skeleton-avatar {
|
||||||
@ -597,6 +558,15 @@ onMounted(() => {
|
|||||||
animation: shimmer 2s infinite;
|
animation: shimmer 2s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.skeleton-icon.small {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: shimmer 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
.skeleton-text {
|
.skeleton-text {
|
||||||
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
||||||
background-size: 200% 100%;
|
background-size: 200% 100%;
|
||||||
@ -637,13 +607,21 @@ onMounted(() => {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.address-info,
|
|
||||||
.text-xs {
|
.text-xs {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact-info {
|
/* Animations */
|
||||||
line-height: 1.2;
|
@keyframes pulse {
|
||||||
|
0% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes shimmer {
|
@keyframes shimmer {
|
||||||
@ -655,7 +633,13 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Responsive adjustments */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
.loading-spinner {
|
||||||
|
top: 10px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.skeleton-text.long {
|
.skeleton-text.long {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,9 @@
|
|||||||
:employee-data="employeeStore.employees"
|
:employee-data="employeeStore.employees"
|
||||||
:loading-data="employeeStore.loading"
|
:loading-data="employeeStore.loading"
|
||||||
:pagination="employeeStore.getPagination"
|
:pagination="employeeStore.getPagination"
|
||||||
:search="search"
|
|
||||||
@push-details="goDetails"
|
@push-details="goDetails"
|
||||||
@delete-employee="confirmDeleteEmployee"
|
@delete-employee="confirmDeleteEmployee"
|
||||||
@change-page="changePage"
|
@change-page="changePage"
|
||||||
@search-change="updateSearch"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Confirm Delete Modal -->
|
<!-- Confirm Delete Modal -->
|
||||||
@ -27,7 +25,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, onMounted, ref } from "vue";
|
import { reactive, onMounted } from "vue";
|
||||||
import EmployeePresentation from "@/components/Organism/Employee/EmployeePresentation.vue";
|
import EmployeePresentation from "@/components/Organism/Employee/EmployeePresentation.vue";
|
||||||
import ConfirmModal from "@/components/molecules/common/ConfirmModal.vue";
|
import ConfirmModal from "@/components/molecules/common/ConfirmModal.vue";
|
||||||
import { useEmployeeStore } from "@/stores/employeeStore";
|
import { useEmployeeStore } from "@/stores/employeeStore";
|
||||||
@ -37,9 +35,6 @@ import { useRouter } from "vue-router";
|
|||||||
const employeeStore = useEmployeeStore();
|
const employeeStore = useEmployeeStore();
|
||||||
const notificationStore = useNotificationStore();
|
const notificationStore = useNotificationStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const search = ref("");
|
|
||||||
let searchDebounceTimeout = null;
|
|
||||||
const DEFAULT_PER_PAGE = 10;
|
|
||||||
|
|
||||||
// Confirm modal state
|
// Confirm modal state
|
||||||
const confirmModal = reactive({
|
const confirmModal = reactive({
|
||||||
@ -57,11 +52,7 @@ const confirmModal = reactive({
|
|||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await employeeStore.fetchEmployees({
|
await employeeStore.fetchEmployees();
|
||||||
page: 1,
|
|
||||||
per_page: DEFAULT_PER_PAGE,
|
|
||||||
search: search.value.trim(),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const goDetails = (id) => {
|
const goDetails = (id) => {
|
||||||
@ -152,12 +143,10 @@ const closeConfirmModal = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const changePage = async (page) => {
|
const changePage = async (page) => {
|
||||||
|
console.log("changePage called in Employees.vue with page:", page);
|
||||||
try {
|
try {
|
||||||
await employeeStore.fetchEmployees({
|
console.log("Fetching employees with page:", page);
|
||||||
page,
|
await employeeStore.fetchEmployees({ page, per_page: 10 });
|
||||||
per_page: employeeStore.getPagination.per_page || DEFAULT_PER_PAGE,
|
|
||||||
search: search.value.trim(),
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error changing page:", error);
|
console.error("Error changing page:", error);
|
||||||
notificationStore.error(
|
notificationStore.error(
|
||||||
@ -166,28 +155,4 @@ const changePage = async (page) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSearch = (value) => {
|
|
||||||
search.value = value;
|
|
||||||
|
|
||||||
if (searchDebounceTimeout) {
|
|
||||||
window.clearTimeout(searchDebounceTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
searchDebounceTimeout = window.setTimeout(async () => {
|
|
||||||
try {
|
|
||||||
await employeeStore.fetchEmployees({
|
|
||||||
page: 1,
|
|
||||||
per_page: employeeStore.getPagination.per_page || DEFAULT_PER_PAGE,
|
|
||||||
search: search.value.trim(),
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error searching employees:", error);
|
|
||||||
notificationStore.error(
|
|
||||||
"Erreur de recherche",
|
|
||||||
"Une erreur est survenue lors de la recherche des employés."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, 300);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -15,15 +15,14 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onMounted, reactive } from "vue";
|
import { computed, onMounted, reactive } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
import UserCreatePresentation from "@/components/Organism/Parametrage/Users/UserCreatePresentation.vue";
|
import UserCreatePresentation from "@/components/Organism/Parametrage/Users/UserCreatePresentation.vue";
|
||||||
import { useAccessControlStore } from "@/stores/accessControlStore";
|
import { useAccessControlStore } from "@/stores/accessControlStore";
|
||||||
import { useNotificationStore } from "@/stores/notification";
|
|
||||||
import { useUserStore } from "@/stores/userStore";
|
import { useUserStore } from "@/stores/userStore";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const accessControlStore = useAccessControlStore();
|
const accessControlStore = useAccessControlStore();
|
||||||
const notificationStore = useNotificationStore();
|
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
id: null,
|
id: null,
|
||||||
@ -40,7 +39,8 @@ const submitDisabled = computed(
|
|||||||
userStore.isLoading ||
|
userStore.isLoading ||
|
||||||
accessControlStore.isLoading ||
|
accessControlStore.isLoading ||
|
||||||
!form.name.trim() ||
|
!form.name.trim() ||
|
||||||
!form.email.trim()
|
!form.email.trim() ||
|
||||||
|
!form.password.trim()
|
||||||
);
|
);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
@ -80,19 +80,26 @@ const submitUser = async () => {
|
|||||||
const user = await userStore.createUser({
|
const user = await userStore.createUser({
|
||||||
name: form.name.trim(),
|
name: form.name.trim(),
|
||||||
email: form.email.trim(),
|
email: form.email.trim(),
|
||||||
password: form.password.trim() || null,
|
password: form.password.trim(),
|
||||||
roles: form.roles,
|
roles: form.roles,
|
||||||
permissions: form.permissions,
|
permissions: form.permissions,
|
||||||
});
|
});
|
||||||
|
|
||||||
notificationStore.created("L'utilisateur");
|
await Swal.fire({
|
||||||
|
icon: "success",
|
||||||
|
title: "Succès",
|
||||||
|
text: "L'utilisateur a été créé avec succès.",
|
||||||
|
confirmButtonText: "Voir le détail",
|
||||||
|
});
|
||||||
|
|
||||||
router.push(`/parametrage/utilisateurs/${user.id}`);
|
router.push(`/parametrage/utilisateurs/${user.id}`);
|
||||||
} catch {
|
} catch {
|
||||||
notificationStore.error(
|
await Swal.fire({
|
||||||
"Erreur de création",
|
icon: "error",
|
||||||
userStore.error || "Impossible de créer l'utilisateur."
|
title: "Erreur",
|
||||||
);
|
text: userStore.error || "Impossible de créer l'utilisateur.",
|
||||||
|
confirmButtonText: "Fermer",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user