From bc6cd7172eabf48505f456962dfcefff7913f625 Mon Sep 17 00:00:00 2001 From: Izan Gil <66965250+SrIzan10@users.noreply.github.com> Date: Fri, 28 Mar 2025 16:37:50 +0100 Subject: [PATCH] dark mode and ui update --- index.html | 549 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 344 insertions(+), 205 deletions(-) diff --git a/index.html b/index.html index bdb69db..c31db21 100644 --- a/index.html +++ b/index.html @@ -1,32 +1,61 @@ - + Group Payment Tracker @@ -389,6 +545,14 @@

Group Payment Tracker

+
@@ -492,7 +656,7 @@

Total cost breakdown:

@@ -634,6 +798,9 @@ const tabs = document.querySelectorAll('.tab'); const tabContents = document.querySelectorAll('.tab-content'); const alertsContainer = document.getElementById('alerts'); + const themeToggle = document.getElementById('theme-toggle'); + const moonIcon = document.getElementById('moon-icon'); + const sunIcon = document.getElementById('sun-icon'); // Forms const addGroupPaymentForm = document.getElementById('add-group-payment-form'); @@ -671,6 +838,45 @@ // List of users (You are always included by default) const users = [{ name: 'You', isPaid: true, isYou: true, amount: 0 }]; + // Theme handling + function toggleTheme() { + const currentTheme = document.documentElement.getAttribute('data-theme'); + const newTheme = currentTheme === 'light' ? 'dark' : 'light'; + + document.documentElement.setAttribute('data-theme', newTheme); + localStorage.setItem('theme', newTheme); + + // Update theme toggle icons + if (newTheme === 'dark') { + moonIcon.classList.add('hidden'); + sunIcon.classList.remove('hidden'); + } else { + moonIcon.classList.remove('hidden'); + sunIcon.classList.add('hidden'); + } + } + + // Set initial theme based on preference + function setInitialTheme() { + const savedTheme = localStorage.getItem('theme'); + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + const theme = savedTheme || (prefersDark ? 'dark' : 'light'); + + document.documentElement.setAttribute('data-theme', theme); + + // Set correct icon + if (theme === 'dark') { + moonIcon.classList.add('hidden'); + sunIcon.classList.remove('hidden'); + } else { + moonIcon.classList.remove('hidden'); + sunIcon.classList.add('hidden'); + } + } + + // Add theme toggle event listener + themeToggle.addEventListener('click', toggleTheme); + // Event Listeners connectBtn.addEventListener('click', connectToAPI); @@ -681,11 +887,9 @@ }); }); - // Modified event listener for group payment form addGroupPaymentForm.addEventListener('submit', addGroupPayment); addIndividualPaymentForm.addEventListener('submit', addIndividualPayment); - // Add user button event listener addUserBtn.addEventListener('click', addUser); newUserInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { @@ -694,20 +898,16 @@ } }); - // Update price when amount changes or payment mode changes groupAmountInput.addEventListener('input', calculatePricePerPerson); perPersonPriceInput.addEventListener('input', updateTotalFromPerPersonPrice); splitEqualRadio.addEventListener('change', onPaymentModeChange); perPersonRadio.addEventListener('change', onPaymentModeChange); - // Initial setup for payment mode UI function onPaymentModeChange() { const paymentMode = document.querySelector('input[name="payment-mode"]:checked').value; if (paymentMode === 'split-equal') { - // Equal split mode - show the per-person price input perPersonContainer.style.display = 'flex'; - // If the per-person price isn't set but total amount is, calculate it if (!perPersonPriceInput.value && groupAmountInput.value) { const totalAmount = parseFloat(groupAmountInput.value) || 0; const pricePerPerson = totalAmount / users.length; @@ -717,7 +917,6 @@ updateTotalFromPerPersonPrice(); } } else { - // Custom split mode - hide the per-person price input perPersonContainer.style.display = 'none'; calculatePricePerPerson(); } @@ -725,23 +924,16 @@ renderUsers(); } - // New function: update total amount from per-person price function updateTotalFromPerPersonPrice() { const pricePerPerson = parseFloat(perPersonPriceInput.value) || 0; const totalUsers = users.length; const totalAmount = pricePerPerson * totalUsers; - // Update total amount field groupAmountInput.value = totalAmount.toFixed(2); - - // Update the auto-calculate text totalAutoCalculate.textContent = `Total: €${totalAmount.toFixed(2)} (${pricePerPerson.toFixed(2)} × ${totalUsers})`; - - // Also update the breakdown calculatePricePerPerson(); } - // Cookie functions for storing and retrieving login data function setCookie(name, value, days) { const date = new Date(); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); @@ -766,7 +958,6 @@ return ""; } - // Check for saved credentials on page load function checkSavedCredentials() { const savedUrl = getCookie("apiUrl"); const savedToken = getCookie("apiToken"); @@ -778,7 +969,6 @@ } } - // Connect to the API function connectToAPI() { apiUrl = apiUrlInput.value.trim(); apiToken = apiTokenInput.value.trim(); @@ -788,11 +978,9 @@ return; } - // Test the connection fetchData(`${apiUrl}/api/database/fields/table/682/`, apiToken) .then(data => { if (data && Array.isArray(data)) { - // Save credentials if remember me is checked if (rememberMeCheckbox.checked) { setCookie("apiUrl", apiUrl, 30); setCookie("apiToken", apiToken, 30); @@ -801,11 +989,8 @@ authSection.classList.add('hidden'); appContent.classList.remove('hidden'); - // Load initial data loadGroupPayments(); loadIndividualPayments(); - - showAlert('Successfully connected to the database!', 'success'); } else { showAlert('Failed to connect. Please check your API URL and token.', 'danger'); } @@ -815,7 +1000,6 @@ }); } - // Fetch data from API async function fetchData(url, token, options = {}) { try { const defaultOptions = { @@ -840,7 +1024,6 @@ } } - // Load group payments data function loadGroupPayments() { groupLoading.style.display = 'flex'; @@ -859,7 +1042,6 @@ }); } - // Load individual payments data function loadIndividualPayments() { individualLoading.style.display = 'flex'; @@ -877,7 +1059,6 @@ }); } - // Render group payments table function renderGroupPayments() { groupPaymentsList.innerHTML = ''; @@ -906,7 +1087,6 @@ }); } - // Render individual payments table function renderIndividualPayments() { individualPaymentsList.innerHTML = ''; @@ -939,7 +1119,6 @@ }); } - // Populate group payments dropdown function populateGroupPaymentsDropdown() { individualGroupSelect.innerHTML = ''; @@ -951,7 +1130,6 @@ }); } - // Add a new user function addUser() { const userName = newUserInput.value.trim(); @@ -967,25 +1145,22 @@ } } - // Remove a user function removeUser(index) { - if (!users[index].isYou) { // Prevent removing yourself + if (!users[index].isYou) { users.splice(index, 1); renderUsers(); calculatePricePerPerson(); } } - // Toggle paid status function togglePaid(index) { - if (!users[index].isYou) { // Your payment status is fixed + if (!users[index].isYou) { users[index].isPaid = !users[index].isPaid; renderUsers(); calculatePricePerPerson(); } } - // Update user amount (for per-person payment mode) function updateUserAmount(index, amount) { if (!users[index].isYou) { users[index].amount = parseFloat(amount) || 0; @@ -993,7 +1168,6 @@ } } - // Render the user list function renderUsers() { userList.innerHTML = ''; const paymentMode = document.querySelector('input[name="payment-mode"]:checked').value; @@ -1003,7 +1177,7 @@ userItem.className = 'user-item'; let userHtml = ` - ${user.name}${user.isYou ? ' (already paid)' : ''} + ${user.name}
Paid
`; - // Add amount input field for per-person payment mode if (paymentMode === 'per-person' && !user.isYou) { userHtml += `
@@ -1030,7 +1203,6 @@ userItem.innerHTML = userHtml; userList.appendChild(userItem); - // Add event listeners if (!user.isYou) { const checkbox = userItem.querySelector(`#is-paid-${index}`); const removeBtn = userItem.querySelector('.btn-remove'); @@ -1038,7 +1210,6 @@ checkbox.addEventListener('change', () => togglePaid(index)); removeBtn.addEventListener('click', () => removeUser(index)); - // Add event listener for amount input if in per-person mode if (paymentMode === 'per-person') { const amountInput = userItem.querySelector(`#amount-${index}`); amountInput.addEventListener('input', (e) => updateUserAmount(index, e.target.value)); @@ -1049,21 +1220,17 @@ totalUsersEl.textContent = users.length; } - // Calculate the price per person function calculatePricePerPerson() { const totalAmount = parseFloat(groupAmountInput.value) || 0; const paymentMode = document.querySelector('input[name="payment-mode"]:checked').value; - // Re-render users when payment mode changes to update the UI renderUsers(); if (totalAmount > 0) { if (paymentMode === 'split-equal') { - // Split equally mode - calculate from per-person price input const totalUsers = users.length; const pricePerPerson = parseFloat(perPersonPriceInput.value) || (totalAmount / totalUsers); - // Update the per person price input if it's empty or doesn't match the calculation if (perPersonPriceInput.value === '' || Math.abs(pricePerPerson * totalUsers - totalAmount) > 0.01) { perPersonPriceInput.value = (totalAmount / totalUsers).toFixed(2); totalAutoCalculate.textContent = `Total: €${totalAmount.toFixed(2)} (${perPersonPriceInput.value} × ${totalUsers})`; @@ -1071,7 +1238,6 @@ pricePerPersonEl.textContent = `€${pricePerPerson.toFixed(2)}`; - // Update cost breakdown costBreakdownEl.innerHTML = ''; users.forEach(user => { const li = document.createElement('li'); @@ -1085,10 +1251,8 @@ costBreakdownEl.appendChild(li); }); } else { - // Custom per-person mode pricePerPersonEl.textContent = `Varies (set individually)`; - // Calculate total assigned amount let assignedTotal = 0; users.forEach(user => { if (!user.isYou) { @@ -1096,11 +1260,9 @@ } }); - // Your amount is what's left const yourAmount = Math.max(0, totalAmount - assignedTotal); users[0].amount = yourAmount; - // Update cost breakdown costBreakdownEl.innerHTML = ''; users.forEach(user => { const li = document.createElement('li'); @@ -1116,11 +1278,10 @@ } } else { pricePerPersonEl.textContent = '€0.00'; - costBreakdownEl.innerHTML = '
  • You: €0.00 (already paid)
  • '; + costBreakdownEl.innerHTML = '
  • You: €0.00
  • '; } } - // Add new group payment with individual expenses function addGroupPayment(e) { e.preventDefault(); @@ -1142,7 +1303,6 @@ "Paid overall": paid }; - // First create the group payment fetchData( `${apiUrl}/api/database/rows/table/682/?user_field_names=true`, apiToken, @@ -1154,18 +1314,15 @@ .then((response) => { showAlert('Group payment added successfully!', 'success'); - // Get the new group payment ID const newGroupPaymentId = response.id; - // Create individual payments for each non-you user let individualPromises = []; if (paymentMode === 'split-equal') { - // Split equally using the per person price const pricePerPerson = parseFloat(perPersonPriceInput.value); individualPromises = users - .filter(user => !user.isYou) // Skip "You" since you already paid + .filter(user => !user.isYou) .map(user => { const individualData = { "For": user.name, @@ -1185,9 +1342,8 @@ ); }); } else { - // Custom per-person payment individualPromises = users - .filter(user => !user.isYou && user.amount > 0) // Skip "You" and users with 0 amount + .filter(user => !user.isYou && user.amount > 0) .map(user => { const individualData = { "For": user.name, @@ -1208,26 +1364,22 @@ }); } - // Wait for all individual payments to be created return Promise.all(individualPromises); }) .then(() => { showAlert('All individual payments created!', 'success'); addGroupPaymentForm.reset(); - // Reset users to just "You" users.length = 1; users[0].amount = 0; renderUsers(); calculatePricePerPerson(); - // Set payment mode back to split-equal splitEqualRadio.checked = true; perPersonContainer.style.display = 'flex'; perPersonPriceInput.value = ''; totalAutoCalculate.textContent = ''; - // Reload data loadGroupPayments(); loadIndividualPayments(); }) @@ -1236,7 +1388,6 @@ }); } - // Add new individual payment (original function) function addIndividualPayment(e) { e.preventDefault(); @@ -1272,7 +1423,6 @@ }); } - // Delete a group payment function deleteGroupPayment(id) { if (confirm('Are you sure you want to delete this group payment?')) { fetchData( @@ -1285,7 +1435,7 @@ .then(() => { showAlert('Group payment deleted successfully!', 'success'); loadGroupPayments(); - loadIndividualPayments(); // Reload individual payments as they might be linked + loadIndividualPayments(); }) .catch(error => { showAlert(`Failed to delete group payment: ${error.message}`, 'danger'); @@ -1293,7 +1443,6 @@ } } - // Delete an individual payment function deleteIndividualPayment(id) { if (confirm('Are you sure you want to delete this individual payment?')) { fetchData( @@ -1313,7 +1462,6 @@ } } - // Toggle group payment paid status function toggleGroupPaid(id, status) { fetchData( `${apiUrl}/api/database/rows/table/682/${id}/?user_field_names=true`, @@ -1332,7 +1480,6 @@ }); } - // Toggle individual payment paid status function toggleIndividualPaid(id, status) { fetchData( `${apiUrl}/api/database/rows/table/684/${id}/?user_field_names=true`, @@ -1351,26 +1498,28 @@ }); } - // Update summary data function updateSummary() { - // Calculate totals let totalGroup = 0; let totalIndividual = 0; + let totalUnpaid = 0; groupPayments.forEach(payment => { totalGroup += parseFloat(payment.Money); }); individualPayments.forEach(payment => { - totalIndividual += parseFloat(payment.Money); + const amount = parseFloat(payment.Money); + totalIndividual += amount; + + if (!payment.Paid) { + totalUnpaid += amount; + } }); - // Update summary elements totalGroupEl.textContent = `€${totalGroup.toFixed(2)}`; totalIndividualEl.textContent = `€${totalIndividual.toFixed(2)}`; - remainingEl.textContent = `€${(totalGroup - totalIndividual).toFixed(2)}`; + remainingEl.textContent = `€${totalUnpaid.toFixed(2)}`; - // Generate summary per person const personSummary = {}; individualPayments.forEach(payment => { @@ -1390,7 +1539,6 @@ } }); - // Render person summary summaryList.innerHTML = ''; if (Object.keys(personSummary).length === 0) { @@ -1416,7 +1564,6 @@ }); } - // Show alert message function showAlert(message, type) { const alert = document.createElement('div'); alert.className = `alert alert-${type}`; @@ -1429,7 +1576,6 @@ }, 5000); } - // Activate tab function activateTab(tabId) { tabs.forEach(tab => { if (tab.getAttribute('data-tab') === tabId) { @@ -1447,25 +1593,18 @@ } }); - // Refresh data when switching to summary tab if (tabId === 'summary') { updateSummary(); } } - // Initialize the UI function init() { - // Initialize the user list + setInitialTheme(); renderUsers(); - - // Initial payment mode setup onPaymentModeChange(); - - // Check for saved credentials checkSavedCredentials(); } - // Run initialization init();