Skip to content

Commit

Permalink
Added tenant property search in dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
hkam0006 committed Oct 2, 2024
1 parent 113c3be commit 7005079
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,7 @@ const SavedProperties = () => {
return (
<div>
<NavigationMenu>
<Grid
container
spacing={2}
style={{ padding: "30px", paddingTop: "110px" }}
justifyContent="flex-start"
/>
<div style={{ padding: "20px" }}>
<div style={{ padding: "20px", marginTop: "64px" }}>
{activeSection === "savedProperties" && (
<Typography variant="h5">Saved Properties</Typography>
)}
Expand Down
38 changes: 38 additions & 0 deletions frontend/src/renter-components/renter_home/ChipContainer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Paper, Chip } from "@mui/material";

const ChipContainer = ({chips, setChips, onDeleteChip}) => {

const handleDelete = (chipToDelete) => () => {
setChips((chips) => chips.filter((chip) => chip !== chipToDelete));
};

if (chips.length === 0) return <></>

return (
<Paper
sx={{
display: 'flex',
justifyContent: 'start',
padding: 1,
mt: 2,
boxShadow: 0,
borderRadius: 5,
gap: 1,
flexWrap: "wrap"
}}
>
{chips.map((data) => {
return (
<Chip
key={data}
label={data}
size='small'
onDelete={handleDelete(data)}
/>
)
})}
</Paper>
)
}

export default ChipContainer
42 changes: 42 additions & 0 deletions frontend/src/renter-components/renter_home/PropertyCard.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const PropertyCard = ({loading, property, showModal}) => {
const navigate = useNavigate();
return <>
{!loading ? <Card sx={{ borderRadius: 0, boxShadow: 2 }}>
<CardMedia
component="img"
height="200"
image={property.property_pictures[0]}
// alt={property.title}
/>
<CardContent>
<Typography variant="subtitle" component="div">
{fullAddress(property)}
</Typography>
<Typography variant="body2" color="text.secondary">
{property.property_type}
</Typography>
<Typography variant="body2" color="text.secondary">
${property.property_rent} {property.property_rent_frequency}
</Typography>
<Button
variant="contained"
color="primary"
fullWidth
sx={{ mt: 2 }}
onClick={showModal}
>
View Details
</Button>
</CardContent>
</Card> : <Box sx={{height: "100%"}}>
<Skeleton animation="wave" height={200} variant='rectangular' width={"100%"}/>
<Skeleton animation="wave" height={50} width={"100%"}/>
<Skeleton animation="wave" height={20} width={"50%"}/>
<Skeleton animation="wave" height={20} width={"20%"}/>
<Skeleton animation="wave" height={70} width={"100%"}/>
</Box>
}
</>
}

export default PropertyCard
121 changes: 120 additions & 1 deletion frontend/src/renter-components/renter_home/RenterHome.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useEffect, useState } from 'react'
import {Container, Paper, Typography} from "@mui/material"
import { Box, Modal, Grid, Container, Card, CardMedia, CardContent, Typography, Button, Skeleton, Paper, InputBase,Divider, IconButton, Chip, Menu, MenuItem, AppBar, Toolbar, Stack, Dialog, DialogTitle, DialogContent} from '@mui/material';
import NavigationMenu from '../navigation_menu/NavigationMenus';
import { supabase } from '../../supabase';
import {PropertyStatCards} from "../../manager-components/property_page/PropertyStatCards";
Expand All @@ -12,13 +12,115 @@ import useGetApplicationsByRenterID from "../../queries/Application/useGetApplic
import useGetPropertiesByCompanyID from "../../queries/Property/useGetPropertiesByCompanyID";
import useGetUserID from "../../queries/useGetUserID";
import useGetRenterByRenterID from "../../queries/Renter/useGetRenterByRenterID";
import SearchPropertyFilter from './SearchPropertyFilter';
import ChipContainer from './ChipContainer';
import { useNavigate } from 'react-router-dom';

const fullAddress = (p) => {
return `${p.property_street_number} ${p.property_street_name}, ${p.property_suburb}, ${p.property_state} ${p.property_postcode}`;
};

const PropertyCard = ({loading, property, viewProperty}) => {
const navigate = useNavigate();
return <>
{!loading ? <Card sx={{ borderRadius: 0, boxShadow: 2 }}>
<CardMedia
component="img"
height="200"
image={property.property_pictures[0]}
// alt={property.title}
/>
<CardContent>
<Typography variant="subtitle" component="div">
{fullAddress(property)}
</Typography>
<Typography variant="body2" color="text.secondary">
{property.property_type}
</Typography>
<Typography variant="body2" color="text.secondary">
${property.property_rent} {property.property_rent_frequency}
</Typography>
<Button
variant="contained"
color="primary"
fullWidth
sx={{ mt: 2 }}
onClick={viewProperty}
>
View Details
</Button>
</CardContent>
</Card> : <Box sx={{height: "100%"}}>
<Skeleton animation="wave" height={200} variant='rectangular' width={"100%"}/>
<Skeleton animation="wave" height={50} width={"100%"}/>
<Skeleton animation="wave" height={20} width={"50%"}/>
<Skeleton animation="wave" height={20} width={"20%"}/>
<Skeleton animation="wave" height={70} width={"100%"}/>
</Box>
}
</>
}

const ActualProperties = ({properties, setShowModal}) => {
const navigate = useNavigate()
return (
<>
{properties.map((p) => {
return (
<Grid key={p.property_id} item xs={12} sm={6} md={4}>
<PropertyCard key={p.property_id} loading={false} property={p} viewProperty={() => navigate(`/application/${p.property_id}`)}/>
</Grid>
)
})}
</>
)
}

const SkeletonProperties = () => {
return <>
{Array.from(Array(6)).map((key) => <Grid key={key} item xs={12} sm={6} md={4}>
<PropertyCard key={key} loading={true} />
</Grid>)}
</>
}

export default function RenterHome() {

const {userID, loading: userLoading} = useGetUserID();
const {renter, loading: renterLoading} = useGetRenterByRenterID(userID);
const {applications, loading: applicationsLoading} = useGetApplicationsByRenterID(userID);

const [loading, setLoading] = useState(true)
const [properties, setProperties] = useState([])
const [chips, setChips] = useState([])
const [search, setSearch] = useState("")


const handleFilterSubmit = () => {
setChips((prev) => [...prev, search])
setSearch("")
}

useEffect(() => {
async function findProperties(queries){
const joint_q = queries.join(" ")
console.log("TRIGGERED")
const { data, error } = await supabase
.from('PROPERTY')
.select('*')
.or(`property_suburb.ilike.%${joint_q}%, property_postcode.ilike.%${joint_q}%, property_state.ilike.%${joint_q}%`)
if (error) {
console.error('Error fetching data:', error)
setLoading(false)
return setProperties([])
}
setLoading(false)
return setProperties(data)
}

findProperties(chips)
}, [chips])

if (userLoading || renterLoading || applicationsLoading) return <AppLoader />

return (
Expand All @@ -31,6 +133,23 @@ export default function RenterHome() {
<Typography variant='h5' fontWeight={700} color="text.primary" style={{paddingLeft: '15px', paddingTop: '10px'}}>Current Applications</Typography>
<ApplicationsTable applications={applications}/>
</Paper>

<Paper sx={{mt: 2, borderRadius: 3}} elevation={3}>
<Typography variant='h5' fontWeight={700} color="text.primary" style={{paddingLeft: '15px', paddingTop: '10px'}}>Find Properties</Typography>
<SearchPropertyFilter
value={search}
onChange={setSearch}
handleSubmit={handleFilterSubmit}
/>
<ChipContainer chips={chips} setChips={setChips}/>
<Grid container spacing={2} mt={1} pl={2} pr={2} pb={2}>
{loading ? <SkeletonProperties /> : <ActualProperties properties={properties}/>}
{properties.length === 0 && !loading && <Stack sx={{textAlign: "center", width: "100%", mt: 3}}>
<Typography variant='h6'>Can't find any properties that match your query</Typography>
</Stack>
}
</Grid>
</Paper>
</div>
</NavigationMenu>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Box, Modal, Grid, Container, Card, CardMedia, CardContent, Typography, Button, Skeleton, Paper, InputBase,Divider, IconButton, Chip, Menu, MenuItem, AppBar, Toolbar, Stack, Dialog, DialogTitle, DialogContent} from '@mui/material';
import HouseIcon from '@mui/icons-material/House';
import FilterAltIcon from '@mui/icons-material/FilterAlt';
import SearchIcon from '@mui/icons-material/Search';

const SearchPropertyFilter = ({onSearch, value, onChange, handleSubmit}) => {
return(
<Paper
component="form"
onSubmit={(e) => {
e.preventDefault();
onSearch();
}}
sx={{ p: '2px 4px', display: 'flex', alignItems: 'center', width: "100%", margin: "auto", borderRadius: 3, minWidth: "300px", mt: 2 }}
>
<Box sx={{ p: '10px', display: "flex", alignItems: "center" }}>
<HouseIcon color='primary' aria-label="menu"/>
</Box>
<InputBase
sx={{ ml: 1, flex: 1 }}
value={value}
autoFocus
placeholder="Search region, suburb or postcode"
inputProps={{ 'aria-label': 'search suburb' }}
onChange={(e) => onChange(e.target.value)}
onKeyDown={(ev) => {
if (ev.key === 'Enter' ){
ev.preventDefault()
handleSubmit(ev)
}
}}
/>
<IconButton sx={{ p: '10px' }} aria-label="search" onClick={(e) => handleSubmit(e)}>
<SearchIcon />
</IconButton>
<Divider sx={{ height: 28, m: 0.5 }} orientation="vertical" />
<IconButton color="primary" sx={{ p: '10px' }} aria-label="directions" >
<FilterAltIcon />
</IconButton>
</Paper>
)
}

export default SearchPropertyFilter

0 comments on commit 7005079

Please sign in to comment.