From 71afb915605c4b6971cc9c2c14a8fb72586b26a0 Mon Sep 17 00:00:00 2001 From: Studio-18 Date: Sun, 2 Nov 2025 12:59:42 -0800 Subject: [PATCH] feat: add find coach confirmation flow --- src/TennisMatchApp.jsx | 9 ++ src/main.jsx | 2 + src/pages/FindCoach.jsx | 296 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+) create mode 100644 src/pages/FindCoach.jsx diff --git a/src/TennisMatchApp.jsx b/src/TennisMatchApp.jsx index fd18736e..26a09e84 100644 --- a/src/TennisMatchApp.jsx +++ b/src/TennisMatchApp.jsx @@ -58,6 +58,7 @@ import { Settings, LogOut, User, + UserCircle, UserCheck, UserMinus, UserPlus, @@ -3267,6 +3268,14 @@ const TennisMatchApp = () => { Find Courts + diff --git a/src/main.jsx b/src/main.jsx index b7fec885..6a88b5e5 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -9,6 +9,7 @@ import CreateMatchPage from "./pages/CreateMatchPage.jsx"; import TennisMatchApp from "./TennisMatchApp.jsx"; // keep your existing home/app import MatchSuccessPreview from "./pages/MatchSuccessPreview.jsx"; import CourtFinder from "./pages/CourtFinder.jsx"; +import FindCoach from "./pages/FindCoach.jsx"; const routes = [ // App home @@ -18,6 +19,7 @@ const routes = [ { path: "/players", element: }, { path: "/create", element: }, { path: "/courts", element: }, + { path: "/coaches", element: }, // Match details (direct link) { path: "/matches/:id", element: }, diff --git a/src/pages/FindCoach.jsx b/src/pages/FindCoach.jsx new file mode 100644 index 00000000..afdc3007 --- /dev/null +++ b/src/pages/FindCoach.jsx @@ -0,0 +1,296 @@ +import React, { useMemo, useState } from "react"; +import { + ArrowLeft, + Calendar, + CheckCircle2, + Clock, + CreditCard, + MapPin, + Users, + UserCircle, +} from "lucide-react"; +import { Link } from "react-router-dom"; + +const LESSONS = [ + { + id: "private-60", + title: "60-Minute Private Tune-Up", + coach: "Coach Priya Desai", + type: "private", + description: + "One-on-one session focused on your serve mechanics and baseline consistency.", + price: "$120", + location: "Penmar Recreation Center", + nextSession: "Saturday, Jan 27 · 9:00 AM", + duration: "60 minutes", + }, + { + id: "group-40", + title: "Adult Doubles Strategy Clinic", + coach: "Coach Mateo Alvarez", + type: "group", + description: + "High-energy clinic for 3.0-3.5 players covering positioning and team communication.", + price: "$45", + location: "Mar Vista Park", + nextSession: "Tuesday, Jan 30 · 7:00 PM", + duration: "90 minutes", + openSpots: 3, + }, +]; + +const typeLabel = { + private: "Private lesson", + group: "Group lesson", +}; + +const FindCoach = () => { + const [selectedLesson, setSelectedLesson] = useState(null); + const [confirmationNotice, setConfirmationNotice] = useState(""); + const [isConfirmed, setIsConfirmed] = useState(false); + + const isPrivateLesson = selectedLesson?.type === "private"; + + const nextSteps = useMemo(() => { + if (!selectedLesson) return []; + + if (isPrivateLesson) { + return [ + `Coach ${selectedLesson.coach} will review your request. Private lessons require a coach to confirm before they're finalized.`, + "You'll only be charged once the coach confirms your lesson.", + "We'll send an email and text as soon as the coach responds.", + ]; + } + + const openSpotCopy = selectedLesson.openSpots + ? `There are currently ${selectedLesson.openSpots} open spots.` + : "We'll let you know if any spots open up."; + + return [ + "Your spot is instantly confirmed as long as there are open spots.", + openSpotCopy, + "We'll send a reminder 24 hours before the clinic.", + ]; + }, [isPrivateLesson, selectedLesson]); + + const statusSummary = isPrivateLesson + ? { + title: "Pending coach confirmation", + body: + "We'll notify you once your coach confirms the lesson. Payment will process after they approve it.", + accent: "border-amber-200 bg-amber-50 text-amber-800", + } + : { + title: "You're confirmed!", + body: + "Group lessons lock in immediately when spots are available. You're all set for this session.", + accent: "border-emerald-200 bg-emerald-50 text-emerald-800", + }; + + const handleStartOver = () => { + setSelectedLesson(null); + setIsConfirmed(false); + setConfirmationNotice(""); + }; + + const handleConfirmLesson = () => { + if (!selectedLesson) return; + + setIsConfirmed(true); + setConfirmationNotice( + isPrivateLesson + ? `You're in! We'll let you know as soon as ${selectedLesson.coach} confirms. You'll only be charged after they approve the lesson.` + : "You're booked! Your spot in this group lesson is confirmed and your payment has been processed.", + ); + }; + + return ( +
+
+
+
+

Find a Coach

+

+ Browse curated coaches and book the session that fits your goals. +

+
+ + + Back to Matchplay + +
+ + {!selectedLesson ? ( +
+ {LESSONS.map((lesson) => { + const isGroupLesson = lesson.type === "group"; + return ( +
+
+
+
+ + {isGroupLesson ? : } + {typeLabel[lesson.type]} + + + + {lesson.duration} + +
+
+

{lesson.title}

+

with {lesson.coach}

+
+

{lesson.description}

+
+
+ + {lesson.nextSession} +
+
+ + {lesson.location} +
+
+ {isGroupLesson && typeof lesson.openSpots === "number" && ( +

+ {lesson.openSpots} open spots remaining +

+ )} +
+
+

{lesson.price}

+ +
+
+
+ ); + })} +
+ ) : ( +
+ + + {confirmationNotice && ( +
+ {confirmationNotice} +
+ )} + +
+
+
+

{selectedLesson.title}

+

with {selectedLesson.coach}

+

{selectedLesson.description}

+
+
+ + {isPrivateLesson ? : } + {typeLabel[selectedLesson.type]} + +

{selectedLesson.price}

+
+
+
+
+ + {selectedLesson.nextSession} +
+
+ + {selectedLesson.duration} +
+
+ + {selectedLesson.location} +
+
+
+

{statusSummary.title}

+

{statusSummary.body}

+
+
+ +
+

What happens next

+
    + {nextSteps.map((step) => ( +
  • + + {step} +
  • + ))} + {isPrivateLesson ? ( +
  • + + Payment runs automatically once your coach confirms the lesson. +
  • + ) : ( +
  • + + Payment has already been processed to hold your spot. +
  • + )} +
+
+ +
+
+
+

+ Confirm this {isPrivateLesson ? "private lesson" : "group lesson"} +

+

+ {isPrivateLesson + ? "We'll reserve the time and notify your coach to approve it." + : "Your spot will be locked in immediately when you confirm."} +

+
+ +
+
+
+ )} +
+
+ ); +}; + +export default FindCoach;