Skip to content

Commit

Permalink
fix: invalid hook call and limit events data fetching (#3753)
Browse files Browse the repository at this point in the history
* add .env file check

* add tests for .env check

* fix: invalid hook calls and limited data fetching

* add tests

* fix: only fetch the upcoming events
  • Loading branch information
bandhan-majumder authored Mar 1, 2025
1 parent 23c2f8f commit 6d2b478
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

> **default**(): `JSX.Element`
Defined in: [src/screens/UserPortal/Events/Events.tsx:62](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/screens/UserPortal/Events/Events.tsx#L62)
Defined in: [src/screens/UserPortal/Events/Events.tsx:63](https://github.com/PalisadoesFoundation/talawa-admin/blob/main/src/screens/UserPortal/Events/Events.tsx#L63)

Component to manage and display events for an organization.

Expand Down
2 changes: 2 additions & 0 deletions src/GraphQl/Queries/PlugInQueries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const ORGANIZATION_EVENTS_CONNECTION = gql`
$location_contains: String
$first: Int
$skip: Int
$upcomingOnly: Boolean
) {
eventsByOrganizationConnection(
where: {
Expand All @@ -74,6 +75,7 @@ export const ORGANIZATION_EVENTS_CONNECTION = gql`
}
first: $first
skip: $skip
upcomingOnly: $upcomingOnly
) {
_id
title
Expand Down
81 changes: 81 additions & 0 deletions src/screens/UserPortal/Events/Events.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { ThemeProvider } from 'react-bootstrap';
import { createTheme } from '@mui/material';
import useLocalStorage from 'utils/useLocalstorage';
import { vi } from 'vitest';
import Loader from 'components/Loader/Loader';

/**
* Unit tests for the Events component.
Expand All @@ -39,6 +40,13 @@ vi.mock('react-toastify', () => ({
},
}));

vi.mock('utils/useLocalstorage', () => ({
default: () => ({
setItem: vi.fn(),
getItem: () => null,
}),
}));

vi.mock('@mui/x-date-pickers/DatePicker', async () => {
const desktopDatePickerModule = await vi.importActual(
'@mui/x-date-pickers/DesktopDatePicker',
Expand Down Expand Up @@ -501,6 +509,15 @@ describe('Testing Events Screen [User Portal]', () => {
})),
});

it('renders without crashing', () => {
render(<Loader />);
const spinnerWrapper = screen.getByTestId('spinner-wrapper');
const spinner = screen.getByTestId('spinner');

expect(spinnerWrapper).toBeInTheDocument();
expect(spinner).toBeInTheDocument();
});

it('Screen should be rendered properly', async () => {
render(
<MockedProvider addTypename={false} link={link}>
Expand Down Expand Up @@ -766,4 +783,68 @@ describe('Testing Events Screen [User Portal]', () => {
target: { value: null },
});
});

it('handles error during event creation', async () => {
const consoleError = vi.spyOn(console, 'error');
const mockError = new Error('Event creation failed');
const errorCreateEventMock = {
request: {
query: CREATE_EVENT_MUTATION,
variables: {
title: 'errorTest',
description: 'errorDescription',
isPublic: true,
recurring: false,
isRegisterable: true,
organizationId: '',
startDate: dayjs(new Date()).format('YYYY-MM-DD'),
endDate: dayjs(new Date()).format('YYYY-MM-DD'),
allDay: true,
location: 'errorLocation',
startTime: null,
endTime: null,
createChat: false,
},
},
error: mockError,
};

const errorLink = new StaticMockLink(
[...MOCKS, errorCreateEventMock],
true,
);

render(
<MockedProvider addTypename={false} link={errorLink}>
<BrowserRouter>
<Provider store={store}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<ThemeProvider theme={theme}>
<I18nextProvider i18n={i18nForTest}>
<Events />
</I18nextProvider>
</ThemeProvider>
</LocalizationProvider>
</Provider>
</BrowserRouter>
</MockedProvider>,
);

await wait();

userEvent.click(screen.getByTestId('createEventModalBtn'));

userEvent.type(screen.getByTestId('eventTitleInput'), 'errorTest');
userEvent.type(
screen.getByTestId('eventDescriptionInput'),
'errorDescription',
);
userEvent.type(screen.getByTestId('eventLocationInput'), 'errorLocation');

userEvent.click(screen.getByTestId('createEventBtn'));

await wait();

expect(consoleError).toHaveBeenCalled();
});
});
86 changes: 53 additions & 33 deletions src/screens/UserPortal/Events/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import EventHeader from 'components/EventCalendar/EventHeader';
import type { Dayjs } from 'dayjs';
import dayjs from 'dayjs';
import type { ChangeEvent } from 'react';
import React from 'react';
import React, { useEffect, useState } from 'react';
import { Button, Form } from 'react-bootstrap';
import Modal from 'react-bootstrap/Modal';
import { useTranslation } from 'react-i18next';
Expand All @@ -20,6 +20,7 @@ import { ViewType } from 'screens/OrganizationEvents/OrganizationEvents';
import { errorHandler } from 'utils/errorHandler';
import useLocalStorage from 'utils/useLocalstorage';
import styles from './../../../style/app.module.css';
import Loader from 'components/Loader/Loader';

/**
* Converts a time string to a Dayjs object.
Expand Down Expand Up @@ -59,7 +60,7 @@ const timeToDayJs = (time: string): Dayjs => {
*
* For more details on the reusable classes, refer to the global CSS file.
*/
export default function events(): JSX.Element {
export default function Events(): JSX.Element {
const { t } = useTranslation('translation', {
keyPrefix: 'userEvents',
});
Expand All @@ -68,39 +69,65 @@ export default function events(): JSX.Element {
const { getItem } = useLocalStorage();

// State variables to manage event details and UI
const [events, setEvents] = React.useState([]);
const [eventTitle, setEventTitle] = React.useState('');
const [eventDescription, setEventDescription] = React.useState('');
const [eventLocation, setEventLocation] = React.useState('');
const [startDate, setStartDate] = React.useState<Date | null>(new Date());
const [endDate, setEndDate] = React.useState<Date | null>(new Date());
const [isPublic, setIsPublic] = React.useState(true);
const [isRegisterable, setIsRegisterable] = React.useState(true);
const [isRecurring, setIsRecurring] = React.useState(false);
const [isAllDay, setIsAllDay] = React.useState(true);
const [startTime, setStartTime] = React.useState('08:00:00');
const [endTime, setEndTime] = React.useState('10:00:00');
const [viewType, setViewType] = React.useState<ViewType>(ViewType.MONTH);
const [createEventModal, setCreateEventmodalisOpen] = React.useState(false);
const [createChatCheck, setCreateChatCheck] = React.useState(false);
const [events, setEvents] = useState([]);
const [eventTitle, setEventTitle] = useState('');
const [eventDescription, setEventDescription] = useState('');
const [eventLocation, setEventLocation] = useState('');
const [startDate, setStartDate] = useState<Date | null>(new Date());
const [endDate, setEndDate] = useState<Date | null>(new Date());
const [isPublic, setIsPublic] = useState(true);
const [isRegisterable, setIsRegisterable] = useState(true);
const [isRecurring, setIsRecurring] = useState(false);
const [isAllDay, setIsAllDay] = useState(true);
const [startTime, setStartTime] = useState('08:00:00');
const [endTime, setEndTime] = useState('10:00:00');
const [viewType, setViewType] = useState<ViewType>(ViewType.MONTH);
const [createEventModal, setCreateEventmodalisOpen] = useState(false);
const [createChatCheck, setCreateChatCheck] = useState(false);
const { orgId: organizationId } = useParams();

// Query to fetch organization details
const { data: orgData } = useQuery(ORGANIZATIONS_LIST, {
variables: { id: organizationId },
fetchPolicy: 'cache-first', // Use cached data if available
});

// Query to fetch events for the organization
const { data, refetch } = useQuery(ORGANIZATION_EVENTS_CONNECTION, {
const { data, loading, refetch } = useQuery(ORGANIZATION_EVENTS_CONNECTION, {
variables: {
organization_id: organizationId,
title_contains: '',
upcomingOnly: true, // Fetch only upcoming events
first: 400, // Fetch up to 400 events
},
skip: !organizationId, // skip if organization ID is not available
fetchPolicy: 'cache-and-network', // Fetch from cache if available, then fetch from network
errorPolicy: 'all', // show partial data if some data is available
onError: (error) => {
// log error if query fails
console.error('Query error:', error);
},
});

// Query to fetch organization details
const { data: orgData } = useQuery(ORGANIZATIONS_LIST, {
variables: { id: organizationId },
});

// Mutation to create a new event
const [create] = useMutation(CREATE_EVENT_MUTATION);

// Update the list of events when the data from the query changes
useEffect(() => {
if (data) {
setEvents(data.eventsByOrganizationConnection);
}
}, [data]);

// Show loading spinner while fetching data
if (loading) {
return (
<div>
<Loader />
</div>
);
}

// Get user details from local storage
const userId = getItem('id') as string;

Expand Down Expand Up @@ -173,7 +200,7 @@ export default function events(): JSX.Element {
* @returns Void.
*/
const handleEventTitleChange = (
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
): void => {
setEventTitle(event.target.value);
};
Expand All @@ -185,7 +212,7 @@ export default function events(): JSX.Element {
* @returns Void.
*/
const handleEventLocationChange = (
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
): void => {
setEventLocation(event.target.value);
};
Expand All @@ -197,18 +224,11 @@ export default function events(): JSX.Element {
* @returns Void.
*/
const handleEventDescriptionChange = (
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
): void => {
setEventDescription(event.target.value);
};

// Update the list of events when the data from the query changes
React.useEffect(() => {
if (data) {
setEvents(data.eventsByOrganizationConnection);
}
}, [data]);

/**
* Shows the modal for creating a new event.
*
Expand Down
Loading

0 comments on commit 6d2b478

Please sign in to comment.