Premier commit
This commit is contained in:
120
resources/js/components/ContractsTable.vue
Normal file
120
resources/js/components/ContractsTable.vue
Normal file
@@ -0,0 +1,120 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user