diff --git a/.eslintrc.json b/.eslintrc.json
index bffb357a7122523ec94045523758c4b825b448ef..09937b6bc985db1393bd7b0a1e713e700a97ced4 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,3 +1,6 @@
 {
-  "extends": "next/core-web-vitals"
+  "extends": "next/core-web-vitals",
+  "rules": {
+    "react-hooks/exhaustive-deps": "off"
+  }
 }
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b899124e174846f40a805bddba0aebde03c75510..56e123401195c3eb3ac4f025f00fc684346b0551 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -17,6 +17,7 @@ deploy:
     - echo "$DO_ACCESS_TOKEN" | docker login registry.digitalocean.com --username $DO_ACCESS_TOKEN --password-stdin
   script:
     - echo "NEXT_PUBLIC_API_URL=$BACKEND_API" > .env.local
+    - echo "NEXT_PUBLIC_BUCKET_URL=$BUCKET_ENDPOINT" >> .env.local
     - docker build -t registry.digitalocean.com/ocw-container/ocw-frontend:latest -t registry.digitalocean.com/ocw-container/ocw-frontend:$CI_COMMIT_TAG .
     - docker push registry.digitalocean.com/ocw-container/ocw-frontend:$CI_COMMIT_TAG
     - docker push registry.digitalocean.com/ocw-container/ocw-frontend:latest
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..55712c19f1dffd66d4fed7a406a354f215c43a14
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+    "typescript.tsdk": "node_modules/typescript/lib"
+}
\ No newline at end of file
diff --git a/cypress.config.ts b/cypress.config.ts
index a5b268ac66008f7ab4d8ce117d8dfe07baea4eed..042a59c313d9631bccbdc3ee577bf2959d979169 100644
--- a/cypress.config.ts
+++ b/cypress.config.ts
@@ -1,6 +1,7 @@
 import { defineConfig } from 'cypress';
 
 export default defineConfig({
+  projectId: 'd434om',
   env: {
     NEXT_PUBLIC_API_URL: 'http://localhost:8888',
   },
diff --git a/src/components/course_banner.tsx b/src/components/course_banner.tsx
index f3a77d8c26df13f2e0d0aca9cd0a721fc5e8d97f..d70b8a300c90d1a4248725fe6f8d7eb680f7e20e 100644
--- a/src/components/course_banner.tsx
+++ b/src/components/course_banner.tsx
@@ -1,11 +1,12 @@
 import {
-  HStack,
-  VStack,
   Box,
+  BoxProps,
   Container,
-  Text,
   Divider,
-  BoxProps,
+  HStack,
+  Skeleton,
+  Text,
+  VStack,
 } from '@chakra-ui/react';
 
 export interface BannerProps extends BoxProps {
@@ -35,24 +36,28 @@ const CourseBanner: React.FC<BannerProps> = ({
       >
         {children}
       </Box>
-      <Container minHeight="90vh" bgColor="biru.500" width="50vh" padding={10}>
+      <Container minHeight="90vh" bgColor="biru.500" width="50vh" padding={10} display={{ base: 'none', lg: 'flex' }}>
         <VStack align={'start'} spacing="6" marginStart="2vh" maxWidth="30vh">
           <VStack spacing="1" align={'start'}>
             <Text align={'start'} fontSize={'m'}>
               Kode Mata Kuliah
             </Text>
-            <Text align={'start'} fontSize={'xl'} wordBreak="break-word">
-              {course_code}
-            </Text>
+            <Skeleton isLoaded={course_code != null}>
+              <Text align={'start'} fontSize={'xl'} wordBreak="break-word">
+                {course_code}
+              </Text>
+            </Skeleton>
           </VStack>
           <Divider borderColor={'black'} borderWidth={'1px'} w="75%" my={4} />
           <VStack spacing="1" align={'start'}>
             <Text align={'start'} fontSize={'m'}>
               Mata Kuliah
             </Text>
-            <Text align={'start'} fontSize={'xl'} wordBreak="break-word">
-              {course_name}
-            </Text>
+            <Skeleton isLoaded={course_name != null}>
+              <Text align={'start'} fontSize={'xl'} wordBreak="break-word">
+                {course_name}
+              </Text>
+            </Skeleton>
           </VStack>
           <Divider borderColor={'black'} borderWidth={'1px'} w="75%" my={4} />
           <VStack spacing="1" align={'start'}>
@@ -60,7 +65,7 @@ const CourseBanner: React.FC<BannerProps> = ({
               Dosen Pengajar
             </Text>
             <Text align={'start'} fontSize={'xl'} wordBreak="break-word">
-              {lecturer}
+              {lecturer ?? '-'}
             </Text>
           </VStack>
         </VStack>
diff --git a/src/components/course_card.tsx b/src/components/course_card.tsx
index a7657e2e7a16fb7c2225fb0412e42a44c4698b9b..ebc6098354965f42f08a5ae90b0210c3f94792ef 100644
--- a/src/components/course_card.tsx
+++ b/src/components/course_card.tsx
@@ -36,7 +36,10 @@ export default function CourseCard({
       >
         <Image
           alt="Course Thumbnail"
-          src={thumbnail ?? 'https://via.placeholder.com/150x100'}
+          src={
+            thumbnail ??
+            'https://images.unsplash.com/photo-1495465798138-718f86d1a4bc?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&q=80'
+          }
           borderRadius="inherit"
         />
         <Box mt={{ base: 2, lg: 5 }} mb={2}>
diff --git a/src/components/management/content/add-content-modal.tsx b/src/components/management/content/add-content-modal.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..0807eea812bcb1706bbea785b427f5cc76686409
--- /dev/null
+++ b/src/components/management/content/add-content-modal.tsx
@@ -0,0 +1,82 @@
+import Modal from '@/components/modal';
+import { Major } from '@/types/major';
+import {
+  FormControl,
+  FormLabel,
+  Input,
+  Select,
+  Stack,
+  Textarea,
+} from '@chakra-ui/react';
+
+export interface AddContentModalProps {
+  isOpen: boolean;
+  onClose: () => void;
+  handleConfirm: () => void;
+  type: string;
+  setType: (name: string) => void;
+  link: string;
+  setLink: (link: string) => void;
+  file: File | undefined;
+  setFile: (file: File | undefined) => void;
+}
+
+const AddContentModal = ({
+  isOpen,
+  onClose,
+  handleConfirm,
+  type,
+  setType,
+  link,
+  setLink,
+  file,
+  setFile,
+}: AddContentModalProps) => {
+  const uploadToClient = (e: any) => {
+    if(e.target.files && e.target.files[0]){
+      const i = e.target.files[0];
+
+      setFile(i);
+    }
+  }
+  return (
+    <Modal
+      isOpen={isOpen}
+      onClose={onClose}
+      onConfirm={handleConfirm}
+      header="Menambahkan Materi Baru"
+    >
+      <Stack>
+        <FormControl isRequired>
+          <FormLabel>Tipe Materi</FormLabel>
+          <Select onChange={(e) => {setType(e.target.value)}}>
+            <option value="handout">File</option>
+            <option value="video">Video</option>
+          </Select>
+        </FormControl>
+        {
+          type == 'video' ?(
+            <FormControl>
+              <FormLabel>Link</FormLabel>
+              <Input
+                name="Link"
+                onChange={(e) => setLink(e.target.value)}
+              />
+            </FormControl>
+          ) : (
+            <FormControl>
+              <FormLabel>File</FormLabel>
+              <Input
+                name="File"
+                type="file"
+                onChange={(e) => uploadToClient(e)}
+              />
+            </FormControl>
+          )
+        }
+      </Stack>
+    </Modal>
+  );
+}
+
+export default AddContentModal;
\ No newline at end of file
diff --git a/src/components/management/content/delete-content-modal.tsx b/src/components/management/content/delete-content-modal.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..29c8c6964d66a47ffe7cea15c60fcd8b7fd5ac0e
--- /dev/null
+++ b/src/components/management/content/delete-content-modal.tsx
@@ -0,0 +1,25 @@
+import Modal from '@/components/modal';
+import { Text } from '@chakra-ui/react';
+
+export interface DeleteMaterialModalProps {
+  isOpen: boolean;
+  onClose: () => void;
+  handleConfirm: () => void;
+}
+
+export default function DeleteMaterialModal({
+  isOpen,
+  onClose,
+  handleConfirm,
+}: DeleteMaterialModalProps) {
+  return (
+    <Modal
+      isOpen={isOpen}
+      onClose={onClose}
+      onConfirm={handleConfirm}
+      header="Hapus Konten"
+    >
+      <Text>Apakah anda yakin ingin menghapus konten ini?</Text>
+    </Modal>
+  );
+}
diff --git a/src/components/management/course/quiz/problem-item.tsx b/src/components/management/course/quiz/problem-item.tsx
index 1254ce01f5e790d6374fe411ea503e144ab1f112..dc5aaa502c5e25123c54bb0e77e3f348ba41d328 100644
--- a/src/components/management/course/quiz/problem-item.tsx
+++ b/src/components/management/course/quiz/problem-item.tsx
@@ -31,7 +31,7 @@ export default function ProblemItem({
   problems,
   setProblems,
 }: ProblemItemProps) {
-  const [answers, setAnswers] = useState<AnswerOption[]>(problem.answers);
+  const [answers, setAnswers] = useState<AnswerOption[]>(problem.answers ?? []);
 
   const handleChangeQuestion = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
     setProblems(
diff --git a/src/components/management/material/add-material-modal.tsx b/src/components/management/material/add-material-modal.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..c716a1790d77d7ec4d059974d775b6aaff6e4b34
--- /dev/null
+++ b/src/components/management/material/add-material-modal.tsx
@@ -0,0 +1,56 @@
+import Modal from '@/components/modal';
+import { Major } from '@/types/major';
+import {
+  FormControl,
+  FormLabel,
+  Input,
+  Select,
+  Stack,
+  Textarea,
+} from '@chakra-ui/react';
+
+export interface AddMaterialModalProps {
+  isOpen: boolean;
+  onClose: () => void;
+  handleConfirm: () => void;
+  name: string;
+  setName: (name: string) => void;
+  week: number;
+  setWeek: (week: number) => void;
+}
+
+const AddMaterialModal = ({
+  isOpen,
+  onClose,
+  handleConfirm,
+  name,
+  setName,
+  week,
+  setWeek
+}: AddMaterialModalProps) => {
+  return (
+    <Modal
+      isOpen={isOpen}
+      onClose={onClose}
+      onConfirm={handleConfirm}
+      header="Menambahkan Materi Baru"
+    >
+      <Stack>
+        <FormControl isRequired>
+          <FormLabel>Judul Materi</FormLabel>
+          <Input name="name" onChange={(e) => setName(e.target.value)}/>
+        </FormControl>
+        <FormControl>
+          <FormLabel>Week</FormLabel>
+          <Input
+            name="Week"
+            type="number"
+            onChange={(e) => setWeek(+e.target.value)}
+          />
+        </FormControl>
+      </Stack>
+    </Modal>
+  );
+}
+
+export default AddMaterialModal;
\ No newline at end of file
diff --git a/src/components/management/material/delete-material-modal.tsx b/src/components/management/material/delete-material-modal.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..41b25fcbfa63261cfa983be0cf7e858b16abbe95
--- /dev/null
+++ b/src/components/management/material/delete-material-modal.tsx
@@ -0,0 +1,25 @@
+import Modal from '@/components/modal';
+import { Text } from '@chakra-ui/react';
+
+export interface DeleteMaterialModalProps {
+  isOpen: boolean;
+  onClose: () => void;
+  handleConfirm: () => void;
+}
+
+export default function DeleteMaterialModal({
+  isOpen,
+  onClose,
+  handleConfirm,
+}: DeleteMaterialModalProps) {
+  return (
+    <Modal
+      isOpen={isOpen}
+      onClose={onClose}
+      onConfirm={handleConfirm}
+      header="Hapus Materi"
+    >
+      <Text>Apakah anda yakin ingin menghapus materi ini?</Text>
+    </Modal>
+  );
+}
diff --git a/src/components/management/material/edit-material-modal.tsx b/src/components/management/material/edit-material-modal.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..5dfa51646b79a7eb07a2d1270e96023b8d2e2ed7
--- /dev/null
+++ b/src/components/management/material/edit-material-modal.tsx
@@ -0,0 +1,118 @@
+import Modal from '@/components/modal';
+import http from '@/lib/http';
+import { Major } from '@/types/major';
+import {
+  FormControl,
+  FormLabel,
+  Input,
+  Select,
+  Stack,
+  TableContainer,
+	Table,
+	Thead,
+	Tbody,
+  Text,
+	Tr,
+	Th,
+	Td,
+	Button,
+} from '@chakra-ui/react';
+import { useEffect, useState } from 'react';
+import Link from 'next/link';
+import { MdDelete } from 'react-icons/md';
+
+export interface EditMaterialModalProps {
+	materialID: string;
+  isOpen: boolean;
+  onClose: () => void;
+  handleConfirm: () => void;
+  name: string;
+  week: number;
+	contents: Content[];
+}
+
+export interface Content {
+	id : string,
+	type : string
+}
+
+const EditMaterialModal = ({
+	materialID,
+  isOpen,
+  onClose,
+  handleConfirm,
+  name,
+  week,
+	contents
+}: EditMaterialModalProps) => {
+	console.log(contents);
+  return (
+    <Modal
+      isOpen={isOpen}
+      onClose={onClose}
+      onConfirm={handleConfirm}
+      header="Menambahkan Materi Baru"
+    >
+      <Stack>
+        <FormControl isRequired>
+          <FormLabel>Judul Materi</FormLabel>
+          <Input 
+						variant="filled" 
+						name="name" 
+						value={name}
+						isDisabled 
+					/>
+        </FormControl>
+        <FormControl>
+          <FormLabel>Week</FormLabel>
+          <Input
+            name="Week"
+            type="number"
+            variant="filled"
+						value={week}
+            isDisabled
+          />
+        </FormControl>
+				<Button>
+					<Text>
+						Add Content
+					</Text>
+				</Button>
+				{
+					contents.length > 0 ? (
+						<TableContainer>
+							<Table variant="simple" bg="white" borderRadius={'lg'} mt={5}>
+								<Thead textTransform="capitalize">
+									<Tr>
+										<Th>Content Type</Th>
+										<Th>Action</Th>
+									</Tr>
+								</Thead>
+								<Tbody>
+									{contents.map((m: Content) => (
+										<Tr key={m.id}>
+											<Td>{m.type}</Td>
+											<Td>
+												<Button size="sm" colorScheme="red">
+      									  <MdDelete />
+      									  <Text ml={2} display={{ base: 'none', lg: 'flex' }}>
+      									    Hapus
+      									  </Text>
+      									</Button>
+											</Td>
+										</Tr>
+									))
+									}
+								</Tbody>
+							</Table>
+						</TableContainer>
+					) : (
+						<Text>No Content</Text>
+					)
+				}
+      </Stack>
+    </Modal>
+  );
+}
+
+export default EditMaterialModal;
\ No newline at end of file
diff --git a/src/components/select_search.tsx b/src/components/select_search.tsx
index 1565893686873e203dcb1b5d995efd1be5928a80..872eeedd674e97c68d657e79a7995be9ad238ed8 100644
--- a/src/components/select_search.tsx
+++ b/src/components/select_search.tsx
@@ -11,6 +11,7 @@ import {
   MenuButton,
   MenuItem,
   MenuList,
+  MenuOptionGroup,
 } from '@chakra-ui/react';
 import Link from 'next/link';
 import { useEffect, useState } from 'react';
@@ -62,26 +63,24 @@ export function SelectSearch() {
         </MenuButton>
         <MenuList>
           {/* TODO: list all major and faculty */}
-          <MenuItem display="flex" w="50rem" isDisabled>
-            Fakultas
-          </MenuItem>
-          {faculty.map((faculty: Faculty) => (
-            <Link href={`/courses/faculty/${faculty.id}`} key={faculty.id}>
-              <MenuItem display="flex" w="50rem">
-                {faculty.abbreviation} - {faculty.name}
-              </MenuItem>
-            </Link>
-          ))}
-          <MenuItem display="flex" w="50rem" isDisabled>
-            Jurusan
-          </MenuItem>
-          {majors.map((major: Major) => (
-            <Link href={`/courses/major/${major.id}`} key={major.id}>
-              <MenuItem display="flex" w="50rem">
-                {major.abbreviation} - {major.name}
-              </MenuItem>
-            </Link>
-          ))}
+          <MenuOptionGroup title="Fakultas">
+            {faculty.map((faculty: Faculty) => (
+              <Link href={`/courses/faculty/${faculty.id}`} key={faculty.id}>
+                <MenuItem display="flex" w="50rem">
+                  {faculty.abbreviation} - {faculty.name}
+                </MenuItem>
+              </Link>
+            ))}
+          </MenuOptionGroup>
+          <MenuOptionGroup title="Jurusan">
+            {majors.map((major: Major) => (
+              <Link href={`/courses/major/${major.id}`} key={major.id}>
+                <MenuItem display="flex" w="50rem">
+                  {major.abbreviation} - {major.name}
+                </MenuItem>
+              </Link>
+            ))}
+          </MenuOptionGroup>
         </MenuList>
       </Menu>
     </Flex>
diff --git a/src/lib/http.ts b/src/lib/http.ts
index d1b7ec4a816f22a0c120ef06974e90aa55a932fe..8b0a96d2e1b7460f4c009f79d58652229d9c06b2 100644
--- a/src/lib/http.ts
+++ b/src/lib/http.ts
@@ -29,21 +29,24 @@ http.interceptors.response.use(
   async (err) => {
     if (axios.isAxiosError(err)) {
       const config = err.config as any;
+      console.log(err.response?.status == HttpResponse.BadRequest);
       if (
         (err.response?.status == HttpResponse.Unauthorized ||
-          err.response?.status == HttpResponse.UnprocessableEntity) &&
-        config.url === '/auth/refresh'
-      ) {
+          err.response?.status == HttpResponse.UnprocessableEntity ||
+          err.response?.status == HttpResponse.BadRequest) &&
+          config.url === '/auth/refresh'
+          ) {
         unsetToken();
       }
 
       if (
-        err.response?.status == HttpResponse.Unauthorized &&
+        (err.response?.status == HttpResponse.Unauthorized ||
+         err.response?.status == HttpResponse.BadRequest) &&
         !config?.sent &&
         config.url !== `/auth/refresh`
       ) {
         const refreshKey = localStorage.getItem(TOKEN_REFRESH_KEY);
-
+        
         if (refreshKey) {
           try {
             const {
@@ -56,9 +59,9 @@ http.interceptors.response.use(
                   authorization: `Bearer ${refreshKey}`,
                 },
               }
-            );
-
-            const newToken = data.token.access as string;
+              );
+              
+              const newToken = data.access_token as string;
             sessionStorage.setItem(TOKEN_ACCESS_KEY, newToken);
 
             config.sent = true;
@@ -70,7 +73,8 @@ http.interceptors.response.use(
             if (axios.isAxiosError(errNew)) {
               if (
                 errNew.response?.status == HttpResponse.Unauthorized ||
-                errNew.response?.status == HttpResponse.UnprocessableEntity
+                errNew.response?.status == HttpResponse.UnprocessableEntity ||
+                errNew.response?.status == HttpResponse.BadRequest
               ) {
                 unsetToken();
               }
diff --git a/src/pages/admin.tsx b/src/pages/admin.tsx
index afaafc28493da9bf5caad8142eab48380a1a54e5..fadd06da6faa0417ed7de21df9be03a84d023846 100644
--- a/src/pages/admin.tsx
+++ b/src/pages/admin.tsx
@@ -190,12 +190,12 @@ export default function Admin() {
       >
         <HStack justifyContent="space-between">
           <Heading>Daftar Pengguna</Heading>
-          <Button bg="biru.600" color="white" onClick={onOpenAdd}>
+          {/* <Button bg="biru.600" color="white" onClick={onOpenAdd}>
             <MdAdd />
             <Text ml={2} display={{ base: 'none', lg: 'flex' }}>
               Tambah Pengguna
             </Text>
-          </Button>
+          </Button> */}
         </HStack>
         <TableContainer>
           <Table variant="simple" bg={'white'} borderRadius="lg" mt={5}>
diff --git a/src/pages/courses/details/[id].tsx b/src/pages/courses/details/[id].tsx
index 3976115cbba9d53f51298bb3fef55c185668ce25..34e2d010283d31a7345795e904567245ca702546 100644
--- a/src/pages/courses/details/[id].tsx
+++ b/src/pages/courses/details/[id].tsx
@@ -85,38 +85,42 @@ export default function CourseDetails() {
                       <Td>{material.week}</Td>
                       <Td>
                         <HStack my={0} py={0}>
-                          {material.contents.map((content) => (
-                            <Link
-                              href={
-                                content.type === 'handout'
-                                  ? process.env.NEXT_PUBLIC_BUCKET_URL +
-                                    '/' +
-                                    content.link
-                                  : content.link
+                          {/* {material.contents.map((content) => (
+                          ))} */}
+                          <Link href={`/content/${material.id}/slide`}>
+                            <Button
+                              size="sm"
+                              colorScheme={
+                                'yellow'
                               }
-                              target="_blank"
-                              key={content.id}
                             >
-                              <Button
-                                size="sm"
-                                colorScheme={
-                                  content.type === 'video' ? 'red' : 'yellow'
-                                }
+                            <MdAttachFile />
+                           
+                              <Text
+                                ml={2}
+                                display={{ base: 'none', lg: 'flex' }}
                               >
-                                {content.type === 'video' ? (
-                                  <MdPlayArrow />
-                                ) : (
-                                  <MdAttachFile />
-                                )}
-                                <Text
-                                  ml={2}
-                                  display={{ base: 'none', lg: 'flex' }}
-                                >
-                                  {content.type}
-                                </Text>
-                              </Button>
-                            </Link>
-                          ))}
+                                handout
+                              </Text>
+                            </Button>
+                          </Link>
+                          <Link href={`/content/${material.id}/video`}>
+                            <Button
+                              size="sm"
+                              colorScheme={
+                                'red'
+                              }
+                            >
+                            <MdPlayArrow />
+                           
+                              <Text
+                                ml={2}
+                                display={{ base: 'none', lg: 'flex' }}
+                              >
+                                video
+                              </Text>
+                            </Button>
+                          </Link>
                         </HStack>
                       </Td>
                     </Tr>
diff --git a/src/pages/courses/faculty/[id].tsx b/src/pages/courses/faculty/[id].tsx
index 09663d831501a1902ece508c8f8848099cd169be..657544a869ac83210d4c22d9a495d622b3c8e22a 100644
--- a/src/pages/courses/faculty/[id].tsx
+++ b/src/pages/courses/faculty/[id].tsx
@@ -20,6 +20,8 @@ import { MdArrowBackIos, MdSearch } from 'react-icons/md';
 
 export default function Courses() {
   const [courses, setCourses] = useState<Course[]>([]);
+  const [rawCourses, setRawCourses] = useState<Course[]>([]);
+  const [searchQuery, setSearchQuery] = useState('');
   const [faculty, setFaculty] = useState<Faculty>();
   const toast = useToast();
 
@@ -32,6 +34,7 @@ export default function Courses() {
     http
       .get(`/course/faculty/courses/${id}`)
       .then((res) => {
+        setRawCourses(res.data.data);
         setCourses(res.data.data);
       })
       .catch((err) => {
@@ -43,6 +46,23 @@ export default function Courses() {
       });
   }, [id]);
 
+  useEffect(() => {
+    if (searchQuery === '') {
+      setCourses(rawCourses);
+      return;
+    }
+    console.log(searchQuery);
+    // filter courses name or lecturer name or course code
+    setCourses(
+      rawCourses.filter(
+        (c) =>
+          c.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+          c.lecturer.toLowerCase().includes(searchQuery.toLowerCase()) ||
+          c.id.toLowerCase().includes(searchQuery.toLowerCase())
+      )
+    );
+  }, [searchQuery]);
+
   useEffect(() => {
     if (!id) return;
     http
@@ -77,7 +97,12 @@ export default function Courses() {
             <InputLeftElement>
               <MdSearch />
             </InputLeftElement>
-            <Input name="search" placeholder="Cari Mata Kuliah" bg="white" />
+            <Input
+              name="search"
+              placeholder="Cari Mata Kuliah"
+              bg="white"
+              onChange={(e) => setSearchQuery(e.target.value)}
+            />
           </InputGroup>
         </Stack>
         <Heading size="lg">
diff --git a/src/pages/courses/index.tsx b/src/pages/courses/index.tsx
index 170da39dfe8a08fc91132481de61869f5d4a7b2e..6cc38bd0110c5eb197fcb0e116d478c57bf8a0b4 100644
--- a/src/pages/courses/index.tsx
+++ b/src/pages/courses/index.tsx
@@ -18,6 +18,8 @@ import { MdArrowBackIos, MdSearch } from 'react-icons/md';
 
 export default function Courses() {
   const [courses, setCourses] = useState<Course[]>([]);
+  const [rawCourses, setRawCourses] = useState<Course[]>([]);
+  const [searchQuery, setSearchQuery] = useState('');
   const toast = useToast();
 
   const router = useRouter();
@@ -26,6 +28,7 @@ export default function Courses() {
     http
       .get('/course')
       .then((res) => {
+        setRawCourses(res.data.data);
         setCourses(res.data.data);
       })
       .catch((err) => {
@@ -38,6 +41,23 @@ export default function Courses() {
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, []);
 
+  useEffect(() => {
+    if (searchQuery === '') {
+      setCourses(rawCourses);
+      return;
+    }
+    console.log(searchQuery);
+    // filter courses name or lecturer name or course code
+    setCourses(
+      rawCourses.filter(
+        (c) =>
+          c.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+          c.lecturer.toLowerCase().includes(searchQuery.toLowerCase()) ||
+          c.id.toLowerCase().includes(searchQuery.toLowerCase())
+      )
+    );
+  }, [searchQuery]);
+
   return (
     <Layout title="Courses List">
       <Stack
@@ -56,7 +76,12 @@ export default function Courses() {
             <InputLeftElement>
               <MdSearch />
             </InputLeftElement>
-            <Input name="search" placeholder="Cari Mata Kuliah" bg="white" />
+            <Input
+              name="search"
+              placeholder="Cari Mata Kuliah"
+              bg="white"
+              onChange={(e) => setSearchQuery(e.target.value)}
+            />
           </InputGroup>
         </Stack>
       </Stack>
diff --git a/src/pages/courses/major/[id].tsx b/src/pages/courses/major/[id].tsx
index 7a3b3fe5961f54f81d09eb2f7af83b04738091e1..2ea935558b4a65136397ae96f36ba26df9e51896 100644
--- a/src/pages/courses/major/[id].tsx
+++ b/src/pages/courses/major/[id].tsx
@@ -20,6 +20,8 @@ import { MdArrowBackIos, MdSearch } from 'react-icons/md';
 
 export default function Courses() {
   const [courses, setCourses] = useState<Course[]>([]);
+  const [rawCourses, setRawCourses] = useState<Course[]>([]);
+  const [searchQuery, setSearchQuery] = useState('');
   const [major, setMajor] = useState<Major>();
   const toast = useToast();
 
@@ -32,6 +34,7 @@ export default function Courses() {
     http
       .get(`/course/major/courses/${id}`)
       .then((res) => {
+        setRawCourses(res.data.data);
         setCourses(res.data.data);
       })
       .catch((err) => {
@@ -43,6 +46,23 @@ export default function Courses() {
       });
   }, [id]);
 
+  useEffect(() => {
+    if (searchQuery === '') {
+      setCourses(rawCourses);
+      return;
+    }
+    console.log(searchQuery);
+    // filter courses name or lecturer name or course code
+    setCourses(
+      rawCourses.filter(
+        (c) =>
+          c.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+          c.lecturer.toLowerCase().includes(searchQuery.toLowerCase()) ||
+          c.id.toLowerCase().includes(searchQuery.toLowerCase())
+      )
+    );
+  }, [searchQuery]);
+
   useEffect(() => {
     if (!id) return;
     http
@@ -77,7 +97,12 @@ export default function Courses() {
             <InputLeftElement>
               <MdSearch />
             </InputLeftElement>
-            <Input name="search" placeholder="Cari Mata Kuliah" bg="white" />
+            <Input
+              name="search"
+              placeholder="Cari Mata Kuliah"
+              bg="white"
+              onChange={(e) => setSearchQuery(e.target.value)}
+            />
           </InputGroup>
         </Stack>
         <Heading size="lg">
diff --git a/src/pages/management/course/[course_id]/index.tsx b/src/pages/management/course/[course_id]/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..bc7399d0c11bed209076535eb215462142a1c578
--- /dev/null
+++ b/src/pages/management/course/[course_id]/index.tsx
@@ -0,0 +1,367 @@
+import RowAction from '@/components/admin/row-action';
+import CourseBanner from '@/components/course_banner';
+import Layout from '@/components/layout';
+import AddMaterialModal from '@/components/management/material/add-material-modal';
+import DeleteMaterialModal from '@/components/management/material/delete-material-modal';
+import Modal from '@/components/modal';
+import NotFound from '@/components/not_found';
+import http from '@/lib/http';
+import { getAvailableUserData } from '@/lib/token';
+import { Content } from '@/types/content';
+import { Material } from '@/types/material';
+import { Quiz } from '@/types/quiz';
+import { UserClaim } from '@/types/token';
+import {
+  Box,
+  Button,
+  Heading,
+  HStack,
+  IconButton,
+  Link,
+  Table,
+  TableContainer,
+  Tbody,
+  Td,
+  Text,
+  Th,
+  Thead,
+  Tr,
+  useDisclosure,
+  useToast,
+} from '@chakra-ui/react';
+import { useRouter } from 'next/router';
+import { useEffect, useState } from 'react';
+import { MdAdd, MdArrowBackIos } from 'react-icons/md';
+
+interface CourseBannerProps {
+  course_code: string;
+  course_name: string;
+  lecturer: string;
+}
+
+const MaterialManagementPage = () => {
+  const router = useRouter();
+  const query = router.query;
+  const course_id = query.course_id;
+
+  const toast = useToast();
+  const {
+    isOpen: isOpenAdd,
+    onOpen: onOpenAdd,
+    onClose: onCloseAdd,
+  } = useDisclosure();
+
+  const {
+    isOpen: isOpenDeleteMaterial,
+    onOpen: onOpenDeleteMaterial,
+    onClose: onCloseDeleteMaterial,
+  } = useDisclosure();
+  const {
+    isOpen: isOpenDeleteQuiz,
+    onOpen: onOpenDeleteQuiz,
+    onClose: onCloseDeleteQuiz,
+  } = useDisclosure();
+  const [courseBannerProps, setCourseBannerProps] = useState<CourseBannerProps>(
+    {
+      course_code: '',
+      course_name: '',
+      lecturer: '',
+    }
+  );
+  const [materialID, setMaterialID] = useState<string>('');
+  const [materialName, setMaterialName] = useState<string>('');
+  const [materialWeek, setMaterialWeek] = useState<number>(0);
+  const [isValid, setIsValid] = useState<boolean>(true);
+  const [materials, setMaterials] = useState<Material[]>([]);
+  const [contents, setContents] = useState<Content[]>([]);
+  const [quizzes, setQuizzes] = useState<Quiz[]>([]);
+  const [selectedQuizId, setSelectedQuizId] = useState<string>('');
+
+  useEffect(() => {
+    if (!router.isReady) return;
+    http
+      .get(`/course/${router.query.course_id}/quiz`)
+      .then((res) => {
+        setQuizzes(res.data.data);
+      })
+      .catch((err) => {
+        console.log(err);
+      });
+  }, [router.isReady, router.query.course_id]);
+
+  useEffect(() => {
+    if (!router.isReady) return;
+    console.log('t');
+    if (course_id) {
+      let user: UserClaim | null = getAvailableUserData();
+      const getCourse = async (course_id: string) => {
+        const course = await http
+          .get(`${process.env.NEXT_PUBLIC_API_URL}/course/${course_id}`)
+          .catch((res) => {
+            toast({
+              title: 'Get Course Failed',
+              description: res.data.message,
+              status: 'error',
+              duration: 9000,
+              isClosable: true,
+            });
+            setIsValid(false);
+          });
+        if (user && course!.data.data.email == user!.email) {
+          setCourseBannerProps({
+            course_code: course_id,
+            course_name: course!.data.data.name,
+            lecturer: course!.data.data.lecturer,
+          });
+        } else {
+          toast({
+            title: 'Forbidden Page',
+            status: 'error',
+            duration: 9000,
+            isClosable: true,
+          });
+          setIsValid(false);
+        }
+      };
+      getCourse(course_id as string).catch((res) => {
+        setIsValid(false);
+      });
+    }
+  }, [isValid, router.isReady]);
+
+  const [materialReady, setMaterialReady] = useState<boolean>(false);
+  useEffect(() => {
+    http
+      .get(`/course/${course_id}/materials`)
+      .then((res) => {
+        setMaterials(res.data.data);
+        setMaterialReady(true);
+      })
+      .catch((err) => {
+        toast({
+          title: 'Error',
+          description: 'Gagal mengambil data materi',
+          status: 'error',
+        });
+      });
+  }, [materialReady]);
+
+  const handleAdd = () => {
+    http
+      .post(`/course/${course_id}/material`, {
+        name: materialName,
+        week: materialWeek,
+      })
+      .then((res) => {
+        toast({
+          title: 'Success',
+          description: 'Berhasil menambah materi',
+          status: 'success',
+          duration: 1000,
+          isClosable: true,
+        });
+        setMaterialReady(false);
+      })
+      .catch((err) => {
+        toast({
+          title: 'Gagal menambah material',
+          description: `${err.response?.data}`,
+          status: 'error',
+          duration: 1000,
+          isClosable: true,
+        });
+      });
+    onCloseAdd();
+  };
+
+  const handleEditButton = (material_id: string) => {
+    router.push(`/management/course/${course_id}/material/${material_id}`);
+  };
+
+  const handleDeleteButton = (material_id: string) => {
+    setMaterialID(material_id);
+    onOpenDeleteMaterial();
+  };
+
+  const handleDelete = () => {
+    http
+      .delete(`/material/${materialID}`)
+      .then((res) => {
+        toast({
+          title: 'Success',
+          description: 'Berhasil menghapus materi.',
+          status: 'success',
+          duration: 1000,
+          isClosable: true,
+        });
+        setMaterialReady(false);
+      })
+      .catch((res) => {
+        toast({
+          title: 'Gagal menghapus materi',
+          description: 'Entah mengapa',
+          status: 'error',
+          duration: 1000,
+          isClosable: true,
+        });
+      });
+
+    onCloseDeleteMaterial();
+  };
+
+  const handleConfirmDeleteQuiz = () => {
+    onCloseDeleteQuiz();
+    http
+      .delete(`/quiz/${selectedQuizId}`)
+      .then(() => {
+        toast({
+          title: 'Latihan berhasil dihapus',
+          status: 'success',
+          duration: 3000,
+          isClosable: true,
+        });
+      })
+      .catch((err) => {
+        toast({
+          title: 'Gagal menghapus latihan',
+          status: 'error',
+          duration: 3000,
+          isClosable: true,
+        });
+        console.log(err);
+      });
+  };
+
+  if (isValid)
+    return (
+      <>
+        <Layout title="Edit Konten Course" py={0} px={0}>
+          <CourseBanner {...courseBannerProps}>
+            <HStack justifyContent="space-between">
+              <HStack>
+                <IconButton
+                  aria-label="back"
+                  icon={<MdArrowBackIos />}
+                  variant="ghost"
+                  onClick={router.back}
+                />
+                <Heading>Daftar Materi</Heading>
+              </HStack>
+              <Button bg="biru.600" color="white" onClick={onOpenAdd}>
+                <MdAdd />
+                <Text ml={2} display={{ base: 'none', lg: 'flex' }}>
+                  New Content
+                </Text>
+              </Button>
+            </HStack>
+            <TableContainer>
+              <Table variant="simple" bg={'white'} borderRadius="lg" mt={5}>
+                <Thead textTransform="capitalize">
+                  <Tr>
+                    <Th>Material</Th>
+                    <Th>Week</Th>
+                    <Th>Action</Th>
+                  </Tr>
+                </Thead>
+                <Tbody>
+                  {materials.map((m: Material) => (
+                    <Tr key={m.id}>
+                      <Td>{m.name}</Td>
+                      <Td>{m.week}</Td>
+                      <Td>
+                        <HStack>
+                          <RowAction
+                            onOpenEdit={() => {
+                              handleEditButton(m.id);
+                            }}
+                            onOpenDelete={() => {
+                              handleDeleteButton(m.id);
+                            }}
+                          />
+                        </HStack>
+                      </Td>
+                    </Tr>
+                  ))}
+                </Tbody>
+              </Table>
+            </TableContainer>
+
+            <HStack justifyContent="space-between" mt={10}>
+              <Box>
+                <Heading>Daftar Latihan Soal</Heading>
+              </Box>
+              <Link href={`${router.asPath}/quiz/add`}>
+                <Button bg="biru.600" color="white">
+                  <MdAdd />
+                  <Text ml={2} display={{ base: 'none', lg: 'flex' }}>
+                    Latihan Baru
+                  </Text>
+                </Button>
+              </Link>
+            </HStack>
+            <TableContainer>
+              <Table variant="simple" bg={'white'} borderRadius="lg" mt={5}>
+                <Thead textTransform="capitalize">
+                  <Tr>
+                    <Th>Judul Latihan Soal</Th>
+                    <Th>Action</Th>
+                  </Tr>
+                </Thead>
+                <Tbody>
+                  {quizzes.map((q: Quiz) => (
+                    <Tr key={q.id}>
+                      <Td>{q.nama}</Td>
+                      <Td>
+                        <RowAction
+                          onOpenEdit={() => {
+                            router.push(`${router.asPath}/quiz/${q.id}/edit`);
+                          }}
+                          onOpenDelete={() => {
+                            setSelectedQuizId(q.id);
+                            onOpenDeleteQuiz();
+                          }}
+                        />
+                      </Td>
+                    </Tr>
+                  ))}
+                </Tbody>
+              </Table>
+            </TableContainer>
+          </CourseBanner>
+        </Layout>
+
+        <AddMaterialModal
+          isOpen={isOpenAdd}
+          onClose={onCloseAdd}
+          handleConfirm={() => handleAdd()}
+          name={materialName}
+          setName={setMaterialName}
+          week={materialWeek}
+          setWeek={setMaterialWeek}
+        />
+
+        <DeleteMaterialModal
+          isOpen={isOpenDeleteMaterial}
+          onClose={onCloseDeleteMaterial}
+          handleConfirm={() => handleDelete()}
+        />
+
+        <Modal
+          isOpen={isOpenDeleteQuiz}
+          onClose={onCloseDeleteQuiz}
+          header={'Hapus Latihan'}
+          onConfirm={handleConfirmDeleteQuiz}
+        >
+          Yakin ingin menghapus latihan?
+        </Modal>
+      </>
+    );
+  else
+    return (
+      <Layout title="Not Found">
+        <NotFound />
+      </Layout>
+    );
+};
+
+export default MaterialManagementPage;
diff --git a/src/pages/management/course/[course_id]/material/[material_id]/file.tsx b/src/pages/management/course/[course_id]/material/[material_id]/file.tsx
index e7488a32339877621dd04abd7b4dfcf028c66b9e..81a452ec5ad8083a772b3af9b4d41b3a89ea2eea 100644
--- a/src/pages/management/course/[course_id]/material/[material_id]/file.tsx
+++ b/src/pages/management/course/[course_id]/material/[material_id]/file.tsx
@@ -83,7 +83,7 @@ const EditContentPage = ({
           console.log("success req to endpoint");
           const upload_link = res.data.data.upload_link;
           axios
-            .put(upload_link, file, {
+              .put(upload_link, file, {
               headers: {
                 'Content-Type' : file!.type,
                 'x-amz-acl' : 'public-read',
diff --git a/src/pages/management/course/[course_id]/material/[material_id]/index.tsx b/src/pages/management/course/[course_id]/material/[material_id]/index.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..5922d81efc3377890962f8c1aef378115f1011c4
--- /dev/null
+++ b/src/pages/management/course/[course_id]/material/[material_id]/index.tsx
@@ -0,0 +1,381 @@
+import CourseBanner from "@/components/course_banner";
+import Layout from "@/components/layout";
+import http from "@/lib/http";
+import { useEffect, useState } from 'react';
+import { useRouter } from 'next/router';
+import NotFound from "@/components/not_found";
+import { UserClaim } from "@/types/token";
+import { getAvailableUserData } from "@/lib/token";
+import { 
+  useToast,
+  HStack,
+  Heading,
+  Button,
+  Text,
+  useDisclosure,
+  Accordion,
+  AccordionItem,
+  AccordionButton,
+  AccordionPanel,
+  AccordionIcon,
+  Box,
+  Card,
+  IconButton,
+  VStack,
+  Skeleton,
+  AspectRatio,
+} from "@chakra-ui/react";
+import { MdAdd, MdDelete, MdArrowBackIos } from "react-icons/md";
+import AddContentModal from "@/components/management/content/add-content-modal";
+import DeleteContentModal from "@/components/management/content/delete-content-modal";
+import { Material } from "@/types/material";
+import { Content } from "@/types/content";
+import axios from "axios";
+
+
+interface CourseBannerProps {
+  course_code : string;
+  course_name : string;
+  lecturer : string;
+}
+
+const ContentManagementPage = () => {
+  const router = useRouter();
+  const query = router.query;
+  const course_id = query.course_id;
+  const material_id = query.material_id;
+  
+  const toast = useToast();
+  const {
+    isOpen: isOpenAdd,
+    onOpen: onOpenAdd,
+    onClose: onCloseAdd
+  } = useDisclosure();
+  const {
+    isOpen: isOpenDelete,
+    onOpen: onOpenDelete,
+    onClose: onCloseDelete
+  } = useDisclosure();
+  const [courseBannerProps, setCourseBannerProps] = useState<CourseBannerProps>({
+    course_code : '',
+    course_name : '',
+    lecturer : ''
+  });
+
+  const [material, setMaterial] = useState<Material>({
+    id: '',
+    course_id: '',
+    creator_email: '',
+    name: '',
+    week: 0,
+    contents: []
+  });
+  const [isValid, setIsValid] = useState<boolean>(true);
+  const [file, setFile] = useState<File|undefined>();
+  const [type, setType] = useState<string>('handout');
+  const [link, setLink] = useState<string>('');
+  const [contentID, setContentID] = useState<string>('');
+  const [indexExpanded, setIndexExpanded] = useState<number>(-1);
+
+  useEffect(() => {
+    if(!router.isReady) return;
+    if(course_id && material_id){
+      let user : UserClaim | null = getAvailableUserData();
+      const getCourse = async(course_id : string) => {
+        const course = await http
+          .get(`/course/${course_id}`)
+          .catch((res) => {
+            toast({
+              title: 'Get Course Failed',
+              description: res.data.message,
+              status: 'error',
+              duration: 9000,
+              isClosable: true
+            });
+            setIsValid(false);
+          });
+        if(user && course!.data.data.email == user!.email){
+          setCourseBannerProps({
+            course_code : course_id,
+            course_name : course!.data.data.name,
+            lecturer : course!.data.data.lecturer
+          });
+        }else{
+          toast({
+            title: 'Forbidden Page',
+            description: "Pengguna bukan contributor",
+            status: 'error',
+            duration: 9000,
+            isClosable: true
+          });
+          setIsValid(false);
+        }
+      }
+
+      const getMaterial = async (material_id : string, course_id : string) => {
+        const material = await http.get(`/material/${material_id}`).catch(
+					(err) => {
+						toast({
+              title: 'Get Course Failed',
+              description: err.data.message,
+              status: 'error',
+              duration: 9000,
+              isClosable: true
+            });
+						setIsValid(false);
+					}
+				);
+				
+				if(material){
+					if(material!.data.data.course_id != course_id){
+						toast({
+							title: 'Wrong Material or Course',
+							description: material.data.message,
+							status: 'error',
+							duration: 9000,
+							isClosable: true
+						});
+						setIsValid(false);
+					}
+				}
+      }
+
+      getCourse(course_id as string)
+				.then((res) => {getMaterial(material_id as string, course_id as string)
+					.catch((err) => {throw err})})
+        .catch((err) => {setIsValid(false)});
+    }
+	  
+  }, [isValid, router.isReady]);
+
+  const [materialReady, setMaterialReady] = useState<boolean>(false);
+  useEffect(() => {
+    if(!router.isReady) return;
+
+    if(material_id){
+      http
+        .get(`/material/${material_id}`)
+        .then((res) => {
+          const material = res.data.data;
+          setMaterial(material);
+          setMaterialReady(true);
+        }).catch((err) => {
+          toast({
+            title: 'Error',
+            description: 'Gagal mengambil data materi',
+            status: 'error'
+          })
+        })
+    }
+  }, [materialReady, router.isReady]);
+
+  const handleAdd = () => {
+    const body = {
+      link : (type === "handout") ? "" : link,
+      type : type
+    }
+    http
+      .post(`/material/${material_id}/content`,body).then((res) => {
+        if(type === "video"){
+          toast({
+            title: 'Success',
+            description: 'Berhasil menambah materi',
+            status: 'success',
+            duration: 1000,
+            isClosable: true,
+          });
+          setMaterialReady(false);
+        }else{
+          const upload_link = res.data.data.upload_link;
+          axios
+            .put(upload_link, file, {
+              headers: {
+                'Content-Type' : file!.type,
+                'x-amz-acl' : 'public-read',
+              }
+            })
+            .then(
+              res => {
+                console.log(res);
+                toast({
+                  title: 'Adding material success!',
+                  description: res.data.message,
+                  status: 'success',
+                  duration: 9000,
+                  isClosable: true
+                });
+                setMaterialReady(false);
+              }, res => {
+                console.log(res);
+                toast({
+                  title: 'Adding material failed!',
+                  description: res.message,
+                  status: 'error',
+                  duration: 9000,
+                  isClosable: true,
+                })
+              }
+            )
+        }
+      }).catch((err) => {
+        toast({
+          title: 'Gagal menambah material',
+          description: `${err.response?.data}`,
+          status: 'error',
+          duration: 1000,
+          isClosable: true,
+        })
+      });
+    onCloseAdd();
+  }
+
+  const handleDeleteButton = (content_id : string) => {
+    setContentID(content_id);
+    onOpenDelete();
+  }
+
+  const handleDelete = () => {
+    if(!contentID){
+      toast({
+        title: 'Gagal menghapus',
+        description: 'Konten belum dipilih',
+        status: 'error',
+        duration: 1000,
+        isClosable: true,
+      });
+      return;
+    }
+    http.delete(`/material/${material_id}/content/${contentID}`).then(
+      (res) => {
+        toast({
+          title: 'Success',
+          description: 'Berhasil menghapus materi.',
+          status: 'success',
+          duration: 1000,
+          isClosable: true,
+        });
+        setMaterialReady(false);
+      }
+    ).catch(
+      err => {
+        console.log(err);
+        toast({
+          title: 'Gagal menghapus materi',
+          description: err.message,
+          status: 'error',
+          duration: 1000,
+          isClosable: true,
+        });
+      }
+    )
+    onCloseDelete();
+  }
+  
+  if(isValid) return (
+    <>
+      <Layout title="Edit Konten" py={0} px={0}>
+        <CourseBanner {...courseBannerProps}>
+          <HStack justifyContent="space-between">
+            <HStack>
+              <IconButton
+                aria-label="back"
+                icon={<MdArrowBackIos />}
+                variant="ghost"
+                onClick={router.back}
+              />
+              <VStack alignItems={'flex-start'}>
+                <Heading>Edit Konten</Heading>
+                <Skeleton isLoaded = {materialReady}>
+                  <Text>{material.name} - Week {material.week}</Text>
+                </Skeleton>
+              </VStack>
+            </HStack>
+            <Button bg="biru.600" color="white" onClick={onOpenAdd}>
+              <MdAdd />
+              <Text ml={2} display={{ base: 'none', lg: 'flex' }}>
+                New Content
+              </Text>
+            </Button>
+          </HStack>
+          <Card mt={10} p={5}>
+            <Accordion allowToggle onChange={(idx) => {setIndexExpanded(idx as number)}}>
+              {material.contents.map((c: Content) => (
+                <AccordionItem key={c.id}>
+                  <AccordionButton>
+                    <Box as="span" flex='1' textAlign='left'>
+                      Content {c.type}
+                    </Box>
+                  </AccordionButton>
+                  <AccordionPanel overflow={'auto'} maxH={'fit-content'} width={'100%'}>
+                    {
+                      //TODO : Tampilin konten di sini
+                      c.type == "handout" ? (
+                        <AspectRatio
+                          height={'100%'}
+                          width={'100%'}
+                        >
+                        <iframe
+                          src={`https://ocw-bucket.s3.idcloudhost.com/static/${c.link}`}
+                          width="100%"
+                          height="100%"
+                          style={{ border: 'none' }}
+                          
+                        />
+                        </AspectRatio>
+                      ):(
+                        <AspectRatio
+                        maxWidth={'5xl'}
+                        maxHeight={'7xl'}
+                        >
+                        <iframe
+                          width="100%"
+                          height="100%"
+                          src={c.link.replace("watch?v=","embed/")}
+                          title="video player"
+                          allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
+                          allowFullScreen
+                          />
+                        </AspectRatio>
+                      )
+                    }
+                    <Button bg="red" color="white" onClick={e => handleDeleteButton(c.id)} mt={5}>
+                      <MdDelete />
+                      <Text ml={2} display={{ base: 'none', lg: 'flex' }}>
+                        Delete
+                      </Text>
+                    </Button>
+                  </AccordionPanel>
+                </AccordionItem>
+              ))}
+            </Accordion>
+          </Card>
+        </CourseBanner>
+      </Layout>
+
+      <AddContentModal
+        isOpen={isOpenAdd}
+        onClose={onCloseAdd}
+        handleConfirm={() => {handleAdd()}}
+        type={type}
+        setType={setType}
+        link={link}
+        setLink={setLink}
+        file={file}
+        setFile={setFile}
+      />
+
+      <DeleteContentModal
+        isOpen={isOpenDelete}
+        onClose={onCloseDelete}
+        handleConfirm={() => handleDelete()}
+      />
+    </>
+  )
+  else return (
+    <Layout title="Not Found">
+      <NotFound/>
+    </Layout>
+  )
+}
+
+export default ContentManagementPage;
\ No newline at end of file
diff --git a/src/pages/management/course/[course_id]/quiz/[quiz_id]/edit.tsx b/src/pages/management/course/[course_id]/quiz/[quiz_id]/edit.tsx
index fc923f6f56af254359c91ff67f320e3cf113f054..4a57f7678a19f7438765c6b730f67d194b454bf1 100644
--- a/src/pages/management/course/[course_id]/quiz/[quiz_id]/edit.tsx
+++ b/src/pages/management/course/[course_id]/quiz/[quiz_id]/edit.tsx
@@ -19,7 +19,7 @@ import { useEffect, useState } from 'react';
 import { MdAdd } from 'react-icons/md';
 import { v4 as uuidv4 } from 'uuid';
 
-export default function NewQuiz() {
+export default function EditQuiz() {
   const router = useRouter();
   const toast = useToast();
   const quizId = router.query.quiz_id as string;
@@ -47,13 +47,13 @@ export default function NewQuiz() {
       })
       .then((res) => {
         // parse the link
-        const link = res.data.data.upload_link;
+        const link = res.data.data.path;
         axios
           .get(`${process.env.NEXT_PUBLIC_BUCKET_URL}/${link}`)
           .then((res) => {
             setProblems(res.data.problems);
-            console.log(problems);
-          });
+          })
+          .catch((err) => console.log(err));
       })
       .catch((err) => console.log(err));
     // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -61,7 +61,7 @@ export default function NewQuiz() {
 
   const handleSubmit = () => {
     http
-      .put(
+      .patch(
         `/quiz/${quizId}`,
         {
           name: quizName,
@@ -75,14 +75,27 @@ export default function NewQuiz() {
       )
       .then((res) => {
         console.log(res.data);
+        const id = res.data.data.id;
         const uploadLink = res.data.data.upload_link;
         axios
-          .put(uploadLink, problems, {
-            headers: {
-              'Content-Type': 'application/json',
-              'x-amz-acl': 'public-read',
+          .put(
+            uploadLink,
+            {
+              id,
+              name: quizName,
+              course_id,
+              description: '',
+              help: '',
+              media: [],
+              problems,
             },
-          })
+            {
+              headers: {
+                'Content-Type': 'application/json',
+                'x-amz-acl': 'public-read',
+              },
+            }
+          )
           .then((res) => {
             console.log(res.data);
             if (res.status === 200) {
@@ -102,13 +115,6 @@ export default function NewQuiz() {
       .catch((err) => {
         console.log(err);
       });
-    // toast({
-    //   title: 'Latihan berhasil dibuat',
-    //   status: 'success',
-    //   duration: 3000,
-    //   isClosable: true,
-    // });
-    // router.back();
   };
 
   return (
diff --git a/src/pages/management/course/[course_id]/quiz/add.tsx b/src/pages/management/course/[course_id]/quiz/add.tsx
index e8bbcbf89dcbe34af6c0ad4f50c3ee96403a0515..6dd7a8162992ef5defbe8eb4c1dde4b0deb73493 100644
--- a/src/pages/management/course/[course_id]/quiz/add.tsx
+++ b/src/pages/management/course/[course_id]/quiz/add.tsx
@@ -22,7 +22,6 @@ import { v4 as uuidv4 } from 'uuid';
 export default function NewQuiz() {
   const router = useRouter();
   const toast = useToast();
-  const quizId = uuidv4();
   const course_id = router.query.course_id as string;
   const [quizName, setQuizName] = useState('');
   const [problems, setProblems] = useState<Problem[]>([]);
@@ -30,7 +29,7 @@ export default function NewQuiz() {
   const handleSubmit = () => {
     http
       .put(
-        `/quiz/${quizId}`,
+        `/quiz`,
         {
           name: quizName,
           course_id,
@@ -43,14 +42,27 @@ export default function NewQuiz() {
       )
       .then((res) => {
         console.log(res.data);
+        const id = res.data.data.id;
         const uploadLink = res.data.data.upload_link;
         axios
-          .put(uploadLink, problems, {
-            headers: {
-              'Content-Type': 'application/json',
-              'x-amz-acl': 'public-read',
+          .put(
+            uploadLink,
+            {
+              id,
+              name: quizName,
+              course_id,
+              description: '',
+              help: '',
+              media: [],
+              problems,
             },
-          })
+            {
+              headers: {
+                'Content-Type': 'application/json',
+                'x-amz-acl': 'public-read',
+              },
+            }
+          )
           .then((res) => {
             console.log(res.data);
             if (res.status === 200) {
diff --git a/src/pages/management/course/[course_id]/quiz/index.tsx b/src/pages/management/course/[course_id]/quiz/index.tsx
deleted file mode 100644
index d5b3d609f3bc60acc96431963abd82b676273142..0000000000000000000000000000000000000000
--- a/src/pages/management/course/[course_id]/quiz/index.tsx
+++ /dev/null
@@ -1,127 +0,0 @@
-import RowAction from '@/components/admin/row-action';
-import Layout from '@/components/layout';
-import Modal from '@/components/modal';
-import http from '@/lib/http';
-import { Quiz } from '@/types/quiz';
-import {
-  Box,
-  Button,
-  Heading,
-  HStack,
-  Table,
-  TableContainer,
-  Tbody,
-  Td,
-  Text,
-  Th,
-  Thead,
-  Tr,
-  useDisclosure,
-  useToast,
-} from '@chakra-ui/react';
-import Link from 'next/link';
-import { useRouter } from 'next/router';
-import { MdAdd } from 'react-icons/md';
-
-const quizzes = [
-  {
-    id: '440e9a7b-5fe5-481c-a98d-a1736e91f42b',
-    nama: 'Latihan Sample',
-    course_id: 'IF3270',
-    creator_email: 'contributor@example.com',
-  },
-  {
-    id: 'ca45c775-bb74-422e-943c-08e9601d6d41',
-    nama: 'Latihan Beneran',
-    course_id: 'IF3270',
-    creator_email: 'contributor@example.com',
-  },
-];
-
-export default function QuizManagement() {
-  const router = useRouter();
-  const toast = useToast();
-  const { isOpen, onOpen, onClose } = useDisclosure();
-
-  const handleConfirmDelete = () => {
-    onClose();
-    http
-      .delete(`/quiz/${router.query.quiz_id}`, {
-        headers: {
-          Authorization: `Bearer ${localStorage.getItem('access_token')}`,
-        },
-      })
-      .then(() => {
-        toast({
-          title: 'Latihan berhasil dihapus',
-          status: 'success',
-          duration: 3000,
-          isClosable: true,
-        });
-      })
-      .catch((err) => {
-        toast({
-          title: 'Gagal menghapus latihan',
-          status: 'error',
-          duration: 3000,
-          isClosable: true,
-        });
-        console.log(err);
-      });
-  };
-
-  return (
-    <>
-      <Layout title="Quiz Management">
-        <HStack justifyContent="space-between">
-          <Box>
-            <Heading>Daftar Latihan</Heading>
-            <Text mt={3}>IF3270 Pembelajaran Mesin</Text>
-          </Box>
-          <Link href={`${router.asPath}/add`}>
-            <Button bg="biru.600" color="white">
-              <MdAdd />
-              <Text ml={2} display={{ base: 'none', lg: 'flex' }}>
-                Latihan Baru
-              </Text>
-            </Button>
-          </Link>
-        </HStack>
-        <TableContainer>
-          <Table variant="simple" bg={'white'} borderRadius="lg" mt={5}>
-            <Thead textTransform="capitalize">
-              <Tr>
-                <Th>Judul Latihan Soal</Th>
-                <Th>Action</Th>
-              </Tr>
-            </Thead>
-            <Tbody>
-              {quizzes.map((q: Quiz) => (
-                <Tr key={q.id}>
-                  <Td>{q.nama}</Td>
-                  <Td>
-                    <RowAction
-                      onOpenEdit={() => {
-                        router.push(`${router.asPath}/${q.id}/edit`);
-                      }}
-                      onOpenDelete={onOpen}
-                    />
-                  </Td>
-                </Tr>
-              ))}
-            </Tbody>
-          </Table>
-        </TableContainer>
-      </Layout>
-
-      <Modal
-        isOpen={isOpen}
-        onClose={onClose}
-        header={'Hapus Latihan'}
-        onConfirm={handleConfirmDelete}
-      >
-        Yakin ingin menghapus latihan?
-      </Modal>
-    </>
-  );
-}
diff --git a/src/pages/management/course/index.tsx b/src/pages/management/course/index.tsx
index c6cc3f3c3c5e5a352497eda25e83265a3f5f1dcc..8c2e662e4969ac06b14fd02d63ad5fe3081987b6 100644
--- a/src/pages/management/course/index.tsx
+++ b/src/pages/management/course/index.tsx
@@ -252,25 +252,17 @@ export default function CourseManagement() {
             <Tbody>
               {courses.map((c: Course) => (
                 <Tr key={c.id}>
-                  <Td>
-                    <Text>{c.name}</Text>
-                    <HStack my={1}>
-                      <Link href={router.asPath + '/'}>
-                        <Button size="sm" onClick={onOpenEdit}>
-                          <Text ml={2} display={{ base: 'none', lg: 'flex' }}>
-                            Materi
-                          </Text>
-                        </Button>
-                      </Link>
-                      <Link href={router.asPath + '/'}>
-                        <Button size="sm" onClick={onOpenEdit}>
-                          <Text ml={2} display={{ base: 'none', lg: 'flex' }}>
-                            Quiz
-                          </Text>
-                        </Button>
-                      </Link>
-                    </HStack>
-                  </Td>
+                  <Link href={`/management/course/${c.id}`}>
+                    <Td>
+                      <Text
+                        _hover={{
+                          textDecoration: 'underline',
+                        }}
+                      >
+                        {c.name}
+                      </Text>
+                    </Td>
+                  </Link>
                   {/* TODO: ask backend for abbreviation */}
                   <Td>{c.id.slice(0, 2)}</Td>
                   <Td>{c.lecturer.length > 0 ? c.lecturer : '-'}</Td>
diff --git a/src/pages/quiz/[id]/pembahasan.tsx b/src/pages/quiz/[id]/pembahasan.tsx
index 24543d80e5235a0adedd790c6eec4069385ed6cb..734f6ea2269f468ec367f48802f80727a52d72b2 100644
--- a/src/pages/quiz/[id]/pembahasan.tsx
+++ b/src/pages/quiz/[id]/pembahasan.tsx
@@ -24,6 +24,9 @@ function Pembahasan() {
   const [userAnswers, setUserAnswers] = useState<UserAnswer[]>([]);
 
   useEffect(() => {
+    if (!router.query.id || !router.query.userAnswers) {
+      return;
+    }
     setUserAnswers(JSON.parse(router.query.userAnswers as string));
     http
       .get(`/quiz/${router.query.id}/solution`, {
@@ -44,49 +47,53 @@ function Pembahasan() {
     <Layout>
       <Heading my={5}>Pembahasan {name}</Heading>
       <Stack gap={3} mt={10}>
-        {problems.map((problem, index) => (
-          <>
-            <Box key={problem.id} bg="white" borderRadius="lg" p={5}>
-              <Text fontWeight="bold">Nomor {index + 1}</Text>
-              <Text my={3}>{problem.question}</Text>
-              <RadioGroup>
-                <Stack gap={2}>
-                  {problem.answers.map((answer) => (
-                    <Radio
-                      key={answer.id}
-                      value={answer.id}
-                      borderWidth="thick"
-                      borderColor={
-                        answer.is_solution
-                          ? 'green.500'
-                          : userAnswers.find(
-                              (userAnswer) =>
-                                userAnswer.problem_id == problem.id &&
-                                userAnswer.answer_id == answer.id
-                            )
-                          ? 'red.500'
-                          : 'gray.300'
-                      }
-                      isReadOnly
-                    >
-                      {answer.answer}
-                    </Radio>
-                  ))}
-                </Stack>
-              </RadioGroup>
-            </Box>
-            <Flex key={problem.id} bg="white" borderRadius="lg" p={5}>
-              <Text fontWeight="bold">Jawaban:</Text>
-              <Text ml={3}>
-                {
-                  // find the answer that has is_solution = true
-                  problem.answers.find((answer) => answer.is_solution == true)
-                    ?.answer || 'Tidak ada jawaban'
-                }
-              </Text>
-            </Flex>
-          </>
-        ))}
+        {problems.length > 0 ? (
+          problems.map((problem, index) => (
+            <>
+              <Box key={problem.id} bg="white" borderRadius="lg" p={5}>
+                <Text fontWeight="bold">Nomor {index + 1}</Text>
+                <Text my={3}>{problem.question}</Text>
+                <RadioGroup>
+                  <Stack gap={2}>
+                    {problem.answers.map((answer) => (
+                      <Radio
+                        key={answer.id}
+                        value={answer.id}
+                        borderWidth="thick"
+                        borderColor={
+                          answer.is_solution
+                            ? 'green.500'
+                            : userAnswers.find(
+                                (userAnswer) =>
+                                  userAnswer.problem_id == problem.id &&
+                                  userAnswer.answer_id == answer.id
+                              )
+                            ? 'red.500'
+                            : 'gray.300'
+                        }
+                        isReadOnly
+                      >
+                        {answer.answer}
+                      </Radio>
+                    ))}
+                  </Stack>
+                </RadioGroup>
+              </Box>
+              <Flex key={problem.id} bg="white" borderRadius="lg" p={5}>
+                <Text fontWeight="bold">Jawaban:</Text>
+                <Text ml={3}>
+                  {
+                    // find the answer that has is_solution = true
+                    problem.answers.find((answer) => answer.is_solution == true)
+                      ?.answer || 'Tidak ada jawaban'
+                  }
+                </Text>
+              </Flex>
+            </>
+          ))
+        ) : (
+          <Text>Belum ada pembahasan</Text>
+        )}
       </Stack>
       <Flex justifyContent="flex-end" mt={10}>
         <Link href="/">
diff --git a/src/pages/quiz/[id]/result.tsx b/src/pages/quiz/[id]/result.tsx
index 44c6e6352e70bbe59e5e44c092ec41a71dcec8ec..70acbd581c026f6acae0eaaeb5b29a4d4931f57b 100644
--- a/src/pages/quiz/[id]/result.tsx
+++ b/src/pages/quiz/[id]/result.tsx
@@ -9,25 +9,44 @@ import { useEffect, useState } from 'react';
 
 function Result() {
   const router = useRouter();
-  const [score, setScore] = useState(0);
+  const [score, setScore] = useState(-1);
   const [userAnswers, setUserAnswers] = useState<UserAnswer[]>([]);
+  const [isDoneLoading, setIsDoneLoading] = useState(false);
 
   useEffect(() => {
+    if (!router.query.id || !router.query.userAnswers) {
+      return;
+    }
+    // if score is already calculated, return
+    if (score !== -1) {
+      return;
+    }
     // parse user answer as UserAnswer[] from router.query.userAnswers
     setUserAnswers(JSON.parse(router.query.userAnswers as string));
     // POST
     http
-      .post(`/quiz/${router.query.id}/finish`, {
-        data: userAnswers,
-        headers: {
-          Authorization: `Bearer ${getAvailableUserData()}`,
+      .post(
+        `/quiz/${router.query.id}/finish`,
+        {
+          data: userAnswers,
         },
-      })
+        {
+          headers: {
+            Authorization: `Bearer ${getAvailableUserData()}`,
+          },
+        }
+      )
       .then((res) => {
         console.log(res.data.data);
         setScore(res.data.data.score);
+      })
+      .catch((err) => {
+        console.log(err.response.data);
+      })
+      .finally(() => {
+        setIsDoneLoading(true);
       });
-  }, [router.query.id, router.query.userAnswers]);
+  }, [router.query.id, router.query.userAnswers, userAnswers]);
 
   return (
     <Layout>
@@ -40,31 +59,37 @@ function Result() {
           width={{ base: '100%', lg: '50%' }}
         >
           <Text fontSize="2xl">Kuis Selesai</Text>
-          <Text mt={10}>Nilai:</Text>
-          <Text mx={10} fontSize="3xl" fontFamily="Merriweather">
-            {score} / 100
-          </Text>
-          <Stack
-            mt={10}
-            justifyContent="space-between"
-            direction={{ base: 'column', lg: 'row' }}
-          >
-            <Link
-              href={{
-                pathname: router.asPath + '/../pembahasan',
-                query: { userAnswers: JSON.stringify(userAnswers) },
-              }}
-            >
-              <Button bg="#4F4F4F" color="white">
-                Cek Pembahasan
-              </Button>
-            </Link>
-            <Link href={`/`}>
-              <Button bg="biru.600" color="white">
-                Kembali ke Course
-              </Button>
-            </Link>
-          </Stack>
+          {isDoneLoading ? (
+            <>
+              <Text mt={10}>Nilai:</Text>
+              <Text mx={10} fontSize="3xl" fontFamily="Merriweather">
+                {score} / 100
+              </Text>
+              <Stack
+                mt={10}
+                justifyContent="space-between"
+                direction={{ base: 'column', lg: 'row' }}
+              >
+                <Link
+                  href={{
+                    pathname: `${router.asPath}/../pembahasan`,
+                    query: { userAnswers: JSON.stringify(userAnswers) },
+                  }}
+                >
+                  <Button bg="#4F4F4F" color="white">
+                    Cek Pembahasan
+                  </Button>
+                </Link>
+                <Link href={`/`}>
+                  <Button bg="biru.600" color="white">
+                    Kembali ke Course
+                  </Button>
+                </Link>
+              </Stack>
+            </>
+          ) : (
+            <Text my={10}>Loading...</Text>
+          )}
         </Box>
       </Flex>
     </Layout>
diff --git a/src/pages/quiz/[id]/start.tsx b/src/pages/quiz/[id]/start.tsx
index 3a1e5b34ccfbe85f4374376ce1107adaad6fe8fa..c05abd31668f0dfb6b224640691aa7b6236fcc1a 100644
--- a/src/pages/quiz/[id]/start.tsx
+++ b/src/pages/quiz/[id]/start.tsx
@@ -24,25 +24,28 @@ function Quiz() {
   // create countdown from 100 minutes
   const [minutes, setMinutes] = useState(30);
   const [seconds, setSeconds] = useState(0);
+  const [isDoneLoading, setIsDoneLoading] = useState(false);
 
   const router = useRouter();
 
   useEffect(() => {
-    const interval = setInterval(() => {
-      if (seconds > 0) {
-        setSeconds(seconds - 1);
-      }
-      if (seconds === 0) {
-        if (minutes === 0) {
-          clearInterval(interval);
-        } else {
-          setMinutes(minutes - 1);
-          setSeconds(59);
+    if (isDoneLoading) {
+      const interval = setInterval(() => {
+        if (seconds > 0) {
+          setSeconds(seconds - 1);
         }
-      }
-    }, 1000);
-    return () => clearInterval(interval);
-  }, [minutes, seconds]);
+        if (seconds === 0) {
+          if (minutes === 0) {
+            clearInterval(interval);
+          } else {
+            setMinutes(minutes - 1);
+            setSeconds(59);
+          }
+        }
+      }, 1000);
+      return () => clearInterval(interval);
+    }
+  }, [isDoneLoading, minutes, seconds]);
 
   // if minutes is 0, then the quiz is over
   useEffect(() => {
@@ -56,6 +59,9 @@ function Quiz() {
   const hours = Math.floor(minutes / 60);
 
   useEffect(() => {
+    if (!router.query.id) {
+      return;
+    }
     http
       .post(`/quiz/${router.query.id}/take`, {
         Authorization: `Bearer ${getAvailableUserData()}`,
@@ -63,8 +69,12 @@ function Quiz() {
       .then((res) => {
         setQuizName(res.data.data.name);
         setProblems(res.data.data.problems);
-      });
-  });
+      })
+      .catch((err) => {
+        console.log(err.response.data);
+      })
+      .finally(() => setIsDoneLoading(true));
+  }, [router.query.id]);
 
   const handleChangeAnswer = (problemId: string, answerId: string) => {
     // if same problemId already exists, then replace it
@@ -100,34 +110,38 @@ function Quiz() {
         <Stack mb={10} px={{ base: 5, md: 20 }} py={{ base: 5, md: 10 }}>
           <Heading my={5}>{quizName}</Heading>
           <Stack gap={3} mt={10}>
-            {problems.map((problem, index) => (
-              <Box key={problem.id} bg="white" borderRadius="lg" p={5}>
-                <Text fontWeight="bold">Nomor {index + 1}</Text>
-                <Text my={3}>{problem.question}</Text>
-                <RadioGroup>
-                  <Stack gap={2}>
-                    {problem.answers.map((answer) => (
-                      <Radio
-                        key={answer.id}
-                        value={answer.id}
-                        onChange={(e: any) =>
-                          handleChangeAnswer(problem.id, e.target.value)
-                        }
-                        checked={
-                          userAnswers.some(
-                            (userAnswer) =>
-                              userAnswer.problem_id === problem.id &&
-                              userAnswer.answer_id === answer.id
-                          ) ?? false
-                        }
-                      >
-                        {answer.answer}
-                      </Radio>
-                    ))}
-                  </Stack>
-                </RadioGroup>
-              </Box>
-            ))}
+            {isDoneLoading ? (
+              problems.map((problem, index) => (
+                <Box key={problem.id} bg="white" borderRadius="lg" p={5}>
+                  <Text fontWeight="bold">Nomor {index + 1}</Text>
+                  <Text my={3}>{problem.question}</Text>
+                  <RadioGroup>
+                    <Stack gap={2}>
+                      {problem.answers.map((answer) => (
+                        <Radio
+                          key={answer.id}
+                          value={answer.id}
+                          onChange={(e: any) =>
+                            handleChangeAnswer(problem.id, e.target.value)
+                          }
+                          checked={
+                            userAnswers.some(
+                              (userAnswer) =>
+                                userAnswer.problem_id === problem.id &&
+                                userAnswer.answer_id === answer.id
+                            ) ?? false
+                          }
+                        >
+                          {answer.answer}
+                        </Radio>
+                      ))}
+                    </Stack>
+                  </RadioGroup>
+                </Box>
+              ))
+            ) : (
+              <Text>Loading...</Text>
+            )}
           </Stack>
           <Flex justifyContent="flex-end" mt={20}>
             <Button bg="biru.600" color="white" onClick={handleSubmit}>