pagiantion defunt liste
This commit is contained in:
parent
67b15ab337
commit
9951ed0ee6
@ -20,18 +20,30 @@ class InterventionSeeder extends Seeder
|
||||
$products = Product::query()
|
||||
->whereHas('category', fn ($query) => $query->where('intervention', true))
|
||||
->get();
|
||||
$deceasedCollection = Deceased::query()->get();
|
||||
$practitioners = Thanatopractitioner::query()->get();
|
||||
$clients = Client::query()->limit(12)->get();
|
||||
$creatorId = User::query()->value('id');
|
||||
$types = ['thanatopraxie', 'toilette_mortuaire', 'exhumation', 'retrait_pacemaker', 'retrait_bijoux', 'autre'];
|
||||
$statuses = ['demande', 'planifie', 'en_cours', 'termine', 'annule'];
|
||||
|
||||
if ($products->isEmpty() || $deceasedCollection->isEmpty() || $clients->isEmpty()) {
|
||||
if ($products->isEmpty() || $clients->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($clients as $index => $client) {
|
||||
$deceased = Deceased::updateOrCreate(
|
||||
[
|
||||
'last_name' => sprintf('Défunt Client %d', $client->id),
|
||||
'first_name' => 'Dossier',
|
||||
],
|
||||
[
|
||||
'birth_date' => now()->subYears(55 + $index)->subDays($index)->format('Y-m-d'),
|
||||
'death_date' => now()->subDays($index + 1)->format('Y-m-d'),
|
||||
'place_of_death' => $client->billing_city ?: 'Antananarivo',
|
||||
'notes' => sprintf('Défunt de démonstration lié au client #%d pour les interventions seedées.', $client->id),
|
||||
]
|
||||
);
|
||||
|
||||
$location = ClientLocation::updateOrCreate(
|
||||
[
|
||||
'client_id' => $client->id,
|
||||
@ -54,7 +66,7 @@ class InterventionSeeder extends Seeder
|
||||
'type' => $types[$index % count($types)],
|
||||
],
|
||||
[
|
||||
'deceased_id' => optional($deceasedCollection->get($index % $deceasedCollection->count()))->id,
|
||||
'deceased_id' => $deceased->id,
|
||||
'order_giver' => $faker->name,
|
||||
'location_id' => $location->id,
|
||||
'product_id' => optional($products->get($index % $products->count()))->id,
|
||||
|
||||
@ -4,13 +4,58 @@
|
||||
<add-button text="Ajouter" @click="add" />
|
||||
</template>
|
||||
<template #select-filter>
|
||||
<filter-table />
|
||||
<div class="defunt-toolbar-controls">
|
||||
<filter-table />
|
||||
<div class="defunt-search-box ms-2">
|
||||
<soft-input
|
||||
id="deceased-list-search"
|
||||
:model-value="search"
|
||||
placeholder="Rechercher par nom du défunt"
|
||||
icon="fas fa-search"
|
||||
icon-dir="left"
|
||||
@update:model-value="emit('search-change', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #defunt-other-action>
|
||||
<table-action />
|
||||
</template>
|
||||
<template #header-pagination>
|
||||
<div
|
||||
v-if="pagination && pagination.last_page > 1"
|
||||
class="d-flex justify-content-center"
|
||||
>
|
||||
<soft-pagination color="success" size="sm">
|
||||
<soft-pagination-item
|
||||
prev
|
||||
:disabled="pagination.current_page <= 1"
|
||||
@click="changePage(pagination.current_page - 1)"
|
||||
/>
|
||||
|
||||
<soft-pagination-item
|
||||
v-for="page in visiblePages"
|
||||
:key="page"
|
||||
:label="page.toString()"
|
||||
:active="pagination.current_page === page"
|
||||
@click="changePage(page)"
|
||||
/>
|
||||
|
||||
<soft-pagination-item
|
||||
next
|
||||
:disabled="pagination.current_page >= pagination.last_page"
|
||||
@click="changePage(pagination.current_page + 1)"
|
||||
/>
|
||||
</soft-pagination>
|
||||
</div>
|
||||
</template>
|
||||
<template #defunt-table>
|
||||
<defunts-list :defunts="defunts" />
|
||||
<defunts-list
|
||||
:defunts="defunts"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
@page-change="emit('page-change', $event)"
|
||||
/>
|
||||
</template>
|
||||
</defunts-template>
|
||||
</template>
|
||||
@ -20,19 +65,110 @@ import addButton from "@/components/molecules/new-button/addButton.vue";
|
||||
import FilterTable from "@/components/molecules/Tables/FilterTable.vue";
|
||||
import TableAction from "@/components/molecules/Tables/TableAction.vue";
|
||||
import DefuntsList from "@/components/molecules/Defunts/DefuntsList.vue";
|
||||
import { defineProps } from "vue";
|
||||
import SoftInput from "@/components/SoftInput.vue";
|
||||
import SoftPagination from "@/components/SoftPagination.vue";
|
||||
import SoftPaginationItem from "@/components/SoftPaginationItem.vue";
|
||||
import { computed, defineEmits, defineProps } from "vue";
|
||||
|
||||
import { useRouter } from "vue-router";
|
||||
const router = useRouter();
|
||||
|
||||
defineProps({
|
||||
const emit = defineEmits(["page-change", "search-change"]);
|
||||
|
||||
const props = defineProps({
|
||||
defunts: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
pagination: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
current_page: 1,
|
||||
last_page: 1,
|
||||
per_page: 10,
|
||||
total: 0,
|
||||
from: 0,
|
||||
to: 0,
|
||||
}),
|
||||
},
|
||||
search: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
|
||||
const add = () => {
|
||||
router.push({ name: "Add Defunts" });
|
||||
};
|
||||
|
||||
const changePage = (page) => {
|
||||
if (typeof page !== "number") return;
|
||||
if (page < 1 || page > (props.pagination?.last_page || 1)) return;
|
||||
if (page === props.pagination?.current_page) return;
|
||||
emit("page-change", page);
|
||||
};
|
||||
|
||||
const visiblePages = computed(() => {
|
||||
if (!props.pagination) return [];
|
||||
|
||||
const currentPage = props.pagination.current_page || 1;
|
||||
const lastPage = props.pagination.last_page || 1;
|
||||
|
||||
if (lastPage <= 7) {
|
||||
return Array.from({ length: lastPage }, (_, index) => index + 1);
|
||||
}
|
||||
|
||||
const pages = [1];
|
||||
let start = Math.max(2, currentPage - 1);
|
||||
let end = Math.min(lastPage - 1, currentPage + 1);
|
||||
|
||||
if (currentPage < 4) {
|
||||
start = 2;
|
||||
end = 4;
|
||||
}
|
||||
|
||||
if (currentPage > lastPage - 3) {
|
||||
start = lastPage - 3;
|
||||
end = lastPage - 1;
|
||||
}
|
||||
|
||||
for (let page = start; page <= end; page++) {
|
||||
pages.push(page);
|
||||
}
|
||||
|
||||
if (!pages.includes(lastPage)) {
|
||||
pages.push(lastPage);
|
||||
}
|
||||
|
||||
return [...new Set(pages)].filter(
|
||||
(page) => typeof page === "number" && page >= 1 && page <= lastPage
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.defunt-toolbar-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.defunt-search-box {
|
||||
min-width: 280px;
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.defunt-toolbar-controls {
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.defunt-search-box {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
<template>
|
||||
<div class="row">
|
||||
<div v-if="!defunts || defunts.length === 0" class="empty-state">
|
||||
<div>
|
||||
<div v-if="loading" class="text-center py-5">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">Chargement...</span>
|
||||
</div>
|
||||
<p class="mt-2 mb-0">Chargement des défunts...</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="!defunts || defunts.length === 0" class="empty-state">
|
||||
<div class="empty-message">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<h3>Aucun défunt trouvé</h3>
|
||||
@ -10,22 +17,37 @@
|
||||
</soft-button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(defunt, index) in defunts"
|
||||
:key="index"
|
||||
class="col-xl-4 col-md-6 col-12 mb-4"
|
||||
>
|
||||
<defunt-card
|
||||
:nom="defunt.last_name"
|
||||
:prenom="defunt.first_name"
|
||||
:date_naissance="defunt.birth_date"
|
||||
:date_deces="defunt.death_date"
|
||||
:lieu_deces="defunt.place_of_death"
|
||||
:description="defunt.notes"
|
||||
:dropdown="dropdownOptions"
|
||||
@dropdown-action="(action) => handleAction(action, defunt)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<div class="row">
|
||||
<div
|
||||
v-for="(defunt, index) in defunts"
|
||||
:key="index"
|
||||
class="col-xl-4 col-md-6 col-12 mb-4"
|
||||
>
|
||||
<defunt-card
|
||||
:nom="defunt.last_name"
|
||||
:prenom="defunt.first_name"
|
||||
:date_naissance="defunt.birth_date"
|
||||
:date_deces="defunt.death_date"
|
||||
:lieu_deces="defunt.place_of_death"
|
||||
:description="defunt.notes"
|
||||
:dropdown="dropdownOptions"
|
||||
@dropdown-action="(action) => handleAction(action, defunt)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="(pagination?.last_page || 1) > 1"
|
||||
class="d-flex justify-content-end align-items-center mt-3 px-1 flex-wrap gap-3"
|
||||
>
|
||||
<div class="text-xs text-secondary font-weight-bold">
|
||||
Affichage de {{ safeFrom }} à {{ safeTo }} sur
|
||||
{{ pagination.total || defunts.length }} défunts
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Intervention Add Modal -->
|
||||
@ -81,8 +103,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import DefuntCard from "@/components/atoms/Defunts/DefuntCard.vue";
|
||||
import { ref } from "vue";
|
||||
import { defineProps } from "vue";
|
||||
import { computed, defineProps, onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import SoftButton from "@/components/SoftButton.vue";
|
||||
import InterventationAddModal from "@/components/molecules/Interventions/InterventationAddModal.vue";
|
||||
@ -90,7 +111,6 @@ import { useInterventionStore } from "@/stores/interventionStore";
|
||||
import { useDeceasedStore } from "@/stores/deceasedStore";
|
||||
import { useClientStore } from "@/stores/clientStore";
|
||||
import { useNotificationStore } from "@/stores/notification";
|
||||
import { onMounted } from "vue";
|
||||
|
||||
// Router
|
||||
const router = useRouter();
|
||||
@ -177,11 +197,58 @@ const handleCreateIntervention = async (form) => {
|
||||
}
|
||||
};
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
defunts: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
pagination: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
current_page: 1,
|
||||
last_page: 1,
|
||||
per_page: 10,
|
||||
total: 0,
|
||||
from: 0,
|
||||
to: 0,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
const safeFrom = computed(() => {
|
||||
if (props.pagination?.from) {
|
||||
return props.pagination.from;
|
||||
}
|
||||
|
||||
if (!props.pagination?.total || props.defunts.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.defunts.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.min(
|
||||
(Number(props.pagination.current_page) || 1) *
|
||||
(Number(props.pagination.per_page) || 10),
|
||||
Number(props.pagination.total) || 0
|
||||
);
|
||||
});
|
||||
|
||||
// Modal management functions
|
||||
|
||||
@ -13,6 +13,9 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="mt-4">
|
||||
<slot name="header-pagination"></slot>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<slot name="defunt-table"></slot>
|
||||
</div>
|
||||
|
||||
@ -21,6 +21,8 @@ export const useDeceasedStore = defineStore("deceased", () => {
|
||||
last_page: 1,
|
||||
per_page: 10,
|
||||
total: 0,
|
||||
from: 0,
|
||||
to: 0,
|
||||
});
|
||||
|
||||
// Getters
|
||||
@ -103,6 +105,8 @@ export const useDeceasedStore = defineStore("deceased", () => {
|
||||
last_page: meta.last_page || 1,
|
||||
per_page: meta.per_page || 10,
|
||||
total: meta.total || 0,
|
||||
from: meta.from || 0,
|
||||
to: meta.to || 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -308,6 +312,8 @@ export const useDeceasedStore = defineStore("deceased", () => {
|
||||
last_page: 1,
|
||||
per_page: 10,
|
||||
total: 0,
|
||||
from: 0,
|
||||
to: 0,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -1,14 +1,52 @@
|
||||
<template>
|
||||
<defunt-presentation :defunts="deceasedStore.deceased" />
|
||||
<defunt-presentation
|
||||
:defunts="deceasedStore.deceased"
|
||||
:loading="deceasedStore.loading"
|
||||
:pagination="deceasedStore.getPagination"
|
||||
:search="search"
|
||||
@page-change="changePage"
|
||||
@search-change="updateSearch"
|
||||
/>
|
||||
</template>
|
||||
<script setup>
|
||||
import DefuntPresentation from "@/components/Organism/Defunts/DefuntPresentation.vue";
|
||||
import { useDeceasedStore } from "@/stores/deceasedStore";
|
||||
import { onMounted } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
|
||||
const deceasedStore = useDeceasedStore();
|
||||
const search = ref("");
|
||||
let searchDebounceTimeout = null;
|
||||
const DEFAULT_PER_PAGE = 10;
|
||||
|
||||
onMounted(async () => {
|
||||
await deceasedStore.fetchDeceased();
|
||||
await deceasedStore.fetchDeceased({
|
||||
page: 1,
|
||||
per_page: DEFAULT_PER_PAGE,
|
||||
search: search.value.trim(),
|
||||
});
|
||||
});
|
||||
|
||||
const changePage = async (page) => {
|
||||
await deceasedStore.fetchDeceased({
|
||||
page,
|
||||
per_page: deceasedStore.getPagination.per_page || DEFAULT_PER_PAGE,
|
||||
search: search.value.trim(),
|
||||
});
|
||||
};
|
||||
|
||||
const updateSearch = (value) => {
|
||||
search.value = value;
|
||||
|
||||
if (searchDebounceTimeout) {
|
||||
window.clearTimeout(searchDebounceTimeout);
|
||||
}
|
||||
|
||||
searchDebounceTimeout = window.setTimeout(async () => {
|
||||
await deceasedStore.fetchDeceased({
|
||||
page: 1,
|
||||
per_page: deceasedStore.getPagination.per_page || DEFAULT_PER_PAGE,
|
||||
search: search.value.trim(),
|
||||
});
|
||||
}, 300);
|
||||
};
|
||||
</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user