Add Prettier for TypeScript formatting
- Install prettier - Configure .prettierrc.json and .prettierignore - Add npm scripts: format, format:check - Add Makefile target: format-frontend - Format all frontend files
This commit is contained in:
parent
4b394b0698
commit
37de6f70e0
44 changed files with 906 additions and 856 deletions
|
|
@ -234,14 +234,17 @@ export default function BookingPage() {
|
|||
const [isLoadingAvailability, setIsLoadingAvailability] = useState(true);
|
||||
|
||||
// Memoize dates to prevent infinite re-renders
|
||||
const dates = useMemo(() => getDateRange(minAdvanceDays, maxAdvanceDays), [minAdvanceDays, maxAdvanceDays]);
|
||||
const dates = useMemo(
|
||||
() => getDateRange(minAdvanceDays, maxAdvanceDays),
|
||||
[minAdvanceDays, maxAdvanceDays]
|
||||
);
|
||||
|
||||
const fetchSlots = useCallback(async (date: Date) => {
|
||||
setIsLoadingSlots(true);
|
||||
setError(null);
|
||||
setAvailableSlots([]);
|
||||
setSelectedSlot(null);
|
||||
|
||||
|
||||
try {
|
||||
const dateStr = formatDate(date);
|
||||
const data = await api.get<AvailableSlotsResponse>(`/api/booking/slots?date=${dateStr}`);
|
||||
|
|
@ -261,7 +264,7 @@ export default function BookingPage() {
|
|||
const fetchAllAvailability = async () => {
|
||||
setIsLoadingAvailability(true);
|
||||
const availabilitySet = new Set<string>();
|
||||
|
||||
|
||||
// Fetch availability for all dates in parallel
|
||||
const promises = dates.map(async (date) => {
|
||||
try {
|
||||
|
|
@ -275,7 +278,7 @@ export default function BookingPage() {
|
|||
console.error(`Failed to fetch availability for ${formatDate(date)}:`, err);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
await Promise.all(promises);
|
||||
setDatesWithAvailability(availabilitySet);
|
||||
setIsLoadingAvailability(false);
|
||||
|
|
@ -307,20 +310,22 @@ export default function BookingPage() {
|
|||
|
||||
const handleBook = async () => {
|
||||
if (!selectedSlot) return;
|
||||
|
||||
|
||||
setIsBooking(true);
|
||||
setError(null);
|
||||
|
||||
|
||||
try {
|
||||
const appointment = await api.post<AppointmentResponse>("/api/booking", {
|
||||
slot_start: selectedSlot.start_time,
|
||||
note: note || null,
|
||||
});
|
||||
|
||||
setSuccessMessage(`Appointment booked for ${formatTime(appointment.slot_start)} - ${formatTime(appointment.slot_end)}`);
|
||||
|
||||
setSuccessMessage(
|
||||
`Appointment booked for ${formatTime(appointment.slot_start)} - ${formatTime(appointment.slot_end)}`
|
||||
);
|
||||
setSelectedSlot(null);
|
||||
setNote("");
|
||||
|
||||
|
||||
// Refresh slots to show the booked one is gone
|
||||
if (selectedDate) {
|
||||
await fetchSlots(selectedDate);
|
||||
|
|
@ -359,13 +364,9 @@ export default function BookingPage() {
|
|||
Select a date to see available {slotDurationMinutes}-minute slots
|
||||
</p>
|
||||
|
||||
{successMessage && (
|
||||
<div style={styles.successBanner}>{successMessage}</div>
|
||||
)}
|
||||
{successMessage && <div style={styles.successBanner}>{successMessage}</div>}
|
||||
|
||||
{error && (
|
||||
<div style={styles.errorBanner}>{error}</div>
|
||||
)}
|
||||
{error && <div style={styles.errorBanner}>{error}</div>}
|
||||
|
||||
{/* Date Selection */}
|
||||
<div style={styles.section}>
|
||||
|
|
@ -376,7 +377,7 @@ export default function BookingPage() {
|
|||
const isSelected = selectedDate && formatDate(selectedDate) === dateStr;
|
||||
const hasAvailability = datesWithAvailability.has(dateStr);
|
||||
const isDisabled = !hasAvailability || isLoadingAvailability;
|
||||
|
||||
|
||||
return (
|
||||
<button
|
||||
key={dateStr}
|
||||
|
|
@ -404,13 +405,14 @@ export default function BookingPage() {
|
|||
{selectedDate && (
|
||||
<div style={styles.section}>
|
||||
<h2 style={styles.sectionTitle}>
|
||||
Available Slots for {selectedDate.toLocaleDateString("en-US", {
|
||||
weekday: "long",
|
||||
month: "long",
|
||||
day: "numeric"
|
||||
Available Slots for{" "}
|
||||
{selectedDate.toLocaleDateString("en-US", {
|
||||
weekday: "long",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})}
|
||||
</h2>
|
||||
|
||||
|
||||
{isLoadingSlots ? (
|
||||
<div style={styles.emptyState}>Loading slots...</div>
|
||||
) : availableSlots.length === 0 ? (
|
||||
|
|
@ -442,27 +444,28 @@ export default function BookingPage() {
|
|||
<div style={styles.confirmCard}>
|
||||
<h3 style={styles.confirmTitle}>Confirm Booking</h3>
|
||||
<p style={styles.confirmTime}>
|
||||
<strong>Time:</strong> {formatTime(selectedSlot.start_time)} - {formatTime(selectedSlot.end_time)}
|
||||
<strong>Time:</strong> {formatTime(selectedSlot.start_time)} -{" "}
|
||||
{formatTime(selectedSlot.end_time)}
|
||||
</p>
|
||||
|
||||
|
||||
<div>
|
||||
<label style={styles.inputLabel}>
|
||||
Note (optional, max {noteMaxLength} chars)
|
||||
</label>
|
||||
<label style={styles.inputLabel}>Note (optional, max {noteMaxLength} chars)</label>
|
||||
<textarea
|
||||
value={note}
|
||||
onChange={(e) => setNote(e.target.value.slice(0, noteMaxLength))}
|
||||
placeholder="Add a note about your appointment..."
|
||||
style={styles.textarea}
|
||||
/>
|
||||
<div style={{
|
||||
...styles.charCount,
|
||||
...(note.length >= noteMaxLength ? styles.charCountWarning : {}),
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
...styles.charCount,
|
||||
...(note.length >= noteMaxLength ? styles.charCountWarning : {}),
|
||||
}}
|
||||
>
|
||||
{note.length}/{noteMaxLength}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div style={styles.buttonRow}>
|
||||
<button
|
||||
onClick={handleBook}
|
||||
|
|
@ -488,4 +491,3 @@ export default function BookingPage() {
|
|||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue