diff --git a/README.md b/README.md index c1c5d7d..6f254cd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Split the Bill +# Split the Trip -A web application for splitting bills among friends during trips. +A web application for splitting expenses among friends during trips. ## Features diff --git a/package-lock.json b/package-lock.json index 2dbd877..6801bf4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2323,6 +2323,66 @@ "node": ">=14.0.0" } }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.4.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.4.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.0", + "dev": true, + "inBundle": true, + "license": "0BSD", + "optional": true + }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", diff --git a/src/app/page.tsx b/src/app/page.tsx index 09f74e7..3adac63 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,7 +2,7 @@ import React, { useState } from 'react'; import { Trip, User, Expense, Payment } from '@/types'; -import { calculatePayments, debugBalances } from '@/utils/billSplitting'; +import { calculatePayments, debugBalances, downloadPaymentSummary } from '@/utils/billSplitting'; export default function Home() { const [trip, setTrip] = useState({ @@ -22,6 +22,8 @@ export default function Home() { const [payments, setPayments] = useState([]); const [showBalanceDetails, setShowBalanceDetails] = useState(false); + const [isEditingTripName, setIsEditingTripName] = useState(false); + const [tempTripName, setTempTripName] = useState(''); const addUser = () => { if (!newUserName.trim()) return; @@ -150,13 +152,33 @@ export default function Home() { })); }; + const saveTripName = () => { + if (tempTripName.trim()) { + setTrip(prev => ({ + ...prev, + name: tempTripName.trim() + })); + } + setIsEditingTripName(false); + }; + + const cancelEditTripName = () => { + setTempTripName(''); + setIsEditingTripName(false); + }; + + const startEditingTripName = () => { + setTempTripName(trip.name); + setIsEditingTripName(true); + }; + return (
{/* Header */}

- Split the Bill + Split the Trip

Easily split expenses and calculate payments for your group's trip @@ -166,9 +188,35 @@ export default function Home() { {/* Trip Name and Demo Controls */}

-

- Trip: {trip.name} -

+
+ {isEditingTripName ? ( +
+ setTempTripName(e.target.value)} + className="flex-1 p-4 border-2 border-blue-300 rounded-xl focus:border-blue-500 focus:ring-0 bg-white dark:bg-slate-700 text-slate-800 dark:text-white placeholder-slate-500 text-lg font-medium transition-colors" + onKeyPress={(e) => e.key === 'Enter' && saveTripName()} + /> + + +
+ ) : ( +

+ Trip: {trip.name} +

+ )} +
- {/* Algorithm Note */} -
-
-
- - - -
-
+ {/* Download Button */} +
+
diff --git a/src/utils/billSplitting.ts b/src/utils/billSplitting.ts index c32d2d4..4e26243 100644 --- a/src/utils/billSplitting.ts +++ b/src/utils/billSplitting.ts @@ -156,4 +156,45 @@ export function debugBalances(trip: Trip): { directDebts, netPayments }; +} + +/** + * Generates CSV content for payment summary download + */ +export function generatePaymentSummaryCSV(payments: Payment[], tripName: string): string { + const headers = ['From', 'To', 'Amount']; + const rows = payments.map(payment => [ + payment.from.name, + payment.to.name, + `$${payment.amount.toFixed(2)}` + ]); + + const csvContent = [ + `Payment Summary for ${tripName}`, + `Generated on ${new Date().toLocaleDateString()}`, + '', + headers.join(','), + ...rows.map(row => row.join(',')) + ].join('\n'); + + return csvContent; +} + +/** + * Triggers download of payment summary as CSV file + */ +export function downloadPaymentSummary(payments: Payment[], tripName: string): void { + const csvContent = generatePaymentSummaryCSV(payments, tripName); + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + const link = document.createElement('a'); + + if (link.download !== undefined) { + const url = URL.createObjectURL(blob); + link.setAttribute('href', url); + link.setAttribute('download', `${tripName.replace(/\s+/g, '_')}_payment_summary.csv`); + link.style.visibility = 'hidden'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } } \ No newline at end of file