|
| 1 | +import { Text, Button, Table, Modal, Group, Transition, ButtonGroup } from '@mantine/core'; |
| 2 | +import { useDisclosure } from '@mantine/hooks'; |
| 3 | +import { notifications } from '@mantine/notifications'; |
| 4 | +import { IconPlus, IconTrash } from '@tabler/icons-react'; |
| 5 | +// import dayjs from 'dayjs'; |
| 6 | +import React, { useEffect, useState } from 'react'; |
| 7 | +// import { useNavigate } from 'react-router-dom'; |
| 8 | +import { z } from 'zod'; |
| 9 | + |
| 10 | +// import { capitalizeFirstLetter } from './ManageEvent.page.js'; |
| 11 | +import FullScreenLoader from '@ui/components/AuthContext/LoadingScreen'; |
| 12 | +import { AuthGuard } from '@ui/components/AuthGuard'; |
| 13 | +import { useApi } from '@ui/util/api'; |
| 14 | +import { AppRoles } from '@common/roles.js'; |
| 15 | + |
| 16 | +// const repeatOptions = ['weekly', 'biweekly'] as const; |
| 17 | + |
| 18 | +const userSchema = z.object({ |
| 19 | + netid: z.string().min(1), |
| 20 | + firstName: z.string().min(1), |
| 21 | + middleName: z.string().optional(), |
| 22 | + lastName: z.string().min(1), |
| 23 | + sig: z.string(), |
| 24 | + // location: z.string(), |
| 25 | + // locationLink: z.optional(z.string().url()), |
| 26 | + // host: z.string(), |
| 27 | + // featured: z.boolean().default(false), |
| 28 | + // paidEventId: z.optional(z.string().min(1)), |
| 29 | +}); |
| 30 | + |
| 31 | +const usersSchema = z.array(userSchema); |
| 32 | + |
| 33 | +// const requestSchema = baseSchema.extend({ |
| 34 | +// repeats: z.optional(z.enum(repeatOptions)), |
| 35 | +// repeatEnds: z.string().optional(), |
| 36 | +// }); |
| 37 | + |
| 38 | +// const getEventSchema = requestSchema.extend({ |
| 39 | +// id: z.string(), |
| 40 | +// upcoming: z.boolean().optional(), |
| 41 | +// }); |
| 42 | + |
| 43 | +export type User = z.infer<typeof userSchema>; |
| 44 | +export type Users = z.infer<typeof usersSchema>; |
| 45 | + |
| 46 | +// export type EventGetResponse = z.infer<typeof getEventSchema>; |
| 47 | +// const getEventsSchema = z.array(getEventSchema); |
| 48 | +// export type EventsGetResponse = z.infer<typeof getEventsSchema>; |
| 49 | + |
| 50 | +export const ScreenPage: React.FC = () => { |
| 51 | + const [userList, setUserList] = useState<Users>([]); |
| 52 | + const api = useApi('core'); |
| 53 | + const [opened, { open, close }] = useDisclosure(false); |
| 54 | + // const [showPrevious, { toggle: togglePrevious }] = useDisclosure(false); // Changed default to false |
| 55 | + const [userRemoved, setRemoveUser] = useState<User | null>(null); |
| 56 | + // const navigate = useNavigate(); |
| 57 | + |
| 58 | + const renderTableRow = (user: User) => { |
| 59 | + // const shouldShow = event.upcoming || (!event.upcoming && showPrevious); |
| 60 | + |
| 61 | + return ( |
| 62 | + // <Transition mounted={shouldShow} transition="fade" duration={400} timingFunction="ease"> |
| 63 | + <Transition mounted={true} transition="fade" duration={400} timingFunction="ease"> |
| 64 | + {(styles) => ( |
| 65 | + // <tr style={{ ...styles, display: shouldShow ? 'table-row' : 'none' }}> |
| 66 | + <tr style={{ ...styles, display: 'table-row' }}> |
| 67 | + <Table.Td>{user.netid}</Table.Td> |
| 68 | + <Table.Td>{user.firstName}</Table.Td> |
| 69 | + <Table.Td>{user.middleName}</Table.Td> |
| 70 | + <Table.Td>{user.lastName}</Table.Td> |
| 71 | + <Table.Td>{user.sig}</Table.Td> |
| 72 | + {/* <Table.Td>{dayjs(event.start).format('MMM D YYYY hh:mm')}</Table.Td> |
| 73 | + <Table.Td>{event.end ? dayjs(event.end).format('MMM D YYYY hh:mm') : 'N/A'}</Table.Td> |
| 74 | + <Table.Td>{event.location}</Table.Td> |
| 75 | + <Table.Td>{event.description}</Table.Td> |
| 76 | + <Table.Td>{event.host}</Table.Td> |
| 77 | + <Table.Td>{event.featured ? 'Yes' : 'No'}</Table.Td> */} |
| 78 | + {/* <Table.Td>{capitalizeFirstLetter(event.repeats || 'Never')}</Table.Td> */} |
| 79 | + {/* <Table.Td> |
| 80 | + <ButtonGroup> |
| 81 | + <Button component="a" href={`/events/edit/${event.id}`}> |
| 82 | + Edit |
| 83 | + </Button> |
| 84 | + <Button |
| 85 | + color="red" |
| 86 | + onClick={() => { |
| 87 | + setDeleteCandidate(event); |
| 88 | + open(); |
| 89 | + }} |
| 90 | + > |
| 91 | + Delete |
| 92 | + </Button> |
| 93 | + </ButtonGroup> |
| 94 | + </Table.Td> */} |
| 95 | + </tr> |
| 96 | + )} |
| 97 | + </Transition> |
| 98 | + ); |
| 99 | + }; |
| 100 | + |
| 101 | + useEffect(() => { |
| 102 | + const getUsers = async () => { |
| 103 | + // const response = await api.get('/api/v1/events'); |
| 104 | + // const upcomingEvents = await api.get('/api/v1/events?upcomingOnly=true'); |
| 105 | + // const upcomingEventsSet = new Set(upcomingEvents.data.map((x: EventGetResponse) => x.id)); |
| 106 | + // const events = response.data; |
| 107 | + // events.sort((a: User, b: User) => { |
| 108 | + // return a.start.localeCompare(b.start); |
| 109 | + // }); |
| 110 | + // const enrichedResponse = response.data.map((item: EventGetResponse) => { |
| 111 | + // if (upcomingEventsSet.has(item.id)) { |
| 112 | + // return { ...item, upcoming: true }; |
| 113 | + // } |
| 114 | + // return { ...item, upcoming: false }; |
| 115 | + // }); |
| 116 | + |
| 117 | + // prettier-ignore |
| 118 | + const mockUserResponse: Users = [ |
| 119 | + { netid: "ethanc12", firstName: "Ethan", middleName:"Yuting", lastName: "Chang", sig: "Infra"}, |
| 120 | + { netid: "johnd01", firstName: "John", lastName: "Doe", sig: "SIGMusic" }, |
| 121 | + { netid: "sarahg23", firstName: "Sarah", middleName: "Grace", lastName: "Gonzalez", sig: "SIGQuantum" }, |
| 122 | + { netid: "miker44", firstName: "Michael", lastName: "Roberts", sig: "SIGPlan" }, |
| 123 | + { netid: "annaw02", firstName: "Anna", middleName: "Marie", lastName: "Williams", sig: "SIGMobile" }, |
| 124 | + { netid: "chrisb19", firstName: "Christopher", lastName: "Brown", sig: "SIGCHI" }, |
| 125 | + { netid: "laurenp87", firstName: "Lauren", middleName: "Patricia", lastName: "Perez", sig: "SIGPwny" }, |
| 126 | + { netid: "ethanw12", firstName: "Ethan", lastName: "Wong", sig: "SIGEcom" }, |
| 127 | + { netid: "emilyh54", firstName: "Emily", lastName: "Hernandez", sig: "SIGRobotics" }, |
| 128 | + { netid: "kevink11", firstName: "Kevin", middleName: "Lee", lastName: "Kim", sig: "Infra" }, |
| 129 | + { netid: "juliel08", firstName: "Julie", lastName: "Lopez", sig: "SIGGRAPH" }, |
| 130 | + { netid: "mattt92", firstName: "Matthew", middleName: "Thomas", lastName: "Taylor", sig: "SIGtricity" }, |
| 131 | + { netid: "rachelb03", firstName: "Rachel", lastName: "Bell", sig: "SIGSYS" }, |
| 132 | + { netid: "stephenj45", firstName: "Stephen", middleName: "James", lastName: "Johnson", sig: "SIGAIDA" }, |
| 133 | + { netid: "ashleyc28", firstName: "Ashley", lastName: "Clark", sig: "SIGNLL" }, |
| 134 | + { netid: "briand77", firstName: "Brian", lastName: "Davis", sig: "SIGMA" }, |
| 135 | + { netid: "meganf65", firstName: "Megan", lastName: "Flores", sig: "SIGPolicy" }, |
| 136 | + { netid: "danielh04", firstName: "Daniel", lastName: "Hughes", sig: "SIGARCH" }, |
| 137 | + { netid: "victorc16", firstName: "Victor", middleName: "Charles", lastName: "Carter", sig: "SIGGLUG" }, |
| 138 | + { netid: "lindam29", firstName: "Linda", lastName: "Martinez", sig: "SIGMobile" }, |
| 139 | + { netid: "paulf31", firstName: "Paul", lastName: "Fisher", sig: "SIGMusic" }, |
| 140 | + { netid: "susana80", firstName: "Susan", middleName: "Ann", lastName: "Anderson", sig: "SIGPwny" }, |
| 141 | + { netid: "markl13", firstName: "Mark", lastName: "Lewis", sig: "SIGCHI" }, |
| 142 | + { netid: "carolynb59", firstName: "Carolyn", lastName: "Barnes", sig: "SIGSYS" }, |
| 143 | + { netid: "patrickh37", firstName: "Patrick", middleName: "Henry", lastName: "Hill", sig: "SIGQuantum" }, |
| 144 | + { netid: "nataliep71", firstName: "Natalie", lastName: "Price", sig: "SIGPolicy" }, |
| 145 | + ]; |
| 146 | + setUserList(mockUserResponse); |
| 147 | + }; |
| 148 | + getUsers(); |
| 149 | + }, []); |
| 150 | + |
| 151 | + const removeUser = async (netid: string) => { |
| 152 | + try { |
| 153 | + // await api.delete(`/api/v1/events/${eventId}`); |
| 154 | + setUserList((prevUsers) => prevUsers.filter((u) => u.netid !== netid)); |
| 155 | + notifications.show({ |
| 156 | + title: 'User removed', |
| 157 | + message: 'The user was successfully removed.', |
| 158 | + }); |
| 159 | + close(); |
| 160 | + } catch (error) { |
| 161 | + console.error(error); |
| 162 | + notifications.show({ |
| 163 | + title: 'Error removing user', |
| 164 | + message: `${error}`, |
| 165 | + color: 'red', |
| 166 | + }); |
| 167 | + } |
| 168 | + }; |
| 169 | + |
| 170 | + if (userList.length === 0) { |
| 171 | + return <FullScreenLoader />; |
| 172 | + } |
| 173 | + |
| 174 | + return ( |
| 175 | + // <AuthGuard resourceDef={{ service: 'core', validRoles: [AppRoles.USERS_ADMIN] }}> |
| 176 | + <AuthGuard resourceDef={{ service: 'core', validRoles: [AppRoles.IAM_ADMIN] }}> |
| 177 | + {userRemoved && ( |
| 178 | + <Modal |
| 179 | + opened={opened} |
| 180 | + onClose={() => { |
| 181 | + setRemoveUser(null); |
| 182 | + close(); |
| 183 | + }} |
| 184 | + title="Confirm action" |
| 185 | + > |
| 186 | + <Text> |
| 187 | + Are you sure you want to remove the user <i>{userRemoved?.netid}</i>? |
| 188 | + </Text> |
| 189 | + <hr /> |
| 190 | + <Group> |
| 191 | + <Button |
| 192 | + leftSection={<IconTrash />} |
| 193 | + onClick={() => { |
| 194 | + removeUser(userRemoved?.netid); |
| 195 | + }} |
| 196 | + > |
| 197 | + Delete |
| 198 | + </Button> |
| 199 | + </Group> |
| 200 | + </Modal> |
| 201 | + )} |
| 202 | + {/* <div style={{ display: 'flex', columnGap: '1vw', verticalAlign: 'middle' }}> |
| 203 | + <Button |
| 204 | + leftSection={<IconPlus size={14} />} |
| 205 | + onClick={() => { |
| 206 | + navigate('/events/add'); |
| 207 | + }} |
| 208 | + > |
| 209 | + New Calendar Event |
| 210 | + </Button> |
| 211 | + <Button onClick={togglePrevious}> |
| 212 | + {showPrevious ? 'Hide Previous Events' : 'Show Previous Events'} |
| 213 | + </Button> |
| 214 | + </div> */} |
| 215 | + <Table style={{ tableLayout: 'fixed', width: '100%' }} data-testid="users-table"> |
| 216 | + <Table.Thead> |
| 217 | + <Table.Tr> |
| 218 | + <Table.Th>NetID</Table.Th> |
| 219 | + <Table.Th>First Name</Table.Th> |
| 220 | + <Table.Th>Middle Name</Table.Th> |
| 221 | + <Table.Th>Last Name</Table.Th> |
| 222 | + <Table.Th>Affiliated Special Interest Group</Table.Th> |
| 223 | + </Table.Tr> |
| 224 | + </Table.Thead> |
| 225 | + <Table.Tbody>{userList.map(renderTableRow)}</Table.Tbody> |
| 226 | + </Table> |
| 227 | + </AuthGuard> |
| 228 | + ); |
| 229 | +}; |
0 commit comments