Skip to content

Commit 60385d9

Browse files
carlygoogeljamesdoh0109kygchng
authored
Interactive map (#15)
* map basics done * map page styling * chapter info * state text * modal content changed to buttons of chapters, with clicking showing either form or no form text * header fixed + colors fixed * fixing lint * dummy data * merge conflicts * adding routing for chapter login on map landing page --------- Co-authored-by: Jihun (James) Doh <[email protected]> Co-authored-by: kygchng <[email protected]>
1 parent 067f027 commit 60385d9

File tree

11 files changed

+964
-685
lines changed

11 files changed

+964
-685
lines changed

client/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"test": "react-scripts test",
1212
"eject": "react-scripts eject",
1313
"format": "prettier --write 'src/**/*.{js,jsx,ts,tsx}' ",
14-
"lint": "eslint --fix 'src/**/*.{js,jsx,ts,tsx} .'",
14+
"lint": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}'",
1515
"postinstall": "echo \"Package install attempted in client directory\""
1616
},
1717
"eslintConfig": {
@@ -33,7 +33,8 @@
3333
]
3434
},
3535
"dependencies": {
36-
"@emotion/styled": "^11.13.0",
36+
"@emotion/react": "^11.14.0",
37+
"@emotion/styled": "^11.14.0",
3738
"@material-ui/icons": "^4.11.2",
3839
"@mui/material": "^5.5.3",
3940
"@mui/system": "^5.5.2",
@@ -46,14 +47,15 @@
4647
"react-redux": "^8.0.0",
4748
"react-router-dom": "^6.3.0",
4849
"react-scripts": "5.0.1",
50+
"react-usa-map": "^1.5.0",
4951
"redux": "^4.1.2",
5052
"redux-persist": "^6.0.0",
5153
"styled-components": "^5.3.5",
5254
"typeface-hk-grotesk": "^1.0.0",
5355
"web-vitals": "^3.0.3"
5456
},
5557
"devDependencies": {
56-
"@mui/styled-engine": "npm:@mui/styled-engine-sc@latest",
58+
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
5759
"@testing-library/jest-dom": "^5.14.1",
5860
"@testing-library/react": "^13.4.0",
5961
"@testing-library/user-event": "^14.4.3",

client/public/logo.png

196 KB
Loading

client/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import EmailResetPasswordPage from './Authentication/EmailResetPasswordPage.tsx'
2222
import ResetPasswordPage from './Authentication/ResetPasswordPage.tsx';
2323
import AlertPopup from './components/AlertPopup.tsx';
2424
import InviteRegisterPage from './Authentication/InviteRegisterPage.tsx';
25+
import Landing from './Landing/Landing.tsx';
2526
import TempAdminDashboardPage from './AdminDashboard/TempAdminDashboardPage.tsx';
2627

2728
function App() {
@@ -34,6 +35,7 @@ function App() {
3435
<CssBaseline>
3536
<AlertPopup />
3637
<Routes>
38+
<Route path="/" element={<Landing />} />
3739
{/* Routes accessed only if user is not authenticated */}
3840
<Route element={<UnauthenticatedRoutesWrapper />}>
3941
<Route path="/login" element={<LoginPage />} />

client/src/Authentication/RegisterPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,8 @@ function RegisterPage() {
236236
</Grid>
237237
<FormRow>
238238
<Grid container justifyContent="center">
239-
<Link component={RouterLink} to="../">
240-
Back to Login
239+
<Link component={RouterLink} to="/home">
240+
Back to Home
241241
</Link>
242242
</Grid>
243243
</FormRow>

client/src/Home/LandingPage.tsx

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
import React, { useState } from 'react';
2+
import {
3+
Button,
4+
Dialog,
5+
DialogTitle,
6+
DialogContent,
7+
IconButton,
8+
Typography,
9+
Box,
10+
List,
11+
ListItem,
12+
Paper,
13+
} from '@mui/material';
14+
15+
function CloseIcon() {
16+
return (
17+
<svg
18+
width="24"
19+
height="24"
20+
viewBox="0 0 24 24"
21+
fill="none"
22+
stroke="currentColor"
23+
strokeWidth="2"
24+
strokeLinecap="round"
25+
strokeLinejoin="round"
26+
>
27+
<line x1="18" y1="6" x2="6" y2="18" />
28+
<line x1="6" y1="6" x2="18" y2="18" />
29+
</svg>
30+
);
31+
}
32+
33+
interface StateCategory {
34+
color: string;
35+
hoverColor: string;
36+
states: string[];
37+
}
38+
39+
interface StateCategories {
40+
[key: string]: StateCategory;
41+
}
42+
43+
interface Chapter {
44+
city: string;
45+
id: string;
46+
}
47+
48+
interface StateData {
49+
name: string;
50+
acceptingRequests: boolean;
51+
chapters: Chapter[];
52+
}
53+
54+
interface StateChapters {
55+
[key: string]: StateData;
56+
}
57+
58+
const stateCategories: StateCategories = {
59+
turquoise: {
60+
color: '#7ACCC8',
61+
hoverColor: '#5FB9B5',
62+
states: ['CA', 'ID', 'CO', 'ND', 'IA', 'MI', 'PA', 'GA', 'TX'],
63+
},
64+
yellow: {
65+
color: '#FFE17B',
66+
hoverColor: '#EBD068',
67+
states: ['AZ', 'NE', 'WI', 'LA', 'NJ', 'HI'],
68+
},
69+
salmon: {
70+
color: '#F7BDB1',
71+
hoverColor: '#E4AAA0',
72+
states: ['WA', 'NV', 'WY', 'MN', 'IL', 'KY', 'AL'],
73+
},
74+
inactive: {
75+
color: '#D3D3D3',
76+
hoverColor: '#C0C0C0',
77+
states: [
78+
'OR',
79+
'MT',
80+
'UT',
81+
'SD',
82+
'KS',
83+
'MO',
84+
'TN',
85+
'NC',
86+
'SC',
87+
'VA',
88+
'WV',
89+
'OH',
90+
'IN',
91+
'MS',
92+
'AR',
93+
'OK',
94+
'NM',
95+
'ME',
96+
'NH',
97+
'VT',
98+
'MA',
99+
'RI',
100+
'CT',
101+
'NY',
102+
'DE',
103+
'MD',
104+
'FL',
105+
'AK',
106+
],
107+
},
108+
};
109+
110+
const stateChapters: StateChapters = {
111+
WI: {
112+
name: 'Wisconsin',
113+
acceptingRequests: true,
114+
chapters: [
115+
{ city: 'Columbia County', id: 'wi-1' },
116+
{ city: 'DeForest', id: 'wi-2' },
117+
{ city: 'Madison', id: 'wi-3' },
118+
{ city: 'Sun Prairie', id: 'wi-4' },
119+
{ city: 'Poynette', id: 'wi-5' },
120+
],
121+
},
122+
};
123+
124+
export default function LandingPage() {
125+
const [selectedState, setSelectedState] = useState<StateData | null>(null);
126+
const [hoveredChapter, setHoveredChapter] = useState<Chapter | null>(null);
127+
128+
const mapHandler = (event: any) => {
129+
const stateData = stateChapters[event.target.dataset.name];
130+
if (stateData) {
131+
setSelectedState(stateData);
132+
}
133+
};
134+
135+
const statesCustomConfig = () => {
136+
const config: { [key: string]: { fill: string } } = {};
137+
138+
Object.entries(stateCategories).forEach(([category, data]) => {
139+
data.states.forEach((state) => {
140+
config[state] = {
141+
fill: data.color,
142+
};
143+
});
144+
});
145+
146+
return config;
147+
};
148+
149+
const handleRequestClick = (chapter: Chapter): void => {
150+
console.log('Navigate to request form for:', chapter);
151+
};
152+
153+
const handleClose = (): void => {
154+
setSelectedState(null);
155+
};
156+
157+
return (
158+
<Box sx={{ minHeight: '100vh', bgcolor: 'background.paper', p: 4 }}>
159+
{/* Header */}
160+
<Box
161+
sx={{
162+
display: 'flex',
163+
justifyContent: 'space-between',
164+
alignItems: 'center',
165+
mb: 4,
166+
}}
167+
>
168+
<Typography variant="h4" component="h1" fontWeight="bold">
169+
Welcome!
170+
</Typography>
171+
<Button
172+
variant="contained"
173+
sx={{
174+
bgcolor: '#EC4899',
175+
'&:hover': {
176+
bgcolor: '#DB2777',
177+
},
178+
}}
179+
onClick={() => console.log('Navigate to login')}
180+
>
181+
Chapter Login
182+
</Button>
183+
</Box>
184+
185+
{/* Instructions */}
186+
<Box sx={{ mb: 4, color: 'text.secondary' }}>
187+
<Typography>
188+
Click on your state below to connect with a local chapter in your
189+
area!
190+
</Typography>
191+
<List sx={{ ml: 2 }}>
192+
<ListItem sx={{ display: 'list-item' }}>
193+
Find your local birthday hero directly in your area
194+
</ListItem>
195+
<ListItem sx={{ display: 'list-item' }}>
196+
Sign up to be a volunteer
197+
</ListItem>
198+
<ListItem sx={{ display: 'list-item' }}>
199+
Donate to your local chapter
200+
</ListItem>
201+
<ListItem sx={{ display: 'list-item' }}>
202+
Sign up for our Milestone program
203+
</ListItem>
204+
<ListItem sx={{ display: 'list-item' }}>
205+
Contact your local chapter leader to learn more ways you can support
206+
our cause
207+
</ListItem>
208+
</List>
209+
</Box>
210+
211+
{/* US Map */}
212+
<Box sx={{ width: '100%', maxWidth: '4xl', mx: 'auto' }} />
213+
214+
{/* State Chapters Modal */}
215+
<Dialog
216+
open={selectedState !== null}
217+
onClose={handleClose}
218+
maxWidth="sm"
219+
fullWidth
220+
>
221+
{selectedState && (
222+
<>
223+
<DialogTitle
224+
sx={{
225+
display: 'flex',
226+
justifyContent: 'space-between',
227+
alignItems: 'center',
228+
}}
229+
>
230+
{selectedState.name}
231+
<IconButton size="small" onClick={handleClose} aria-label="close">
232+
<CloseIcon />
233+
</IconButton>
234+
</DialogTitle>
235+
<DialogContent>
236+
<Typography variant="subtitle1" sx={{ mb: 2 }}>
237+
Local Chapters
238+
</Typography>
239+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
240+
{selectedState.chapters.map((chapter) => (
241+
<Paper
242+
key={chapter.id}
243+
elevation={1}
244+
sx={{
245+
p: 2,
246+
position: 'relative',
247+
'&:hover': { bgcolor: 'action.hover' },
248+
cursor: 'pointer',
249+
}}
250+
onMouseEnter={() => setHoveredChapter(chapter)}
251+
onMouseLeave={() => setHoveredChapter(null)}
252+
>
253+
<Typography>{chapter.city}</Typography>
254+
{hoveredChapter === chapter && (
255+
<Box
256+
sx={{
257+
position: 'absolute',
258+
right: 16,
259+
top: '50%',
260+
transform: 'translateY(-50%)',
261+
}}
262+
>
263+
{selectedState.acceptingRequests ? (
264+
<Button
265+
size="small"
266+
variant="contained"
267+
sx={{
268+
bgcolor: '#EC4899',
269+
'&:hover': {
270+
bgcolor: '#DB2777',
271+
},
272+
}}
273+
onClick={() => handleRequestClick(chapter)}
274+
>
275+
Request Form
276+
</Button>
277+
) : (
278+
<Typography color="error" variant="body2">
279+
Sorry, we are not receiving requests. Please try
280+
again next month!
281+
</Typography>
282+
)}
283+
</Box>
284+
)}
285+
</Paper>
286+
))}
287+
</Box>
288+
</DialogContent>
289+
</>
290+
)}
291+
</Dialog>
292+
</Box>
293+
);
294+
}

client/src/Landing/Landing.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from 'react';
2+
import { Button } from '@mui/material';
3+
import { Link as RouterLink } from 'react-router-dom';
4+
import LandingMap from './LandingMap.tsx';
5+
import LandingHeader from './LandingHeader.tsx';
6+
import { useAppSelector } from '../util/redux/hooks.ts';
7+
import { selectUser } from '../util/redux/userSlice.ts';
8+
9+
export default function Landing() {
10+
const user = useAppSelector(selectUser);
11+
// Check if user has an email, which indicates they're logged in
12+
const isLoggedIn = Boolean(user.email);
13+
const loginPath = isLoggedIn ? '/home' : '/login';
14+
15+
return (
16+
<div style={{ position: 'relative' }}>
17+
<LandingHeader />
18+
<div style={{ display: 'flex', justifyContent: 'center' }}>
19+
<Button
20+
component={RouterLink}
21+
to={loginPath}
22+
variant="contained"
23+
sx={{
24+
backgroundColor: '#f28f8a',
25+
width: '200px',
26+
height: '50px',
27+
fontSize: '17px',
28+
'&:hover': {
29+
backgroundColor: '#ff3f73',
30+
},
31+
}}
32+
>
33+
CHAPTER LOGIN
34+
</Button>
35+
</div>
36+
<LandingMap />
37+
</div>
38+
);
39+
}

0 commit comments

Comments
 (0)