From 2264369d072fdbd28f381eb0be02eb5d29672404 Mon Sep 17 00:00:00 2001 From: SurabhiKeesara Date: Sun, 3 Dec 2023 15:31:41 -0500 Subject: [PATCH] Add Delete --- src/api/test/protectedApiClient.test.ts | 27 ++++++++++++ src/components/careEntry/index.tsx | 9 ++-- src/components/confirmationModal/index.tsx | 6 +-- src/components/treePage/siteImageCarousel.tsx | 44 ++++++++++++++----- src/containers/treePage/ducks/types.ts | 1 + .../treePage/test/selectors.test.ts | 5 +++ 6 files changed, 71 insertions(+), 21 deletions(-) diff --git a/src/api/test/protectedApiClient.test.ts b/src/api/test/protectedApiClient.test.ts index 27be87dd..daa0b12f 100644 --- a/src/api/test/protectedApiClient.test.ts +++ b/src/api/test/protectedApiClient.test.ts @@ -1489,6 +1489,33 @@ describe('Protected API Client Tests', () => { expect(result).toEqual(response); }); }); + + describe('deleteSiteImage', () => { + it('makes the right request', async () => { + const response = 'Image Deleted Correctly'; + + nock(BASE_URL) + .post(ParameterizedApiRoutes.DELETE_IMAGE(1)) + .reply(200, response); + + const result = await ProtectedApiClient.deleteImage(1); + + expect(result).toEqual(response); + }); + it('makes a bad request', async () => { + const response = 'Invalid Image ID'; + + nock(BASE_URL) + .post(ParameterizedApiRoutes.DELETE_IMAGE(-1)) + .reply(400, response); + + const result = await ProtectedApiClient.deleteImage(-1).catch( + (err) => err.response.data, + ); + + expect(result).toEqual(response); + }); + }); }); describe('updateSite', () => { diff --git a/src/components/careEntry/index.tsx b/src/components/careEntry/index.tsx index c89ef519..6a8fe1dc 100644 --- a/src/components/careEntry/index.tsx +++ b/src/components/careEntry/index.tsx @@ -13,10 +13,7 @@ import { EditOutlined, DeleteOutlined } from '@ant-design/icons'; import { useDispatch, useSelector } from 'react-redux'; import { isAdmin, getUserID } from '../../auth/ducks/selectors'; import styled from 'styled-components'; -import { - EditButton, - StyledClose, -} from '../themedComponents'; +import { EditButton, StyledClose } from '../themedComponents'; import StewardshipForm from '../forms/stewardshipForm'; import { LinkButton } from '../linkButton'; import { useParams } from 'react-router-dom'; @@ -29,7 +26,7 @@ import { C4CState } from '../../store'; import { useTranslation } from 'react-i18next'; import { site } from '../../constants'; import { n } from '../../utils/stringFormat'; -import ConfirmationModel from '../confirmationModal'; +import ConfirmationModal from '../confirmationModal'; const Entry = styled.div` margin: 15px; @@ -179,7 +176,7 @@ const CareEntry: React.FC = ({ activity }) => { initialDate={treeCareToMoment(activity)} /> - setShowDeleteForm(false)} onCancel={() => setShowDeleteForm(false)} diff --git a/src/components/confirmationModal/index.tsx b/src/components/confirmationModal/index.tsx index 32d8d647..8dc06daf 100644 --- a/src/components/confirmationModal/index.tsx +++ b/src/components/confirmationModal/index.tsx @@ -13,7 +13,7 @@ const ConfirmDeleteButton = styled(Button)` } `; -interface ConfirmationModelProps { +interface ConfirmationModalProps { visible: boolean; onOk: () => void; onCancel: () => void; @@ -22,7 +22,7 @@ interface ConfirmationModelProps { onConfirm: () => void; } -const ConfirmationModel: React.FC = ({ +const ConfirmationModal: React.FC = ({ visible, onOk, onCancel, @@ -47,4 +47,4 @@ const ConfirmationModel: React.FC = ({ ); }; -export default ConfirmationModel; +export default ConfirmationModal; diff --git a/src/components/treePage/siteImageCarousel.tsx b/src/components/treePage/siteImageCarousel.tsx index 25b9c95b..4f6dca23 100644 --- a/src/components/treePage/siteImageCarousel.tsx +++ b/src/components/treePage/siteImageCarousel.tsx @@ -2,9 +2,14 @@ import React, { useState } from 'react'; import { Carousel, message, Space } from 'antd'; import LeftOutlined from '@ant-design/icons/lib/icons/LeftOutlined'; import RightOutlined from '@ant-design/icons/lib/icons/RightOutlined'; +import { isAdmin, getUserID } from '../../auth/ducks/selectors'; import { useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { useTranslation } from 'react-i18next'; import styled from 'styled-components'; +import { getSiteData } from '../../containers/treePage/ducks/thunks'; +import { TreeParams } from '../../containers/treePage'; +import { useParams } from 'react-router-dom'; import { getLatestEntrySiteImages } from '../../containers/treePage/ducks/selectors'; import { C4CState } from '../../store'; @@ -13,7 +18,7 @@ import { site } from '../../constants'; import { LinkButton } from '../linkButton'; import { LIGHT_GREY, LIGHT_RED, WHITE } from '../../utils/colors'; import protectedApiClient from '../../api/protectedApiClient'; -import ConfirmationModel from '../confirmationModal'; +import ConfirmationModal from '../confirmationModal'; const CarouselContainer = styled.div` margin-top: 20px; @@ -69,6 +74,8 @@ export const DeleteSiteImageButton = styled(LinkButton)` export const SiteImageCarousel: React.FC = () => { const { t } = useTranslation(n(site, 'treePage'), { nsMode: 'fallback' }); + const dispatch = useDispatch(); + const id = Number(useParams().id); const [showDeleteForm, setShowDeleteForm] = useState(false); @@ -81,12 +88,22 @@ export const SiteImageCarousel: React.FC = () => { const onAfterChange = (currentSlide: number) => setCurrSlideIndex(currentSlide); function onClickDeleteImage(imageId: number) { - protectedApiClient.deleteImage(imageId).then((ok) => { - message.success('success'); - setShowDeleteForm(false); - }); + protectedApiClient + .deleteImage(imageId) + .then((ok) => { + message.success('success'); + setShowDeleteForm(false); + }) + .finally(() => dispatch(getSiteData(id))); } + const userIsAdmin: boolean = useSelector((state: C4CState) => + isAdmin(state.authenticationState.tokens), + ); + + const userId: number = useSelector((state: C4CState) => + getUserID(state.authenticationState.tokens), + ); return ( <> {latestEntrySiteImages.length > 0 && ( @@ -118,16 +135,19 @@ export const SiteImageCarousel: React.FC = () => { t('site_image.no_upload_date')}
- setShowDeleteForm(!showDeleteForm)} - > - Delete - + {(latestEntrySiteImages[currSlideIndex].uploaderId === userId || + userIsAdmin) && ( + setShowDeleteForm(!showDeleteForm)} + > + Delete + + )}
- setShowDeleteForm(false)} onCancel={() => setShowDeleteForm(false)} diff --git a/src/containers/treePage/ducks/types.ts b/src/containers/treePage/ducks/types.ts index f7482e1c..ad041ef4 100644 --- a/src/containers/treePage/ducks/types.ts +++ b/src/containers/treePage/ducks/types.ts @@ -295,6 +295,7 @@ export interface SiteEntryImage { uploaderUsername: string; uploadedAt: string; imageUrl: string; + uploaderId: number; } export interface TreeBenefits { diff --git a/src/containers/treePage/test/selectors.test.ts b/src/containers/treePage/test/selectors.test.ts index 43fcf202..9f4ceb7f 100644 --- a/src/containers/treePage/test/selectors.test.ts +++ b/src/containers/treePage/test/selectors.test.ts @@ -124,12 +124,14 @@ describe('Tree Page Selectors', () => { imageUrl: 'http://www.some-address.com', uploadedAt: '09/06/2023', uploaderUsername: 'First Last', + uploaderId: 1, }, { imageId: 2, imageUrl: 'http://www.some-other-address.com', uploadedAt: '01/01/2023', uploaderUsername: 'Hello World', + uploaderId: 2, }, ], }, @@ -147,6 +149,7 @@ describe('Tree Page Selectors', () => { imageUrl: 'http://www.should-not-be-returned.com', uploadedAt: '01/01/2022', uploaderUsername: 'Code4Community', + uploaderId: 1, }, ], }, @@ -281,12 +284,14 @@ describe('Tree Page Selectors', () => { imageUrl: 'http://www.some-address.com', uploadedAt: '09/06/2023', uploaderUsername: 'First Last', + uploaderId: 1, }, { imageId: 2, imageUrl: 'http://www.some-other-address.com', uploadedAt: '01/01/2023', uploaderUsername: 'Hello World', + uploaderId: 2, }, ];