diff --git a/public/locales/en.json b/public/locales/en.json index 0cd8da3084..97d63a5073 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -885,12 +885,21 @@ "noMembers": "No Members to show" }, "postCard": { + "edit": "Edit", + "delete": "Delete", "likes": "Likes", "comments": "Comments" }, "home": { + "posts": "Posts", + "post": "Post", + "title": "Title", + "textArea": "Something on your mind?", "feed": "Feed", - "pinnedPosts": "View Pinned Posts", + "loading": "Loading", + "pinnedPosts": "Pinned Posts", + "yourFeed": "Your Feed", + "nothingToShowHere": "Nothing to show here", "somethingOnYourMind": "Something on your mind?", "addPost": "Add Post", "startPost": "Start a post", diff --git a/src/components/UserPortal/CommentCard/CommentCard.module.css b/src/components/UserPortal/CommentCard/CommentCard.module.css index 1124f6369d..b765d60bbd 100644 --- a/src/components/UserPortal/CommentCard/CommentCard.module.css +++ b/src/components/UserPortal/CommentCard/CommentCard.module.css @@ -1,13 +1,11 @@ .mainContainer { - width: 100%; - display: flex; - flex-direction: row; - padding: 10px; + width: auto; + overflow: hidden; background-color: white; + margin-top: 1rem; + padding: 0.5rem; + border: 1px solid #dddddd; border-radius: 10px; - box-shadow: 2px 2px 8px 0px #c8c8c8; - overflow: hidden; - margin-top: 10px; } .personDetails { diff --git a/src/components/UserPortal/CommentCard/CommentCard.tsx b/src/components/UserPortal/CommentCard/CommentCard.tsx index 955f136cb5..57cc6a1cf4 100644 --- a/src/components/UserPortal/CommentCard/CommentCard.tsx +++ b/src/components/UserPortal/CommentCard/CommentCard.tsx @@ -8,6 +8,7 @@ import { toast } from 'react-toastify'; import HourglassBottomIcon from '@mui/icons-material/HourglassBottom'; import ThumbUpOffAltIcon from '@mui/icons-material/ThumbUpOffAlt'; import useLocalStorage from 'utils/useLocalstorage'; +import AccountCircleIcon from '@mui/icons-material/AccountCircle'; interface InterfaceCommentCardProps { id: string; @@ -80,7 +81,10 @@ function commentCard(props: InterfaceCommentCardProps): JSX.Element { return (
- {creatorName} +
+ + {creatorName} +
{props.text}
- {likes} - {` ${t('likes')}`} - - {numComments} - {` ${t('comments')}`}
- - -
- - {postCreator} + + +
+ postImg
- {props.image && ( - - )} -
{props.text}
-
- - {likes} - {` ${t('likes')}`} +
+
+
+
+ + {likes} + {` ${t('likes')}`} +
+
+ + {numComments} + {` ${t('comments')}`} +
+
+ + + + {commentLoading ? ( + + ) : ( + + )} + + +
-

Comments

- {numComments ? ( - comments.map((comment: any, index: any) => { - const cardProps: InterfaceCommentCardProps = { - id: comment.id, - creator: { - id: comment.creator.id, - firstName: comment.creator.firstName, - lastName: comment.creator.lastName, - email: comment.creator.email, - }, - likeCount: comment.likeCount, - likedBy: comment.likedBy, - text: comment.text, - handleLikeComment: handleLikeComment, - handleDislikeComment: handleDislikeComment, - }; - - return ; - }) - ) : ( - <>No comments to show. - )} -
- - - - {commentLoading ? ( - - ) : ( - - )} - -
-
+ + +

+ Edit Post +

+
+ + + + + + +
+ ); } diff --git a/src/components/UserPortal/StartPostModal/StartPostModal.test.tsx b/src/components/UserPortal/StartPostModal/StartPostModal.test.tsx index 65faca9fa1..5a2e815af4 100644 --- a/src/components/UserPortal/StartPostModal/StartPostModal.test.tsx +++ b/src/components/UserPortal/StartPostModal/StartPostModal.test.tsx @@ -26,9 +26,10 @@ const MOCKS = [ request: { query: CREATE_POST_MUTATION, variables: { - title: 'Dummy Post', + title: '', text: 'This is dummy text', organizationId: '123', + file: '', }, result: { data: { @@ -94,6 +95,7 @@ const renderStartPostModal = ( }, }, organizationId: '123', + img: '', }; return render( @@ -121,22 +123,6 @@ describe('Testing StartPostModal Component: User Portal', () => { expect(modal).toBeInTheDocument(); }); - test('triggers file input when the upload icon is clicked', async () => { - const clickSpy = jest.spyOn(HTMLInputElement.prototype, 'click'); - renderStartPostModal(true, null); - - await wait(); - const postImageInput = screen.getByTestId('postImageInput'); - expect(postImageInput).toHaveAttribute('type', 'file'); - expect(postImageInput).toHaveStyle({ display: 'inline-block' }); - - const iconButton = screen.getByTestId('addMediaBtn'); - fireEvent.click(iconButton); - - expect(clickSpy).toHaveBeenCalled(); - clickSpy.mockRestore(); - }); - test('On invalid post submission with empty body Error toast should be shown', async () => { const toastSpy = jest.spyOn(toast, 'error'); renderStartPostModal(true, null); @@ -150,7 +136,7 @@ describe('Testing StartPostModal Component: User Portal', () => { renderStartPostModal(true, null); await wait(); - const randomPostInput = 'test post input'; + const randomPostInput = 'This is dummy text'; userEvent.type(screen.getByTestId('postInput'), randomPostInput); expect(screen.queryByText(randomPostInput)).toBeInTheDocument(); @@ -158,6 +144,10 @@ describe('Testing StartPostModal Component: User Portal', () => { expect(toast.error).not.toBeCalledWith(); expect(toast.info).toBeCalledWith('Processing your post. Please wait.'); + // await wait(); + // expect(toast.success).toBeCalledWith( + // 'Your post is now visible in the feed.', + // ); }); test('If user image is null then default image should be shown', async () => { @@ -178,48 +168,4 @@ describe('Testing StartPostModal Component: User Portal', () => { const userImage = screen.getByTestId('userImage'); expect(userImage).toHaveAttribute('src', 'image.png'); }); - - test('should update post image state when a file is selected', async () => { - renderStartPostModal(true, null); - await wait(); - - const file = new File(['(⌐□_□)'], 'chad.png', { type: 'image/png' }); - const input = screen.getByTestId('postImageInput') as HTMLInputElement; - await act(async () => { - fireEvent.change(input, { target: { files: [file] } }); - }); - - expect(input.files?.[0]).toEqual(file); - const previewImage = await screen.findByAltText('Post Image Preview'); - expect(previewImage).toBeInTheDocument(); - }); - - test('should not update post image state when no file is selected', async () => { - renderStartPostModal(true, null); - await wait(); - - const input = screen.getByTestId('postImageInput') as HTMLInputElement; - await act(async () => { - fireEvent.change(input, { target: { files: null } }); - }); - - const previewImage = screen.queryByAltText('Post Image Preview'); - expect(previewImage).not.toBeInTheDocument(); - }); - - test('triggers file input when fileInputRef exists', async () => { - const clickSpy = jest.spyOn(HTMLInputElement.prototype, 'click'); - const refMock = { current: { click: clickSpy } }; - - renderStartPostModal(true, null); - await wait(); - - jest.spyOn(React, 'useRef').mockReturnValueOnce(refMock); - - const iconButton = screen.getByTestId('addMediaBtn'); - fireEvent.click(iconButton); - - expect(clickSpy).toHaveBeenCalled(); - clickSpy.mockRestore(); - }); }); diff --git a/src/components/UserPortal/StartPostModal/StartPostModal.tsx b/src/components/UserPortal/StartPostModal/StartPostModal.tsx index 20fe4aae2d..036bcd904e 100644 --- a/src/components/UserPortal/StartPostModal/StartPostModal.tsx +++ b/src/components/UserPortal/StartPostModal/StartPostModal.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState } from 'react'; +import React, { useState } from 'react'; import type { ChangeEvent } from 'react'; import { Button, Form, Image, Modal } from 'react-bootstrap'; import { toast } from 'react-toastify'; @@ -6,7 +6,6 @@ import { useMutation } from '@apollo/client'; import { useTranslation } from 'react-i18next'; import { errorHandler } from 'utils/errorHandler'; -import convertToBase64 from 'utils/convertToBase64'; import UserDefault from '../../../assets/images/defaultImg.png'; import styles from './StartPostModal.module.css'; import { CREATE_POST_MUTATION } from 'GraphQl/Mutations/mutations'; @@ -18,6 +17,7 @@ interface InterfaceStartPostModalProps { fetchPosts: () => void; userData: InterfaceQueryUserListItem | undefined; organizationId: string; + img: string | null; } const startPostModal = ({ @@ -26,11 +26,10 @@ const startPostModal = ({ fetchPosts, userData, organizationId, + img, }: InterfaceStartPostModalProps): JSX.Element => { const { t } = useTranslation('translation', { keyPrefix: 'home' }); const [postContent, setPostContent] = useState(''); - const [postImage, setPostImage] = useState(''); - const fileInputRef = useRef(null); const [createPost] = useMutation(CREATE_POST_MUTATION); @@ -40,15 +39,9 @@ const startPostModal = ({ const handleHide = (): void => { setPostContent(''); - setPostImage(''); onHide(); }; - const uploadMedia = (e: React.MouseEvent): void => { - e.preventDefault(); - fileInputRef.current?.click(); - }; - const handlePost = async (): Promise => { try { if (!postContent) { @@ -61,7 +54,7 @@ const startPostModal = ({ title: '', text: postContent, organizationId: organizationId, - file: postImage, + file: img, }, }); /* istanbul ignore next */ @@ -121,58 +114,17 @@ const startPostModal = ({ placeholder={t('somethingOnYourMind')} value={postContent} /> - , - ): Promise => { - const file = e.target.files && e.target.files[0]; - if (file) { - const image = await convertToBase64(file); - setPostImage(image); - } else { - toast.info('Error uploading image. Please try again.'); - } - }} - /> - {postImage && ( + {img && (
- Post Image Preview + Post Image Preview
)} -
- -
- - - - -
-
- -
-

{t('media')}

-
- - -
- {/*
{eventSvg}
*/} -
- -
-

{t('event')}

-
- - -
-
- -
-

{t('article')}

-
- -
- +

{t('posts')}

+
+
{t('startPost')}
+
+ + + , + ): Promise => { + setPostImg(''); + const target = e.target as HTMLInputElement; + const file = target.files && target.files[0]; + const base64file = file && (await convertToBase64(file)); + setPostImg(base64file); + }} + /> + + +
+
+ +
+
-

{t('feed')}

-
- - {t('pinnedPosts')} - - -
+

{t('feed')}

+ {pinnedPosts.length > 0 && ( +
+

{t(`pinnedPosts`)}

+
+ {pinnedPosts.map(({ node }: { node: InterfacePostNode }) => { + const cardProps = getCardProps(node); + return ; + })} +
+
+ )}
{filteredAd.length > 0 && ( @@ -262,94 +345,37 @@ export default function home(): JSX.Element { ))}
)} - +

{t(`yourFeed`)}

+
{loadingPosts ? (
- Loading... + {t(`loading`)}...
) : ( <> - {posts.map(({ node }: { node: InterfacePostNode }) => { - const { - creator, - _id, - imageUrl, - videoUrl, - title, - text, - likeCount, - commentCount, - likedBy, - comments, - } = node; - - const allLikes: any = []; - - likedBy.forEach((value: any) => { - const singleLike = { - firstName: value.firstName, - lastName: value.lastName, - id: value._id, - }; - allLikes.push(singleLike); - }); - - const postComments: any = []; - - comments.forEach((value: any) => { - const commentLikes: any = []; - value.likedBy.forEach((commentLike: any) => { - const singleLike = { - id: commentLike._id, - }; - commentLikes.push(singleLike); - }); - - const comment = { - id: value._id, - creator: { - firstName: value.creator.firstName, - lastName: value.creator.lastName, - id: value.creator._id, - email: value.creator.email, - }, - likeCount: value.likeCount, - likedBy: commentLikes, - text: value.text, - }; - postComments.push(comment); - }); - - const cardProps: InterfacePostCard = { - id: _id, - creator: { - id: creator._id, - firstName: creator.firstName, - lastName: creator.lastName, - email: creator.email, - }, - image: imageUrl, - video: videoUrl, - title, - text, - likeCount, - commentCount, - comments: postComments, - likedBy: allLikes, - }; - - return ; - })} + {posts.length > 0 ? ( + + {posts.map(({ node }: { node: InterfacePostNode }) => { + const cardProps = getCardProps(node); + return ; + })} + + ) : ( +

+ {t(`nothingToShowHere`)} +

+ )} )} + - ); diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index 0ce6e2f78f..abaac2efcc 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -399,6 +399,7 @@ export interface InterfacePostCard { email: string; id: string; }; + postedAt: string; image: string | null; video: string | null; text: string; @@ -424,6 +425,7 @@ export interface InterfacePostCard { lastName: string; id: string; }[]; + fetchPosts: () => void; } export interface InterfaceCreateCampaign { campaignName: string;