feat: Add customer appointment details modal and ATM-style currency input
- Add appointment detail modal to CustomerDashboard with payment info display - Shows service, date/time, duration, status, and notes - Displays payment summary: service price, deposit paid, payment made, amount due - Print receipt functionality with secure DOM manipulation - Cancel appointment button for upcoming appointments - Add CurrencyInput component for ATM-style price entry - Digits entered as cents, shift left as more digits added (e.g., "1234" → $12.34) - Robust input validation: handles keyboard, mobile, paste, drop, IME - Only allows integer digits (0-9) - Update useAppointments hook to map payment fields from backend - Converts amounts from cents to dollars for display - Update Services page to use CurrencyInput for price and deposit fields 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,14 @@ export const useAppointments = (filters?: AppointmentFilters) => {
|
||||
durationMinutes: a.duration_minutes || calculateDuration(a.start_time, a.end_time),
|
||||
status: a.status as AppointmentStatus,
|
||||
notes: a.notes || '',
|
||||
// Payment fields (amounts stored in cents, convert to dollars for display)
|
||||
depositAmount: a.deposit_amount ? parseFloat(a.deposit_amount) / 100 : null,
|
||||
depositTransactionId: a.deposit_transaction_id || '',
|
||||
finalPrice: a.final_price ? parseFloat(a.final_price) / 100 : null,
|
||||
finalChargeTransactionId: a.final_charge_transaction_id || '',
|
||||
isVariablePricing: a.is_variable_pricing || false,
|
||||
remainingBalance: a.remaining_balance ? parseFloat(a.remaining_balance) / 100 : null,
|
||||
overpaidAmount: a.overpaid_amount ? parseFloat(a.overpaid_amount) / 100 : null,
|
||||
}));
|
||||
},
|
||||
});
|
||||
@@ -85,6 +93,14 @@ export const useAppointment = (id: string) => {
|
||||
durationMinutes: data.duration_minutes || calculateDuration(data.start_time, data.end_time),
|
||||
status: data.status as AppointmentStatus,
|
||||
notes: data.notes || '',
|
||||
// Payment fields (amounts stored in cents, convert to dollars for display)
|
||||
depositAmount: data.deposit_amount ? parseFloat(data.deposit_amount) / 100 : null,
|
||||
depositTransactionId: data.deposit_transaction_id || '',
|
||||
finalPrice: data.final_price ? parseFloat(data.final_price) / 100 : null,
|
||||
finalChargeTransactionId: data.final_charge_transaction_id || '',
|
||||
isVariablePricing: data.is_variable_pricing || false,
|
||||
remainingBalance: data.remaining_balance ? parseFloat(data.remaining_balance) / 100 : null,
|
||||
overpaidAmount: data.overpaid_amount ? parseFloat(data.overpaid_amount) / 100 : null,
|
||||
};
|
||||
},
|
||||
enabled: !!id,
|
||||
|
||||
Reference in New Issue
Block a user