+
{props.title}
+
+ Posted On:
+ {formatDate(props.createdAt)}
- {props.image && (
-
- )}
-
{props.text}
-
-
- {likes}
- {` ${t('likes')}`}
+
+ {props.text}
+
+
+
-
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,
- };
-
- return
;
- })
- ) : (
- <>No comments to show.>
- )}
-
-
-
-
- {commentLoading ? (
-
- ) : (
-
- )}
-
-
-
-
+
+
>
);
}
diff --git a/src/screens/UserPortal/Home/Home.module.css b/src/screens/UserPortal/Home/Home.module.css
index 48643b3445..dd69868e29 100644
--- a/src/screens/UserPortal/Home/Home.module.css
+++ b/src/screens/UserPortal/Home/Home.module.css
@@ -1,71 +1,157 @@
-.borderNone {
- border: none;
+.mainContainer {
+ display: flex;
}
-.colorWhite {
- color: white;
+.postPageContainer {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ width: 80%;
+ height: 100%;
+ background-color: rgb(242, 247, 255);
+ font-family: 'Inter', sans-serif;
+ font-size: 18px;
+ margin-left: 15px;
}
-.maxWidth {
- max-width: 300px;
+.mainHeading {
+ color: rgb(0, 0, 0);
+ font-size: 40px;
+ font-weight: 600;
+ line-height: 48px;
+ letter-spacing: 0%;
+ margin-top: 40px;
+ margin-bottom: 45px;
}
-.colorLight {
- background-color: #f5f5f5;
+.startPostText {
+ color: rgb(0, 0, 0);
+ font-size: 24px;
+ font-weight: 400;
+ line-height: 22px;
+ letter-spacing: 0%;
+ text-align: left;
+ margin-bottom: 1%;
}
-.mainContainer {
- width: 50%;
- flex-grow: 3;
- padding: 30px;
- max-height: 100%;
- overflow: auto;
+.yourPostInput {
+ width: 90%;
+ height: 51px;
+ left: 379px;
+ right: -367px;
+ top: 161px;
+ bottom: 508px;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 0px 8px 8px 0px;
+ background: rgb(241, 243, 246);
}
-.containerHeight {
- height: calc(100vh - 66px);
+.yourPostInput::placeholder {
+ text-indent: 20px;
}
-.link {
- text-decoration: none !important;
- color: black;
+.startPostAndBrowse {
+ margin: 2%;
+ color: red;
}
-.postInputContainer {
- background-color: white;
- padding: 10px;
- border-radius: 10px;
- margin-bottom: 20px;
+.browseButton {
+ height: 51px;
+ width: 116px;
+ background-color: rgb(49, 187, 107);
+ color: white;
+ font-size: 16px;
+ font-weight: 600;
+ border-radius: 8px 0px 0px 8px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 2px;
+ cursor: pointer;
}
-.postActionContainer {
+.browseAndPostInput {
+ display: flex;
+ justify-content: baseline;
+ align-items: center;
+}
+
+.cardStyle {
+ width: 98%;
+ height: 220px;
+}
+
+.postButton {
+ width: 130px;
+ height: 45px;
+ border-radius: 8px;
+ padding: 5px;
display: flex;
flex-direction: row;
- justify-content: space-between;
- gap: 10px;
+ justify-content: center;
+ align-items: center;
}
-.postActionBtn {
- background-color: white;
- border: none;
- color: black;
+.postButtonDiv {
+ width: 98%;
+ display: flex;
+ justify-content: end;
+ align-items: center;
+ margin-top: 25px;
}
-.postActionBtn:hover {
- background-color: ghostwhite;
- border: none;
- color: black;
+.postButtonIcon {
+ margin-left: 10px;
}
-.postInput {
- height: 200px !important;
- resize: none;
- border: none;
- box-shadow: none;
- background-color: white;
+.feedText {
+ color: rgb(0, 0, 0);
+ font-size: 40px;
+ font-weight: 600;
+ line-height: 48px;
+ letter-spacing: 0%;
+ margin-top: 40px;
+ margin-bottom: 20px;
+}
+
+.secondaryHeading {
+ font-size: 25px;
+ font-weight: 600px;
margin-bottom: 10px;
}
-.imageInput {
- background-color: white;
+.pinnedPosts {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 3%;
+ margin-bottom: 30px;
+ width: 100%;
+ overflow-x: auto;
+ transition: transform 0.5s ease;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
+}
+
+.pinnedPosts::-webkit-scrollbar {
+ display: none;
+}
+
+.rightScrollButton {
+ height: 40px;
+ width: 40px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: absolute;
+ right: 1%;
+ top: 28%;
+ transform: translateY(-50%);
+}
+
+.feedPosts {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 3%;
+ margin-bottom: 40px;
}
diff --git a/src/screens/UserPortal/Home/Home.test.tsx b/src/screens/UserPortal/Home/Home.test.tsx
index 105714cfc8..a6b88ef8da 100644
--- a/src/screens/UserPortal/Home/Home.test.tsx
+++ b/src/screens/UserPortal/Home/Home.test.tsx
@@ -2,19 +2,17 @@ import React from 'react';
import { act, render, screen } from '@testing-library/react';
import { MockedProvider } from '@apollo/react-testing';
import { I18nextProvider } from 'react-i18next';
-
import { ORGANIZATION_POST_CONNECTION_LIST } from 'GraphQl/Queries/Queries';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { store } from 'state/store';
-import i18nForTest from 'utils/i18nForTest';
import { StaticMockLink } from 'utils/StaticMockLink';
-import Home from './Home';
-import userEvent from '@testing-library/user-event';
-import * as getOrganizationId from 'utils/getOrganizationId';
import { CREATE_POST_MUTATION } from 'GraphQl/Mutations/mutations';
-import { toast } from 'react-toastify';
+import * as getOrganizationId from 'utils/getOrganizationId';
+import i18nForTest from 'utils/i18nForTest';
+import userEvent from '@testing-library/user-event';
import dayjs from 'dayjs';
+import Home from './Home';
jest.mock('react-toastify', () => ({
toast: {
@@ -47,6 +45,7 @@ const MOCKS = [
firstName: 'Aditya',
lastName: 'Shelke',
email: 'adidacreator1@gmail.com',
+ image: '',
},
createdAt: dayjs(new Date()).add(1, 'day'),
likeCount: 0,
@@ -190,14 +189,7 @@ describe('Testing Home Screen [User Portal]', () => {
expect(getOrganizationIdSpy).toHaveBeenCalled();
});
-
- test('Screen should be rendered properly when user types on the Post Input', async () => {
- const getOrganizationIdSpy = jest
- .spyOn(getOrganizationId, 'default')
- .mockImplementation(() => {
- return '';
- });
-
+ test('Should render the main heading', async () => {
render(
@@ -212,21 +204,11 @@ describe('Testing Home Screen [User Portal]', () => {
await wait();
- expect(getOrganizationIdSpy).toHaveBeenCalled();
-
- const randomPostInput = 'This is a test';
- userEvent.type(screen.getByTestId('postInput'), randomPostInput);
-
- expect(screen.queryByText(randomPostInput)).toBeInTheDocument();
+ const mainHeading = screen.getByText('POSTS');
+ expect(mainHeading).toBeInTheDocument();
});
- test('Error toast should be visible when user tries to create a post with an empty body', async () => {
- const getOrganizationIdSpy = jest
- .spyOn(getOrganizationId, 'default')
- .mockImplementation(() => {
- return '';
- });
-
+ test('Should render the sub heading', async () => {
render(
@@ -241,22 +223,11 @@ describe('Testing Home Screen [User Portal]', () => {
await wait();
- expect(getOrganizationIdSpy).toHaveBeenCalled();
-
- userEvent.click(screen.getByTestId('postAction'));
-
- expect(toast.error).toBeCalledWith(
- "Can't create a post with an empty body."
- );
+ const mainHeading = screen.getByText('Feed');
+ expect(mainHeading).toBeInTheDocument();
});
- test('Info toast should be visible when user tries to create a post with a valid body', async () => {
- const getOrganizationIdSpy = jest
- .spyOn(getOrganizationId, 'default')
- .mockImplementation(() => {
- return '';
- });
-
+ test('Should display "Start a Post" text', async () => {
render(
@@ -271,15 +242,27 @@ describe('Testing Home Screen [User Portal]', () => {
await wait();
- expect(getOrganizationIdSpy).toHaveBeenCalled();
+ const startPostText = screen.getByText('Start a Post');
+ expect(startPostText).toBeInTheDocument();
+ });
- const randomPostInput = 'This is a test';
- userEvent.type(screen.getByTestId('postInput'), randomPostInput);
- expect(screen.queryByText(randomPostInput)).toBeInTheDocument();
+ test('Should update postContent state on input change', async () => {
+ render(
+
+
+
+
+
+
+
+
+
+ );
- userEvent.click(screen.getByTestId('postAction'));
+ await wait();
- expect(toast.error).not.toBeCalledWith();
- expect(toast.info).toBeCalledWith('Processing your post. Please wait.');
+ const postInput = screen.getByTestId('postInput');
+ userEvent.type(postInput, 'Testing post content');
+ expect(postInput).toHaveValue('Testing post content');
});
});
diff --git a/src/screens/UserPortal/Home/Home.tsx b/src/screens/UserPortal/Home/Home.tsx
index ad02e1b26c..5dc9f6db71 100644
--- a/src/screens/UserPortal/Home/Home.tsx
+++ b/src/screens/UserPortal/Home/Home.tsx
@@ -1,43 +1,54 @@
-import React from 'react';
+import React, { useRef, useEffect } from 'react';
import type { ChangeEvent } from 'react';
-import OrganizationNavbar from 'components/UserPortal/OrganizationNavbar/OrganizationNavbar';
+import { Button, Card } from 'react-bootstrap';
import styles from './Home.module.css';
import UserSidebar from 'components/UserPortal/UserSidebar/UserSidebar';
-import OrganizationSidebar from 'components/UserPortal/OrganizationSidebar/OrganizationSidebar';
-import ChevronRightIcon from '@mui/icons-material/ChevronRight';
-import { Button, FloatingLabel, Form } from 'react-bootstrap';
-import { Link } from 'react-router-dom';
+import OrganizationNavbar from 'components/UserPortal/OrganizationNavbar/OrganizationNavbar';
+import convertToBase64 from 'utils/convertToBase64';
import getOrganizationId from 'utils/getOrganizationId';
-import SendIcon from '@mui/icons-material/Send';
-import PostCard from 'components/UserPortal/PostCard/PostCard';
import { useMutation, useQuery } from '@apollo/client';
-import {
- ADVERTISEMENTS_GET,
- ORGANIZATION_POST_CONNECTION_LIST,
-} from 'GraphQl/Queries/Queries';
+import { ORGANIZATION_POST_CONNECTION_LIST } from 'GraphQl/Queries/Queries';
import { CREATE_POST_MUTATION } from 'GraphQl/Mutations/mutations';
+import { toast } from 'react-toastify';
import { errorHandler } from 'utils/errorHandler';
import { useTranslation } from 'react-i18next';
-import convertToBase64 from 'utils/convertToBase64';
-import { toast } from 'react-toastify';
+import PostCard from 'components/UserPortal/PostCard/PostCard';
+import { ReactComponent as PostIcon } from 'assets/svgs/postUser.svg';
+import { ReactComponent as RightScrollIcon } from 'assets/svgs/rightScroll.svg';
import HourglassBottomIcon from '@mui/icons-material/HourglassBottom';
-import PromotedPost from 'components/UserPortal/PromotedPost/PromotedPost';
interface InterfacePostCardProps {
id: string;
creator: {
firstName: string;
lastName: string;
- email: string;
+ image: string;
id: string;
};
image: string;
video: string;
text: string;
title: string;
- likeCount: number;
+ createdAt: number;
+}
+
+interface InterfacePost {
+ _id: string;
+ title: string;
+ text: string;
+ imageUrl: string;
+ videoUrl: string;
+ creator: {
+ _id: string;
+ firstName: string;
+ lastName: string;
+ image: string;
+ };
+ createdAt: number;
+ likeCount: string;
commentCount: number;
comments: {
+ _id: string;
creator: {
_id: string;
firstName: string;
@@ -46,37 +57,51 @@ interface InterfacePostCardProps {
};
likeCount: number;
likedBy: {
- id: string;
- }[];
+ _id: string;
+ };
text: string;
- }[];
+ };
likedBy: {
+ _id: string;
firstName: string;
lastName: string;
- id: string;
- }[];
+ };
+ pinned: boolean;
}
export default function home(): JSX.Element {
const { t } = useTranslation('translation', { keyPrefix: 'home' });
-
const organizationId = getOrganizationId(window.location.href);
- const [posts, setPosts] = React.useState([]);
+ const [posts, setPosts] = React.useState([]);
const [postContent, setPostContent] = React.useState('');
const [postImage, setPostImage] = React.useState('');
- const currentOrgId = window.location.href.split('/id=')[1] + '';
- const [adContent, setAdContent] = React.useState([]);
+ const [hasPinnedPost, setHasPinnedPost] = React.useState(false);
+
+ useEffect(() => {
+ const hasPinned = posts.some((post) => post.pinned === true);
+ setHasPinnedPost(hasPinned);
+ });
+
+ const carouselRef = useRef(null);
+
+ const handleScrollRight = (): void => {
+ if (carouselRef.current) {
+ (carouselRef.current as any).scrollBy({
+ left: 300, // Adjust this value to change the scroll amount
+ behavior: 'smooth',
+ });
+ }
+ };
+
+ const handlePostInput = (e: ChangeEvent): void => {
+ const content = e.target.value;
+ setPostContent(content);
+ };
const navbarProps = {
currentPage: 'home',
};
- const {
- data: promotedPostsData,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- refetch: _promotedPostsRefetch,
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- loading: promotedPostsLoading,
- } = useQuery(ADVERTISEMENTS_GET);
+
const {
data,
refetch,
@@ -116,114 +141,146 @@ export default function home(): JSX.Element {
}
};
- const handlePostInput = (e: ChangeEvent): void => {
- const content = e.target.value;
-
- setPostContent(content);
- };
-
React.useEffect(() => {
if (data) {
setPosts(data.postsByOrganizationConnection.edges);
}
}, [data]);
- React.useEffect(() => {
- if (promotedPostsData) {
- setAdContent(promotedPostsData.getAdvertisements);
- }
- }, [data]);
-
return (
<>
-
+
-
-
-
-
-
-
-
=> {
- const target = e.target as HTMLInputElement;
- const file = target.files && target.files[0];
- if (file) {
- const image = await convertToBase64(file);
- setPostImage(image);
+
+
POSTS
+
+
+
Start a Post
+
+
-
-
-
{t('feed')}
-
-
- {t('pinnedPosts')}
-
+ Browse Files
+
+
-
+
+
-
- {adContent
- .filter((ad: any) => ad.orgId == currentOrgId)
- .filter((ad: any) => new Date(ad.endDate) > new Date()).length == 0
- ? ''
- : adContent
- .filter((ad: any) => ad.orgId == currentOrgId)
- .filter((ad: any) => new Date(ad.endDate) > new Date())
- .map((post: any) => (
-
- ))}
+
+ Feed
+ Pinned post
{loadingPosts ? (
Loading...
) : (
- <>
- {posts.map((post: any) => {
+
+ {posts
+ .filter((post) => post.pinned === true)
+ .map((post: any) => {
+ const allLikes: any = [];
+ post.likedBy.forEach((value: any) => {
+ const singleLike = {
+ firstName: value.firstName,
+ lastName: value.lastName,
+ id: value._id,
+ };
+ allLikes.push(singleLike);
+ });
+
+ const postComments: any = [];
+ post.comments.forEach((value: any) => {
+ const commentLikes: any = [];
+
+ value.likedBy.forEach((commentLike: any) => {
+ const singleLike = {
+ id: commentLike._id,
+ };
+ commentLikes.push(singleLike);
+ });
+
+ const singleCommnet: any = {
+ id: value._id,
+ creator: {
+ firstName: value.creator.firstName,
+ lastName: value.creator.lastName,
+ id: value.creator._id,
+ image: value.creator.image,
+ },
+ likeCount: value.likeCount,
+ likedBy: commentLikes,
+ text: value.text,
+ };
+
+ postComments.push(singleCommnet);
+ });
+
+ const cardProps: InterfacePostCardProps = {
+ id: post._id,
+ creator: {
+ id: post.creator._id,
+ firstName: post.creator.firstName,
+ lastName: post.creator.lastName,
+ image: post.creator.image,
+ },
+ image: post.imageUrl,
+ video: post.videoUrl,
+ title: post.title,
+ text: post.text,
+ createdAt: post.createdAt,
+ };
+
+ return
;
+ })}
+ {hasPinnedPost && (
+
+ )}
+
+ )}
+
+ Your Feed
+
+ {posts
+ .filter((post) => post.pinned === false)
+ .map((post: any) => {
const allLikes: any = [];
post.likedBy.forEach((value: any) => {
const singleLike = {
@@ -251,7 +308,7 @@ export default function home(): JSX.Element {
firstName: value.creator.firstName,
lastName: value.creator.lastName,
id: value.creator._id,
- email: value.creator.email,
+ image: value.creator.image,
},
likeCount: value.likeCount,
likedBy: commentLikes,
@@ -267,24 +324,19 @@ export default function home(): JSX.Element {
id: post.creator._id,
firstName: post.creator.firstName,
lastName: post.creator.lastName,
- email: post.creator.email,
+ image: post.creator.image,
},
image: post.imageUrl,
video: post.videoUrl,
title: post.title,
text: post.text,
- likeCount: post.likeCount,
- commentCount: post.commentCount,
- comments: postComments,
- likedBy: allLikes,
+ createdAt: post.createdAt,
};
return
;
})}
- >
- )}
+
-
>
);