From 080a4d07d8dd6b5972dd4fd9c1f32d7f22716cf6 Mon Sep 17 00:00:00 2001 From: Sahil Kashyap <32007662+sahilkashyap64@users.noreply.github.com> Date: Tue, 31 Mar 2026 14:29:13 +0530 Subject: [PATCH] Redesign coach profile page chrome and booking layout --- src/pages/CoachProfilePage.css | 153 +++++++++++++++++++++++++++++++++ src/pages/CoachProfilePage.tsx | 119 ++++++++++++++++++++++--- 2 files changed, 262 insertions(+), 10 deletions(-) diff --git a/src/pages/CoachProfilePage.css b/src/pages/CoachProfilePage.css index 5eb5c594..d9117ad8 100644 --- a/src/pages/CoachProfilePage.css +++ b/src/pages/CoachProfilePage.css @@ -54,6 +54,159 @@ overscroll-behavior-x: none; } +.coach-profile-fixed-chrome { + position: sticky; + top: 0; + z-index: 30; + background: rgba(255, 255, 255, 0.96); + backdrop-filter: blur(8px); + border-bottom: 1px solid #e2e8f0; +} + +.coach-profile-fixed-chrome__header, +.coach-profile-fixed-chrome__tabs, +.coach-profile-fixed-chrome__coach { + max-width: 1240px; + margin: 0 auto; + padding: 10px 20px; +} + +.coach-profile-fixed-chrome__header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.coach-profile-fixed-chrome__back, +.coach-profile-fixed-chrome__message { + display: inline-flex; + align-items: center; + gap: 8px; + color: #334155; + font-weight: 600; + text-decoration: none; + background: transparent; + border: 0; +} + +.coach-profile-fixed-chrome__tabs { + display: flex; + gap: 8px; + border-top: 1px solid #f1f5f9; +} + +.coach-profile-fixed-chrome__tab { + border: 0; + background: transparent; + padding: 8px 12px; + color: #64748b; + border-bottom: 2px solid transparent; +} + +.coach-profile-fixed-chrome__tab--active { + color: #7c3aed; + border-bottom-color: #7c3aed; +} + +.coach-profile-fixed-chrome__coach { + display: flex; + align-items: center; + gap: 10px; + border-top: 1px solid #f1f5f9; +} + +.coach-profile-fixed-chrome__coach-avatar { + width: 34px; + height: 34px; + border-radius: 999px; + object-fit: cover; +} + +.coach-profile-fixed-chrome__coach-copy { + display: flex; + flex-direction: column; + line-height: 1.1; +} + +.coach-profile-fixed-chrome__coach-copy span { + font-size: 12px; + color: #64748b; +} + +.coach-profile-fixed-chrome__coach-price { + margin-left: auto; + color: #7c3aed; + font-weight: 700; +} + +.coach-profile-bio { + margin-top: 16px; +} + +.coach-profile-bio__text { + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.coach-profile-bio__text--expanded { + display: block; +} + +.coach-profile-bio__toggle { + margin-top: 8px; + border: 0; + background: transparent; + color: #7c3aed; + font-weight: 600; +} + +.coach-profile-about-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 10px; + margin-bottom: 16px; +} + +.coach-profile-about-grid > div { + border: 1px solid #e2e8f0; + border-radius: 12px; + padding: 12px; + background: #f8fafc; +} + +.coach-profile-about-grid strong { + display: block; + font-size: 14px; +} + +.coach-profile-about-grid span { + font-size: 12px; + color: #64748b; +} + +.coach-profile-credit-strip { + margin: 0 0 12px; + border: 1px solid #ddd6fe; + border-radius: 12px; + background: #f5f3ff; + padding: 10px 12px; + display: flex; + justify-content: space-between; + align-items: center; + color: #6d28d9; + font-weight: 600; + font-size: 13px; +} + +.coach-profile-credit-strip button { + border: 0; + background: transparent; + color: #7c3aed; + font-weight: 700; +} + @supports not (overflow-x: clip) { .coach-profile-page { overflow-x: hidden; diff --git a/src/pages/CoachProfilePage.tsx b/src/pages/CoachProfilePage.tsx index 0c354ec3..494ee4a1 100644 --- a/src/pages/CoachProfilePage.tsx +++ b/src/pages/CoachProfilePage.tsx @@ -780,6 +780,13 @@ const CoachProfilePage = () => { const [selection, setSelection] = useState(() => ({ lessonType: "all", })); + const [showFullBio, setShowFullBio] = useState(false); + const profileScrollRef = useRef(null); + const packagesRef = useRef(null); + const aboutRef = useRef(null); + const specialtiesRef = useRef(null); + const courtsRef = useRef(null); + const [activeTab, setActiveTab] = useState<"about" | "specialties" | "courts">("about"); const [bookingInFlight, setBookingInFlight] = useState(null); const [bookingError, setBookingError] = useState(null); const [bookingSuccess, setBookingSuccess] = useState(null); @@ -1637,8 +1644,8 @@ const CoachProfilePage = () => { const lessonFilters = [ { id: "all", label: "All", ariaLabel: "All lesson formats" }, - { id: "private", label: "Privates", ariaLabel: "Private lessons" }, - { id: "group", label: "Groups", ariaLabel: "Group sessions" }, + { id: "private", label: "Private", ariaLabel: "Private lessons" }, + { id: "group", label: "Group", ariaLabel: "Group sessions" }, ]; const playerLessonCredits = useMemo(() => { @@ -1787,6 +1794,10 @@ const CoachProfilePage = () => { const selectedDate = selectedDateEntry?.date; const filteredSlots = selectedDateEntry?.slots ?? []; + const slotsThisWeek = useMemo( + () => availableDates.reduce((sum, date) => sum + (date.totalSlots ?? date.slots.length ?? 0), 0), + [availableDates], + ); const lessonTypeDetailMap = useMemo(() => { return bookingLessonTypes.reduce( @@ -1874,6 +1885,30 @@ const CoachProfilePage = () => { return profile.highlightChips.filter((chip) => !/utr/i.test(chip.label)); }, [profile]); + const experienceLabel = useMemo( + () => highlightChips.find((chip) => /year|yrs|experience/i.test(chip.label))?.label ?? "Experience listed", + [highlightChips], + ); + const studentsLabel = useMemo( + () => highlightChips.find((chip) => /student/i.test(chip.label))?.label ?? "Students coached", + [highlightChips], + ); + + const scrollToSection = (section: "about" | "specialties" | "courts") => { + setActiveTab(section); + const targetMap = { + about: aboutRef, + specialties: specialtiesRef, + courts: courtsRef, + }; + const target = targetMap[section].current; + if (!target) return; + target.scrollIntoView({ behavior: "smooth", block: "start" }); + }; + + const scrollToPackages = () => { + packagesRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }); + }; const resolveIsoDate = (date?: BookingDate) => { if (!date) return undefined; @@ -2637,7 +2672,45 @@ const extractLocationId = (slot?: BookingSlot) => { rosterError={rosterStatusError ?? undefined} rosterLoading={rosterStatusLoading} /> -
+
+
+
+ + Coaches + + +
+
+ {([ + ["about", "About"], + ["specialties", "Specialties"], + ["courts", "Courts"], + ] as const).map(([key, label]) => ( + + ))} +
+
+ {coachDisplayName} +
+ {coachDisplayName} + {certifications[0] ?? coachTitle} +
+
+ {lessonDetails.find((lesson) => lesson.id === "private")?.price ?? "Pricing available"} +
+
+
@@ -2684,6 +2757,18 @@ const extractLocationId = (slot?: BookingSlot) => { ); })}
+
+

+ {profile.about} +

+ +
@@ -2696,12 +2781,16 @@ const extractLocationId = (slot?: BookingSlot) => {
-
+

About {coachFirstName}

-

{profile.about}

+
+
{experienceLabel}Experience
+
{studentsLabel}Students
+
{languages.join(", ") || "—"}Languages
+
{certifications.length > 0 && (
{certifications.map((certification) => ( @@ -2714,7 +2803,7 @@ const extractLocationId = (slot?: BookingSlot) => { )}
-
+

Specialties

@@ -2731,9 +2820,9 @@ const extractLocationId = (slot?: BookingSlot) => { )}
-
+
-

Coaching Locations

+

Courts

Certified to coach at these nearby courts and clubs.

@@ -2773,7 +2862,7 @@ const extractLocationId = (slot?: BookingSlot) => { ))} )} -
+

Package deals

@@ -2903,10 +2992,20 @@ const extractLocationId = (slot?: BookingSlot) => {

{bookingHeadline}

-

Select your preferred date and time

+

+ {slotsThisWeek > 0 ? `${slotsThisWeek} slots this week` : "No slots this week"} +

+ {hasCreditsRemaining ? ( +
+ {availableCredits} credits · can be applied at booking + +
+ ) : null}