Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interactive map #15

Merged
merged 12 commits into from
Jan 25, 2025
8 changes: 5 additions & 3 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"test": "react-scripts test",
"eject": "react-scripts eject",
"format": "prettier --write 'src/**/*.{js,jsx,ts,tsx}' ",
"lint": "eslint --fix 'src/**/*.{js,jsx,ts,tsx} .'",
"lint": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}'",
"postinstall": "echo \"Package install attempted in client directory\""
},
"eslintConfig": {
Expand All @@ -33,7 +33,8 @@
]
},
"dependencies": {
"@emotion/styled": "^11.13.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@material-ui/icons": "^4.11.2",
"@mui/material": "^5.5.3",
"@mui/system": "^5.5.2",
Expand All @@ -46,14 +47,15 @@
"react-redux": "^8.0.0",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"react-usa-map": "^1.5.0",
"redux": "^4.1.2",
"redux-persist": "^6.0.0",
"styled-components": "^5.3.5",
"typeface-hk-grotesk": "^1.0.0",
"web-vitals": "^3.0.3"
},
"devDependencies": {
"@mui/styled-engine": "npm:@mui/styled-engine-sc@latest",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
Expand Down
Binary file added client/public/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import EmailResetPasswordPage from './Authentication/EmailResetPasswordPage.tsx'
import ResetPasswordPage from './Authentication/ResetPasswordPage.tsx';
import AlertPopup from './components/AlertPopup.tsx';
import InviteRegisterPage from './Authentication/InviteRegisterPage.tsx';
import Landing from './Landing/Landing.tsx';
import TempAdminDashboardPage from './AdminDashboard/TempAdminDashboardPage.tsx';

function App() {
Expand All @@ -34,6 +35,7 @@ function App() {
<CssBaseline>
<AlertPopup />
<Routes>
<Route path="/" element={<Landing />} />
{/* Routes accessed only if user is not authenticated */}
<Route element={<UnauthenticatedRoutesWrapper />}>
<Route path="/login" element={<LoginPage />} />
Expand Down
4 changes: 2 additions & 2 deletions client/src/Authentication/RegisterPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@ function RegisterPage() {
</Grid>
<FormRow>
<Grid container justifyContent="center">
<Link component={RouterLink} to="../">
Back to Login
<Link component={RouterLink} to="/home">
Back to Home
</Link>
</Grid>
</FormRow>
Expand Down
294 changes: 294 additions & 0 deletions client/src/Home/LandingPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
import React, { useState } from 'react';
import {
Button,
Dialog,
DialogTitle,
DialogContent,
IconButton,
Typography,
Box,
List,
ListItem,
Paper,
} from '@mui/material';

function CloseIcon() {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18" />
<line x1="6" y1="6" x2="18" y2="18" />
</svg>
);
}

interface StateCategory {
color: string;
hoverColor: string;
states: string[];
}

interface StateCategories {
[key: string]: StateCategory;
}

interface Chapter {
city: string;
id: string;
}

interface StateData {
name: string;
acceptingRequests: boolean;
chapters: Chapter[];
}

interface StateChapters {
[key: string]: StateData;
}

const stateCategories: StateCategories = {
turquoise: {
color: '#7ACCC8',
hoverColor: '#5FB9B5',
states: ['CA', 'ID', 'CO', 'ND', 'IA', 'MI', 'PA', 'GA', 'TX'],
},
yellow: {
color: '#FFE17B',
hoverColor: '#EBD068',
states: ['AZ', 'NE', 'WI', 'LA', 'NJ', 'HI'],
},
salmon: {
color: '#F7BDB1',
hoverColor: '#E4AAA0',
states: ['WA', 'NV', 'WY', 'MN', 'IL', 'KY', 'AL'],
},
inactive: {
color: '#D3D3D3',
hoverColor: '#C0C0C0',
states: [
'OR',
'MT',
'UT',
'SD',
'KS',
'MO',
'TN',
'NC',
'SC',
'VA',
'WV',
'OH',
'IN',
'MS',
'AR',
'OK',
'NM',
'ME',
'NH',
'VT',
'MA',
'RI',
'CT',
'NY',
'DE',
'MD',
'FL',
'AK',
],
},
};

const stateChapters: StateChapters = {
WI: {
name: 'Wisconsin',
acceptingRequests: true,
chapters: [
{ city: 'Columbia County', id: 'wi-1' },
{ city: 'DeForest', id: 'wi-2' },
{ city: 'Madison', id: 'wi-3' },
{ city: 'Sun Prairie', id: 'wi-4' },
{ city: 'Poynette', id: 'wi-5' },
],
},
};

export default function LandingPage() {
const [selectedState, setSelectedState] = useState<StateData | null>(null);
const [hoveredChapter, setHoveredChapter] = useState<Chapter | null>(null);

const mapHandler = (event: any) => {
const stateData = stateChapters[event.target.dataset.name];
if (stateData) {
setSelectedState(stateData);
}
};

const statesCustomConfig = () => {
const config: { [key: string]: { fill: string } } = {};

Object.entries(stateCategories).forEach(([category, data]) => {
data.states.forEach((state) => {
config[state] = {
fill: data.color,
};
});
});

return config;
};

const handleRequestClick = (chapter: Chapter): void => {
console.log('Navigate to request form for:', chapter);
};

const handleClose = (): void => {
setSelectedState(null);
};

return (
<Box sx={{ minHeight: '100vh', bgcolor: 'background.paper', p: 4 }}>
{/* Header */}
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
mb: 4,
}}
>
<Typography variant="h4" component="h1" fontWeight="bold">
Welcome!
</Typography>
<Button
variant="contained"
sx={{
bgcolor: '#EC4899',
'&:hover': {
bgcolor: '#DB2777',
},
}}
onClick={() => console.log('Navigate to login')}
>
Chapter Login
</Button>
</Box>

{/* Instructions */}
<Box sx={{ mb: 4, color: 'text.secondary' }}>
<Typography>
Click on your state below to connect with a local chapter in your
area!
</Typography>
<List sx={{ ml: 2 }}>
<ListItem sx={{ display: 'list-item' }}>
Find your local birthday hero directly in your area
</ListItem>
<ListItem sx={{ display: 'list-item' }}>
Sign up to be a volunteer
</ListItem>
<ListItem sx={{ display: 'list-item' }}>
Donate to your local chapter
</ListItem>
<ListItem sx={{ display: 'list-item' }}>
Sign up for our Milestone program
</ListItem>
<ListItem sx={{ display: 'list-item' }}>
Contact your local chapter leader to learn more ways you can support
our cause
</ListItem>
</List>
</Box>

{/* US Map */}
<Box sx={{ width: '100%', maxWidth: '4xl', mx: 'auto' }} />

{/* State Chapters Modal */}
<Dialog
open={selectedState !== null}
onClose={handleClose}
maxWidth="sm"
fullWidth
>
{selectedState && (
<>
<DialogTitle
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
{selectedState.name}
<IconButton size="small" onClick={handleClose} aria-label="close">
<CloseIcon />
</IconButton>
</DialogTitle>
<DialogContent>
<Typography variant="subtitle1" sx={{ mb: 2 }}>
Local Chapters
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
{selectedState.chapters.map((chapter) => (
<Paper
key={chapter.id}
elevation={1}
sx={{
p: 2,
position: 'relative',
'&:hover': { bgcolor: 'action.hover' },
cursor: 'pointer',
}}
onMouseEnter={() => setHoveredChapter(chapter)}
onMouseLeave={() => setHoveredChapter(null)}
>
<Typography>{chapter.city}</Typography>
{hoveredChapter === chapter && (
<Box
sx={{
position: 'absolute',
right: 16,
top: '50%',
transform: 'translateY(-50%)',
}}
>
{selectedState.acceptingRequests ? (
<Button
size="small"
variant="contained"
sx={{
bgcolor: '#EC4899',
'&:hover': {
bgcolor: '#DB2777',
},
}}
onClick={() => handleRequestClick(chapter)}
>
Request Form
</Button>
) : (
<Typography color="error" variant="body2">
Sorry, we are not receiving requests. Please try
again next month!
</Typography>
)}
</Box>
)}
</Paper>
))}
</Box>
</DialogContent>
</>
)}
</Dialog>
</Box>
);
}
39 changes: 39 additions & 0 deletions client/src/Landing/Landing.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import { Button } from '@mui/material';
import { Link as RouterLink } from 'react-router-dom';
import LandingMap from './LandingMap.tsx';
import LandingHeader from './LandingHeader.tsx';
import { useAppSelector } from '../util/redux/hooks.ts';
import { selectUser } from '../util/redux/userSlice.ts';

export default function Landing() {
const user = useAppSelector(selectUser);
// Check if user has an email, which indicates they're logged in
const isLoggedIn = Boolean(user.email);
const loginPath = isLoggedIn ? '/home' : '/login';

return (
<div style={{ position: 'relative' }}>
<LandingHeader />
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Button
component={RouterLink}
to={loginPath}
variant="contained"
sx={{
backgroundColor: '#f28f8a',
width: '200px',
height: '50px',
fontSize: '17px',
'&:hover': {
backgroundColor: '#ff3f73',
},
}}
>
CHAPTER LOGIN
</Button>
</div>
<LandingMap />
</div>
);
}
Loading