Skip to content

Commit

Permalink
Refactor PhotoCarousel component and add photo carousel functionality
Browse files Browse the repository at this point in the history
- Refactor the PhotoCarousel component to update the styling to handle large photos.
- Add functionality to the PhotoCarousel component to display a carousel with a different start index
- The PhotoCarousel component now accepts a start index as a prop.
- Clicking on a photo in the Review and AdminReview components triggers the photo carousel with the corresponding photos and start index.
  • Loading branch information
kea-roy committed Oct 24, 2024
1 parent dfe54dd commit 277b718
Show file tree
Hide file tree
Showing 8 changed files with 215 additions and 12 deletions.
22 changes: 20 additions & 2 deletions frontend/src/components/Admin/AdminReview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ type Props = {
readonly setToggle: React.Dispatch<React.SetStateAction<boolean>>;
/** Indicates if the review is in the declined section. */
readonly declinedSection: boolean;
/** Function to trigger the photo carousel. */
readonly triggerPhotoCarousel: (photos: readonly string[], startIndex: number) => void;
};

/**
Expand Down Expand Up @@ -70,14 +72,24 @@ const useStyles = makeStyles(() => ({
borderRadius: '4px',
height: '15em',
width: '15em',
cursor: 'pointer',
transition: '0.3s ease-in-out',
'&:hover': {
filter: 'brightness(0.85)',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.5)',
transform: 'scale(1.02)',
},
},
photoRowStyle: {
overflowX: 'auto',
overflowY: 'hidden',
display: 'flex',
flexDirection: 'row',
gap: '1vw',
paddingTop: '2%',
paddingLeft: '0.6%',
paddingRight: '0.6%',
paddingBottom: '2%',
},
}));

Expand All @@ -87,7 +99,12 @@ const useStyles = makeStyles(() => ({
* @param review review - The review to approve
* @returns The rendered component.
*/
const AdminReviewComponent = ({ review, setToggle, declinedSection }: Props): ReactElement => {
const AdminReviewComponent = ({
review,
setToggle,
declinedSection,
triggerPhotoCarousel,
}: Props): ReactElement => {
const { detailedRatings, overallRating, bedrooms, price, date, reviewText, photos } = review;
const formattedDate = format(new Date(date), 'MMM dd, yyyy').toUpperCase();
const { root, dateText, bedroomsPriceText, ratingInfo, photoStyle, photoRowStyle } = useStyles();
Expand Down Expand Up @@ -202,14 +219,15 @@ const AdminReviewComponent = ({ review, setToggle, declinedSection }: Props): Re
{photos.length > 0 && (
<Grid container>
<Grid item className={photoRowStyle}>
{photos.map((photo) => {
{photos.map((photo, i) => {
return (
<CardMedia
component="img"
alt="Apt image"
image={photo}
title="Apt image"
className={photoStyle}
onClick={() => triggerPhotoCarousel(photos, i)}
/>
);
})}
Expand Down
23 changes: 21 additions & 2 deletions frontend/src/components/PhotoCarousel/PhotoCarousel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react';
import { Modal, Box, styled, Container, CardMedia, Dialog, makeStyles } from '@material-ui/core';
import { Box, styled, Container, CardMedia, Dialog, makeStyles } from '@material-ui/core';
import Carousel from 'react-material-ui-carousel';

interface Props {
photos: readonly string[];
open: boolean;
onClose?: () => void;
startIndex: number;
}

const useStyles = makeStyles((theme) => ({
Expand All @@ -24,9 +25,26 @@ const ImageBox = styled(Box)({
margin: 'auto',
borderRadius: '10px',
overflow: 'hidden',
maxHeight: '80dvh',
objectFit: 'contain',
});

const PhotoCarousel = ({ photos, open, onClose }: Props) => {
/**
* PhotoCarousel - this component displays a modal with a carousel of photos.
*
* @remarks
* This component is used to display a modal with a carousel of photos.
* It dynamically adjusts to different screen sizes and can be navigated using the arrow buttons.
*
* @component
* @param {readonly string[]} props.photos - An array of photo URLs to display in the carousel.
* @param {boolean} props.open - A boolean indicating whether the modal is open.
* @param {() => void} [props.onClose] - An optional callback function to handle closing the modal.
* @param {number} props.startIndex - The starting index of the carousel.
*
* @returns {JSX.Element} The rendered PhotoCarousel component.
*/
const PhotoCarousel = ({ photos, open, onClose, startIndex }: Props) => {
const { modalBackground, navButton } = useStyles();
return (
<Dialog
Expand All @@ -41,6 +59,7 @@ const PhotoCarousel = ({ photos, open, onClose }: Props) => {
autoPlay={false}
navButtonsAlwaysVisible={true}
navButtonsProps={{ className: navButton }}
index={startIndex}
>
{photos.map((src, index) => {
return (
Expand Down
19 changes: 16 additions & 3 deletions frontend/src/components/Review/Review.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ type Props = {
readonly addLike: (reviewId: string) => Promise<void>;
readonly removeLike: (reviewId: string) => Promise<void>;
setToggle: React.Dispatch<React.SetStateAction<boolean>>;
readonly triggerEditToast?: () => void;
readonly triggerEditToast: () => void;
readonly triggerPhotoCarousel: (photos: readonly string[], startIndex: number) => void;
user: firebase.User | null;
setUser: React.Dispatch<React.SetStateAction<firebase.User | null>>;
readonly showLabel: boolean;
Expand Down Expand Up @@ -91,14 +92,24 @@ const useStyles = makeStyles(() => ({
borderRadius: '4px',
height: '15em',
width: '15em',
cursor: 'pointer',
transition: '0.3s ease-in-out',
'&:hover': {
filter: 'brightness(0.85)',
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.5)',
transform: 'scale(1.02)',
},
},
photoRowStyle: {
overflowX: 'auto',
display: 'flex',
lexDirection: 'row',
flexDirection: 'row',
gap: '1vw',
paddingTop: '2%',
paddingLeft: '0.6%',
overflowY: 'hidden',
paddingRight: '0.6%',
paddingBottom: '2%',
},
bedroomsPrice: {
display: 'flex',
Expand All @@ -122,6 +133,7 @@ const ReviewComponent = ({
removeLike,
setToggle,
triggerEditToast,
triggerPhotoCarousel,
user,
setUser,
showLabel,
Expand Down Expand Up @@ -374,14 +386,15 @@ const ReviewComponent = ({
{reviewData.photos.length > 0 && (
<Grid container>
<Grid item className={photoRowStyle}>
{reviewData.photos.map((photo) => {
{reviewData.photos.map((photo, i) => {
return (
<CardMedia
component="img"
alt="Apt image"
image={photo}
title="Apt image"
className={photoStyle}
onClick={() => triggerPhotoCarousel(reviewData.photos, i)}
/>
);
})}
Expand Down
36 changes: 36 additions & 0 deletions frontend/src/pages/AdminPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import AdminReviewComponent from '../components/Admin/AdminReview';
import { useTitle } from '../utils';
import { Chart } from 'react-google-charts';
import { sortReviews } from '../utils/sortReviews';
import PhotoCarousel from '../components/PhotoCarousel/PhotoCarousel';

const useStyles = makeStyles((theme) => ({
container: {
Expand Down Expand Up @@ -51,6 +52,10 @@ const AdminPage = (): ReactElement => {
const [pendingApartment, setPendingApartmentData] = useState<CantFindApartmentForm[]>([]);
const [pendingContactQuestions, setPendingContactQuestions] = useState<QuestionForm[]>([]);

const [carouselOpen, setCarouselOpen] = useState(false);
const [carouselPhotos, setCarouselPhotos] = useState<readonly string[]>([]);
const [carouselStartIndex, setCarouselStartIndex] = useState<number>(0);

const { container } = useStyles();

useTitle('Admin');
Expand Down Expand Up @@ -115,6 +120,34 @@ const AdminPage = (): ReactElement => {
});
}, [toggle]);

/**
* showPhotoCarousel – Opens the photo carousel modal with the provided photos and start index.
*
* @remarks
* This function sets the photos and start index for the photo carousel and then opens the carousel modal.
* If no photos are provided, it defaults to no photos.
*
* @param {readonly string[]} [photos] – The array of photo URLs to display in the carousel.
* @param {number} [startIndex] – The index of the photo to start the carousel from.
* @return {void} – This function does not return anything.
*/
const showPhotoCarousel = (photos: readonly string[] = [], startIndex: number = 0) => {
setCarouselPhotos(photos);
setCarouselStartIndex(startIndex);
setCarouselOpen(true);
};

const Modals = (
<>
<PhotoCarousel
photos={carouselPhotos}
open={carouselOpen}
onClose={() => setCarouselOpen(false)}
startIndex={carouselStartIndex}
/>
</>
);

// Reviews tab
const reviews = (
<Container className={container}>
Expand Down Expand Up @@ -157,6 +190,7 @@ const AdminPage = (): ReactElement => {
review={review}
setToggle={setToggle}
declinedSection={false}
triggerPhotoCarousel={showPhotoCarousel}
/>
</Grid>
))}
Expand All @@ -175,6 +209,7 @@ const AdminPage = (): ReactElement => {
review={review}
setToggle={setToggle}
declinedSection={true}
triggerPhotoCarousel={showPhotoCarousel}
/>
</Grid>
))}
Expand Down Expand Up @@ -246,6 +281,7 @@ const AdminPage = (): ReactElement => {

{selectedTab === 'Reviews' && reviews}
{selectedTab === 'Contact' && contact}
{Modals}
</div>
);
};
Expand Down
32 changes: 28 additions & 4 deletions frontend/src/pages/ApartmentPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ const ApartmentPage = ({ user, setUser }: Props): ReactElement => {
const [reviewOpen, setReviewOpen] = useState(false);
const [mapOpen, setMapOpen] = useState(false);
const [carouselOpen, setCarouselOpen] = useState(false);
const [carouselPhotos, setCarouselPhotos] = useState<readonly string[]>([]);
const [carouselStartIndex, setCarouselStartIndex] = useState<number>(0);
const [showConfirmation, setShowConfirmation] = useState(false);
const [showEditSuccessConfirmation, setShowEditSuccessConfirmation] = useState(false);
const [buildings, setBuildings] = useState<Apartment[]>([]);
Expand Down Expand Up @@ -372,6 +374,26 @@ const ApartmentPage = ({ user, setUser }: Props): ReactElement => {
setReviewOpen(true);
};

/**
* showPhotoCarousel – Opens the photo carousel modal with the provided photos and start index.
*
* @remarks
* This function sets the photos and start index for the photo carousel and then opens the carousel modal.
* If no photos are provided, it defaults to the apartment's photos.
*
* @param {readonly string[]} [photos] – The array of photo URLs to display in the carousel.
* @param {number} [startIndex] – The index of the photo to start the carousel from.
* @return {void} – This function does not return anything.
*/
const showPhotoCarousel = (
photos: readonly string[] = apt ? apt.photos : [],
startIndex: number = 0
) => {
setCarouselPhotos(photos);
setCarouselStartIndex(startIndex);
setCarouselOpen(true);
};

const Modals = landlordData && apt && (
<>
<MapModal
Expand All @@ -397,8 +419,9 @@ const ApartmentPage = ({ user, setUser }: Props): ReactElement => {
user={user}
/>
<PhotoCarousel
photos={apt.photos}
photos={carouselPhotos}
open={carouselOpen}
startIndex={carouselStartIndex}
onClose={() => setCarouselOpen(false)}
/>
</>
Expand Down Expand Up @@ -464,7 +487,7 @@ const ApartmentPage = ({ user, setUser }: Props): ReactElement => {
color="secondary"
variant="contained"
disableElevation
onClick={() => setCarouselOpen(true)}
onClick={() => showPhotoCarousel()}
>
Show all photos
</Button>
Expand Down Expand Up @@ -538,7 +561,7 @@ const ApartmentPage = ({ user, setUser }: Props): ReactElement => {
color="secondary"
variant="contained"
disableElevation
onClick={() => setCarouselOpen(true)}
onClick={() => showPhotoCarousel()}
>
Show all photos
</Button>
Expand Down Expand Up @@ -632,7 +655,7 @@ const ApartmentPage = ({ user, setUser }: Props): ReactElement => {
averageRating={getAverageRating(reviewData)}
apartment={apt!}
numReviews={reviewData.length}
handleClick={() => setCarouselOpen(true)}
handleClick={() => showPhotoCarousel()}
/>
</Container>
)}
Expand Down Expand Up @@ -715,6 +738,7 @@ const ApartmentPage = ({ user, setUser }: Props): ReactElement => {
removeLike={removeLike}
setToggle={setToggle}
triggerEditToast={showEditSuccessConfirmationToast}
triggerPhotoCarousel={showPhotoCarousel}
user={user}
setUser={setUser}
/>
Expand Down
Loading

0 comments on commit 277b718

Please sign in to comment.