= ({ updated }) => (
+ | {new Date(updated).toLocaleDateString()} |
+);
+
+type ActionsCellPropsType = {
+ onEdit: () => void;
+ onDelete: () => void;
+};
+export const ActionsCell: React.FC = ({ onEdit, onDelete }) => (
+
+
+ |
+);
diff --git a/src/components/Tables/CategoryTableList/index.tsx b/src/components/Tables/CategoryTableList/index.tsx
new file mode 100644
index 0000000..5d6e569
--- /dev/null
+++ b/src/components/Tables/CategoryTableList/index.tsx
@@ -0,0 +1,4 @@
+import CategoryTableList from './CategoryTableList';
+import CategoryTableRow from './CategoryTableRow';
+
+export { CategoryTableList, CategoryTableRow };
diff --git a/src/config.ts b/src/config.ts
index 99c03cf..cf64b60 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -6,6 +6,7 @@ import {
MdOutlineVideoLibrary,
MdOutlineGroup,
MdOutlineAccountCircle,
+ MdOutlineCategory,
} from 'react-icons/md';
import { IAppConfig } from './interfaces';
import packageJson from '../package.json';
@@ -33,6 +34,13 @@ const config: IAppConfig = {
icon: MdOutlineVideoLibrary,
roles: [UserRoleEnum.ADMIN, UserRoleEnum.TEACHER],
},
+ {
+ location: '/categories',
+ title: 'Categories',
+ public: false,
+ roles: [UserRoleEnum.ADMIN, UserRoleEnum.TEACHER],
+ icon: MdOutlineCategory,
+ },
{ location: '/messages', title: 'Messages', public: true, icon: MdOutlineMessage },
{ location: '/profile', title: 'Profile', public: true, icon: MdOutlineAccountCircle },
{
diff --git a/src/hooks/queries/category/useFetchCategories.ts b/src/hooks/queries/category/useFetchCategories.ts
new file mode 100644
index 0000000..38967ba
--- /dev/null
+++ b/src/hooks/queries/category/useFetchCategories.ts
@@ -0,0 +1,29 @@
+import useAsync from '@educt/hooks/useAsync';
+import { ICategory } from '@educt/interfaces';
+import { CategoryServiceInstance } from '@educt/services';
+import { useEffect } from 'react';
+import { useErrorHandler } from 'react-error-boundary';
+
+const useFetchCategories = () => {
+ const handleError = useErrorHandler();
+
+ const fetchCategories = async () => {
+ try {
+ const result = await CategoryServiceInstance.fetchAll();
+ return result.data;
+ } catch (error) {
+ handleError(error);
+ return Promise.reject(error);
+ }
+ };
+
+ const { execute: fetch, ...state } = useAsync>(fetchCategories);
+
+ useEffect(() => {
+ fetch();
+ }, []);
+
+ return { ...state };
+};
+
+export { useFetchCategories };
diff --git a/src/hooks/queries/index.ts b/src/hooks/queries/index.ts
index f120192..29fba6e 100644
--- a/src/hooks/queries/index.ts
+++ b/src/hooks/queries/index.ts
@@ -29,6 +29,11 @@ import { useDeleteUser } from './user/useDeleteUser';
import { useUpdateUser } from './user/useUpdateUser';
import { useUpdateUserEmail } from './user/useUpdateUserEmail';
+/**
+ * Category
+ */
+import { useFetchCategories } from './category/useFetchCategories';
+
export {
useLogin,
useLogout,
@@ -45,4 +50,5 @@ export {
useDeleteUser,
useUpdateUser,
useUpdateUserEmail,
+ useFetchCategories,
};
diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts
index 91675ab..45f73d7 100644
--- a/src/interfaces/index.ts
+++ b/src/interfaces/index.ts
@@ -75,6 +75,8 @@ export interface ICategory {
title: string;
description: string;
color?: ColorType | undefined | null;
+ created_at: string;
+ updated_at: string;
}
/**
diff --git a/src/pages/categories/categories.tsx b/src/pages/categories/categories.tsx
new file mode 100644
index 0000000..676047c
--- /dev/null
+++ b/src/pages/categories/categories.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { IPageProps } from '@educt/interfaces';
+import { PageContent, PageHeading, PageWrapper } from '@educt/components/PageElements';
+import { CategoryTableList, CategoryTableRow } from '@educt/components/Tables/CategoryTableList';
+import { useFetchCategories } from '@educt/hooks/queries';
+import LoadingPage from '@educt/components/LoadingPage';
+
+/**
+ * Categories page
+ */
+const CategoriesPage: React.FC = () => {
+ const { isLoading, data: categories } = useFetchCategories();
+
+ if (isLoading || categories === null) return ;
+
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default CategoriesPage;
diff --git a/src/pages/courses/components/CourseList/CourseList.tsx b/src/pages/courses/components/CourseList/CourseList.tsx
index 3151dab..85a172a 100644
--- a/src/pages/courses/components/CourseList/CourseList.tsx
+++ b/src/pages/courses/components/CourseList/CourseList.tsx
@@ -17,7 +17,7 @@ import LoadingList from '@educt/components/LoadingList';
/**
* Hooks
*/
-import { useContext, useEffect } from 'react';
+import { useEffect } from 'react';
import { useSetCourseStatus } from '@educt/hooks/queries';
import { useRootStore } from '@educt/hooks/useRootStore';
import { useDisclosure } from '@chakra-ui/hooks';
@@ -25,7 +25,6 @@ import { useDisclosure } from '@chakra-ui/hooks';
/**
* Contexts
*/
-import { CoursesPageContext } from '@educt/contexts';
import { ICourse } from '@educt/interfaces';
type CourseListPropsType = {
@@ -41,7 +40,7 @@ const CourseList: React.FC = ({ render: Item }) => {
const [deleting, setDeleting] = useState | null>(null);
const { onOpen: onOpenDeleteDialog, onClose: onCloseDeleteDialog, isOpen: isOpenDeleteDialog } = useDisclosure();
const { setCourseStatus } = useSetCourseStatus();
- const { courses, isLoading, loadCourses } = courseStore;
+ const { courses, isLoading } = courseStore;
const handleDeleteCourse = (course: Pick) => {
setDeleting(course);
diff --git a/src/pages/courses/edit/components/index.tsx b/src/pages/courses/edit/components/index.tsx
index 594ca5c..ba99758 100644
--- a/src/pages/courses/edit/components/index.tsx
+++ b/src/pages/courses/edit/components/index.tsx
@@ -24,7 +24,7 @@ export const StatusButton: React.FC = props => {
setStatus(newStatus);
};
- const Components: { [k in keyof typeof CourseStatusEnum]: any } = {
+ const Components: { [k in keyof typeof CourseStatusEnum]: React.ReactElement } = {
PUBLISHED: (