diff --git a/src/components/SideNavigation/UserCard/UserCard.tsx b/src/components/SideNavigation/UserCard/UserCard.tsx
index 2527edb..169736a 100644
--- a/src/components/SideNavigation/UserCard/UserCard.tsx
+++ b/src/components/SideNavigation/UserCard/UserCard.tsx
@@ -4,7 +4,7 @@ import PATHS from 'constants/paths';
import useUser from 'hooks/useUser';
import { useTranslation } from 'next-i18next';
import Link from 'next/link';
-import { MdOutlineAdminPanelSettings } from 'react-icons/md';
+import { MdAdminPanelSettings } from 'react-icons/md';
import styles from './user-card.module.scss';
export default function UserCard() {
@@ -26,12 +26,20 @@ export default function UserCard() {
/>
{user.name}
- {user.is_admin && (
-
-
-
- )}
-
+
+ {user.is_admin && (
+
+
+
+ )}
+
+
);
}
diff --git a/src/components/SideNavigation/UserCard/user-card.module.scss b/src/components/SideNavigation/UserCard/user-card.module.scss
index 7cc49b0..4ad9954 100644
--- a/src/components/SideNavigation/UserCard/user-card.module.scss
+++ b/src/components/SideNavigation/UserCard/user-card.module.scss
@@ -12,17 +12,8 @@
justify-content: space-between;
align-items: center;
- & .user-card {
- display: flex;
- gap: 0.5em;
- align-items: center;
-
- & img {
- border-radius: 50%;
- }
- }
-
- & button {
+ & button,
+ & a {
cursor: pointer;
color: $blue;
display: flex;
@@ -33,3 +24,18 @@
}
}
}
+
+.user-card {
+ display: flex;
+ gap: 0.5em;
+ align-items: center;
+
+ & img {
+ border-radius: 50%;
+ }
+}
+
+.user-controls {
+ display: flex;
+ gap: 0.35em;
+}
diff --git a/src/constants/date.ts b/src/constants/date.ts
new file mode 100644
index 0000000..0f31091
--- /dev/null
+++ b/src/constants/date.ts
@@ -0,0 +1 @@
+export const DATE_FORMAT = 'DD MMM YYYY (HH:mm)';
diff --git a/src/pages/admin.tsx b/src/pages/admin.tsx
index 055d4d0..51df03d 100644
--- a/src/pages/admin.tsx
+++ b/src/pages/admin.tsx
@@ -3,13 +3,13 @@ import clsx from 'clsx';
import Navbar from 'components/Navbar/Navbar';
import PageTransition from 'components/PageTransition';
import RoundedImage from 'components/RoundedImage/RoundedImage';
+import { DATE_FORMAT } from 'constants/date';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import { getServerSideTranslation } from 'i18n';
import getUsers from 'lib/user/getUsers';
import { useTranslation } from 'next-i18next';
import { ReactNode } from 'react';
-import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
import 'react-tabs/style/react-tabs.css';
import styles from 'styles/admin.module.scss';
import { withAuthentication } from 'utils/session';
@@ -19,103 +19,114 @@ dayjs.extend(relativeTime);
type UserExtended = User & { _count: { categories: number; links: number } };
export default function AdminDashboard({ users }: { users: UserExtended[] }) {
- const { t } = useTranslation('common');
+ const { t } = useTranslation();
+ const totalCategories = users.reduce(
+ (acc, user) => (acc = acc + user._count.categories),
+ 0,
+ );
+ const totalLinks = users.reduce(
+ (acc, user) => (acc = acc + user._count.links),
+ 0,
+ );
return (
-
-
- {t('common:users')}
- {t('common:stats')}
-
-
-
-
- {users.length} {t('admin:users')}
-
-
-
-
- | # |
- {t('common:name')} |
- {t('common:email')} |
- {t('common:category.categories')} |
- {t('common:link.links')} |
- {t('admin:role')} |
- {t('admin:created_at')} |
- {t('admin:updated_at')} |
-
-
-
- {users.length !== 0 &&
- users.map(
- ({
- id,
- image,
- name,
- email,
- _count,
- is_admin,
- createdAt,
- updatedAt,
- }) => (
-
- | {id} |
-
- {image && (
-
- )}
- {name ?? '-'}
- |
- {email} |
- {_count.categories} |
- {_count.links} |
-
- {is_admin ? (
-
- {t('admin:admin')}
-
- ) : (
-
- {t('admin:user')}
-
- )}
- |
- {dayjs(createdAt).fromNow()} |
- {dayjs(updatedAt).fromNow()} |
-
- ),
- )}
-
-
-
- {/* // stats */}
-
+
+
+
+
+ #
+ {t('common:name')}
+ {t('common:email')}
+
+ {t('common:category.categories')} ({totalCategories})
+
+
+ {t('common:link.links')} ({totalLinks})
+
+ {t('admin:role')}
+ {t('admin:created_at')}
+ {t('admin:updated_at')}
+
+
+
+ {users.length !== 0 &&
+ users.map((user) => (
+
+ ))}
+
+
+
);
}
-type TableItem = { children: ReactNode; fixed?: boolean };
-function TH({ children, fixed = false }: TableItem) {
+function TableUserRow({ user }: { user: UserExtended }) {
+ const { t } = useTranslation();
+ const { id, image, name, email, _count, is_admin, createdAt, updatedAt } =
+ user;
return (
-
- {children}
- |
+
+ {id}
+
+ {image && (
+
+ )}
+ {name ?? '-'}
+
+ {email}
+ {_count.categories}
+ {_count.links}
+
+ {is_admin ? (
+ {t('admin:admin')}
+ ) : (
+ {t('admin:user')}
+ )}
+
+
+ {dayjs(createdAt).fromNow()}
+ {dayjs(createdAt).format(DATE_FORMAT)}
+
+ {dayjs(updatedAt).fromNow()}
+
);
}
-function TD({ children, fixed = false }: TableItem) {
- return (
-
- {children}
- |
+type TableItem = {
+ children: ReactNode;
+ fixed?: boolean;
+ column?: boolean;
+ type: 'td' | 'th';
+};
+
+function TableCell({
+ children,
+ fixed = false,
+ column = false,
+ type,
+}: TableItem) {
+ const child = (
+
+ {children}
+
);
+ return type === 'td' ? {child} | : {child} | ;
}
export const getServerSideProps = withAuthentication(
diff --git a/src/styles/globals.scss b/src/styles/globals.scss
index b033b55..c80aeb2 100644
--- a/src/styles/globals.scss
+++ b/src/styles/globals.scss
@@ -37,7 +37,7 @@ body {
flex-direction: column;
}
-a {
+a:not(.reset) {
width: fit-content;
color: $blue;
border-bottom: 1px solid transparent;
@@ -51,6 +51,12 @@ a {
}
}
+a.reset {
+ color: $blue;
+ text-decoration: none;
+ transition: 0;
+}
+
h1,
h2,
h3,
diff --git a/src/styles/table.scss b/src/styles/table.scss
index dc71447..cdd7535 100644
--- a/src/styles/table.scss
+++ b/src/styles/table.scss
@@ -4,16 +4,21 @@ table {
height: auto;
width: 100%;
border-collapse: collapse;
- width: 100%;
+}
+
+th {
+ font-weight: 400;
+ background-color: $lightest-blue;
}
td,
th {
- padding: 1em;
+ padding: 0.45em;
}
-th {
- background-color: $lightest-blue;
+th,
+td {
+ white-space: nowrap;
}
tr:nth-child(even) {
@@ -24,9 +29,14 @@ table .cell {
width: 100%;
display: flex;
align-items: center;
- gap: 0.25em;
+ gap: 0.35em;
&:not(.fixed) {
justify-content: center;
}
+
+ &.column {
+ gap: 0;
+ flex-direction: column;
+ }
}