Skip to content

Commit 7e85e9f

Browse files
UwicyezaGGSinseswa721
authored andcommitted
Fetch invitation statistics (#463)
1 parent c65e379 commit 7e85e9f

File tree

6 files changed

+481
-163
lines changed

6 files changed

+481
-163
lines changed

src/Mutations/invitationMutation.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,24 @@ export const UPLOAD_INVITATION_FILE = gql`
2727
sentEmails
2828
}
2929
}
30-
`;
30+
`;
31+
32+
export const DELETE_INVITATION = gql`
33+
mutation DeleteInvitation($invitationId: ID!) {
34+
deleteInvitation(invitationId: $invitationId) {
35+
message
36+
}
37+
}
38+
`;
39+
export const UPDATE_INVITATION = gql`
40+
mutation UpdateInvitation($invitationId: ID!, $email: String, $role: String) {
41+
updateInvitation(invitationId: $invitationId, email: $email, role: $role) {
42+
status
43+
updatedInvitee {
44+
email
45+
role
46+
}
47+
message
48+
}
49+
}
50+
`;

src/Mutations/invitationStats.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { gql } from '@apollo/client';
2+
23
export const GET_INVITATIONS_STATISTICS_QUERY = gql`
3-
query GetInvitationStatistics($orgToken: String!){
4-
getInvitationStatistics(orgToken: $orgToken){
4+
query GetInvitationStatistics($orgToken: String!) {
5+
getInvitationStatistics(orgToken: $orgToken) {
56
totalInvitations
67
pendingInvitationsCount
78
getPendingInvitationsPercentsCount
89
getAcceptedInvitationsPercentsCount
910
acceptedInvitationsCount
1011
}
1112
}
12-
`;
13+
`;

src/components/InvitationTable.tsx

Lines changed: 159 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,178 @@
1-
import React from 'react';
1+
// @ts-nocheck
2+
import React, { useState, useMemo } from 'react';
3+
import { useTranslation } from 'react-i18next';
4+
import {
5+
useGlobalFilter,
6+
usePagination,
7+
useSortBy,
8+
useTable,
9+
} from 'react-table';
210
import DataPagination from './DataPagination';
311

4-
interface Invitee {
5-
email: string;
6-
role: string;
12+
interface TableData {
13+
data: any[];
14+
columns: any;
15+
error: string | null;
16+
loading?: boolean;
17+
className?: string;
718
}
819

9-
interface Invitation {
10-
invitees: Invitee[];
11-
status: string;
12-
id: string;
13-
}
20+
function DataTableStats({ data, columns, error, loading }: TableData) {
21+
const [filterInput, setFilterInput] = useState('');
22+
const { t } = useTranslation();
1423

15-
interface InvitationTableProps {
16-
invitations: Invitation[];
17-
pageIndex: number;
18-
pageSize: number;
19-
canNextPage: boolean;
20-
canPreviousPage: boolean;
21-
pageOptions: any[];
22-
setPageSize: (size: number) => void;
23-
gotoPage: (page: number) => void;
24-
nextPage: () => void;
25-
previousPage: () => void;
26-
}
24+
// Memoize columns and data to prevent unnecessary re-renders
25+
const memoizedColumns = useMemo(() => [...columns], [columns]);
26+
const memoizedData = useMemo(() => [...data], [data]);
2727

28-
function InvitationTable({
29-
invitations,
30-
pageIndex,
31-
pageSize,
32-
canNextPage,
33-
canPreviousPage,
34-
pageOptions,
35-
setPageSize,
36-
gotoPage,
37-
nextPage,
38-
previousPage,
39-
}: InvitationTableProps) {
40-
return (
41-
<div className="w-full overflow-x-auto pb-4 mt-6">
42-
<div className="min-w-[600px] border border-gray-300 pb-2 rounded-lg bg-[#F3F0FE] dark:bg-dark-bg">
43-
{/* Header Row */}
44-
<div className="bg-[#C7B9F9] flex justify-between font-bold p-4 border-b border-gray-300 dark:text-black">
45-
<span className="flex-1 p-4 text-left">Email</span>
46-
<span className="flex-1 p-4 text-left">Role</span>
47-
<span className="flex-1 p-4 text-left">Status</span>
48-
</div>
28+
// Table instance
29+
const tableInstance = useTable(
30+
{
31+
data: memoizedData,
32+
columns: memoizedColumns,
33+
initialState: { pageSize: 3, globalFilter: filterInput },
34+
},
35+
useGlobalFilter,
36+
useSortBy,
37+
usePagination,
38+
);
39+
40+
const {
41+
getTableProps,
42+
setGlobalFilter,
43+
getTableBodyProps,
44+
page,
45+
nextPage,
46+
previousPage,
47+
canPreviousPage,
48+
canNextPage,
49+
gotoPage,
50+
pageCount,
51+
setPageSize,
52+
pageOptions,
53+
headerGroups,
54+
prepareRow,
55+
state: { pageIndex, pageSize },
56+
} = tableInstance;
4957

50-
{/* Data Rows */}
51-
{invitations?.map((invitation) => (
52-
<div
53-
key={invitation.id}
54-
className="w-full bg-[#F3F0FE] border-b border-gray-300"
55-
>
56-
{invitation.invitees?.map((invitee, idx) => (
57-
<div
58-
key={invitation.id}
59-
className="flex justify-between p-4 dark:bg-dark-bg"
60-
>
61-
<span className="flex-1 p-4 text-left">{invitee.email}</span>
62-
<span className="flex-1 p-4 text-left">{invitee.role}</span>
63-
<span className="flex-1 p-4 text-left">{invitation.status}</span>
64-
</div>
58+
const handleFilterChange = (e) => {
59+
const value = e.target.value || '';
60+
setGlobalFilter(value);
61+
setFilterInput(value);
62+
};
63+
64+
return (
65+
<div className="">
66+
<div className="flex items-center justify-between pb-6 " />
67+
<div style={{ overflowX: 'auto' }}>
68+
<table className="min-w-full leading-normal" {...getTableProps()}>
69+
<thead>
70+
{headerGroups.map((headerGroup) => (
71+
<tr key={headerGroup.id} {...headerGroup.getHeaderGroupProps()}>
72+
{headerGroup.headers.map((column) => (
73+
<th
74+
key={column.id}
75+
className={column.isSorted ? 'sort-asc thead' : ' thead'}
76+
{...column.getHeaderProps(column.getSortByToggleProps())}
77+
>
78+
{column.render('Header')}
79+
</th>
80+
))}
81+
</tr>
6582
))}
66-
</div>
67-
))}
83+
</thead>
84+
<tbody {...getTableBodyProps()}>
85+
{!loading && memoizedData.length === 0 ? (
86+
<tr>
87+
<td
88+
colSpan={columns.length}
89+
className="px-6 py-4 text-sm text-center text-gray-500 dark:text-gray-300"
90+
aria-label="Empty cell" // Added for accessibility
91+
>
92+
&nbsp;{' '}
93+
{/* Non-breaking space to ensure it's not an empty tag */}
94+
</td>
95+
</tr>
96+
) : (
97+
page.map((row) => {
98+
prepareRow(row);
99+
return (
100+
<tr
101+
key={row.id}
102+
className={`border-b dark:border-gray-700 ${
103+
row.index % 2 === 0
104+
? 'bg-light-bg dark:bg-neutral-600'
105+
: 'bg-white dark:bg-dark-bg'
106+
}`}
107+
{...row.getRowProps()}
108+
>
109+
{row.cells.map((cell) => (
110+
<td
111+
key={cell.id}
112+
className="data-cell "
113+
{...cell.getCellProps()}
114+
>
115+
{cell.render('Cell')}
116+
</td>
117+
))}
118+
</tr>
119+
);
120+
})
121+
)}
122+
{loading && (
123+
<tr>
124+
<td
125+
colSpan={columns.length}
126+
className="px-6 py-4 text-sm text-center text-gray-500 dark:text-gray-300 "
127+
>
128+
Loading...
129+
</td>
130+
</tr>
131+
)}
132+
{error && (
133+
<tr>
134+
<td
135+
colSpan={columns.length}
136+
className="px-6 py-4 text-sm text-center text-gray-500 dark:text-gray-300 "
137+
>
138+
Error occurred
139+
</td>
140+
</tr>
141+
)}
142+
{!loading && !error && data.length === 0 && (
143+
<tr>
144+
{' '}
145+
<td colSpan={columns.length || 100} className="text-center p-4">
146+
<div className="flex flex-col items-center justify-center space-y-4">
147+
{' '}
148+
<p className="text-gray-600 dark:text-gray-400 text-lg font-medium">
149+
{' '}
150+
No records available{' '}
151+
</p>
152+
</div>{' '}
153+
</td>{' '}
154+
</tr>
155+
)}
156+
</tbody>
157+
</table>
158+
</div>
159+
<div className="px-6 py-4">
68160
<DataPagination
69-
pageIndex={pageIndex}
70-
pageSize={pageSize}
161+
pageOptions={pageOptions}
71162
canNextPage={canNextPage}
163+
gotoPage={gotoPage}
164+
columnLength={columns.length}
72165
canPreviousPage={canPreviousPage}
73-
pageOptions={pageOptions}
166+
pageSize={pageSize}
74167
setPageSize={setPageSize}
75-
gotoPage={gotoPage}
76-
nextPage={nextPage}
77168
previousPage={previousPage}
78-
columnLength={3}
169+
nextPage={nextPage}
170+
pageCount={pageCount}
171+
pageIndex={pageIndex}
79172
/>
80173
</div>
81174
</div>
82175
);
83176
}
84177

85-
export default InvitationTable;
178+
export default DataTableStats;

src/components/invitationModal.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ function InviteForm({ onClose }: InviteFormProps) {
102102
setEmail('');
103103
setRole('Role');
104104
setOrgToken('');
105+
onClose();
105106
} catch (e: any) {
106107
toast.error(`Error sending invitation: ${e.message}`);
107108
}

0 commit comments

Comments
 (0)