diff --git a/src/App.tsx b/src/App.tsx index 57a5857802..a188822b5e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -42,6 +42,7 @@ import SecuredRouteForUser from 'components/UserPortal/SecuredRouteForUser/Secur import FundCampaignPledge from 'screens/FundCampaignPledge/FundCampaignPledge'; import useLocalStorage from 'utils/useLocalstorage'; +import EventDashboardScreen from 'components/EventDashboardScreen/EventDashboardScreen'; const { setItem } = useLocalStorage(); @@ -125,10 +126,7 @@ function app(): JSX.Element { } /> } /> } /> - } - /> + } @@ -152,7 +150,9 @@ function app(): JSX.Element { {extraRoutes} - + }> + } /> + } /> {/* User Portal Routes */} }> diff --git a/src/components/EventDashboardScreen/EventDashboardScreen.module.css b/src/components/EventDashboardScreen/EventDashboardScreen.module.css new file mode 100644 index 0000000000..33c642dd95 --- /dev/null +++ b/src/components/EventDashboardScreen/EventDashboardScreen.module.css @@ -0,0 +1,198 @@ +.gap { + gap: 20px; +} + +.mainContainer { + width: 50%; + flex-grow: 3; + padding: 20px; + max-height: 100%; + overflow: auto; +} + +.containerHeight { + height: calc(100vh - 66px); +} + +.colorLight { + background-color: #f1f3f6; +} + +.pageContainer { + display: flex; + flex-direction: column; + min-height: 100vh; + padding: 1rem 1.5rem 0 calc(300px + 2rem + 1.5rem); +} + +.expand { + padding-left: 4rem; + animation: moveLeft 0.5s ease-in-out; +} +.avatarStyle { + border-radius: 100%; +} +.profileContainer { + border: none; + padding: 2.1rem 0.5rem; + height: 52px; + border-radius: 8px 0px 0px 8px; + display: flex; + align-items: center; + background-color: white !important; + box-shadow: + 0 4px 4px 0 rgba(177, 177, 177, 0.2), + 0 6px 20px 0 rgba(151, 151, 151, 0.19); +} +.profileContainer:focus { + outline: none; + background-color: var(--bs-gray-100); +} +.imageContainer { + width: 56px; +} +.profileContainer .profileText { + flex: 1; + text-align: start; + overflow: hidden; + margin-right: 4px; +} +.angleDown { + margin-left: 4px; +} +.profileContainer .profileText .primaryText { + font-size: 1rem; + font-weight: 600; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; /* number of lines to show */ + -webkit-box-orient: vertical; + word-wrap: break-word; + white-space: normal; +} +.profileContainer .profileText .secondaryText { + font-size: 0.8rem; + font-weight: 400; + color: var(--bs-secondary); + display: block; + text-transform: capitalize; +} + +.contract { + padding-left: calc(300px + 2rem + 1.5rem); + animation: moveRight 0.5s ease-in-out; +} + +.collapseSidebarButton { + position: fixed; + height: 40px; + bottom: 0; + z-index: 9999; + width: calc(300px + 2rem); + background-color: rgba(245, 245, 245, 0.7); + color: black; + border: none; + border-radius: 0px; +} + +.collapseSidebarButton:hover, +.opendrawer:hover { + opacity: 1; + color: black !important; +} +.opendrawer { + position: fixed; + display: flex; + align-items: center; + justify-content: center; + top: 0; + left: 0; + width: 40px; + height: 100vh; + z-index: 9999; + background-color: rgba(245, 245, 245); + border: none; + border-radius: 0px; + margin-right: 20px; + color: black; +} +.profileDropdown { + background-color: transparent !important; +} +.profileDropdown .dropdown-toggle .btn .btn-normal { + display: none !important; + background-color: transparent !important; +} +.dropdownToggle { + background-image: url(/public/images/svg/angleDown.svg); + background-repeat: no-repeat; + background-position: center; + background-color: azure; +} + +.dropdownToggle::after { + border-top: none !important; + border-bottom: none !important; +} + +.opendrawer:hover { + transition: background-color 0.5s ease; + background-color: var(--bs-primary); +} +.collapseSidebarButton:hover { + transition: background-color 0.5s ease; + background-color: var(--bs-primary); +} + +@media (max-width: 1120px) { + .contract { + padding-left: calc(250px + 2rem + 1.5rem); + } + .collapseSidebarButton { + width: calc(250px + 2rem); + } +} + +@media (max-height: 900px) { + .pageContainer { + padding: 1rem 1.5rem 0 calc(300px + 2rem); + } + .collapseSidebarButton { + height: 30px; + width: calc(300px + 1rem); + } +} +@media (max-height: 650px) { + .pageContainer { + padding: 1rem 1.5rem 0 calc(270px); + } + .collapseSidebarButton { + width: 250px; + height: 20px; + } + .opendrawer { + width: 30px; + } +} + +/* For tablets */ +@media (max-width: 820px) { + .pageContainer { + padding-left: 2.5rem; + } + + .opendrawer { + width: 25px; + } + + .contract, + .expand { + animation: none; + } + + .collapseSidebarButton { + width: 100%; + left: 0; + right: 0; + } +} diff --git a/src/components/EventDashboardScreen/EventDashboardScreen.test.tsx b/src/components/EventDashboardScreen/EventDashboardScreen.test.tsx new file mode 100644 index 0000000000..6b48b034a9 --- /dev/null +++ b/src/components/EventDashboardScreen/EventDashboardScreen.test.tsx @@ -0,0 +1,166 @@ +import React from 'react'; +import { MockedProvider } from '@apollo/react-testing'; +import { fireEvent, render, screen } from '@testing-library/react'; +import { I18nextProvider } from 'react-i18next'; +import 'jest-location-mock'; +import { Provider } from 'react-redux'; +import { BrowserRouter } from 'react-router-dom'; +import { store } from 'state/store'; +import i18nForTest from 'utils/i18nForTest'; +import EventDashboardScreen from './EventDashboardScreen'; +import { ORGANIZATIONS_LIST } from 'GraphQl/Queries/Queries'; +import { StaticMockLink } from 'utils/StaticMockLink'; +import useLocalStorage from 'utils/useLocalstorage'; +const { setItem } = useLocalStorage(); + +Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // Deprecated + removeListener: jest.fn(), // Deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + +let mockID: string | undefined = '123'; +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: () => ({ orgId: mockID }), +})); + +const MOCKS = [ + { + request: { + query: ORGANIZATIONS_LIST, + variables: { id: '123' }, + }, + result: { + data: { + organizations: [ + { + _id: '123', + image: null, + creator: { + firstName: 'John', + lastName: 'Doe', + email: 'JohnDoe@example.com', + }, + name: 'Test Organization', + description: 'Testing this organization', + address: { + city: 'Mountain View', + countryCode: 'US', + dependentLocality: 'Some Dependent Locality', + line1: '123 Main Street', + line2: 'Apt 456', + postalCode: '94040', + sortingCode: 'XYZ-789', + state: 'CA', + }, + userRegistrationRequired: true, + visibleInSearch: true, + members: [], + admins: [], + membershipRequests: [], + blockedUsers: [], + }, + ], + }, + }, + }, +]; +const link = new StaticMockLink(MOCKS, true); + +const resizeWindow = (width: number): void => { + window.innerWidth = width; + fireEvent(window, new Event('resize')); +}; + +const clickToggleMenuBtn = (toggleButton: HTMLElement): void => { + fireEvent.click(toggleButton); +}; + +describe('Testing LeftDrawer in OrganizationScreen', () => { + test('should be redirected to / if IsLoggedIn is false', async () => { + setItem('IsLoggedIn', false); + render( + + + + + + + + + , + ); + expect(window.location.pathname).toEqual('/'); + }); + test('should be redirected to / if ss is false', async () => { + setItem('IsLoggedIn', true); + render( + + + + + + + + + , + ); + }); + test('Testing LeftDrawer in page functionality', async () => { + setItem('IsLoggedIn', true); + setItem('AdminFor', [ + { _id: '6637904485008f171cf29924', __typename: 'Organization' }, + ]); + render( + + + + + + + + + , + ); + const toggleButton = screen.getByTestId('toggleMenuBtn') as HTMLElement; + const icon = toggleButton.querySelector('i'); + + // Resize window to a smaller width + resizeWindow(800); + clickToggleMenuBtn(toggleButton); + expect(icon).toHaveClass('fa fa-angle-double-right'); + // Resize window back to a larger width + + resizeWindow(1000); + clickToggleMenuBtn(toggleButton); + expect(icon).toHaveClass('fa fa-angle-double-left'); + + clickToggleMenuBtn(toggleButton); + expect(icon).toHaveClass('fa fa-angle-double-right'); + }); + + test('should be redirected to / if orgId is undefined', async () => { + mockID = undefined; + render( + + + + + + + + + , + ); + expect(window.location.pathname).toEqual('/'); + }); +}); diff --git a/src/components/EventDashboardScreen/EventDashboardScreen.tsx b/src/components/EventDashboardScreen/EventDashboardScreen.tsx new file mode 100644 index 0000000000..198b6384c3 --- /dev/null +++ b/src/components/EventDashboardScreen/EventDashboardScreen.tsx @@ -0,0 +1,151 @@ +import LeftDrawerOrg from 'components/LeftDrawerOrg/LeftDrawerOrg'; +import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; +import { Navigate, Outlet, useLocation, useParams } from 'react-router-dom'; +import { updateTargets } from 'state/action-creators'; +import type { RootState } from 'state/reducers'; +import type { TargetsType } from 'state/reducers/routesReducer'; +import styles from './EventDashboardScreen.module.css'; +import ProfileDropdown from 'components/ProfileDropdown/ProfileDropdown'; +import UserSidebar from 'components/UserPortal/UserSidebar/UserSidebar'; +import OrganizationNavbar from 'components/UserPortal/OrganizationNavbar/OrganizationNavbar'; +import useLocalStorage from 'utils/useLocalstorage'; +const { getItem } = useLocalStorage(); + +const EventDashboardScreen = (): JSX.Element => { + const navbarProps = { + currentPage: 'events', + }; + const isLoggedIn = getItem('IsLoggedIn'); + const adminFor = getItem('AdminFor'); + const location = useLocation(); + const titleKey: string | undefined = map[location.pathname.split('/')[1]]; + const { t } = useTranslation('translation', { keyPrefix: titleKey }); + const [hideDrawer, setHideDrawer] = useState(null); + const { orgId } = useParams(); + if (!orgId) { + return ; + } + + if (isLoggedIn === false) return ; + if (adminFor === null) { + return ( + <> + +
+ +
+
+
+

{t('title')}

+
+ +
+
+
+ + ); + } + + const appRoutes: { + targets: TargetsType[]; + } = useSelector((state: RootState) => state.appRoutes); + const { targets } = appRoutes; + + const dispatch = useDispatch(); + useEffect(() => { + dispatch(updateTargets(orgId)); + }, [orgId]); // Added orgId to the dependency array + + const handleResize = (): void => { + if (window.innerWidth <= 820 && !hideDrawer) { + setHideDrawer(true); + } + }; + + const toggleDrawer = (): void => { + setHideDrawer(!hideDrawer); + }; + + useEffect(() => { + handleResize(); + window.addEventListener('resize', handleResize); + return () => { + window.removeEventListener('resize', handleResize); + }; + }, [hideDrawer]); + + return ( + <> + +
+ +
+
+
+
+

{t('title')}

+
+ +
+ +
+ + ); +}; + +export default EventDashboardScreen; + +interface InterfaceMapType { + [key: string]: string; +} + +const map: InterfaceMapType = { + orgdash: 'dashboard', + orgpeople: 'organizationPeople', + requests: 'requests', + orgads: 'advertisement', + member: 'memberDetail', + orgevents: 'organizationEvents', + orgactionitems: 'organizationActionItems', + orgcontribution: 'orgContribution', + orgpost: 'orgPost', + orgfunds: 'funds', + orgfundcampaign: 'fundCampaign', + fundCampaignPledge: 'pledges', + orgsetting: 'orgSettings', + orgstore: 'addOnStore', + blockuser: 'blockUnblockUser', + orgvenues: 'organizationVenues', + event: 'eventManagement', +}; diff --git a/src/screens/EventManagement/EventManagement.test.tsx b/src/screens/EventManagement/EventManagement.test.tsx index 5e72bd5e68..c51f4eaf12 100644 --- a/src/screens/EventManagement/EventManagement.test.tsx +++ b/src/screens/EventManagement/EventManagement.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import type { RenderResult } from '@testing-library/react'; -import { act, render, screen, waitFor } from '@testing-library/react'; +import { render, screen, waitFor } from '@testing-library/react'; import { MockedProvider } from '@apollo/react-testing'; import { I18nextProvider } from 'react-i18next'; import i18nForTest from 'utils/i18nForTest'; @@ -11,6 +11,8 @@ import EventManagement from './EventManagement'; import userEvent from '@testing-library/user-event'; import { StaticMockLink } from 'utils/StaticMockLink'; import { MOCKS_WITH_TIME } from 'components/EventManagement/Dashboard/EventDashboard.mocks'; +import useLocalStorage from 'utils/useLocalstorage'; +const { setItem } = useLocalStorage(); const mockWithTime = new StaticMockLink(MOCKS_WITH_TIME, true); @@ -33,6 +35,12 @@ const renderEventManagement = (): RenderResult => { path="/orgevents/:orgId" element={
eventsScreen
} /> + userEventsScreen + } + /> @@ -64,8 +72,8 @@ describe('Event Management', () => { expect(dashboardTab).toBeInTheDocument(); }); - - test('Testing back button navigation', async () => { + test('Testing back button navigation when userType is SuperAdmin', async () => { + setItem('SuperAdmin', true); renderEventManagement(); const backButton = screen.getByTestId('backBtn'); diff --git a/src/screens/EventManagement/EventManagement.tsx b/src/screens/EventManagement/EventManagement.tsx index af934fc798..869992cc0f 100644 --- a/src/screens/EventManagement/EventManagement.tsx +++ b/src/screens/EventManagement/EventManagement.tsx @@ -12,6 +12,7 @@ import { useTranslation } from 'react-i18next'; import { Button } from 'react-bootstrap'; import EventDashboard from 'components/EventManagement/Dashboard/EventDashboard'; import EventActionItems from 'components/EventManagement/EventActionItems/EventActionItems'; +import useLocalStorage from 'utils/useLocalstorage'; const eventDashboardTabs: { value: TabOptions; @@ -42,6 +43,17 @@ const EventManagement = (): JSX.Element => { keyPrefix: 'eventManagement', }); + const { getItem } = useLocalStorage(); + + const superAdmin = getItem('SuperAdmin'); + const adminFor = getItem('AdminFor'); + /*istanbul ignore next*/ + const userRole = superAdmin + ? 'SUPERADMIN' + : adminFor?.length > 0 + ? 'ADMIN' + : 'USER'; + const { eventId, orgId } = useParams(); /*istanbul ignore next*/ if (!eventId || !orgId) { @@ -94,7 +106,12 @@ const EventManagement = (): JSX.Element => { height={28} fill={'var(--bs-secondary)'} data-testid="backBtn" - onClick={() => navigate(`/orgevents/${orgId}`)} + onClick={() => { + /*istanbul ignore next*/ + userRole === 'USER' + ? navigate(`/user/events/${orgId}`) + : navigate(`/orgevents/${orgId}`); + }} className="mt-1" />