121 lines
5.5 KiB
Vue
121 lines
5.5 KiB
Vue
<script setup>
|
|
import { ref, computed } from 'vue';
|
|
|
|
const props = defineProps({
|
|
initialContracts: {
|
|
type: Object, // Laravel Paginate Object
|
|
required: true
|
|
}
|
|
});
|
|
|
|
const contracts = ref(props.initialContracts.data);
|
|
const searchQuery = ref('');
|
|
const typeFilter = ref('');
|
|
|
|
const filteredContracts = computed(() => {
|
|
return contracts.value.filter(contract => {
|
|
const matchesSearch = contract.name.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
|
|
contract.provider.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
|
|
(contract.municipality && contract.municipality.name.toLowerCase().includes(searchQuery.value.toLowerCase()));
|
|
const matchesType = typeFilter.value ? contract.type === typeFilter.value : true;
|
|
return matchesSearch && matchesType;
|
|
});
|
|
});
|
|
|
|
const formatDate = (date) => {
|
|
return new Date(date).toLocaleDateString('fr-FR');
|
|
};
|
|
|
|
const formatCurrency = (amount, currency) => {
|
|
return new Intl.NumberFormat('fr-FR', { style: 'currency', currency: currency }).format(amount);
|
|
};
|
|
|
|
const isExpiringSoon = (dateString) => {
|
|
if (!dateString) return false;
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
const date = new Date(dateString);
|
|
|
|
// Create date 2 months from now
|
|
const twoMonthsFromNow = new Date();
|
|
twoMonthsFromNow.setMonth(today.getMonth() + 2);
|
|
|
|
// Check if date is in the future (or today) AND before 2 months from now
|
|
// If it's already past, it's expired, not expiring soon (unless we want to highlight overdue too but usually that's red)
|
|
// The user asked "arrivant a échéances", implies approaching.
|
|
// Let's include today.
|
|
return date >= today && date <= twoMonthsFromNow;
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-header border-bottom-0 d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0 text-white">Contrats</h5>
|
|
<div class="d-flex gap-2">
|
|
<input v-model="searchQuery" type="text" class="form-control form-control-sm" placeholder="Rechercher...">
|
|
<select v-model="typeFilter" class="form-select form-select-sm">
|
|
<option value="">Tous les types</option>
|
|
<option value="microsoft_365">Microsoft 365</option>
|
|
<option value="fiber">Fibre</option>
|
|
<option value="domain">Domaine</option>
|
|
<option value="software">Logiciel</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Nom</th>
|
|
<th>Commune</th>
|
|
<th>Fournisseur</th>
|
|
<th>Type</th>
|
|
<th>État</th>
|
|
<th>Date fin</th>
|
|
<th>Montant</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="contract in filteredContracts" :key="contract.id" :class="{ 'table-warning': isExpiringSoon(contract.end_date) && contract.status !== 'expired' }">
|
|
<td>
|
|
{{ contract.name }}
|
|
<i v-if="isExpiringSoon(contract.end_date) && contract.status !== 'expired'" class="bi bi-exclamation-triangle-fill text-warning ms-1" title="Expire dans moins de 2 mois"></i>
|
|
</td>
|
|
<td>
|
|
<span v-if="contract.municipality" class="badge bg-secondary">{{ contract.municipality.name }}</span>
|
|
<span v-else class="text-muted small">Global</span>
|
|
</td>
|
|
<td>{{ contract.provider }}</td>
|
|
<td>
|
|
<span class="badge bg-info">{{ contract.type }}</span>
|
|
</td>
|
|
<td>
|
|
<span :class="{
|
|
'badge bg-success': contract.status === 'active',
|
|
'badge bg-warning': contract.status === 'pending',
|
|
'badge bg-danger': contract.status === 'expired',
|
|
'badge bg-secondary': contract.status === 'draft' || contract.status === 'cancelled'
|
|
}">{{ contract.status }}</span>
|
|
</td>
|
|
<td :class="{ 'fw-bold text-danger': isExpiringSoon(contract.end_date) && contract.status !== 'expired' }">
|
|
{{ contract.end_date ? formatDate(contract.end_date) : '-' }}
|
|
</td>
|
|
<td>{{ contract.amount ? formatCurrency(contract.amount, contract.currency) : '-' }}</td>
|
|
<td>
|
|
<a :href="`/contracts/${contract.id}`" class="btn btn-sm btn-outline-primary me-1">
|
|
Voir
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<tr v-if="filteredContracts.length === 0">
|
|
<td colspan="8" class="text-center py-3 text-muted">Aucun contrat trouvé.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<!-- Simple Pagination links (could be enhanced) -->
|
|
</div>
|
|
</template>
|