Skip to content
This repository was archived by the owner on Jan 12, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13,145 changes: 7,077 additions & 6,068 deletions apps/frontend/src/GIBostonSites.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import FacebookIcon from '@material-ui/icons/Facebook';
import YouTubeIcon from '@material-ui/icons/YouTube';
import generateInstagramIcon from './InstagramIcon';
import { Link } from 'react-router-dom';
import { UserModel } from '../../pages/volunteerPage/VolunteerPage';

const textStyles = {
fontFamily: 'Montserrat, sans-serif',
Expand All @@ -20,10 +21,12 @@ const boxStyles = {

interface VolunteerDashboardProps {
setMaintenanceChecklistOpen: React.Dispatch<React.SetStateAction<boolean>>;
userData: UserModel | null;
}

function VolunteerDashboard({
setMaintenanceChecklistOpen,
userData,
}: VolunteerDashboardProps) {
return (
<div
Expand All @@ -33,7 +36,9 @@ function VolunteerDashboard({
<Box display="flex" flexDirection="row" justifyContent="space-between">
<div>
<text style={{ ...textStyles, fontWeight: '500' }}>Welcome, </text>
<text style={{ ...textStyles, fontWeight: '700' }}>Volunteer</text>
<text style={{ ...textStyles, fontWeight: '700' }}>
{userData ? userData.firstName : 'Volunteer'}
</text>
</div>

<Box display="flex" flexDirection="row" gap={3}>
Expand Down Expand Up @@ -91,7 +96,7 @@ function VolunteerDashboard({
}}
>
<Box sx={{ ...boxStyles, height: '100%', width: '100%' }}>
My Adopted Green Infrastructure
<p>My Adopted Green Infrastructure</p>
</Box>
</Link>
<Box
Expand Down
124 changes: 96 additions & 28 deletions apps/frontend/src/pages/myAdoptedGIPage/MyAdoptedGIPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ import SearchIcon from '@mui/icons-material/Search';
import Navbar from '../Navbar';
import { CSSProperties, useEffect, useState } from 'react';
import AdoptedGIBlock from './AdoptedGIBlock';
import BioswaleImage from './BioswaleImage.png';
import { SiteType, SITES } from '../../GIBostonSites'
import BioswaleImage from './siteImg/BioswaleImage.png';
import BioRetentionImage from './siteImg/bioretention.png';
import GreenRoofImage from './siteImg/greenRoof_Planter.png';
import RainGardenImage from './siteImg/raingarden.png';
import TreeTrenchImage from './siteImg/tree trench_planter.png';
import { SiteType } from '../../GIBostonSites';
import { fetchSiteInfo, fetchUserInfo } from '../volunteerPage/VolunteerPage';

const containerStyles: CSSProperties = {
width: '100%',
Expand Down Expand Up @@ -47,22 +52,31 @@ const blueLineStyle: CSSProperties = {
marginTop: '1.5%',
};

interface Props {
selectedFilter: string
interface FormControlsProps {
selectedFilter: string;
setSelectedFilter: (value: string) => void;
selectedName: string
selectedName: string;
setSelectedName: (value: string) => void;
}
function RenderFormControls({ selectedFilter, setSelectedFilter, selectedName, setSelectedName }: Props) {
const handleSiteTypeChange = (event: { target: { value: string; }; }) => {
function RenderFormControls({
selectedFilter,
setSelectedFilter,
selectedName,
setSelectedName,
}: FormControlsProps) {
const handleSiteTypeChange = (event: { target: { value: string } }) => {
setSelectedFilter(event.target.value);
};
const handleNameChange = (event: { target: { value: string; }; }) => {
const handleNameChange = (event: { target: { value: string } }) => {
setSelectedName(event.target.value);
};
return (
<div style={rowStyles}>
<FormControl variant="filled" sx={{ width: '20%', marginRight: '20px' }} size="small">
<FormControl
variant="filled"
sx={{ width: '20%', marginRight: '20px' }}
size="small"
>
<InputLabel
id="filter-one-select"
sx={{ fontFamily: 'Montserrat, sans-serif', color: 'black' }}
Expand All @@ -77,7 +91,9 @@ function RenderFormControls({ selectedFilter, setSelectedFilter, selectedName, s
sx={{ borderRadius: '7px' }}
disableUnderline
>
<MenuItem value="" style={{ color: 'grey' }}>Show All</MenuItem>
<MenuItem value="" style={{ color: 'grey' }}>
Show All
</MenuItem>
<MenuItem value="Rain Garden">Rain Garden</MenuItem>
<MenuItem value="Bioswale">Bioswale</MenuItem>
<MenuItem value="Bioretention">Bioretention</MenuItem>
Expand Down Expand Up @@ -111,26 +127,70 @@ function RenderFormControls({ selectedFilter, setSelectedFilter, selectedName, s
</div>
);
}
function getFeatureImage(symbolType: string) {
switch (symbolType) {
case 'Rain Garden':
return RainGardenImage;
case 'Bioretention':
return BioRetentionImage;
case 'Green Roof/Planter':
return GreenRoofImage;
case 'Bioswale':
return BioswaleImage;
case 'Tree Trench/Pit':
return TreeTrenchImage;
default:
return BioswaleImage; // Default fallback image
}
}

export default function MyAdoptedGIPage() {
const [sites, setSites] = useState<Array<SiteType>>([]);
const [selectedFilter, setSelectedFilter] = useState('')
const [selectedName, setSelectedName] = useState('')
const [selectedFilter, setSelectedFilter] = useState('');
const [selectedName, setSelectedName] = useState('');

useEffect(() => {
const sitesCopy = [...SITES];
const splicedSites = sitesCopy.splice(0, 30);
setSites(splicedSites);
}, [])
async function fetchData() {
const volunteerInfo = await fetchUserInfo();

const sitePromises = volunteerInfo.siteIds.map(async (siteId: number) => {
const siteData = await fetchSiteInfo(siteId);
return {
'Object ID?': siteData.siteID,
'Asset Name': siteData.siteName,
'Asset Type': siteData.symbolType,
'Symbol Type': siteData.symbolType,
Lat: siteData.siteLatitude,
Long: siteData.siteLongitude,
Address: siteData.address,
Neighborhood: siteData.neighborhood,
'Partner Depts.': '',
'Maintenance Agreement?': '',
'Link to Maintenance Agreement': '',
'Link to Maintenance Checklist': '',
'Link to Construction Cost + Plans': '',
'Link to RFQ or Bid Invitation': '',
'Link to Final Reports': '',
};
});

const allSites = await Promise.all(sitePromises);
setSites(allSites);
}
fetchData();
}, []);

return (
<div>
<Navbar />
<div style={containerStyles}>
<div style={titleStyles}>My Adopted Green Infrastructure</div>
<RenderFormControls
selectedFilter={selectedFilter}
setSelectedFilter={setSelectedFilter}
selectedName={selectedName}
setSelectedName={setSelectedName} />
<RenderFormControls
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the formatting changes!

selectedFilter={selectedFilter}
setSelectedFilter={setSelectedFilter}
selectedName={selectedName}
setSelectedName={setSelectedName}
/>
</div>
<div
style={{
Expand All @@ -143,16 +203,24 @@ export default function MyAdoptedGIPage() {
<div style={bioswaleTitleStyles}>Features</div>
<div className="blueLine" style={blueLineStyle} />
{sites
.filter(props => props["Asset Type"].toLowerCase().includes(selectedFilter.toLowerCase()))
.filter(props => props["Asset Name"].toLowerCase().includes(selectedName.toLowerCase()))
.filter((props) =>
props['Asset Type']
.toLowerCase()
.includes(selectedFilter.toLowerCase()),
)
.filter((props) =>
props['Asset Name']
.toLowerCase()
.includes(selectedName.toLowerCase()),
)
.map((props, index) => (
<AdoptedGIBlock
key={index}
img={BioswaleImage}
featureName={props["Asset Name"]}
featureType={props["Asset Type"]}
featureAddress={props["Address"]}
lastMaintenanceDate='Last Maintenance Date'
img={getFeatureImage(props['Symbol Type'])}
featureName={props['Asset Name']}
featureType={props['Symbol Type']}
featureAddress={props['Address']}
lastMaintenanceDate="Last Maintenance Date"
/>
))}
</div>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
89 changes: 88 additions & 1 deletion apps/frontend/src/pages/volunteerPage/VolunteerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,110 @@ import VolunteerDashboard from '../../components/volunteerDashboard/VolunteerDas
import MaintenanceChecklistPopup from '../../components/volunteerDashboard/MaintenanceChecklistPopup';
import Map from '../../components/map/Map';
import MapLegend from '../../components/map/MapLegend';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { SITE_STATUS_ROADMAP } from '../../constants';
import axios from 'axios';

const icons: string[] = SITE_STATUS_ROADMAP.map((option) => option.image);
export enum Role {
VOLUNTEER = 'Volunteer',
ADMIN = 'Admin',
}

export enum UserStatus {
APPROVED = 'Approved',
PENDING = 'Pending',
DENIED = 'Denied',
}
export interface UserModel {
userId: number;
firstName: string;
lastName: string;
phoneNumber: number;
email: string;
siteIds: number[];
zipCode: number;
birthDate: Date;
role: Role;
status: UserStatus;
}

export enum SiteStatus {
ADOPTED = 'Adopted',
AVAILABLE = 'Available',
INACTIVE = 'Inactive',
}

export enum SymbolType {
RAIN_GARDEN = 'Rain Garden',
BIOSWALE = 'Bioswale',
BIORETENTION = 'Bioretention',
TREE_TRENCH_PIT = 'Tree Trench/Pit',
GREEN_ROOF_PLANTER = 'Green Roof/Planter',
}

export type SiteModel = {
siteID: number;
siteName: string;
siteStatus: SiteStatus;
assetType: string;
symbolType: SymbolType;
siteLatitude: string;
siteLongitude: number;
dateAdopted: Date;
maintenanceReports: number[];
neighborhood: string;
address: string;
};

const baseUrl = process.env.VITE_API_BASSE_URL;

export const fetchUserInfo = async () => {
const tempUserID = '400';
const response = await axios.get(
`${baseUrl}/users/${tempUserID}`,
);
if (response.status !== 200) {
console.error('Failed to fetch user information');
console.error(response);
}
const userData: UserModel = await response.data;
return userData;
};
export const fetchSiteInfo = async (siteId: number) => {
const response = await axios.get(
`${baseUrl}/sites/${siteId}`,
);
if (response.status !== 200) {
console.error('Failed to fetch site information');
console.error(response);
}
const siteData: SiteModel = await response.data;
return siteData;
};

export default function VolunteerPage() {
const [selectedFeatures, setSelectedFeatures] = useState<string[]>([]);
const [selectedStatuses, setSelectedStatuses] = useState<string[]>([]);
const [maintenanceChecklistOpen, setMaintenanceChecklistOpen] =
useState(false);
const [volunteerInfo, setVolunteerInfo] = useState<UserModel | null>(null);

useEffect(() => {
async function fetchData() {
const volunteerInfo = await fetchUserInfo();
setVolunteerInfo(volunteerInfo);
}
fetchData();
}, []);

return (
<div>
<Navbar />
<div style={{ marginTop: '50px' }} />
<VolunteerDashboard
setMaintenanceChecklistOpen={setMaintenanceChecklistOpen}
userData={volunteerInfo}
/>
<MaintenanceChecklistPopup
maintenanceChecklistOpen={maintenanceChecklistOpen}
Expand Down
Loading