- {props.image ? (
-
+
+
+
+ {image ? (
+
) : (
-
)}
-
-
{props.name}
-
- Owner:
- {props.firstName}
-
-
- {props.lastName}
-
-
-
-
+
+
+ {name}
+
+
+ {description}
+
+ {address && address.city && (
+
+
+ {address.line1},
+ {address.city},
+ {address.countryCode}
+
+
+ )}
+
+ {tCommon('admins')}: {admins?.length}
+ {tCommon('members')}: {members?.length}
+
+
-
+ {membershipRequestStatus === 'accepted' && (
+
+ )}
+
+ {membershipRequestStatus === 'pending' && (
+
+ )}
+
+ {membershipRequestStatus === '' && (
+
+ )}
+
);
}
diff --git a/src/screens/OrganizationTags/OrganizationTags.spec.tsx b/src/screens/OrganizationTags/OrganizationTags.spec.tsx
index c35b69f631..0654c9a205 100644
--- a/src/screens/OrganizationTags/OrganizationTags.spec.tsx
+++ b/src/screens/OrganizationTags/OrganizationTags.spec.tsx
@@ -48,6 +48,8 @@ vi.mock('react-toastify', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
+ warn: vi.fn(),
+ info: vi.fn(),
},
}));
@@ -272,26 +274,51 @@ describe('Organisation Tags Page', () => {
test('creates a new user tag', async () => {
renderOrganizationTags(link);
- await wait();
-
+ // Wait for initial render
await waitFor(() => {
expect(screen.getByTestId('createTagBtn')).toBeInTheDocument();
});
- userEvent.click(screen.getByTestId('createTagBtn'));
- userEvent.click(screen.getByTestId('createTagSubmitBtn'));
+ // Open create tag modal
+ await act(async () => {
+ userEvent.click(screen.getByTestId('createTagBtn'));
+ });
+
+ // Wait for modal to be visible
+ await waitFor(() => {
+ expect(screen.getByTestId('createTagSubmitBtn')).toBeInTheDocument();
+ });
+
+ // Before submitting the form, we'll verify it exists
+ const form = screen.getByTestId('createTagSubmitBtn').closest('form');
+ if (form == null) {
+ throw new Error('Form not found');
+ }
+
+ // Submit empty form
+ await act(async () => {
+ fireEvent.submit(form); // No non-null assertion here
+ });
+ // Wait for error toast
await waitFor(() => {
expect(toast.error).toHaveBeenCalledWith(translations.enterTagName);
});
- userEvent.type(
- screen.getByPlaceholderText(translations.tagNamePlaceholder),
- 'userTag 12',
- );
+ // Type tag name
+ await act(async () => {
+ userEvent.type(
+ screen.getByPlaceholderText(translations.tagNamePlaceholder),
+ 'userTag 12',
+ );
+ });
- userEvent.click(screen.getByTestId('createTagSubmitBtn'));
+ // Submit form with valid data
+ await act(async () => {
+ fireEvent.submit(form); // Again, no non-null assertion here
+ });
+ // Wait for success toast
await waitFor(() => {
expect(toast.success).toHaveBeenCalledWith(
translations.tagCreationSuccess,
diff --git a/src/screens/UserPortal/Organizations/Organizations.tsx b/src/screens/UserPortal/Organizations/Organizations.tsx
index 59f5500d02..f8c405cebc 100644
--- a/src/screens/UserPortal/Organizations/Organizations.tsx
+++ b/src/screens/UserPortal/Organizations/Organizations.tsx
@@ -1,49 +1,14 @@
import { useQuery } from '@apollo/client';
-import { SearchOutlined } from '@mui/icons-material';
-import HourglassBottomIcon from '@mui/icons-material/HourglassBottom';
+import useLocalStorage from 'utils/useLocalstorage';
import {
USER_CREATED_ORGANIZATIONS,
USER_JOINED_ORGANIZATIONS,
USER_ORGANIZATION_CONNECTION,
} from 'GraphQl/Queries/Queries';
-import PaginationList from 'components/PaginationList/PaginationList';
-import OrganizationCard from 'components/UserPortal/OrganizationCard/OrganizationCard';
-import UserSidebar from 'components/UserPortal/UserSidebar/UserSidebar';
import React, { useEffect, useState } from 'react';
-import { Button, Dropdown, Form, InputGroup } from 'react-bootstrap';
-import { useTranslation } from 'react-i18next';
-import useLocalStorage from 'utils/useLocalstorage';
-import styles from './Organizations.module.css';
-import ProfileDropdown from 'components/ProfileDropdown/ProfileDropdown';
-
-const { getItem } = useLocalStorage();
-
-interface InterfaceOrganizationCardProps {
- id: string;
- name: string;
- image: string;
- description: string;
- admins: [];
- members: [];
- address: {
- city: string;
- countryCode: string;
- line1: string;
- postalCode: string;
- state: string;
- };
- membershipRequestStatus: string;
- userRegistrationRequired: boolean;
- membershipRequests: {
- _id: string;
- user: {
- _id: string;
- };
- }[];
-}
/**
- * Interface defining the structure of organization properties.
+ * Interface for the organization object.
*/
interface InterfaceOrganization {
_id: string;
@@ -69,54 +34,22 @@ interface InterfaceOrganization {
}[];
}
+const { getItem } = useLocalStorage();
+
/**
- * Component for displaying and managing user organizations.
+ * Component to render the organizations of a user with pagination and filtering.
*
+ * @returns \{JSX.Element\} The Organizations component.
*/
-export default function organizations(): JSX.Element {
- const { t } = useTranslation('translation', {
- keyPrefix: 'userOrganizations',
- });
-
- const [hideDrawer, setHideDrawer] = useState
(null);
-
- /**
- * Handles window resize events to toggle drawer visibility.
- */
- const handleResize = (): void => {
- if (window.innerWidth <= 820) {
- setHideDrawer(!hideDrawer);
- }
- };
-
- useEffect(() => {
- handleResize();
- window.addEventListener('resize', handleResize);
- return () => {
- window.removeEventListener('resize', handleResize);
- };
- }, []);
-
- const [page, setPage] = React.useState(0);
- const [rowsPerPage, setRowsPerPage] = React.useState(5);
- const [organizations, setOrganizations] = React.useState([]);
- const [filterName, setFilterName] = React.useState('');
- const [mode, setMode] = React.useState(0);
-
- const modes = [
- t('allOrganizations'),
- t('joinedOrganizations'),
- t('createdOrganizations'),
- ];
+export default function Organizations(): JSX.Element {
const userId: string | null = getItem('userId');
+ const [organizations, setOrganizations] = useState(
+ [],
+ );
- const {
- data,
- refetch,
- loading: loadingOrganizations,
- } = useQuery(USER_ORGANIZATION_CONNECTION, {
- variables: { filter: filterName },
+ const { data } = useQuery(USER_ORGANIZATION_CONNECTION, {
+ variables: { filter: '' }, // Filter value can be implemented later
});
const { data: joinedOrganizationsData } = useQuery(
@@ -133,79 +66,9 @@ export default function organizations(): JSX.Element {
},
);
- /**
- * Handles page change in pagination.
- *
- * @param _event - The event triggering the page change.
- * @param newPage - The new page number.
- */
- /* istanbul ignore next */
- const handleChangePage = (
- _event: React.MouseEvent | null,
- newPage: number,
- ): void => {
- setPage(newPage);
- };
-
- /**
- * Handles change in the number of rows per page.
- *
- * @param event - The event triggering the change.
- */
- /* istanbul ignore next */
- const handleChangeRowsPerPage = (
- event: React.ChangeEvent,
- ): void => {
- const newRowsPerPage = event.target.value;
-
- setRowsPerPage(parseInt(newRowsPerPage, 10));
- setPage(0);
- };
-
- /**
- * Searches organizations based on the provided filter value.
- *
- * @param value - The search filter value.
- */
- const handleSearch = (value: string): void => {
- setFilterName(value);
-
- refetch({
- filter: value,
- });
- };
-
- /**
- * Handles search input submission by pressing the Enter key.
- *
- * @param e - The keyboard event.
- */
- const handleSearchByEnter = (
- e: React.KeyboardEvent,
- ): void => {
- if (e.key === 'Enter') {
- const { value } = e.target as HTMLInputElement;
- handleSearch(value);
- }
- };
-
- /**
- * Handles search button click to search organizations.
- */
- const handleSearchByBtnClick = (): void => {
- const value =
- (document.getElementById('searchUserOrgs') as HTMLInputElement)?.value ||
- '';
- handleSearch(value);
- };
-
- /**
- * Updates the list of organizations based on query results and selected mode.
- */
- /* istanbul ignore next */
useEffect(() => {
if (data) {
- const organizations = data.organizationsConnection.map(
+ const orgs = data.organizationsConnection.map(
(organization: InterfaceOrganization) => {
let membershipRequestStatus = '';
if (
@@ -224,205 +87,46 @@ export default function organizations(): JSX.Element {
return { ...organization, membershipRequestStatus };
},
);
- setOrganizations(organizations);
+ setOrganizations(orgs);
}
- }, [data]);
+ }, [data, userId]);
- /**
- * Updates the list of organizations based on the selected mode and query results.
- */
- /* istanbul ignore next */
useEffect(() => {
- if (mode === 0) {
- if (data) {
- const organizations = data.organizationsConnection.map(
- (organization: InterfaceOrganization) => {
- let membershipRequestStatus = '';
- if (
- organization.members.find(
- (member: { _id: string }) => member._id === userId,
- )
- )
- membershipRequestStatus = 'accepted';
- else if (
- organization.membershipRequests.find(
- (request: { user: { _id: string } }) =>
- request.user._id === userId,
- )
- )
- membershipRequestStatus = 'pending';
- return { ...organization, membershipRequestStatus };
- },
- );
- setOrganizations(organizations);
- }
- } else if (mode === 1) {
- if (joinedOrganizationsData && joinedOrganizationsData.users.length > 0) {
- const organizations =
- joinedOrganizationsData.users[0]?.user?.joinedOrganizations || [];
- setOrganizations(organizations);
- }
- } else if (mode === 2) {
- if (
- createdOrganizationsData &&
- createdOrganizationsData.users.length > 0
- ) {
- const organizations =
- createdOrganizationsData.users[0]?.appUserProfile
- ?.createdOrganizations || [];
- setOrganizations(organizations);
- }
+ if (joinedOrganizationsData?.users?.length > 0) {
+ const orgs =
+ joinedOrganizationsData.users[0]?.user?.joinedOrganizations.map(
+ (org: InterfaceOrganization) => ({
+ ...org,
+ membershipRequestStatus: 'accepted',
+ isJoined: true,
+ }),
+ ) || [];
+ setOrganizations(orgs);
+ } else if (createdOrganizationsData?.users?.length > 0) {
+ const orgs =
+ createdOrganizationsData.users[0]?.appUserProfile?.createdOrganizations.map(
+ (org: InterfaceOrganization) => ({
+ ...org,
+ membershipRequestStatus: 'accepted',
+ isJoined: true,
+ }),
+ ) || [];
+ setOrganizations(orgs);
}
- }, [mode, data, joinedOrganizationsData, createdOrganizationsData, userId]);
+ }, [joinedOrganizationsData, createdOrganizationsData, userId]);
return (
- <>
- {hideDrawer ? (
-
+
+ {organizations.length > 0 ? (
+ organizations.map((org) => (
+
+
{org.name}
+
{org.description}
+
+ ))
) : (
-
+
No organizations found
)}
-
-
-
-
-
-
{t('selectOrganization')}
-
-
-
-
-
-
-
-
-
-
-
-
-
- {modes[mode]}
-
-
- {modes.map((value, index) => {
- return (
- setMode(index)}
- >
- {value}
-
- );
- })}
-
-
-
-
-
-
- {loadingOrganizations ? (
-
- Loading...
-
- ) : (
- <>
- {' '}
- {organizations && organizations.length > 0 ? (
- (rowsPerPage > 0
- ? organizations.slice(
- page * rowsPerPage,
- page * rowsPerPage + rowsPerPage,
- )
- : /* istanbul ignore next */
- organizations
- ).map((organization: InterfaceOrganization, index) => {
- const cardProps: InterfaceOrganizationCardProps = {
- name: organization.name,
- image: organization.image,
- id: organization._id,
- description: organization.description,
- admins: organization.admins,
- members: organization.members,
- address: organization.address,
- membershipRequestStatus:
- organization.membershipRequestStatus,
- userRegistrationRequired:
- organization.userRegistrationRequired,
- membershipRequests: organization.membershipRequests,
- };
- return
;
- })
- ) : (
-
{t('nothingToShow')}
- )}
- >
- )}
-
-
-
-
-
- >
+
);
}
diff --git a/talawa-admin b/talawa-admin
new file mode 160000
index 0000000000..ba4d344312
--- /dev/null
+++ b/talawa-admin
@@ -0,0 +1 @@
+Subproject commit ba4d3443123886e380d78ecfa91ba85ec6fd4294