From ab961142955f73e0177b8ef99bab5b81d1660b45 Mon Sep 17 00:00:00 2001 From: elpatron Date: Mon, 29 Sep 2025 19:50:10 +0200 Subject: [PATCH] Add Stargil Nails logo and favicon - Replace emoji icons with Stargil Nails logo in header and loading spinner - Add favicon.png to public directory - Copy logo to public/assets for browser access - Update vite.config.ts to serve public directory - Add favicon link to HTML head section --- .gitignore | 13 ++- assets/stargilnails_logo_transparent_112.png | Bin 0 -> 7483 bytes .../stargilnails_logo_transparent_112.png | Bin 0 -> 7483 bytes public/favicon.png | Bin 0 -> 7483 bytes src/client/app.tsx | 14 ++- src/client/components/admin-availability.tsx | 98 +++++++++++++----- src/client/components/booking-form.tsx | 84 ++++++++------- src/server/routes/client-entry.tsx | 3 +- src/server/rpc/availability.ts | 35 ++++--- vite.config.ts | 16 +-- 10 files changed, 175 insertions(+), 88 deletions(-) create mode 100644 assets/stargilnails_logo_transparent_112.png create mode 100644 public/assets/stargilnails_logo_transparent_112.png create mode 100644 public/favicon.png diff --git a/.gitignore b/.gitignore index 8a43bb6..45c8b9d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,13 +7,16 @@ yarn-error.log* pnpm-debug.log* lerna-debug.log* -node_modules -dist -dist-ssr +node_modules/ +dist/ +dist-ssr/ +.vite/ +coverage/ # Editor directories and files .idea .DS_Store +Thumbs.db *.suo *.ntvs* *.njsproj @@ -37,8 +40,10 @@ dist-ssr etilqs_* # Environment variables -.env* +.env +.env.* !.env.example +.env.local # TypeScript cache *.tsbuildinfo \ No newline at end of file diff --git a/assets/stargilnails_logo_transparent_112.png b/assets/stargilnails_logo_transparent_112.png new file mode 100644 index 0000000000000000000000000000000000000000..8492cc0e740896c61ad7d64152694acc424112a4 GIT binary patch literal 7483 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 literal 0 HcmV?d00001 diff --git a/public/assets/stargilnails_logo_transparent_112.png b/public/assets/stargilnails_logo_transparent_112.png new file mode 100644 index 0000000000000000000000000000000000000000..8492cc0e740896c61ad7d64152694acc424112a4 GIT binary patch literal 7483 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 literal 0 HcmV?d00001 diff --git a/public/favicon.png b/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8492cc0e740896c61ad7d64152694acc424112a4 GIT binary patch literal 7483 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 literal 0 HcmV?d00001 diff --git a/src/client/app.tsx b/src/client/app.tsx index 3cea443..83d18fe 100644 --- a/src/client/app.tsx +++ b/src/client/app.tsx @@ -17,7 +17,11 @@ function App() { return (
-
đź’…
+ Stargil Nails Logo
Lade...
@@ -45,9 +49,13 @@ function App() { {/* Header */}
-
+
-
đź’…
+ Stargil Nails Logo

Stargirlnails Kiel

Professional Nail Design & Care

diff --git a/src/client/components/admin-availability.tsx b/src/client/components/admin-availability.tsx index 19aea43..ddec3c5 100644 --- a/src/client/components/admin-availability.tsx +++ b/src/client/components/admin-availability.tsx @@ -8,10 +8,13 @@ export function AdminAvailability() { const [time, setTime] = useState("09:00"); const [duration, setDuration] = useState(30); - const { data: slots } = useQuery( - queryClient.availability.live.byDate.experimental_liveOptions(selectedDate) + const { data: allSlots } = useQuery( + queryClient.availability.live.list.experimental_liveOptions() ); + const [errorMsg, setErrorMsg] = useState(""); + const [successMsg, setSuccessMsg] = useState(""); + const { mutate: createSlot, isPending: isCreating } = useMutation( queryClient.availability.create.mutationOptions() ); @@ -20,8 +23,38 @@ export function AdminAvailability() { ); const addSlot = () => { - if (!selectedDate || !time || !duration) return; - createSlot({ sessionId: localStorage.getItem("sessionId") || "", date: selectedDate, time, durationMinutes: duration }); + setErrorMsg(""); + setSuccessMsg(""); + if (!selectedDate || !time || !duration) { + setErrorMsg("Bitte Datum, Uhrzeit und Dauer angeben."); + return; + } + const sessionId = localStorage.getItem("sessionId") || ""; + if (!sessionId) { + setErrorMsg("Nicht eingeloggt. Bitte als Inhaber anmelden."); + return; + } + createSlot( + { sessionId, date: selectedDate, time, durationMinutes: duration }, + { + onSuccess: () => { + setSuccessMsg("Slot angelegt."); + // advance time to next 30-minute step + const [hStr, mStr] = time.split(":"); + let h = parseInt(hStr, 10); + let m = parseInt(mStr, 10); + m += 30; + if (m >= 60) { h += 1; m -= 60; } + if (h >= 24) { h = 0; } + const next = `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`; + setTime(next); + }, + onError: (err: any) => { + const msg = (err && (err.message || (err as any).toString())) || "Fehler beim Anlegen."; + setErrorMsg(msg); + }, + } + ); }; return ( @@ -58,32 +91,43 @@ export function AdminAvailability() {
+ {(errorMsg || successMsg) && ( +
+ {errorMsg &&
{errorMsg}
} + {successMsg &&
{successMsg}
} +
+ )} +
-

Slots am {selectedDate}

+

Alle freien Slots

- {slots?.sort((a, b) => a.time.localeCompare(b.time)).map((slot) => ( -
-
- {slot.time} - {slot.durationMinutes} Min - - {slot.status === "free" ? "frei" : "reserviert"} - + {allSlots + ?.filter((s) => s.status === "free") + .sort((a, b) => (a.date === b.date ? a.time.localeCompare(b.time) : a.date.localeCompare(b.date))) + .map((slot) => ( +
+
+ {slot.date} + {slot.time} + {slot.durationMinutes} Min + + {slot.status === "free" ? "frei" : "reserviert"} + +
+
+ +
-
- -
-
- ))} - {slots?.length === 0 && ( -
Keine Slots vorhanden.
+ ))} + {allSlots?.filter((s) => s.status === "free").length === 0 && ( +
Keine freien Slots vorhanden.
)}
diff --git a/src/client/components/booking-form.tsx b/src/client/components/booking-form.tsx index 4397641..b1d22c7 100644 --- a/src/client/components/booking-form.tsx +++ b/src/client/components/booking-form.tsx @@ -14,18 +14,23 @@ export function BookingForm() { const { data: treatments } = useQuery( queryClient.treatments.live.list.experimental_liveOptions() ); - const { data: slotsByDate } = useQuery( - appointmentDate - ? queryClient.availability.live.byDate.experimental_liveOptions(appointmentDate) - : queryClient.availability.live.byDate.experimental_liveOptions("") + + // Lade alle Slots live und filtere freie Slots + const { data: allSlots } = useQuery( + queryClient.availability.live.list.experimental_liveOptions() ); + const freeSlots = (allSlots || []).filter((s) => s.status === "free"); + const availableDates = Array.from(new Set(freeSlots.map((s) => s.date))).sort(); + const slotsByDate = appointmentDate + ? freeSlots.filter((s) => s.date === appointmentDate) + : []; const { mutate: createBooking, isPending } = useMutation( queryClient.bookings.create.mutationOptions() ); - const selectedTreatmentData = treatments?.find(t => t.id === selectedTreatment); - const availableSlots = (slotsByDate || []).filter(s => s.status === "free"); + const selectedTreatmentData = treatments?.find((t) => t.id === selectedTreatment); + const availableSlots = (slotsByDate || []).filter((s) => s.status === "free"); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); @@ -33,33 +38,36 @@ export function BookingForm() { alert("Bitte fülle alle erforderlichen Felder aus"); return; } - const slot = availableSlots.find(s => s.id === selectedSlotId); + const slot = availableSlots.find((s) => s.id === selectedSlotId); const appointmentTime = slot?.time || ""; - createBooking({ - treatmentId: selectedTreatment, - customerName, - customerEmail, - customerPhone, - appointmentDate, - appointmentTime, - notes, - slotId: selectedSlotId, - }, { - onSuccess: () => { - setSelectedTreatment(""); - setCustomerName(""); - setCustomerEmail(""); - setCustomerPhone(""); - setAppointmentDate(""); - setSelectedSlotId(""); - setNotes(""); - alert("Buchung erfolgreich erstellt! Wir werden dich kontaktieren, um deinen Termin zu bestätigen."); + createBooking( + { + treatmentId: selectedTreatment, + customerName, + customerEmail, + customerPhone, + appointmentDate, + appointmentTime, + notes, + slotId: selectedSlotId, + }, + { + onSuccess: () => { + setSelectedTreatment(""); + setCustomerName(""); + setCustomerEmail(""); + setCustomerPhone(""); + setAppointmentDate(""); + setSelectedSlotId(""); + setNotes(""); + alert("Buchung erfolgreich erstellt! Wir werden dich kontaktieren, um deinen Termin zu bestätigen."); + }, } - }); + ); }; - // Get minimum date (today) - const today = new Date().toISOString().split('T')[0]; + // Get minimum date (today) – nicht mehr genutzt, Datumsauswahl erfolgt aus freien Slots + const today = new Date().toISOString().split("T")[0]; return (
@@ -134,16 +142,22 @@ export function BookingForm() {
- setAppointmentDate(e.target.value)} - min={today} + onChange={(e) => { setAppointmentDate(e.target.value); setSelectedSlotId(""); }} className="w-full p-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-pink-500 focus:border-pink-500" required - /> + > + + {availableDates.map((d) => ( + + ))} + + {availableDates.length === 0 && ( +

Aktuell keine freien Termine verfĂĽgbar.

+ )}