Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jolin/admin dashboard #67

Merged
merged 28 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a5e3fbd
initialize admin dashbaord structure
jche521 Sep 4, 2024
d0a282f
styling of table with round corners
jche521 Sep 4, 2024
83d747a
style the table and sizing
jche521 Sep 4, 2024
d512a5b
tidy up code
jche521 Sep 4, 2024
4067f20
extract table to a component
jche521 Sep 4, 2024
1e100be
add filter component
jche521 Sep 4, 2024
c90375e
layout of admin wither filters
jche521 Sep 4, 2024
7fe2201
attempt to make filter working
jche521 Sep 4, 2024
05d4d67
fix status filtering
jche521 Sep 4, 2024
4d69574
fix table not rerendering
jche521 Sep 4, 2024
9134065
Merge branch 'main' into jolin/admin-dashboard
Kinzi-c Sep 7, 2024
21c3f88
fix responsive issue with admin dashboard
jche521 Sep 9, 2024
7c56777
remove unused code and tidy up
jche521 Sep 9, 2024
58688bb
Merge branch 'jolin/admin-dashboard' of https://github.com/UoaWDCC/fs…
Kinzi-c Sep 11, 2024
849b69b
migrate to use datatable
jche521 Sep 12, 2024
e54468c
Merge branch 'jolin/admin-dashboard' of https://github.com/UoaWDCC/fs…
jche521 Sep 12, 2024
cfefad4
convert bg to black with on hover to grey
jche521 Sep 13, 2024
b6d9c1c
admin dashboard with pagination
jche521 Sep 13, 2024
fdb35f4
Revert "admin dashboard with pagination"
jche521 Sep 14, 2024
bb2df19
Revert "convert bg to black with on hover to grey"
jche521 Sep 14, 2024
4b455ff
Revert "migrate to use datatable"
jche521 Sep 14, 2024
22e398b
pagination done
jche521 Sep 14, 2024
66d051b
Merge branch 'jolin/admin-dashboard' of https://github.com/UoaWDCC/fs…
Kinzi-c Sep 14, 2024
f99117c
Make boards titles consistent
Kinzi-c Sep 14, 2024
26e20c3
Merge branch 'main' into jolin/admin-dashboard (yarn lock brokn)
jche521 Sep 15, 2024
5e4502f
remove yarn lock (no need since we are using npm) abd duplicate function
jche521 Sep 15, 2024
47cd523
Merge latest main
Kinzi-c Sep 28, 2024
5eab397
Fix role.ts conflicts
Kinzi-c Sep 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions web/src/app/components/AdminDashboard/AdminDashboard.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* table */
.tableContainer {
background-color: black;
width: 90%;
max-width: 1000px;
}

.table {
border-collapse: separate;
border-spacing: 0 1rem;
background-color: black;
}

.tableHeader {
border: none;
background-color: black;
border-bottom: 1px solid white;
padding: 1rem;
}

.tableRow {
background-color: var(--mantine-color-customCharcoalGrey-1);
color: white;
}

.leftRoundedCell {
padding: 1rem;
border-top-left-radius: 0.5rem;
border-bottom-left-radius: 0.5rem;
}

.rightRoundedCell {
padding: 1rem;
border-top-right-radius: 0.5rem;
border-bottom-right-radius: 0.5rem;
}

.pagination {
background: transparent;
color: white;
margin-top: 2rem;
button {
background: transparent;
color: white;
border-color: transparent;
width: 2.5rem;
height: 2.5rem;
font-size: 1.2rem;
&[data-active='true'] {
color: white;
background: var(--mantine-color-customDarkBlue-1);
}
}
button:hover {
background-color: var(--mantine-color-customAzureBlue-1);
}
}
67 changes: 67 additions & 0 deletions web/src/app/components/AdminDashboard/AdminDashboardTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { Group, Pagination, Stack, Table, Text } from '@mantine/core';
import styles from './AdminDashboard.module.css';
import { AdminReview } from '@/app/models/adminReview';
import { FC, useState } from 'react';
import { date2string } from '@/app/features/date/dateConverter';
import { ceil } from 'lodash';

interface Props {
data: AdminReview[];
}
const AdminDashboardTable: FC<Props> = ({ data }) => {
const [page, onChange] = useState(1);
const entriesPerPage = 6;

const totalPages = ceil(data.length / entriesPerPage);

// Calculate the starting and ending index for the current page
const startIdx = (page - 1) * entriesPerPage;
const endIdx = startIdx + entriesPerPage;
const currentPageData = data.slice(startIdx, endIdx);

return (
<Stack justify="center" align="center" gap="md" mt="md">
<div className={styles.tableContainer}>
<Table.ScrollContainer minWidth={500} h={500}>
<Table className={styles.table} stickyHeader stickyHeaderOffset={0}>
<Table.Thead>
<Table.Tr>
<Table.Th className={styles.tableHeader}>Name</Table.Th>
<Table.Th className={styles.tableHeader}>User Type</Table.Th>
<Table.Th className={styles.tableHeader}>Date</Table.Th>
<Table.Th className={styles.tableHeader}>Status</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{currentPageData.map((review) => (
<Table.Tr key={review.id} className={styles.tableRow}>
<Table.Td className={styles.leftRoundedCell}>{review.name}</Table.Td>
<Table.Td>{review.userType}</Table.Td>
<Table.Td>{date2string(review.date)}</Table.Td>
<Table.Td className={styles.rightRoundedCell}>{review.status}</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Table.ScrollContainer>
<Pagination.Root
total={totalPages}
value={page}
onChange={onChange}
className={styles.pagination}
mt="xl"
>
<Group gap={20} justify="center">
<Pagination.First />
<Pagination.Previous />
<Pagination.Items />
<Pagination.Next />
<Pagination.Last />
</Group>
</Pagination.Root>
</div>
</Stack>
);
};

export default AdminDashboardTable;
163 changes: 163 additions & 0 deletions web/src/app/components/Filter/AdminFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { Checkbox, Title, Button, Stack, Modal, Flex } from '@mantine/core';
import styles from './Filter.module.css';
import { FC, useState } from 'react';
import { IconArrowDown } from '@tabler/icons-react';
import { Role, roleToString, stringToRole, stringsToRoles } from '@/app/type/role';
import { Status, statusToString, stringsToStatuses } from '@/app/type/status';

interface Props {
filterUserTypes: Role[];
setFilterUserTypes: (filterRoles: Role[]) => void;
filterStatus: Status[];
setFilterStatus: (filterFields: Status[]) => void;
}

const AdminFilter: FC<Props> = ({
filterUserTypes,
setFilterUserTypes,
filterStatus,
setFilterStatus,
}) => {
const roles = [
{ label: 'Sponsor', value: Role.Sponsor },
{ label: 'Student', value: Role.Student },
{ label: 'Alumni', value: Role.Alumni },
// Add other roles as needed
];

const statusOptions = [
{ value: Status.Pending, label: 'Pending' },
{ value: Status.Approved, label: 'Approved' },
{ value: Status.Rejected, label: 'Rejected' },
];

const [isModalOpen, setIsModalOpen] = useState(false);
const [isPortrait, setIsPortrait] = useState(window.innerHeight > window.innerWidth);

const openModal = () => setIsModalOpen(true);
const closeModal = () => setIsModalOpen(false);

return (
<>
{!isPortrait && (
<Stack mt={70} pl={30}>
<Title fs="italic" className={styles.filterHeading}>
Filters
</Title>

<Stack>
<Checkbox.Group
value={filterUserTypes.map((role) => stringToRole(role))} // Convert enums to strings
onChange={(selectedValues: string[]) => {
setFilterUserTypes(stringsToRoles(selectedValues)); // Convert strings back to RoleType
}}
label="User Type"
labelProps={{ style: { color: 'customAzureBlue.1' } }}
classNames={{ label: styles.filterSubheading }}
>
{roles.map((role) => (
<Checkbox
key={role.value}
value={role.value.toString()}
label={role.label}
color="customAzureBlue.1"
className={styles.checkbox}
size="md"
/>
))}
</Checkbox.Group>
</Stack>

<Stack>
<Checkbox.Group
value={filterStatus.map((status) => statusToString(status))} // Convert enums to strings
onChange={(selectedValues: string[]) => {
setFilterStatus(stringsToStatuses(selectedValues));
}}
label="Status"
labelProps={{ style: { color: 'customAzureBlue.1' } }}
classNames={{ label: styles.filterSubheading }}
>
{statusOptions.map((status) => (
<Checkbox
key={status.value}
value={status.value as unknown as string} // Cast enum values to strings
label={status.label}
color="customAzureBlue.1"
className={styles.checkbox}
size="md"
/>
))}
</Checkbox.Group>
</Stack>
</Stack>
)}
{isPortrait && (
<>
<Flex justify="flex-end">
<Button
rightSection={<IconArrowDown size={14} />}
variant="transparent"
size="lg"
onClick={openModal}
>
Filter
</Button>
</Flex>
<Modal
opened={isModalOpen}
onClose={closeModal}
centered
classNames={{ content: styles.modal, header: styles.modalHeader }}
>
<Stack>
<Checkbox.Group
value={filterUserTypes.map((role) => roleToString(role))} // Convert enums to strings
onChange={(selectedValues: string[]) => {
setFilterUserTypes(stringsToRoles(selectedValues)); // Convert strings back to RoleType
}}
label="User Type"
labelProps={{ style: { color: 'customAzureBlue.1' } }}
classNames={{ label: styles.filterSubheading }}
>
{roles.map((role) => (
<Checkbox
key={role.value}
value={role.value.toString()}
label={role.label}
color="customAzureBlue.1"
className={styles.checkbox}
size="md"
/>
))}
</Checkbox.Group>
</Stack>
<Stack>
<Checkbox.Group
value={filterStatus.map((status) => statusToString(status))} // Convert enums to strings
onChange={(selectedValues: string[]) => {
setFilterStatus(stringsToStatuses(selectedValues));
}}
label="Status"
classNames={{ label: styles.filterSubheading }}
>
{statusOptions.map((status) => (
<Checkbox
key={status.value}
value={status.value.toString()}
label={status.label}
color="customAzureBlue.1"
className={styles.checkbox}
size="md"
/>
))}
</Checkbox.Group>
</Stack>
</Modal>
</>
)}
</>
);
};

export default AdminFilter;
8 changes: 4 additions & 4 deletions web/src/app/components/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ function Navbar() {
<Flex
className={styles.Navbar}
gap="md"
pt="md"
pr="md"
pl="md"
pb="md"
pt="lg"
pr="lg"
pl="lg"
pb="lg"
justify="space-between"
align="center"
>
Expand Down
2 changes: 1 addition & 1 deletion web/src/app/components/SearchBar/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const SearchBar: FC<SearchBarProps> = ({ search, setSearch, title, placeholder }
const [isPortrait, setIsPortrait] = useState(window.innerHeight > window.innerWidth);

return (
<Grid mt={70} mb="xs">
<Grid mt={90} mb="xs">
{!isPortrait ? (
<>
<Grid.Col pl={30} span={6}>
Expand Down
5 changes: 5 additions & 0 deletions web/src/app/features/date/dateConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function date2string(date: Date): string {
const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0'); // getMonth() returns 0-indexed month
return `${day}/${month}`;
}
10 changes: 10 additions & 0 deletions web/src/app/models/adminReview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { UserType } from '../features/user/userSlice';
import { Status } from '../type/status';

export interface AdminReview {
id: string;
name: string;
userType: UserType;
date: Date;
status: Status;
}
Loading
Loading