diff --git a/src/components/players/BestMatchCTA.tsx b/src/components/players/BestMatchCTA.tsx new file mode 100644 index 00000000..f76e9001 --- /dev/null +++ b/src/components/players/BestMatchCTA.tsx @@ -0,0 +1,101 @@ +import React from "react"; + +interface BestMatchCTAProps { + onClick: () => void; + isMobile: boolean; +} + +const BestMatchCTA: React.FC = ({ onClick, isMobile }) => { + return ( +
+
+
+ + + +
+ +
+

+ Find your perfect tennis partner +

+

+ We'll match you based on skill, availability & location +

+
+
+ + +
+ ); +}; + +export default BestMatchCTA; diff --git a/src/components/players/BestMatchCard.tsx b/src/components/players/BestMatchCard.tsx new file mode 100644 index 00000000..9c217d55 --- /dev/null +++ b/src/components/players/BestMatchCard.tsx @@ -0,0 +1,289 @@ +import React from "react"; + +interface Player { + id?: string; + name: string; + photo?: string; + avatarUrl?: string; + ntrp: string; + isVerified?: boolean; + court?: string; + courts?: string[]; + matchScore: number; + matchReasons: string[]; +} + +interface BestMatchCardProps { + player: Player; + onConnect: () => void; + onViewProfile: () => void; + isMobile: boolean; +} + +const BestMatchCard: React.FC = ({ + player, + onConnect, + onViewProfile, + isMobile, +}) => { + return ( +
+
+ {player.matchScore}% + match +
+ + {isMobile ? ( + <> +
+ {player.name} +
+
+ {player.name} + + NTRP {player.ntrp} + + {player.isVerified && ( + + + + + )} +
+
+ + + + + {player.court || player.courts?.[0]} +
+
+
+ +
+ {player.matchReasons?.map((reason, ridx) => ( + + + + + {reason} + + ))} +
+ +
+ + +
+ + ) : ( + <> + {player.name} + +
+
+ {player.name} + + NTRP {player.ntrp} + + {player.isVerified && ( + + + + + )} +
+ +
+ {player.matchReasons?.map((reason, ridx) => ( + + + + + {reason} + + ))} +
+ +
+ + + + + {player.court || player.courts?.[0]} +
+
+ +
+ + +
+ + )} +
+ ); +}; + +export default BestMatchCard; diff --git a/src/components/players/BestMatchesPanel.tsx b/src/components/players/BestMatchesPanel.tsx new file mode 100644 index 00000000..7a4f1c68 --- /dev/null +++ b/src/components/players/BestMatchesPanel.tsx @@ -0,0 +1,152 @@ +import React from "react"; +import BestMatchCard from "./BestMatchCard"; + +interface Player { + id?: string; + name: string; + photo?: string; + avatarUrl?: string; + ntrp: string; + isVerified?: boolean; + court?: string; + courts?: string[]; + matchScore: number; + matchReasons: string[]; + sourcePlayer?: unknown; +} + +interface BestMatchesPanelProps { + matches: Player[]; + onClose: () => void; + onConnect: (player: Player) => void; + onViewProfile: (player: Player) => void; + isMobile: boolean; +} + +const BestMatchesPanel: React.FC = ({ + matches, + onClose, + onConnect, + onViewProfile, + isMobile, +}) => { + return ( +
+
+
+
+ + + +
+
+

+ Your Best Matches +

+

+ {isMobile ? "Matched by skill, availability & location" : "Players matched by skill level, availability, location & play style"} +

+
+
+ + +
+ +
+ {matches.map((player, idx) => ( + onConnect(player)} + onViewProfile={() => onViewProfile(player)} + isMobile={isMobile} + /> + ))} +
+ +
+ Want better matches? + +
+
+ ); +}; + +export default BestMatchesPanel; diff --git a/src/components/players/MyProfileQuickView.tsx b/src/components/players/MyProfileQuickView.tsx new file mode 100644 index 00000000..4012ad79 --- /dev/null +++ b/src/components/players/MyProfileQuickView.tsx @@ -0,0 +1,468 @@ +import React from "react"; + +interface MyProfileQuickViewProps { + user: { + name: string; + photo?: string; + avatarUrl?: string; + ntrp: string; + isVerified?: boolean; + verificationCount?: number; + tagline?: string; + bio?: string; + availability?: string[]; + courts?: string[]; + }; + onEdit: () => void; + onRequestVerification: () => void; + isMobile: boolean; +} + +const toInitials = (name: string) => { + const trimmed = name.trim(); + if (!trimmed) { + return "TP"; + } + const segments = trimmed.split(/\s+/).filter(Boolean); + if (segments.length === 1) { + return segments[0].slice(0, 2).toUpperCase(); + } + return `${segments[0][0]}${segments[segments.length - 1][0]}`.toUpperCase(); +}; + +const MyProfileQuickView: React.FC = ({ + user, + onEdit, + onRequestVerification, + isMobile, +}) => { + const isVerified = user?.isVerified ?? false; + const verificationCount = user?.verificationCount ?? 2; + const verificationsNeeded = 3; + const imageSrc = user.photo || user.avatarUrl; + const availability = user.availability?.length ? user.availability : ["Add availability"]; + const courts = user.courts?.length ? user.courts : ["Add local courts"]; + + return ( +
+
+ {imageSrc ? ( + {user.name} + ) : ( + + )} + +
+
+

+ {user.name} +

+ +
+ + NTRP {user.ntrp} + + + {isVerified && ( + + + + + Verified rating + + )} +
+
+ +

+ {user.tagline || user.bio} +

+ + {!isVerified && ( +
+
+
+ + + +
+ +
+ + Get your rating verified + + + {verificationCount} of {verificationsNeeded} player confirmations + +
+ + {!isMobile && ( +
+
+
+
+
+ )} +
+ + +
+ )} + +
+
+ + AVAILABILITY + +
+ {availability.map((slot, idx) => ( + + {slot} + + ))} +
+
+ +
+ + LOCAL COURTS + +
+ {courts.map((court, idx) => ( +
+ + + + + {court} +
+ ))} +
+
+
+
+
+ +
+ +
+
+ ); +}; + +export default MyProfileQuickView; diff --git a/src/components/players/PlayersFilterBar.tsx b/src/components/players/PlayersFilterBar.tsx index 9ad7c18c..e7324275 100644 --- a/src/components/players/PlayersFilterBar.tsx +++ b/src/components/players/PlayersFilterBar.tsx @@ -20,6 +20,12 @@ type PlayersFilterBarProps = { genderOptions: string[]; selectedGender: string; onGenderChange: (value: string) => void; + playTypeOptions?: string[]; + selectedPlayType?: string; + onPlayTypeChange?: (value: string) => void; + availabilityOptions?: string[]; + selectedAvailability?: string; + onAvailabilityChange?: (value: string) => void; verifiedOnly: boolean; onVerifiedOnlyChange: (value: boolean) => void; }; @@ -40,6 +46,12 @@ const PlayersFilterBar = ({ genderOptions, selectedGender, onGenderChange, + playTypeOptions, + selectedPlayType, + onPlayTypeChange, + availabilityOptions, + selectedAvailability, + onAvailabilityChange, verifiedOnly, onVerifiedOnlyChange, }: PlayersFilterBarProps) => { @@ -121,6 +133,42 @@ const PlayersFilterBar = ({
+ {playTypeOptions && selectedPlayType && onPlayTypeChange ? ( +
+ +
+ ) : null} + + {availabilityOptions && selectedAvailability && onAvailabilityChange ? ( +
+ +
+ ) : null} +