Skip to content

Commit 120da8c

Browse files
Enable admin to view all programs with user breakdown per program-#422
1 parent 4321d94 commit 120da8c

File tree

2 files changed

+168
-10
lines changed

2 files changed

+168
-10
lines changed
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import React from 'react';
2+
import { gql, useQuery } from '@apollo/client';
3+
import DataTable from './DataTable';
4+
import Dialog from '@mui/material/Dialog';
5+
import DialogContent from '@mui/material/DialogContent';
6+
import DialogTitle from '@mui/material/DialogTitle';
7+
import { styled } from '@mui/material/styles';
8+
9+
interface User {
10+
email: string;
11+
role: string;
12+
program: {
13+
name: string;
14+
} | null;
15+
organizations: string[];
16+
}
17+
18+
interface ProgramUsersModalProps {
19+
programId: string;
20+
programName: string;
21+
isOpen: boolean;
22+
onClose: () => void;
23+
}
24+
25+
interface CellProps {
26+
value: string;
27+
}
28+
29+
// Styled components for dark mode support
30+
const StyledDialog = styled(Dialog)(({ theme }) => ({
31+
'& .MuiDialog-paper': {
32+
backgroundColor: 'transparent',
33+
boxShadow: 'none',
34+
},
35+
}));
36+
37+
const StyledDialogTitle = styled(DialogTitle)({
38+
padding: '16px 24px',
39+
margin: 0,
40+
});
41+
42+
const StyledDialogContent = styled(DialogContent)({
43+
padding: '20px 24px',
44+
});
45+
46+
const GET_ALL_USERS = gql`
47+
query GetAllUsers($orgToken: String) {
48+
getAllUsers(orgToken: $orgToken) {
49+
role
50+
program {
51+
name
52+
}
53+
organizations
54+
email
55+
}
56+
}
57+
`;
58+
59+
export function ProgramUsersModal({ programId, isOpen, onClose, programName }: ProgramUsersModalProps) {
60+
const { data, loading, error } = useQuery(GET_ALL_USERS, {
61+
variables: {
62+
orgToken: localStorage.getItem('orgToken'),
63+
},
64+
skip: !isOpen,
65+
});
66+
67+
const programUsers = data?.getAllUsers.filter(
68+
(user: User) => user.program?.name === programName
69+
) || [];
70+
71+
const columns = [
72+
{
73+
Header: 'Email',
74+
accessor: 'email',
75+
Cell: ({ value }: CellProps) => (
76+
<div className="flex items-center">
77+
<span className="hidden ml-2 md:inline-block h-8 w-8 rounded-full overflow-hidden bg-gray-100 dark:bg-gray-700">
78+
<svg className="h-full w-full text-gray-300 dark:text-gray-500" fill="currentColor" viewBox="0 0 24 24">
79+
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
80+
</svg>
81+
</span>
82+
<span className="ml-3 dark:text-gray-200">{value}</span>
83+
</div>
84+
),
85+
},
86+
{
87+
Header: 'Role',
88+
accessor: 'role',
89+
Cell: ({ value }: CellProps) => (
90+
<span className="capitalize dark:text-gray-200">{value}</span>
91+
),
92+
},
93+
{
94+
Header: 'Organization',
95+
accessor: 'organizations',
96+
Cell: ({ value }: { value: string[] }) => (
97+
<span className="dark:text-gray-200">{value.join(', ')}</span>
98+
),
99+
},
100+
];
101+
102+
return (
103+
<StyledDialog
104+
open={isOpen}
105+
onClose={onClose}
106+
maxWidth="md"
107+
fullWidth
108+
>
109+
<div className="bg-white dark:bg-gray-800 rounded-t-lg">
110+
<StyledDialogTitle className="text-gray-900 dark:text-white border-b dark:border-gray-700">
111+
{programName} - Users
112+
</StyledDialogTitle>
113+
<StyledDialogContent className="bg-white dark:bg-gray-800">
114+
{loading ? (
115+
<div className="flex justify-center items-center h-48">
116+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
117+
</div>
118+
) : error ? (
119+
<div className="text-red-500 text-center">
120+
Error loading users. Please try again.
121+
</div>
122+
) : programUsers.length === 0 ? (
123+
<div className="text-center py-8 text-gray-500 dark:text-gray-400">
124+
No users found for this program
125+
</div>
126+
) : (
127+
<div className="dark:bg-gray-800">
128+
<DataTable
129+
data={programUsers}
130+
columns={columns}
131+
title="Program Users"
132+
/>
133+
</div>
134+
)}
135+
</StyledDialogContent>
136+
</div>
137+
</StyledDialog>
138+
);
139+
}

src/containers/admin-dashBoard/Programs.tsx

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import CreateProgramModal from './CreateProgramModal';
1010
import DeleteProgramModal from './DeleteProgramModal';
1111
import UpdateProgramModal from './UpdateProgramModal';
1212
import TtlSkeleton from '../../Skeletons/ttl.skeleton';
13+
import { ProgramUsersModal } from '../../components/ProgramUsersModal';
1314

1415
export interface Program {
1516
id: string;
@@ -53,10 +54,27 @@ function ActionButtons({
5354
setCurrentProgram,
5455
setUpdateProgramModal,
5556
setDeleteProgramModal,
57+
setViewUsersModal,
5658
...props
5759
}: any) {
5860
return (
5961
<div className="flex relative flex-row align-middle justify-center items-center">
62+
<div
63+
onClick={() => {
64+
const program = getData?.getAllPrograms[props.row.index];
65+
setCurrentProgram(program);
66+
setViewUsersModal(true);
67+
}}
68+
>
69+
<Icon
70+
icon="heroicons:eye"
71+
className="mr-2"
72+
width="25"
73+
height="25"
74+
cursor="pointer"
75+
color="#9e85f5"
76+
/>
77+
</div>
6078
<div
6179
data-testid="updateIcon"
6280
onClick={() => {
@@ -76,9 +94,7 @@ function ActionButtons({
7694
</div>
7795
<div
7896
data-testid="deleteIcon"
79-
/* istanbul ignore next */
8097
onClick={() => {
81-
/* istanbul ignore next */
8298
const program = getData?.getAllPrograms[props.row.index];
8399
setCurrentProgram(program);
84100
setDeleteProgramModal(true);
@@ -120,6 +136,7 @@ function AdminPrograms() {
120136
const [createProgramModel, setCreateProgramModel] = useState(false);
121137
const [updateProgramModal, setUpdateProgramModal] = useState(false);
122138
const [deleteProgramModal, setDeleteProgramModal] = useState(false);
139+
const [viewUsersModal, setViewUsersModal] = useState(false);
123140
const [currentProgram, setCurrentProgram] = useState<Program | undefined>(
124141
undefined,
125142
);
@@ -131,7 +148,6 @@ function AdminPrograms() {
131148
{ Header: t('Manager'), accessor: 'manager' },
132149
{ Header: t('Organization'), accessor: 'organization' },
133150
{ Header: t('Description'), accessor: 'description' },
134-
135151
{
136152
Header: t('Actions'),
137153
accessor: '',
@@ -141,11 +157,12 @@ function AdminPrograms() {
141157
setCurrentProgram,
142158
setUpdateProgramModal,
143159
setDeleteProgramModal,
160+
setViewUsersModal,
144161
...props,
145162
}),
146163
},
147164
];
148-
/* istanbul ignore next */
165+
149166
const programListData = getData
150167
? getData.getAllPrograms.map(
151168
({
@@ -163,15 +180,14 @@ function AdminPrograms() {
163180
}),
164181
)
165182
: [{}];
166-
/* istanbul ignore next */
183+
167184
const removeModel = () => {
168185
const newState = !createProgramModel;
169186
setCreateProgramModel(newState);
170187
};
171188

172189
return (
173190
<>
174-
{/* =========================== Start:: CreateProgramModel =============================== */}
175191
<CreateProgramModal
176192
data={getData}
177193
createProgramModel={createProgramModel}
@@ -181,7 +197,6 @@ function AdminPrograms() {
181197
<UpdateProgramModal
182198
data={getData}
183199
updateProgramModal={updateProgramModal}
184-
/* istanbul ignore next */
185200
currentProgram={currentProgram}
186201
removeModel={() => {
187202
setUpdateProgramModal(false);
@@ -190,14 +205,18 @@ function AdminPrograms() {
190205
/>
191206
<DeleteProgramModal
192207
deleteProgramModal={deleteProgramModal}
193-
/* istanbul ignore next */
194208
currentProgram={currentProgram}
195209
removeModel={() => {
196210
setDeleteProgramModal(false);
197211
}}
198212
refetch={getRefetch}
199213
/>
200-
{/* =========================== End:: CreateProgramModel =============================== */}
214+
<ProgramUsersModal
215+
programId={currentProgram?.id ?? ''}
216+
programName={currentProgram?.name ?? ''}
217+
isOpen={viewUsersModal}
218+
onClose={() => setViewUsersModal(false)}
219+
/>
201220

202221
<div className="bg-light-bg dark:bg-dark-frame-bg ">
203222
<div className="flex items-left pb-8">
@@ -231,4 +250,4 @@ function AdminPrograms() {
231250
);
232251
}
233252

234-
export default AdminPrograms;
253+
export default AdminPrograms;

0 commit comments

Comments
 (0)