diff --git a/src/client/components/admin-bookings.tsx b/src/client/components/admin-bookings.tsx
index 522c9e4..610db15 100644
--- a/src/client/components/admin-bookings.tsx
+++ b/src/client/components/admin-bookings.tsx
@@ -212,8 +212,8 @@ export function AdminBookings() {
{booking.customerName}
- {booking.customerEmail}
- {booking.customerPhone}
+ {booking.customerEmail || '—'}
+ {booking.customerPhone || '—'}
|
diff --git a/src/client/components/admin-calendar.tsx b/src/client/components/admin-calendar.tsx
index 7250f2c..4a88e5d 100644
--- a/src/client/components/admin-calendar.tsx
+++ b/src/client/components/admin-calendar.tsx
@@ -5,6 +5,30 @@ import { queryClient } from "@/client/rpc-client";
export function AdminCalendar() {
const [currentMonth, setCurrentMonth] = useState(new Date());
const [selectedDate, setSelectedDate] = useState(null);
+ const [showDeleteConfirm, setShowDeleteConfirm] = useState(null);
+ const [sendDeleteEmail, setSendDeleteEmail] = useState(false);
+ const [deleteActionType, setDeleteActionType] = useState<'delete' | 'cancel'>('delete');
+
+ // Manual booking modal state
+ const [showCreateModal, setShowCreateModal] = useState(false);
+ const [createFormData, setCreateFormData] = useState({
+ customerName: '',
+ treatmentId: '',
+ appointmentDate: '',
+ appointmentTime: '',
+ customerEmail: '',
+ customerPhone: '',
+ notes: ''
+ });
+ const [createError, setCreateError] = useState('');
+
+ // Reschedule modal state
+ const [showRescheduleModal, setShowRescheduleModal] = useState(null);
+ const [rescheduleFormData, setRescheduleFormData] = useState({
+ appointmentDate: '',
+ appointmentTime: ''
+ });
+ const [rescheduleError, setRescheduleError] = useState('');
const { data: bookings } = useQuery(
queryClient.bookings.live.list.experimental_liveOptions()
@@ -14,10 +38,45 @@ export function AdminCalendar() {
queryClient.treatments.live.list.experimental_liveOptions()
);
+ // Optional query for available times when treatment and date are selected
+ const { data: availableTimes } = useQuery({
+ ...queryClient.recurringAvailability.getAvailableTimes.queryOptions({
+ input: {
+ date: createFormData.appointmentDate,
+ treatmentId: createFormData.treatmentId
+ }
+ }),
+ enabled: !!createFormData.appointmentDate && !!createFormData.treatmentId
+ });
+
+ // Available times for reschedule modal
+ const { data: rescheduleAvailableTimes } = useQuery({
+ ...queryClient.recurringAvailability.getAvailableTimes.queryOptions({
+ input: {
+ date: rescheduleFormData.appointmentDate,
+ treatmentId: (showRescheduleModal ? bookings?.find(b => b.id === showRescheduleModal)?.treatmentId : '') || ''
+ }
+ }),
+ enabled: !!showRescheduleModal && !!rescheduleFormData.appointmentDate
+ });
+
const { mutate: updateBookingStatus } = useMutation(
queryClient.bookings.updateStatus.mutationOptions()
);
+ const { mutate: removeBooking } = useMutation(
+ queryClient.bookings.remove.mutationOptions()
+ );
+
+ const { mutate: createManualBooking } = useMutation(
+ queryClient.bookings.createManual.mutationOptions()
+ );
+
+ // Propose reschedule mutation
+ const { mutate: proposeReschedule, isLoading: isProposingReschedule } = useMutation(
+ queryClient.bookings.proposeReschedule.mutationOptions()
+ );
+
const getTreatmentName = (treatmentId: string) => {
return treatments?.find(t => t.id === treatmentId)?.name || "Unbekannte Behandlung";
};
@@ -106,8 +165,116 @@ export function AdminCalendar() {
});
};
+ const handleDeleteBooking = () => {
+ const sessionId = localStorage.getItem('sessionId');
+ if (!sessionId || !showDeleteConfirm) return;
+
+ if (deleteActionType === 'cancel') {
+ // For cancel action, use updateStatus instead of remove
+ updateBookingStatus({
+ sessionId,
+ id: showDeleteConfirm,
+ status: "cancelled"
+ }, {
+ onSuccess: () => {
+ setShowDeleteConfirm(null);
+ setSendDeleteEmail(false);
+ setDeleteActionType('delete');
+ },
+ onError: () => {
+ // no-op; errors can be surfaced via existing patterns/toasts later
+ }
+ });
+ } else {
+ // For delete action, use remove with email option
+ removeBooking({
+ sessionId,
+ id: showDeleteConfirm,
+ sendEmail: sendDeleteEmail,
+ }, {
+ onSuccess: () => {
+ setShowDeleteConfirm(null);
+ setSendDeleteEmail(false);
+ setDeleteActionType('delete');
+ },
+ onError: () => {
+ // no-op; errors can be surfaced via existing patterns/toasts later
+ }
+ });
+ }
+ };
+
const today = new Date().toISOString().split('T')[0];
+ const handleCreateBooking = () => {
+ const sessionId = localStorage.getItem('sessionId');
+ if (!sessionId) return;
+
+ createManualBooking({
+ sessionId,
+ ...createFormData
+ }, {
+ onSuccess: () => {
+ setShowCreateModal(false);
+ setCreateFormData({
+ customerName: '',
+ treatmentId: '',
+ appointmentDate: '',
+ appointmentTime: '',
+ customerEmail: '',
+ customerPhone: '',
+ notes: ''
+ });
+ setCreateError('');
+ },
+ onError: (error: any) => {
+ setCreateError(error?.message || 'Fehler beim Erstellen der Buchung');
+ }
+ });
+ };
+
+ const handleFormChange = (field: string, value: string) => {
+ setCreateFormData(prev => ({
+ ...prev,
+ [field]: value,
+ // Reset time when treatment or date changes
+ ...(field === 'treatmentId' || field === 'appointmentDate' ? { appointmentTime: '' } : {})
+ }));
+ setCreateError('');
+ };
+
+ const handleRescheduleFormChange = (field: string, value: string) => {
+ setRescheduleFormData(prev => ({
+ ...prev,
+ [field]: value,
+ ...(field === 'appointmentDate' ? { appointmentTime: '' } : {})
+ }));
+ setRescheduleError('');
+ };
+
+ const handleRescheduleBooking = () => {
+ const sessionId = localStorage.getItem('sessionId');
+ if (!sessionId || !showRescheduleModal) return;
+ const booking = bookings?.find(b => b.id === showRescheduleModal);
+ if (!booking) return;
+
+ proposeReschedule({
+ sessionId,
+ bookingId: booking.id,
+ proposedDate: rescheduleFormData.appointmentDate,
+ proposedTime: rescheduleFormData.appointmentTime,
+ }, {
+ onSuccess: () => {
+ setShowRescheduleModal(null);
+ setRescheduleFormData({ appointmentDate: '', appointmentTime: '' });
+ setRescheduleError('');
+ },
+ onError: (error: any) => {
+ setRescheduleError(error?.message || 'Fehler beim Senden des Vorschlags');
+ }
+ });
+ };
+
return (
Kalender - Bevorstehende Buchungen
@@ -153,9 +320,17 @@ export function AdminCalendar() {
-
- {monthNames[month]} {year}
-
+
+
+ {monthNames[month]} {year}
+
+
+
- E-Mail: {booking.customerEmail}
+ E-Mail: {booking.customerEmail || '—'}
- Telefon: {booking.customerPhone}
+ Telefon: {booking.customerPhone || '—'}
@@ -293,7 +468,11 @@ export function AdminCalendar() {
Bestätigen
|