Skip to content

Commit fa1a7e5

Browse files
committed
add admin &members page
1 parent 3efc2c4 commit fa1a7e5

File tree

3 files changed

+370
-2
lines changed

3 files changed

+370
-2
lines changed
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
'use client';
2+
3+
import { useEffect, useState } from 'react';
4+
import { graphql } from '@/gql';
5+
import { AddUserToOrganizationInput } from '@/gql/generated/graphql';
6+
import { useMutation, useQuery } from '@tanstack/react-query';
7+
import { Button, Dialog, Label, Select, toast } from 'opub-ui';
8+
9+
import { GraphQL } from '@/lib/api';
10+
import { toTitleCase } from '@/lib/utils';
11+
import { FetchUsers } from '../usecases/edit/[id]/contributors/query';
12+
13+
const addUserDoc: any = graphql(`
14+
mutation addUserToOrganization($input: AddUserToOrganizationInput!) {
15+
addUserToOrganization(input: $input) {
16+
__typename
17+
... on TypeOrganizationMembership {
18+
role {
19+
name
20+
id
21+
}
22+
}
23+
}
24+
}
25+
`);
26+
const allRolesDoc: any = graphql(`
27+
query AllRolesDoc {
28+
roles {
29+
id
30+
name
31+
description
32+
}
33+
}
34+
`);
35+
36+
const AddUser = ({
37+
setIsOpen,
38+
selectedUser,
39+
title,
40+
isOpen,
41+
refetchUsers,
42+
isEdit,
43+
}: {
44+
setIsOpen: (isOpen: boolean) => void;
45+
selectedUser: any;
46+
title: string;
47+
isOpen: boolean;
48+
refetchUsers: any;
49+
isEdit: boolean;
50+
}) => {
51+
const [searchValue, setSearchValue] = useState('');
52+
53+
const Users: { data: any; isLoading: boolean; refetch: any } = useQuery(
54+
[`fetch_users_list`],
55+
() =>
56+
GraphQL(
57+
FetchUsers,
58+
{},
59+
{
60+
limit: 10,
61+
searchTerm: searchValue,
62+
}
63+
),
64+
{
65+
enabled: searchValue?.length > 0,
66+
keepPreviousData: true,
67+
}
68+
);
69+
70+
71+
72+
const RolesList: { data: any; isLoading: boolean; refetch: any } = useQuery(
73+
[`fetch_UseCaseData`],
74+
() => GraphQL(allRolesDoc, {}, [])
75+
);
76+
77+
useEffect(() => {
78+
if (selectedUser) {
79+
setSearchValue(selectedUser.name || '');
80+
setFormData({
81+
userId: selectedUser.id || '',
82+
roleId: selectedUser.role?.id || '',
83+
});
84+
} else {
85+
setFormData({ userId: '', roleId: '' });
86+
setSearchValue('');
87+
}
88+
}, [selectedUser]);
89+
90+
const { mutate, isLoading: addUserLoading } = useMutation(
91+
(input: { input: AddUserToOrganizationInput }) =>
92+
GraphQL(addUserDoc, {}, input),
93+
{
94+
onSuccess: (res: any) => {
95+
toast('User added successfully');
96+
// Optionally, reset form or perform other actions
97+
setIsOpen(false);
98+
setFormData({
99+
userId: '',
100+
roleId: '',
101+
});
102+
refetchUsers();
103+
},
104+
onError: (err: any) => {
105+
toast('Failed to add user');
106+
},
107+
}
108+
);
109+
110+
const [formData, setFormData] = useState({
111+
userId: '',
112+
roleId: '',
113+
});
114+
const handleChange = (field: string, value: any) => {
115+
setFormData((prev) => ({
116+
...prev,
117+
[field]: value,
118+
}));
119+
};
120+
121+
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
122+
123+
const filteredOptions = Users.data?.searchUsers;
124+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
125+
const value = e.target.value;
126+
setSearchValue(value);
127+
setIsDropdownOpen(true); // Keep dropdown open while typing
128+
Users.refetch(); // Refetch when search term changes
129+
};
130+
131+
const handleSelectOption = (option: any) => {
132+
handleChange('userId', option.id);
133+
setSearchValue(option.fullName);
134+
setIsDropdownOpen(false); // Close dropdown
135+
};
136+
137+
138+
return (
139+
<div>
140+
<Dialog open={isOpen} onOpenChange={setIsOpen}>
141+
{isOpen && (
142+
<Dialog.Content
143+
title={isEdit ? "Edit Admin / Member": "Add Admin / Member"}
144+
className="h-72 overflow-y-auto"
145+
>
146+
<div className="m-auto mb-6 flex flex-col gap-6">
147+
<div className="relative w-full">
148+
<Label>Select User</Label>
149+
<input
150+
type="text"
151+
id="combobox"
152+
disabled={isEdit}
153+
value={searchValue}
154+
onChange={handleInputChange}
155+
className="border border-gray-100 placeholder:text-sm mt-1 block w-full px-3 py-1"
156+
placeholder={'Select user'}
157+
/>
158+
{isDropdownOpen && filteredOptions?.length > 0 && (
159+
<div className="border border-gray-300 rounded-md shadow-lg absolute left-0 right-0 z-1 mt-2 max-h-60 overflow-y-auto rounded-2 bg-white px-1 py-2 shadow-basicXl">
160+
{filteredOptions.map((option: any) => (
161+
<div
162+
key={option.value}
163+
className="cursor-pointer rounded-2 px-4 py-2 hover:bg-baseGraySlateSolid3"
164+
onClick={() => handleSelectOption(option)}
165+
>
166+
{option.fullName}
167+
</div>
168+
))}
169+
</div>
170+
)}
171+
</div>
172+
<Select
173+
name=""
174+
value={formData.roleId}
175+
placeholder="Select a role"
176+
onChange={(e) => handleChange('roleId', e)}
177+
options={RolesList.data?.roles
178+
.filter((role: any) => role.name !== 'owner')
179+
.map((role: any) => ({
180+
label: toTitleCase(role.name),
181+
value: role.id,
182+
}))}
183+
label="Select a role"
184+
helpText={RolesList.data?.roles
185+
.filter((role: any) => role.id === formData.roleId)
186+
.map((role: any) => role.description)}
187+
/>
188+
</div>
189+
<div className="flex justify-center">
190+
<Button
191+
kind="primary"
192+
className="m-auto"
193+
onClick={() => {
194+
setIsOpen(false);
195+
mutate({ input: formData });
196+
}}
197+
>
198+
{isEdit ? "Update" : "Add"}
199+
</Button>
200+
</div>
201+
</Dialog.Content>
202+
)}
203+
</Dialog>
204+
</div>
205+
);
206+
};
207+
208+
export default AddUser;
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
'use client';
2+
3+
import React, { useState } from 'react';
4+
import { useParams } from 'next/navigation';
5+
import { graphql } from '@/gql';
6+
import { useQuery } from '@tanstack/react-query';
7+
import { Button, DataTable, SearchInput, Text } from 'opub-ui';
8+
9+
import { GraphQL } from '@/lib/api';
10+
import { formatDate } from '@/lib/utils';
11+
import { Loading } from '@/components/loading';
12+
import AddUser from './addUser';
13+
14+
const usersListDoc: any = graphql(`
15+
query userByOrganization {
16+
users {
17+
id
18+
fullName
19+
organizationMemberships {
20+
role {
21+
name
22+
id
23+
}
24+
createdAt
25+
updatedAt
26+
}
27+
}
28+
}
29+
`);
30+
31+
const Admin = () => {
32+
const params = useParams();
33+
const usersList: { data: any; isLoading: boolean; refetch: any } = useQuery(
34+
[`fetch_users_list_admin_members`],
35+
() => GraphQL(usersListDoc, {}, [])
36+
);
37+
const [isOpen, setIsOpen] = useState(false);
38+
const [isEdit, setIsEdit] = useState(false);
39+
const [selectedUser, setSelectedUser] = useState({});
40+
41+
const table = {
42+
columns: [
43+
{
44+
accessorKey: 'name',
45+
header: 'Name',
46+
},
47+
{ accessorKey: 'role', header: 'Role' },
48+
{ accessorKey: 'created', header: 'Date Created' },
49+
{ accessorKey: 'modified', header: 'Date Modified' },
50+
{
51+
accessorKey: 'edit',
52+
header: 'Edit',
53+
cell: ({ row }: any) => (
54+
<Button
55+
onClick={() => {
56+
setSelectedUser({
57+
id: row.original.id,
58+
name: row.original.name,
59+
role: {
60+
id: row.original.roleId,
61+
name: row.original.role,
62+
},
63+
});
64+
setIsOpen(true);
65+
setIsEdit(true)
66+
}}
67+
>
68+
Edit
69+
</Button>
70+
),
71+
},
72+
],
73+
rows:
74+
usersList.data?.users.map((item: any) => ({
75+
name: item.fullName,
76+
role: item.organizationMemberships[0]?.role?.name || 'N/A',
77+
roleId: item.organizationMemberships[0]?.role?.id || '',
78+
created:
79+
formatDate(item.organizationMemberships[0]?.createdAt) || 'N/A',
80+
modified:
81+
formatDate(item.organizationMemberships[0]?.updatedAt) || 'N/A',
82+
id: item.id,
83+
})) || [],
84+
};
85+
86+
const [filteredRows, setFilteredRows] = React.useState<any[]>(table.rows);
87+
const handleSearchChange = (e: string) => {
88+
const searchTerm = e.toLowerCase();
89+
const filtered = table.rows.filter((row: any) =>
90+
row.name.toLowerCase().includes(searchTerm)
91+
);
92+
setFilteredRows(filtered);
93+
};
94+
95+
return (
96+
<div>
97+
<div className="my-4 lg:my-8 flex flex-wrap items-center justify-between gap-4 lg:gap-6 px-4 lg:flex-nowrap">
98+
<Text>Showing {usersList.data?.users?.length || 0} of {usersList.data?.users?.length || 0} People</Text>
99+
<SearchInput
100+
className="w-full lg:w-1/2 "
101+
placeholder="Search for a person"
102+
label="Search"
103+
name="Search"
104+
onChange={(e) => handleSearchChange(e)}
105+
/>
106+
<div>
107+
<Button
108+
onClick={() => {
109+
setSelectedUser({
110+
id: '',
111+
name: '',
112+
role: {
113+
id: '',
114+
name: '',
115+
},
116+
});
117+
setIsEdit(false)
118+
setIsOpen(true);
119+
}}
120+
>
121+
Add User
122+
</Button>
123+
</div>
124+
{isOpen && (
125+
<AddUser
126+
setIsOpen={setIsOpen}
127+
selectedUser={selectedUser}
128+
title="Add User"
129+
isOpen={isOpen}
130+
refetchUsers={usersList.refetch()}
131+
isEdit={isEdit}
132+
/>
133+
)}
134+
</div>
135+
<div>
136+
{usersList.data?.users?.length > 0 ? (
137+
<DataTable
138+
columns={table.columns}
139+
rows={table.rows}
140+
hideSelection
141+
hideViewSelector
142+
/>
143+
) : (
144+
<Loading />
145+
)}
146+
</div>
147+
</div>
148+
);
149+
};
150+
151+
export default Admin;

0 commit comments

Comments
 (0)