Files
e-cosplay/assets/class/RegisterPayment.js
Serreau Jovann 21f70606ee feat(Customer): Ajoute la relation OneToMany avec l'entité Website
 feat(esyweb): Ajoute un contrôleur et une page pour les sites web

🐛 fix(register): Corrige l'enregistrement des paiements partiels et complets

 feat(Payment): Gère les paiements complets et partiels via Stancer

 feat(BillingEvent): Ajoute des listeners pour les paiements complétés
2025-10-07 14:04:16 +02:00

224 lines
14 KiB
JavaScript

export class RegisterPayment extends HTMLButtonElement {
connectedCallback() {
let element = this;
element.addEventListener('click', (event) => {
event.preventDefault();
const advertId = element.getAttribute('id');
let modal = document.createElement('div');
// Classes de fond du modal Dark Mode
modal.classList.add("modal-payment", "fixed", "inset-0", "bg-gray-900", "bg-opacity-75", "flex", "items-center", "justify-center", "z-50");
modal.innerHTML = `
<div class="modal-payment-content dark:bg-gray-800 bg-white shadow-2xl rounded-lg p-6 relative" style="max-width: 900px; width: 90%; max-height: 90vh; overflow-y: auto;">
<h2 class="text-xl font-bold dark:text-white mb-4">Enregistrer un paiement</h2>
<a class="block dbclose bg-red-600 hover:bg-red-700 text-white px-3 py-1 rounded" style="cursor: pointer; position: absolute; top: 10px; right: 10px;">Fermer</a>
<form method="post" class="content mb-6">
<div class="flex space-x-4 w-full">
<fieldset class="form-group form-group--horizontal w-full">
<div class="flex space-x-4">
<div class="flex-1">
<div class="form-field">
<div class="mb-1">
<label for="type" class="block mb-2 text-sm font-medium dark:text-gray-300 text-gray-900">Type</label>
<select name="type" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" required>
<option value="CB">Carte Bancaire</option>
<option value="PREV">Prélèvement</option>
<option value="CH">Chèque</option>
<option value="ESP">Espèce</option>
<option value="VIR">Virement</option>
</select>
</div>
</div>
</div>
<div class="flex-1">
<div class="form-field">
<div class="mb-1">
<label for="date" class="block mb-2 text-sm font-medium dark:text-gray-300 text-gray-900">Date</label>
<input name="date" type="date" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" required />
</div>
</div>
</div>
<div class="flex-1">
<div class="form-field">
<div class="mb-1">
<label for="amount" class="block mb-2 text-sm font-medium dark:text-gray-300 text-gray-900">Montant</label>
<input name="amount" type="number" step="0.01" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" required />
</div>
</div>
</div>
<div class="flex-1">
<div class="form-field">
<div class="mb-1">
<label for="num_cheque" class="block mb-2 text-sm font-medium dark:text-gray-300 text-gray-900">Numéro de remise de chèque</label>
<input name="num_cheque" type="text" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" />
</div>
</div>
</div>
</div>
</fieldset>
</div>
<button type="submit" class="w-full bg-purple-600 hover:bg-purple-700 text-white font-semibold px-4 py-2 rounded mt-3">Enregistrer</button>
</form>
<h3 class="mt-6 text-lg font-semibold dark:text-white border-t border-gray-700 pt-4">Historique des paiements</h3>
<div class="overflow-x-auto">
<table id="payment-table" class="min-w-full divide-y dark:divide-gray-700 divide-gray-200">
<thead>
<tr>
<th class="px-6 py-3 dark:bg-gray-700 bg-gray-100 text-left text-xs font-medium dark:text-gray-300 text-gray-500 uppercase tracking-wider">Date</th>
<th class="px-6 py-3 dark:bg-gray-700 bg-gray-100 text-left text-xs font-medium dark:text-gray-300 text-gray-500 uppercase tracking-wider">Type</th>
<th class="px-6 py-3 dark:bg-gray-700 bg-gray-100 text-left text-xs font-medium dark:text-gray-300 text-gray-500 uppercase tracking-wider">Montant</th>
<th class="px-6 py-3 dark:bg-gray-700 bg-gray-100 text-left text-xs font-medium dark:text-gray-300 text-gray-500 uppercase tracking-wider">Réf. Chèque</th>
<th class="px-6 py-3 dark:bg-gray-700 bg-gray-100 text-right text-xs font-medium dark:text-gray-300 text-gray-500 uppercase tracking-wider">Action</th>
</tr>
</thead>
<tbody class="dark:bg-gray-800 bg-white divide-y dark:divide-gray-700 divide-gray-200">
<tr id="loading-row"><td colspan="6" class="px-6 py-4 whitespace-nowrap text-sm dark:text-gray-400 text-gray-500 text-center">Chargement...</td></tr>
</tbody>
</table>
</div>
</div>
`;
const paymentTableBody = modal.querySelector('#payment-table tbody');
/**
* Fonction pour récupérer et afficher la liste des paiements.
*/
const loadPayments = () => {
paymentTableBody.innerHTML = '<tr id="loading-row"><td colspan="6" class="px-6 py-4 whitespace-nowrap text-sm dark:text-gray-400 text-gray-500 text-center">Chargement...</td></tr>';
fetch(`/api-interne/intranet/customer/register?idPayment=${advertId}`)
.then(response => {
if (!response.ok) {
throw new Error('Erreur lors de la récupération des paiements (code: ' + response.status + ')');
}
return response.json();
})
.then((listPayment) => {
paymentTableBody.innerHTML = '';
if (listPayment && listPayment.length > 0) {
listPayment.forEach(payment => {
const tr = document.createElement('tr');
const date = new Date(payment.date).toLocaleDateString();
// Classes pour les lignes (alternance des couleurs en dark mode)
tr.classList.add('hover:bg-gray-700');
tr.innerHTML = `
<td class="px-6 py-4 whitespace-nowrap text-sm dark:text-gray-300 text-gray-500">${date}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm dark:text-gray-300 text-gray-500">${payment.type}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-bold text-green-400 dark:text-green-400">${parseFloat(payment.amount).toFixed(2)} €</td>
<td class="px-6 py-4 whitespace-nowrap text-sm dark:text-gray-300 text-gray-500">${payment.num_cheque || ''}</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button data-payment-id="${payment.id}" class="delete-payment-btn text-red-500 hover:text-red-400 dark:text-red-400 dark:hover:text-red-300">Supprimer</button>
</td>
`;
paymentTableBody.appendChild(tr);
});
modal.querySelectorAll('.delete-payment-btn').forEach(button => {
button.addEventListener('click', (e) => {
const paymentId = e.currentTarget.dataset.paymentId;
deletePayment(paymentId, loadPayments);
});
});
} else {
paymentTableBody.innerHTML = '<tr><td colspan="6" class="px-6 py-4 whitespace-nowrap text-sm dark:text-gray-400 text-gray-500 text-center">Aucun paiement enregistré pour le moment.</td></tr>';
}
})
.catch(error => {
console.error("Erreur de chargement des paiements:", error);
paymentTableBody.innerHTML = `<tr><td colspan="6" class="px-6 py-4 whitespace-nowrap text-sm text-red-500 text-center">Erreur lors du chargement: ${error.message}</td></tr>`;
});
};
/**
* Fonction pour envoyer la requête de suppression de paiement.
*/
const deletePayment = (paymentId, callback) => {
if (!confirm(`Êtes-vous sûr de vouloir supprimer le paiement ?`)) {
return;
}
fetch(`/api-interne/intranet/customer/payment/${paymentId}`, {
method: 'DELETE',
})
.then(response => {
if (!response.ok) {
throw new Error('Échec de la suppression.');
}
return response.json();
})
.then(() => {
alert('Paiement supprimé avec succès.');
callback(); // Recharger la liste des paiements
})
.catch(error => {
console.error("Erreur de suppression:", error);
alert(`Erreur lors de la suppression du paiement: ${error.message}`);
});
};
// 1. Charger les paiements lors de l'ouverture du modal
loadPayments();
// 2. Écouteur pour la fermeture du modal
modal.querySelector('.dbclose').addEventListener('click', () => {
modal.remove();
})
// 3. Écouteur pour l'enregistrement d'un nouveau paiement
modal.querySelector('form').addEventListener('submit', (e) => {
e.preventDefault();
const formData = {};
const formElements = e.target.elements;
for (let i = 0; i < formElements.length; i++) {
const el = formElements[i];
if (el.name) {
formData[el.name] = el.value;
}
}
formData['idAdvert'] = advertId;
let inputs = [];
Object.keys(formData).forEach(key => inputs.push({ name: key, val: formData[key] }));
fetch("/api-interne/intranet/customer/register", {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(inputs),
})
.then(response => {
if (!response.ok) {
return response.json().then(err => { throw new Error(err.message || 'Erreur lors de l\'enregistrement'); });
}
return response.json();
})
.then(() => {
loadPayments();
e.target.reset();
})
.catch(error => {
console.error("Erreur lors de l'enregistrement:", error);
alert("Une erreur est survenue lors de l'enregistrement du paiement: " + error.message);
});
})
document.body.appendChild(modal);
})
}
}