Skip to content

Commit 5844d2f

Browse files
committed
a start
1 parent c5dc2d3 commit 5844d2f

2 files changed

Lines changed: 219 additions & 0 deletions

File tree

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import { Box, Button, Flex, Image, Text } from "@chakra-ui/react"
2+
3+
import { useColorModeValue } from "@/components/ui/color-mode"
4+
5+
type MarketKey = "home" | "draw" | "away"
6+
7+
interface BookInfo {
8+
name: string
9+
logoUrl?: string
10+
}
11+
12+
interface Market {
13+
value: string
14+
book?: BookInfo
15+
}
16+
17+
export interface OddsCardProps {
18+
homeTeam: string
19+
awayTeam: string
20+
marketLabel?: string
21+
home: Market
22+
draw: Market
23+
away: Market
24+
onSelect?: (market: MarketKey) => void
25+
}
26+
27+
interface OddsPillProps extends Market {
28+
label: string
29+
onSelect?: () => void
30+
}
31+
32+
function BookBadge({ logoUrl, name }: BookInfo) {
33+
const badgeBg = useColorModeValue("gray.100", "gray.700")
34+
const badgeColor = useColorModeValue("gray.600", "gray.300")
35+
36+
return (
37+
<Flex
38+
mt={2}
39+
align="center"
40+
gap={2}
41+
bg={badgeBg}
42+
color={badgeColor}
43+
px={2}
44+
py={1}
45+
borderRadius="md"
46+
fontSize="xs"
47+
maxW="100%"
48+
>
49+
{logoUrl ? (
50+
<Image src={logoUrl} alt={name} h={4} w="auto" objectFit="contain" />
51+
) : (
52+
<Text fontWeight="medium">{name}</Text>
53+
)}
54+
</Flex>
55+
)
56+
}
57+
58+
function OddsPill({ value, book, label, onSelect }: OddsPillProps) {
59+
const borderColor = useColorModeValue("gray.200", "gray.600")
60+
const hoverBg = useColorModeValue("gray.50", "gray.700")
61+
62+
return (
63+
<Button
64+
onClick={onSelect}
65+
variant="outline"
66+
borderRadius="xl"
67+
borderColor={borderColor}
68+
w="100px"
69+
py={3}
70+
px={3}
71+
display="flex"
72+
flexDir="column"
73+
alignItems="center"
74+
transition="all 0.2s ease-in-out"
75+
_hover={{ transform: "translateY(-3px)", bg: hoverBg, shadow: "md" }}
76+
>
77+
<Text fontSize="lg" fontWeight="semibold" letterSpacing="tight" color="fg.default">
78+
{value}
79+
</Text>
80+
{book ? <BookBadge {...book} /> : <Text fontSize="xs">{label}</Text>}
81+
</Button>
82+
)
83+
}
84+
85+
function MarketColumn({
86+
title,
87+
market,
88+
onSelect,
89+
}: {
90+
title: string
91+
market: Market
92+
onSelect?: () => void
93+
}) {
94+
return (
95+
<Flex direction="column" align="center" gap={2}>
96+
<Text fontSize="xs" fontWeight="semibold" color="fg.muted" letterSpacing="wide">
97+
{title}
98+
</Text>
99+
<OddsPill label={title} value={market.value} book={market.book} onSelect={onSelect} />
100+
</Flex>
101+
)
102+
}
103+
104+
export function OddsCard({
105+
homeTeam,
106+
awayTeam,
107+
marketLabel = "Moneyline",
108+
home,
109+
draw,
110+
away,
111+
onSelect,
112+
}: OddsCardProps) {
113+
const wrapperBg = useColorModeValue("gray.50", "gray.800")
114+
const borderColor = useColorModeValue("gray.200", "gray.600")
115+
116+
return (
117+
<Box
118+
borderWidth="1px"
119+
borderRadius="2xl"
120+
borderColor={borderColor}
121+
bg={wrapperBg}
122+
p={6}
123+
w="full"
124+
maxW="3xl"
125+
>
126+
<Flex
127+
direction={{ base: "column", sm: "row" }}
128+
justify="space-between"
129+
align={{ base: "stretch", sm: "center" }}
130+
gap={6}
131+
>
132+
<Box>
133+
<Text fontSize="xl" fontWeight="semibold" lineHeight="shorter">
134+
{homeTeam}
135+
</Text>
136+
<Text fontSize="xl" fontWeight="semibold" lineHeight="shorter">
137+
{awayTeam}
138+
</Text>
139+
<Text mt={3} fontSize="sm" fontWeight="medium" color="teal.600">
140+
{marketLabel}
141+
</Text>
142+
</Box>
143+
144+
<Flex
145+
justify="space-between"
146+
gap={4}
147+
w="full"
148+
maxW={{ base: "full", sm: "auto" }}
149+
>
150+
<MarketColumn
151+
title="HOME"
152+
market={home}
153+
onSelect={() => onSelect?.("home")}
154+
/>
155+
<MarketColumn
156+
title="DRAW"
157+
market={draw}
158+
onSelect={() => onSelect?.("draw")}
159+
/>
160+
<MarketColumn
161+
title="AWAY"
162+
market={away}
163+
onSelect={() => onSelect?.("away")}
164+
/>
165+
</Flex>
166+
</Flex>
167+
</Box>
168+
)
169+
}
170+
171+
export default OddsCard

frontend/src/routes/landing.tsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,43 @@ import { Box, Button, Container, Flex, Heading, Text } from "@chakra-ui/react"
22
import { Link as RouterLink, createFileRoute } from "@tanstack/react-router"
33

44
import { isLoggedIn } from "@/hooks/useAuth"
5+
import { OddsCard } from "@/components/Landing/OddsCard"
6+
import useCustomToast from "@/hooks/useCustomToast"
57

68
export const Route = createFileRoute("/landing")({
79
component: LandingPage,
810
})
911

1012
function LandingPage() {
1113
const loggedIn = isLoggedIn()
14+
const { showSuccessToast } = useCustomToast()
15+
16+
const featuredMarket = {
17+
homeTeam: "Tampines Rovers",
18+
awayTeam: "Pohang Steelers",
19+
marketLabel: "Moneyline",
20+
home: {
21+
value: "+640",
22+
book: {
23+
name: "BC.GAME",
24+
logoUrl: "https://dummyimage.com/60x18/eeeeee/333333&text=BC.GAME",
25+
},
26+
},
27+
draw: {
28+
value: "+350",
29+
book: {
30+
name: "MOSTBET",
31+
logoUrl: "https://dummyimage.com/64x18/eeeeee/333333&text=MOSTBET",
32+
},
33+
},
34+
away: {
35+
value: "-129",
36+
book: {
37+
name: "betway",
38+
logoUrl: "https://dummyimage.com/64x18/eeeeee/333333&text=betway",
39+
},
40+
},
41+
}
1242

1343
return (
1444
<Container
@@ -62,6 +92,24 @@ function LandingPage() {
6292
description="Invite your team and share insights to coordinate faster trades."
6393
/>
6494
</Flex>
95+
96+
<Flex direction="column" gap={6}>
97+
<Heading as="h2" size="lg">
98+
Track markets effortlessly
99+
</Heading>
100+
<Text color="fg.muted">
101+
Explore a unified view of the best odds across your books. Select a side to
102+
fast-track it into your workflow.
103+
</Text>
104+
<OddsCard
105+
{...featuredMarket}
106+
onSelect={(market) =>
107+
showSuccessToast(
108+
`Selected ${market.toUpperCase()} market from the featured matchup.`,
109+
)
110+
}
111+
/>
112+
</Flex>
65113
</Container>
66114
)
67115
}

0 commit comments

Comments
 (0)