Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
c1aeb7c38b | |||
889e110dd9 | |||
a603232ed8 | |||
f0037226a9 | |||
12da9812df |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "quests-template-basic",
|
||||
"private": true,
|
||||
"version": "0.1.5",
|
||||
"version": "0.1.5.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"check:types": "tsc --noEmit",
|
||||
|
@@ -3,6 +3,7 @@ import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { queryClient } from "@/client/rpc-client";
|
||||
|
||||
export function AdminBookings() {
|
||||
const [filterMode, setFilterMode] = useState<"upcoming" | "all" | "date">("upcoming");
|
||||
const [selectedDate, setSelectedDate] = useState(new Date().toISOString().split('T')[0]);
|
||||
const [selectedPhoto, setSelectedPhoto] = useState<string>("");
|
||||
const [showPhotoModal, setShowPhotoModal] = useState(false);
|
||||
@@ -139,9 +140,23 @@ export function AdminBookings() {
|
||||
return appointmentDate >= today;
|
||||
};
|
||||
|
||||
const filteredBookings = bookings?.filter(booking =>
|
||||
selectedDate ? booking.appointmentDate === selectedDate : true
|
||||
).sort((a, b) => {
|
||||
// Filter bookings based on selected filter mode
|
||||
const filteredBookings = bookings?.filter(booking => {
|
||||
if (filterMode === "upcoming") {
|
||||
// Show all future bookings (not cancelled)
|
||||
const bookingDate = new Date(booking.appointmentDate);
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
bookingDate.setHours(0, 0, 0, 0);
|
||||
return bookingDate >= today && booking.status !== "cancelled";
|
||||
} else if (filterMode === "date") {
|
||||
// Show bookings for specific date
|
||||
return booking.appointmentDate === selectedDate;
|
||||
} else {
|
||||
// Show all bookings
|
||||
return true;
|
||||
}
|
||||
}).sort((a, b) => {
|
||||
if (a.appointmentDate === b.appointmentDate) {
|
||||
return a.appointmentTime.localeCompare(b.appointmentTime);
|
||||
}
|
||||
@@ -218,22 +233,54 @@ export function AdminBookings() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Date Filter */}
|
||||
{/* Filter Section */}
|
||||
<div className="bg-white rounded-lg shadow p-4 mb-6">
|
||||
<div className="flex items-center space-x-4">
|
||||
<label className="text-sm font-medium text-gray-700">Filter by date:</label>
|
||||
<input
|
||||
type="date"
|
||||
value={selectedDate}
|
||||
onChange={(e) => setSelectedDate(e.target.value)}
|
||||
className="p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-pink-500 focus:border-pink-500"
|
||||
/>
|
||||
<button
|
||||
onClick={() => setSelectedDate("")}
|
||||
className="text-sm text-pink-600 hover:text-pink-800"
|
||||
>
|
||||
Show All
|
||||
</button>
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<label className="text-sm font-medium text-gray-700">Filter:</label>
|
||||
<button
|
||||
onClick={() => setFilterMode("upcoming")}
|
||||
className={`px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
||||
filterMode === "upcoming"
|
||||
? "bg-pink-600 text-white"
|
||||
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||
}`}
|
||||
>
|
||||
Zukünftige
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilterMode("all")}
|
||||
className={`px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
||||
filterMode === "all"
|
||||
? "bg-pink-600 text-white"
|
||||
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||
}`}
|
||||
>
|
||||
Alle
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilterMode("date")}
|
||||
className={`px-4 py-2 rounded-md text-sm font-medium transition-colors ${
|
||||
filterMode === "date"
|
||||
? "bg-pink-600 text-white"
|
||||
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
|
||||
}`}
|
||||
>
|
||||
Datum
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{filterMode === "date" && (
|
||||
<div className="flex items-center space-x-4 pl-16">
|
||||
<label className="text-sm font-medium text-gray-700">Wähle Datum:</label>
|
||||
<input
|
||||
type="date"
|
||||
value={selectedDate}
|
||||
onChange={(e) => setSelectedDate(e.target.value)}
|
||||
className="p-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-pink-500 focus:border-pink-500"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -369,8 +416,10 @@ export function AdminBookings() {
|
||||
|
||||
{!filteredBookings?.length && (
|
||||
<div className="text-center py-8 text-gray-500">
|
||||
{selectedDate
|
||||
{filterMode === "date"
|
||||
? `Keine Buchungen für ${new Date(selectedDate).toLocaleDateString()} gefunden`
|
||||
: filterMode === "upcoming"
|
||||
? "Keine zukünftigen Buchungen verfügbar."
|
||||
: "Keine Buchungen verfügbar."
|
||||
}
|
||||
</div>
|
||||
|
@@ -3,7 +3,7 @@ import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { queryClient } from "@/client/rpc-client";
|
||||
|
||||
// Feature flag for multi-treatments availability API compatibility
|
||||
const USE_MULTI_TREATMENTS_AVAILABILITY = false;
|
||||
const USE_MULTI_TREATMENTS_AVAILABILITY = true;
|
||||
|
||||
export function BookingForm() {
|
||||
const [selectedTreatments, setSelectedTreatments] = useState<Array<{id: string, name: string, duration: number, price: number}>>([]);
|
||||
|
@@ -14,12 +14,12 @@ function renderTreatmentList(
|
||||
options: { showPrices: boolean } = { showPrices: true }
|
||||
): string {
|
||||
const totalDuration = treatments.reduce((sum, t) => sum + t.duration, 0);
|
||||
const totalPrice = treatments.reduce((sum, t) => sum + t.price, 0);
|
||||
const totalPrice = treatments.reduce((sum, t) => sum + (t.price / 100), 0);
|
||||
|
||||
const treatmentItems = treatments.map(t =>
|
||||
options.showPrices
|
||||
? `<li><strong>${t.name}</strong> - ${t.duration} Min - ${t.price.toFixed(2)} €</li>`
|
||||
: `<li>${t.name} - ${t.duration} Min - ${t.price.toFixed(2)} €</li>`
|
||||
? `<li><strong>${t.name}</strong> - ${t.duration} Min - ${(t.price / 100).toFixed(2)} €</li>`
|
||||
: `<li>${t.name} - ${t.duration} Min - ${(t.price / 100).toFixed(2)} €</li>`
|
||||
).join('');
|
||||
|
||||
const totalLine = options.showPrices
|
||||
|
@@ -72,10 +72,10 @@ function createICSFile(params: {
|
||||
// Build treatments list for SUMMARY and DESCRIPTION
|
||||
const treatmentNames = icsEscape(treatments.map(t => t.name).join(', '));
|
||||
const totalDuration = treatments.reduce((sum, t) => sum + t.duration, 0);
|
||||
const totalPrice = treatments.reduce((sum, t) => sum + t.price, 0);
|
||||
const totalPrice = treatments.reduce((sum, t) => sum + (t.price / 100), 0);
|
||||
|
||||
const treatmentDetails = treatments.map(t =>
|
||||
`${icsEscape(t.name)} (${t.duration} Min, ${t.price.toFixed(2)} EUR)`
|
||||
`${icsEscape(t.name)} (${t.duration} Min, ${(t.price / 100).toFixed(2)} EUR)`
|
||||
).join('\\n');
|
||||
|
||||
const description = `Behandlungen:\\n${treatmentDetails}\\n\\nGesamt: ${totalDuration} Min, ${totalPrice.toFixed(2)} EUR\\n\\nTermin bei Stargirlnails Kiel`;
|
||||
|
@@ -84,11 +84,11 @@ X-WR-TIMEZONE:Europe/Berlin
|
||||
treatmentNames = booking.treatments.map(t => t.name).join(', ');
|
||||
|
||||
duration = booking.treatments.reduce((sum, t) => sum + (t.duration || 0), 0);
|
||||
totalPrice = booking.treatments.reduce((sum, t) => sum + (t.price || 0), 0);
|
||||
totalPrice = booking.treatments.reduce((sum, t) => sum + ((t.price || 0) / 100), 0);
|
||||
|
||||
// Build detailed treatment list for description
|
||||
treatmentDetails = booking.treatments
|
||||
.map(t => `- ${t.name} (${t.duration} Min., ${t.price}€)`)
|
||||
.map(t => `- ${t.name} (${t.duration} Min., ${(t.price / 100).toFixed(2)}€)`)
|
||||
.join('\\n');
|
||||
|
||||
if (booking.treatments.length > 1) {
|
||||
@@ -101,7 +101,7 @@ X-WR-TIMEZONE:Europe/Berlin
|
||||
duration = booking.bookedDurationMinutes || treatment?.duration || 60;
|
||||
treatmentDetails = `Behandlung: ${treatmentNames}`;
|
||||
if (treatment?.price) {
|
||||
treatmentDetails += ` (${duration} Min., ${treatment.price}€)`;
|
||||
treatmentDetails += ` (${duration} Min., ${(treatment.price / 100).toFixed(2)}€)`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -367,9 +367,9 @@ const create = os
|
||||
treatments: input.treatments
|
||||
});
|
||||
|
||||
const treatmentsText = input.treatments.map(t => `- ${t.name} (${t.duration} Min, ${t.price.toFixed(2)} €)`).join('\n');
|
||||
const treatmentsText = input.treatments.map(t => `- ${t.name} (${t.duration} Min, ${(t.price / 100).toFixed(2)} €)`).join('\n');
|
||||
const totalDuration = input.treatments.reduce((sum, t) => sum + t.duration, 0);
|
||||
const totalPrice = input.treatments.reduce((sum, t) => sum + t.price, 0);
|
||||
const totalPrice = input.treatments.reduce((sum, t) => sum + (t.price / 100), 0);
|
||||
|
||||
await sendEmail({
|
||||
to: input.customerEmail,
|
||||
@@ -394,9 +394,9 @@ const create = os
|
||||
});
|
||||
|
||||
const homepageUrl = generateUrl();
|
||||
const treatmentsText = input.treatments.map(t => ` - ${t.name} (${t.duration} Min, ${t.price.toFixed(2)} €)`).join('\n');
|
||||
const treatmentsText = input.treatments.map(t => ` - ${t.name} (${t.duration} Min, ${(t.price / 100).toFixed(2)} €)`).join('\n');
|
||||
const totalDuration = input.treatments.reduce((sum, t) => sum + t.duration, 0);
|
||||
const totalPrice = input.treatments.reduce((sum, t) => sum + t.price, 0);
|
||||
const totalPrice = input.treatments.reduce((sum, t) => sum + (t.price / 100), 0);
|
||||
|
||||
const adminText = `Neue Buchungsanfrage eingegangen:\n\n` +
|
||||
`Name: ${input.customerName}\n` +
|
||||
@@ -483,9 +483,9 @@ const updateStatus = os
|
||||
treatments: booking.treatments
|
||||
});
|
||||
|
||||
const treatmentsText = booking.treatments.map(t => `- ${t.name} (${t.duration} Min, ${t.price.toFixed(2)} €)`).join('\n');
|
||||
const treatmentsText = booking.treatments.map(t => `- ${t.name} (${t.duration} Min, ${(t.price / 100).toFixed(2)} €)`).join('\n');
|
||||
const totalDuration = booking.treatments.reduce((sum, t) => sum + t.duration, 0);
|
||||
const totalPrice = booking.treatments.reduce((sum, t) => sum + t.price, 0);
|
||||
const totalPrice = booking.treatments.reduce((sum, t) => sum + (t.price / 100), 0);
|
||||
|
||||
if (booking.customerEmail) {
|
||||
await sendEmailWithAGBAndCalendar({
|
||||
@@ -512,9 +512,9 @@ const updateStatus = os
|
||||
treatments: booking.treatments
|
||||
});
|
||||
|
||||
const treatmentsText = booking.treatments.map(t => `- ${t.name} (${t.duration} Min, ${t.price.toFixed(2)} €)`).join('\n');
|
||||
const treatmentsText = booking.treatments.map(t => `- ${t.name} (${t.duration} Min, ${(t.price / 100).toFixed(2)} €)`).join('\n');
|
||||
const totalDuration = booking.treatments.reduce((sum, t) => sum + t.duration, 0);
|
||||
const totalPrice = booking.treatments.reduce((sum, t) => sum + t.price, 0);
|
||||
const totalPrice = booking.treatments.reduce((sum, t) => sum + (t.price / 100), 0);
|
||||
|
||||
if (booking.customerEmail) {
|
||||
await sendEmail({
|
||||
@@ -571,9 +571,9 @@ const remove = os
|
||||
treatments: booking.treatments
|
||||
});
|
||||
|
||||
const treatmentsText = booking.treatments.map(t => `- ${t.name} (${t.duration} Min, ${t.price.toFixed(2)} €)`).join('\n');
|
||||
const treatmentsText = booking.treatments.map(t => `- ${t.name} (${t.duration} Min, ${(t.price / 100).toFixed(2)} €)`).join('\n');
|
||||
const totalDuration = booking.treatments.reduce((sum, t) => sum + t.duration, 0);
|
||||
const totalPrice = booking.treatments.reduce((sum, t) => sum + t.price, 0);
|
||||
const totalPrice = booking.treatments.reduce((sum, t) => sum + (t.price / 100), 0);
|
||||
|
||||
await sendEmail({
|
||||
to: booking.customerEmail,
|
||||
@@ -693,8 +693,8 @@ const createManual = os
|
||||
treatments: input.treatments
|
||||
});
|
||||
|
||||
const treatmentsText = input.treatments.map(t => `- ${t.name} (${t.duration} Min, ${t.price.toFixed(2)} €)`).join('\n');
|
||||
const totalPrice = input.treatments.reduce((sum, t) => sum + t.price, 0);
|
||||
const treatmentsText = input.treatments.map(t => `- ${t.name} (${t.duration} Min, ${(t.price / 100).toFixed(2)} €)`).join('\n');
|
||||
const totalPrice = input.treatments.reduce((sum, t) => sum + (t.price / 100), 0);
|
||||
|
||||
await sendEmailWithAGBAndCalendar({
|
||||
to: input.customerEmail!,
|
||||
@@ -874,8 +874,8 @@ export const router = {
|
||||
treatments: updated.treatments,
|
||||
});
|
||||
|
||||
const treatmentsText = updated.treatments.map(t => `- ${t.name} (${t.duration} Min, ${t.price.toFixed(2)} €)`).join('\n');
|
||||
const totalPrice = updated.treatments.reduce((sum, t) => sum + t.price, 0);
|
||||
const treatmentsText = updated.treatments.map(t => `- ${t.name} (${t.duration} Min, ${(t.price / 100).toFixed(2)} €)`).join('\n');
|
||||
const totalPrice = updated.treatments.reduce((sum, t) => sum + (t.price / 100), 0);
|
||||
|
||||
await sendEmailWithAGBAndCalendar({
|
||||
to: updated.customerEmail,
|
||||
@@ -929,9 +929,9 @@ export const router = {
|
||||
if (booking.customerEmail) {
|
||||
const bookingAccessToken = await queryClient.cancellation.createToken({ bookingId: booking.id });
|
||||
|
||||
const treatmentsText = booking.treatments.map(t => `- ${t.name} (${t.duration} Min, ${t.price.toFixed(2)} €)`).join('\n');
|
||||
const treatmentsText = booking.treatments.map(t => `- ${t.name} (${t.duration} Min, ${(t.price / 100).toFixed(2)} €)`).join('\n');
|
||||
const totalDuration = booking.treatments.reduce((sum, t) => sum + t.duration, 0);
|
||||
const totalPrice = booking.treatments.reduce((sum, t) => sum + t.price, 0);
|
||||
const totalPrice = booking.treatments.reduce((sum, t) => sum + (t.price / 100), 0);
|
||||
|
||||
await sendEmail({
|
||||
to: booking.customerEmail,
|
||||
|
@@ -137,7 +137,7 @@ const getBookingByToken = os
|
||||
// New bookings with treatments array
|
||||
treatments = booking.treatments;
|
||||
totalDuration = treatments.reduce((sum, t) => sum + t.duration, 0);
|
||||
totalPrice = treatments.reduce((sum, t) => sum + t.price, 0);
|
||||
totalPrice = treatments.reduce((sum, t) => sum + (t.price / 100), 0);
|
||||
} else if (booking.treatmentId) {
|
||||
// Old bookings with single treatmentId (backward compatibility)
|
||||
const treatmentsKV = createKV<any>("treatments");
|
||||
@@ -151,7 +151,7 @@ const getBookingByToken = os
|
||||
price: treatment.price,
|
||||
}];
|
||||
totalDuration = treatment.duration;
|
||||
totalPrice = treatment.price;
|
||||
totalPrice = treatment.price / 100;
|
||||
} else {
|
||||
// Fallback if treatment not found
|
||||
treatments = [];
|
||||
@@ -333,7 +333,7 @@ export const router = {
|
||||
// New bookings with treatments array
|
||||
treatments = booking.treatments;
|
||||
totalDuration = treatments.reduce((sum, t) => sum + t.duration, 0);
|
||||
totalPrice = treatments.reduce((sum, t) => sum + t.price, 0);
|
||||
totalPrice = treatments.reduce((sum, t) => sum + (t.price / 100), 0);
|
||||
} else if (booking.treatmentId) {
|
||||
// Old bookings with single treatmentId (backward compatibility)
|
||||
const treatmentsKV = createKV<any>("treatments");
|
||||
@@ -347,7 +347,7 @@ export const router = {
|
||||
price: treatment.price,
|
||||
}];
|
||||
totalDuration = treatment.duration;
|
||||
totalPrice = treatment.price;
|
||||
totalPrice = treatment.price / 100;
|
||||
} else {
|
||||
// Fallback if treatment not found
|
||||
treatments = [];
|
||||
|
Reference in New Issue
Block a user