Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1b76737
add borrower insurance
odysseu Jan 26, 2025
c1afe70
no more .switch-help; added a few details of form steps
Feb 4, 2025
18e05d3
html
Feb 4, 2025
03db647
corrected ajouterLoyer and isValidNumber
Feb 4, 2025
21d8a32
added taux-assurance in form and handlers; TODO : make sure it is use…
Feb 4, 2025
4407dda
Add Doc
Feb 4, 2025
f1a944d
added reminder on TAEG
odysseu Feb 13, 2025
9457443
avoid DOM issue with dynamic document input element
odysseu Feb 26, 2025
666d81e
no name in main.yml so it is clearer what's running
odysseu Feb 4, 2025
d7bc869
give short name to CI yml so it's clean in the pipelines page
odysseu Feb 5, 2025
683dd39
moved contributing and funding in specific files
Feb 10, 2025
3827c25
Update FUNDING.yml
odysseu Feb 11, 2025
2abac79
repair logo vertical centering
Feb 11, 2025
bd66405
make sure pdf extension is not duplicated for file download
Feb 11, 2025
7f89b1e
repair calculerPertesAchat;; add input-overlay class;
odysseu Mar 9, 2025
7fa7a98
merged from main
odysseu Mar 12, 2025
3679104
renaming CODE OF CONDUCT file
odysseu Mar 12, 2025
b91c74f
merge main code of conduct
odysseu Mar 12, 2025
9054046
add logos for smartphone home use
odysseu Mar 12, 2025
a7b730e
hint for when developing on codespace
odysseu Mar 14, 2025
22cef08
translateY for calculated assurance TAEG
odysseu Mar 14, 2025
2c39de7
added "file fees", "reset form help", moved calculateTAEG in seperate…
odysseu Mar 16, 2025
fce66cb
calculateTAEG
odysseu Mar 18, 2025
a220cd8
TAEG now reacts to frais-dossier, prix, commission, apport, duree-pre…
odysseu Mar 19, 2025
e0a9d26
hide overflow-x; translations is now fully accessible; Calculer TAEG …
odysseu Mar 22, 2025
3136ded
better tooltip for helpTauxAppreciation
odysseu Mar 22, 2025
f4188cf
node update
odysseu Mar 22, 2025
d76024e
add LICENCE
odysseu Mar 23, 2025
42d156b
jspdf and jspdf-autotable use lastest versions disponible, we test TA…
odysseu Mar 30, 2025
d1db8a3
Improved reporting: Adjusted the canvas size for the chart and update…
odysseu Mar 30, 2025
1ab993c
Updated Chart.js to version 4.4.8 and added new features to the PDF r…
odysseu Mar 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 26 additions & 17 deletions form-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,26 @@
const currentDurationValue = container.querySelector('input[name="duree-location-0"]').value.trim();

let newLoyer;
if (isValidNumber(currentLoyerValue) && isValidNumber(currentDurationValue)) {
// If both are valid numbers, create the new inputs
newLoyer = document.createElement('div');
newLoyer.className = 'loyer-container';
newLoyer.innerHTML = `
<input type="number" id="loyer-${loyerCount}" name="loyer-${loyerCount}" value="${currentLoyerValue}" placeholder="Loyer mensuel (€)" required>
<input type="number" step="0.01" id="duree-location-${loyerCount}" name="duree-location-${loyerCount}" value="${currentDurationValue}" placeholder="Durée (% de l'année)" required>
<button type="button" onclick="supprimerLoyer(this)">-</button>
`;

container.appendChild(newLoyer);
loyerCount++;
} else {
// Handle the case where the input fields are empty or not valid numbers
// You can show a message to the user, or just not add the new fields
console.log("Invalid input. Please check the form.");
try {
if (isValidNumber(currentLoyerValue) && isValidNumber(currentDurationValue)) {
// If both are valid numbers, create the new inputs
newLoyer = document.createElement('div');
newLoyer.className = 'loyer-container';
newLoyer.innerHTML = `
<input type="number" id="loyer-${loyerCount}" name="loyer-${loyerCount}" value="${currentLoyerValue}" placeholder="Loyer mensuel (€)" required>
<input type="number" step="0.01" id="duree-location-${loyerCount}" name="duree-location-${loyerCount}" value="${currentDurationValue}" placeholder="Durée (% de l'année)" required>
<button type="button" onclick="supprimerLoyer(this)">-</button>
`;
container.appendChild(newLoyer);
loyerCount++;
} else {
console.log("Invalid input detected:", {
loyer: currentLoyerValue,
duration: currentDurationValue
});
}
} catch (error) {
console.log("Error validating input:", error);
}

// Reset the fields to ensure next inputs will be added in the form
Expand All @@ -41,7 +45,12 @@
}

function isValidNumber(value) {
return !isNaN(parseFloat(value)) && isFinite(value) && value.trim() !== "";
try {
return !isNaN(parseFloat(value)) && isFinite(value) && value.trim() !== "";
} catch (error) {
console.error("Error checking if value is a valid number:", { value, error });
return false;
}
}

function supprimerLoyer(button) {
Expand Down
14 changes: 8 additions & 6 deletions handle-language.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,16 @@ function updateContent(translations) {
const tauxAppreciationLabel = document.querySelector('label[for="taux-appreciation"]');
const commissionLabel = document.querySelector('label[for="commission"]');
const apportLabel = document.querySelector('label[for="apport"]');
const tauxLabel = document.querySelector('label[for="taux"]');
const tauxInteretLabel = document.querySelector('label[for="taux-interet"]');
const dureePretLabel = document.querySelector('label[for="duree-pret"]');
const tauxAssuranceLabel = document.querySelector('label[for="taux-assurance"]');
const loyerFictifLabel = document.querySelector('label[for="loyer-fictif"]');
const tauxLoyerFictifLabel = document.querySelector('label[for="taux-loyer-fictif"]');
const taxeHabitationLabel = document.querySelector('label[for="taxe-habitation"]');
const taxeFonciereLabel = document.querySelector('label[for="taxe-fonciere"]');
const calculerButton = document.getElementById('calculer-button');
const pdfFilenameLabel = document.querySelector('label[for="pdf-filename"]');
const pdfFilenamePlaceHolder = document.getElementById('placerholder["pdf-filename"]');
const pdfFileNameLabel = document.querySelector('label[for="pdf-filename"]');
const pdfFileNamePlaceHolder = document.getElementById('placerholder["pdf-filename"]');
const telechargerButton = document.querySelector('#telecharger-button button');
const loyer0 = document.getElementById('loyer-0');
const dureeLocation0 = document.getElementById('duree-location-0');
Expand All @@ -77,15 +78,16 @@ function updateContent(translations) {
if (tauxAppreciationLabel) tauxAppreciationLabel.innerHTML = `${translations.tauxAppreciation} <span class="help-icon">? <span class="help-text">${translations.helpTauxAppreciation}</span></span>`;
if (commissionLabel) commissionLabel.innerHTML = `${translations.commission} <span class="help-icon">? <span class="help-text">${translations.helpCommission}</span></span>`;
if (apportLabel) apportLabel.innerHTML = `${translations.apport} <span class="help-icon">? <span class="help-text">${translations.helpApport}</span></span>`;
if (tauxLabel) tauxLabel.innerHTML = `${translations.taux} <span class="help-icon">? <span class="help-text">${translations.helpTaux}</span></span>`;
if (tauxInteretLabel) tauxInteretLabel.innerHTML = `${translations.tauxInteret} <span class="help-icon">? <span class="help-text">${translations.helpTauxInteret}</span></span>`;
if (dureePretLabel) dureePretLabel.innerHTML = `${translations.dureePret} <span class="help-icon">? <span class="help-text">${translations.helpDureePret}</span></span>`;
if (tauxAssuranceLabel) tauxAssuranceLabel.innerHTML = `${translations.tauxAssurance} <span class="help-icon">? <span class="help-text">${translations.helpTauxAssurance}</span></span>`;
if (loyerFictifLabel) loyerFictifLabel.innerHTML = `${translations.loyerFictif} <span class="help-icon">? <span class="help-text">${translations.helpLoyerFictif}</span></span>`;
if (tauxLoyerFictifLabel) tauxLoyerFictifLabel.innerHTML = `${translations.tauxLoyerFictif} <span class="help-icon">? <span class="help-text">${translations.helpTauxLoyerFictif}</span></span>`;
if (taxeHabitationLabel) taxeHabitationLabel.innerHTML = `${translations.taxeHabitation} <span class="help-icon">? <span class="help-text">${translations.helpTaxeHabitation}</span></span>`;
if (taxeFonciereLabel) taxeFonciereLabel.innerHTML = `${translations.taxeFonciere} <span class="help-icon">? <span class="help-text">${translations.helpTaxeFonciere}</span></span>`;
if (calculerButton) calculerButton.textContent = translations.generateReport;
if (pdfFilenameLabel) pdfFilenameLabel.textContent = translations.pdfFilename;
if (pdfFilenamePlaceHolder) pdfFilenamePlaceHolder.placeholder = translations.pdfFilenamePlaceHolder;
if (pdfFileNameLabel) pdfFileNameLabel.textContent = translations.pdfFileName;
if (pdfFileNamePlaceHolder) pdfFileNamePlaceHolder.placeholder = translations.pdfFileNamePlaceHolder;
if (telechargerButton) telechargerButton.textContent = translations.downloadPDF;
if (loyer0) loyer0.placeholder = translations.helpLoyerMensuel;
if (dureeLocation0) dureeLocation0.placeholder = translations.helpDureeLocation;
Expand Down
20 changes: 11 additions & 9 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@
</div>
<h1 id="section-titre">Calculette Projet Immobilier</h1>
<div class="header-right">
<a href="https://github.com/odysseu/python-app" target="_blank">
<a href="https://github.com/odysseu/python-app" target="_self">
<img src="logos/github-logo-light.png" alt="GitHub Logo" class="github-logo" id="github-logo">
<span class="logo-help">Aller au projet GitHub</span>
</a>
<label class="switch">
<input type="checkbox" id="dark-mode-toggle">
<span class="slider round"></span>
<span class="switch-help">Changer de mode</span>
</label>
<select id="language-select">
<option value="fr">Français 🇫🇷</option>
Expand All @@ -42,7 +41,7 @@ <h1 id="section-titre">Calculette Projet Immobilier</h1>
<div>
<h2 id="section-achat">Achat</h2>
<label for="prix">Prix du bien (€): <span class="help-icon">? <span class="help-text">Le prix d'achat du bien immobilier.</span></span></label>
<input type="number" id="prix" name="prix" value="300000" required>
<input type="number" step="0.01" id="prix" name="prix" value="300000" required>

<label for="notaire">Frais de notaire (%): <span class="help-icon">? <span class="help-text">Pourcentage des frais de notaire applicables (4 pour du neuf, 8 pour de l'ancien).</span></span></label>
<input type="number" step="0.01" id="notaire" name="notaire" value="8" required>
Expand All @@ -59,13 +58,16 @@ <h2 id="section-achat">Achat</h2>
<div>
<h2 id="section-emprunt">Emprunt</h2>
<label for="apport">Apport personnel (€): <span class="help-icon">? <span class="help-text">Montant de l'apport personnel.</span></span></label>
<input type="number" id="apport" name="apport" value="0" required>
<input type="number" step="0.01" id="apport" name="apport" value="0" required>

<label for="taux">Taux d'intérêt (%): <span class="help-icon">? <span class="help-text">Taux d'intérêt annuel du prêt immobilier.</span></span></label>
<input type="number" step="0.01" id="taux" name="taux" value="1" required>
<label for="taux-interet">Taux d'intérêt (%): <span class="help-icon">? <span class="help-text">Taux d'intérêt annuel du prêt immobilier.</span></span></label>
<input type="number" step="0.01" id="taux-interet" name="taux-interet" value="1" required>

<label for="taux-assurance">Assurance emprunteur (%): <span class="help-icon">? <span class="help-text">Pourcentage du prêt pour l'assurance emprunteur.</span></span></label>
<input type="number" step="0.01" id="taux-assurance" name="taux-assurance" value="0" required>

<label for="duree-pret">Durée du prêt (années): <span class="help-icon">? <span class="help-text">Durée totale du prêt en années.</span></span></label>
<input type="number" id="duree-pret" name="duree-pret" value="20" required>
<input type="number" step="1" id="duree-pret" name="duree-pret" value="20" required>
</div>
</div>
<h2 id="section-financement">Financement</h2>
Expand Down Expand Up @@ -98,9 +100,9 @@ <h2 id="section-financement">Financement</h2>
<div id="rapport-bouton"></div>
<div id="telecharger-button"></div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jspdf@2.5.2/dist/jspdf.umd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jspdf-autotable@3.8.4/dist/jspdf.plugin.autotable.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.js"></script>
<script src="form-handler.js"></script>
<script src="report-handler.js"></script>
<script src="dark-mode.js"></script>
Expand Down
29 changes: 18 additions & 11 deletions report-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ function genererRapport() {
const tauxAppreciation = parseFloat(document.getElementById('taux-appreciation').value) / 100;
const commission = parseFloat(document.getElementById('commission').value) / 100;
const apport = parseFloat(document.getElementById('apport').value);
const taux = parseFloat(document.getElementById('taux').value) / 100;
const tauxInteret = parseFloat(document.getElementById('taux-interet').value) / 100;
const dureePret = parseInt(document.getElementById('duree-pret').value);
const tauxAssurance = parseInt(document.getElementById('taux-assurance').value);
const loyerFictif = parseFloat(document.getElementById('loyer-fictif').value);
const taxeHabitation = parseFloat(document.getElementById('taxe-habitation').value);
const taxeFonciere = parseFloat(document.getElementById('taxe-fonciere').value);
Expand All @@ -20,7 +21,7 @@ function genererRapport() {
const fraisCommission = prix * commission;
const totalAchat = prix + fraisNotaire + fraisCommission;
const montantEmprunte = totalAchat - apport;
const mensualite = taux === 0 ? montantEmprunte / (dureePret * 12) : (montantEmprunte * taux / 12) / (1 - Math.pow(1 + taux / 12, -dureePret * 12));
const mensualite = tauxInteret === 0 ? montantEmprunte / (dureePret * 12) : (montantEmprunte * tauxInteret / 12) / (1 - Math.pow(1 + tauxInteret / 12, -dureePret * 12));
const coutTotalEmprunt = mensualite * dureePret * 12;
const coutTotalInterets = coutTotalEmprunt - montantEmprunte;
const cumulLoyers = extraireLoyers();
Expand Down Expand Up @@ -59,7 +60,7 @@ function genererRapport() {
<td style="text-align: right;">${totalAchat.toFixed(2)} €</td>
</tr>
<tr>
<td>${translations.copropriete}:</td>
<td>${translations.reportCopropriete}:</td>
<td style="text-align: right;">${fraisCopropriete.toFixed(2)} €</td>
</tr>
</table>
Expand All @@ -71,9 +72,13 @@ function genererRapport() {
<td>${translations.reportMontantEmprunte}:</td>
<td style="text-align: right;">${montantEmprunte.toFixed(2)} €</td>
</tr>
<tr>
<td>${translations.reportTauxAssurance}:</td>
<td style="text-align: right;">${(tauxAssurance * 100).toFixed(2)} %</td>
</tr>
<tr>
<td>${translations.reportTauxInteret}:</td>
<td style="text-align: right;">${(taux * 100).toFixed(2)} %</td>
<td style="text-align: right;">${(tauxInteret * 100).toFixed(2)} %</td>
</tr>
<tr>
<td>${translations.reportMensualite}:</td>
Expand Down Expand Up @@ -134,7 +139,7 @@ function genererRapport() {
genererGraphique(cumulLocation, cumulAchat, maxDuree);

const rapportBouton = `
<label for="pdf-filename">${translations.pdfFilename}</label>
<label for="pdf-filename">${translations.pdfFileName}</label>
<input type="text" id="pdf-filename" name="pdf-filename" placeholder="rapport-immobilier.pdf" required>
<button id="telecharger-button">${translations.downloadPDF}</button>
`;
Expand Down Expand Up @@ -212,8 +217,9 @@ function telechargerPDF() {
const tauxLoyerFictif = parseFloat(document.getElementById('taux-loyer-fictif').value) / 100;
const commission = parseFloat(document.getElementById('commission').value) / 100;
const apport = parseFloat(document.getElementById('apport').value);
const taux = parseFloat(document.getElementById('taux').value) / 100;
const tauxInteret = parseFloat(document.getElementById('taux-interet').value) / 100;
const dureePret = parseInt(document.getElementById('duree-pret').value);
const tauxAssurance = parseInt(document.getElementById('taux-assurance').value);
const loyerFictif = parseFloat(document.getElementById('loyer-fictif').value);
const taxeHabitation = parseFloat(document.getElementById('taxe-habitation').value);
const taxeFonciere = parseFloat(document.getElementById('taxe-fonciere').value);
Expand All @@ -222,7 +228,7 @@ function telechargerPDF() {
const fraisCommission = prix * commission;
const totalAchat = prix + fraisNotaire + fraisCommission;
const montantEmprunte = totalAchat - apport;
const mensualite = taux === 0 ? montantEmprunte / (dureePret * 12) : (montantEmprunte * taux / 12) / (1 - Math.pow(1 + taux / 12, -dureePret * 12));
const mensualite = tauxInteret === 0 ? montantEmprunte / (dureePret * 12) : (montantEmprunte * tauxInteret / 12) / (1 - Math.pow(1 + tauxInteret / 12, -dureePret * 12));
const coutTotalEmprunt = mensualite * dureePret * 12;
const coutTotalInterets = coutTotalEmprunt - montantEmprunte;

Expand All @@ -235,7 +241,7 @@ function telechargerPDF() {
[`${translations.reportTauxAppreciation}`, `${(tauxAppreciation * 100).toFixed(2)} %`],
[`${translations.reportCommission}`, `${fraisCommission.toFixed(2)} €`],
[`${translations.reportTotalAchat}`, `${totalAchat.toFixed(2)} €`],
[`${translations.copropriete}`, `${copropriete.toFixed(2)} €`]
[`${translations.reportCopropriete}`, `${copropriete.toFixed(2)} €`]
],
startY: 20
});
Expand All @@ -245,7 +251,8 @@ function telechargerPDF() {
head: [[`${translations.reportEmprunt}`, `${translations.reportPrix}`]],
body: [
[`${translations.reportMontantEmprunte}`, `${montantEmprunte.toFixed(0)} €`],
[`${translations.reportTauxInteret}`, `${(taux * 100).toFixed(2)} %`],
[`${translations.reportTauxInteret}`, `${(tauxInteret * 100).toFixed(2)} %`],
[`${translations.reportTauxAssurance}`, `${(tauxAssurance * 100).toFixed(2)} %`],
[`${translations.reportMensualite}`, `${mensualite.toFixed(0)} €`],
[`${translations.reportInteretsTotaux}`, `${coutTotalInterets.toFixed(0)} €`],
[`${translations.reportCoutTotalEmprunt}`, `${coutTotalEmprunt.toFixed(0)} €`]
Expand All @@ -270,8 +277,8 @@ function telechargerPDF() {
const chartImage = chart.toDataURL('image/png');
doc.addImage(chartImage, 'PNG', 15, 200, 180, 90);

const pdfFilename = (document.getElementById('pdf-filename').value || translations.pdfFilenamePlaceHolder) + ".pdf";
doc.save(pdfFilename);
const pdfFileName = (document.getElementById('pdf-filename').value || translations.pdfFileNamePlaceHolder) + ".pdf";
doc.save(pdfFileName);

restaurerMode(wasDarkMode);
}
15 changes: 4 additions & 11 deletions styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,12 @@ body.dark-mode .sticky-header {
}

.header-left, .header-right {
flex: 1;
display: flex;
align-items: center;
}

.header-left {
flex: 1;
}

.header-right {
flex: 1;
justify-content: flex-end;
}

Expand All @@ -69,8 +65,7 @@ body.dark-mode .sticky-header {

/* Help logo and switch styles */
.header-left .logo-help,
.header-right .logo-help,
.header-right .switch-help {
.header-right .logo-help {
visibility: hidden;
max-width: 200px;
color: #ffffff;
Expand All @@ -84,8 +79,7 @@ body.dark-mode .sticky-header {
}

.header-left .logo-help::after,
.header-right .logo-help::after,
.header-right .switch-help::after {
.header-right .logo-help::after {
content: "";
position: absolute;
top: -5px;
Expand All @@ -97,8 +91,7 @@ body.dark-mode .sticky-header {
}

.header-left .logo-container:hover .logo-help,
.header-right .logo-container:hover .logo-help,
.header-right .switch:hover .switch-help {
.header-right .logo-container:hover .logo-help {
visibility: visible;
opacity: 1;
}
Expand Down
Loading