diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 28c8827..0062780 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -167,6 +167,7 @@ model Project { Group Group @relation(fields: [idGroup], references: [id]) idGroup String name String + status Int @default(0) // 0 = pending, 1 = ongoing, 2 = done, 3 = cancelled desc String @db.Text isActive Boolean @default(true) User User @relation(fields: [createdBy], references: [id]) diff --git a/src/app/(application)/project/[id]/page.tsx b/src/app/(application)/project/[id]/page.tsx index 7ec8b5b..a3143a5 100644 --- a/src/app/(application)/project/[id]/page.tsx +++ b/src/app/(application)/project/[id]/page.tsx @@ -1,9 +1,18 @@ -import { ViewDetailProject } from '@/module/project'; +import { LiatAnggotaDetailProject, ListFileDetailProject, ListTugasDetailProject, NavbarDetailProject, ProgressDetailProject, ViewDetailProject } from '@/module/project'; +import { Box } from '@mantine/core'; import React from 'react'; function Page() { return ( - + + + + + + + + + ); } diff --git a/src/app/(application)/project/page.tsx b/src/app/(application)/project/page.tsx index 8602aa0..6e84723 100644 --- a/src/app/(application)/project/page.tsx +++ b/src/app/(application)/project/page.tsx @@ -1,12 +1,12 @@ import { ViewFilter } from '@/module/_global'; -import { ViewProject } from '@/module/project'; +import { TabProject, ViewProject } from '@/module/project'; import React from 'react'; function Page({ searchParams }: { searchParams: { cat: string } }) { if (searchParams.cat == 'filter') return return ( - + ); } diff --git a/src/app/api/project/[id]/route.ts b/src/app/api/project/[id]/route.ts new file mode 100644 index 0000000..875b6e0 --- /dev/null +++ b/src/app/api/project/[id]/route.ts @@ -0,0 +1,41 @@ +import { prisma } from "@/module/_global"; +import { funGetUserByCookies } from "@/module/auth"; +import { NextResponse } from "next/server"; + + +// GET ONE PROJECT +export async function GET(request: Request, context: { params: { id: string } }) { + try { + const { id } = context.params; + const user = await funGetUserByCookies() + if (user.id == undefined) { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + } + + const data = await prisma.project.findFirst({ + where: { + id: id + }, + select: { + id: true, + name: true, + desc: true, + status: true, + ProjectMember: { + where: { + isActive: true + }, + select: { + idUser: true + } + } + } + }) + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan project", data: data, }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan project, coba lagi nanti", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/project/route.ts b/src/app/api/project/route.ts new file mode 100644 index 0000000..5e42fb0 --- /dev/null +++ b/src/app/api/project/route.ts @@ -0,0 +1,65 @@ +import { prisma } from "@/module/_global"; +import { funGetUserByCookies } from "@/module/auth"; +import _ from "lodash"; +import { NextResponse } from "next/server"; + + + +// GET ALL DATA PROJECT +export async function GET(request: Request) { + try { + const user = await funGetUserByCookies() + if (user.id == undefined) { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + } + + const { searchParams } = new URL(request.url); + + const name = searchParams.get('search'); + const status = searchParams.get('status'); + const villageId = user.idVillage + const groupId = user.idGroup + const userId = user.id + + + const data = await prisma.project.findMany({ + where: { + isActive: true, + idVillage: String(villageId), + idGroup: String(groupId), + createdBy: String(userId), + name: { + contains: (name == undefined || name == "null") ? "" : name, + mode: "insensitive" + }, + status: (status == "0" || status == "1" || status == "2" || status == "3") ? Number(status) : 0 + }, + select: { + id: true, + name: true, + desc: true, + status: true, + ProjectMember: { + where: { + isActive: true + }, + select: { + idUser: true + } + } + } + }) + + const omitData = data.map((v: any) => ({ + ..._.omit(v, ["ProjectMember"]), + member: v.ProjectMember.length + })) + + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan project", data: omitData, }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan project, coba lagi nanti", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/module/project/components/detail_project/detail_project.tsx b/src/module/project/components/detail_project/detail_project.tsx index b7d81f1..eda087d 100644 --- a/src/module/project/components/detail_project/detail_project.tsx +++ b/src/module/project/components/detail_project/detail_project.tsx @@ -109,7 +109,6 @@ export default function DetailProject() { size="xl" value={60} /> - 18 Juni 2024 diff --git a/src/module/project/index.ts b/src/module/project/index.ts index 4e86bf5..2a20dfb 100644 --- a/src/module/project/index.ts +++ b/src/module/project/index.ts @@ -9,6 +9,12 @@ import DetailCreateUserProject from "./components/detail_project/detail_create_u import DetailDateEndTask from "./components/detail_project/detail_date_end_task"; import DetailFileSave from "./components/detail_project/detail_file_save"; import FileUploadProgres from "./components/detail_project/file_upload_progres"; +import TabProject from "./ui/tab_project"; +import NavbarDetailProject from "./ui/navbar_detail_project"; +import ProgressDetailProject from "./ui/progress_detail_project"; +import ListTugasDetailProject from "./ui/list_tugas_detail_project"; +import ListFileDetailProject from "./ui/list_file_detail_project"; +import LiatAnggotaDetailProject from "./ui/liat_anggota_detail_project"; export { ViewProject } export { ViewCreateProject } @@ -20,4 +26,10 @@ export { ViewUpdateProgres } export { DetailCreateUserProject } export { DetailDateEndTask } export { DetailFileSave } -export { FileUploadProgres } \ No newline at end of file +export { FileUploadProgres } +export { TabProject } +export { NavbarDetailProject } +export { ProgressDetailProject } +export { ListTugasDetailProject } +export { ListFileDetailProject } +export { LiatAnggotaDetailProject } \ No newline at end of file diff --git a/src/module/project/lib/api_project.ts b/src/module/project/lib/api_project.ts index e69de29..0761698 100644 --- a/src/module/project/lib/api_project.ts +++ b/src/module/project/lib/api_project.ts @@ -0,0 +1,6 @@ + + +export const funGetAllProject = async (path?: string) => { + const response = await fetch(`/api/project${(path) ? path : ''}`, { next: { tags: ['project'] } }); + return await response.json().catch(() => null); +} \ No newline at end of file diff --git a/src/module/project/lib/type_project.ts b/src/module/project/lib/type_project.ts index e69de29..b5e40ab 100644 --- a/src/module/project/lib/type_project.ts +++ b/src/module/project/lib/type_project.ts @@ -0,0 +1,7 @@ +export interface IDataProject { + id: string + name: string + desc: string + status: number + member: number + } \ No newline at end of file diff --git a/src/module/project/ui/.gitkeep b/src/module/project/ui/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/module/project/ui/liat_anggota_detail_project.tsx b/src/module/project/ui/liat_anggota_detail_project.tsx new file mode 100644 index 0000000..5b4b5a9 --- /dev/null +++ b/src/module/project/ui/liat_anggota_detail_project.tsx @@ -0,0 +1,90 @@ +'use client' +import { WARNA } from '@/module/_global'; +import { Avatar, Box, Flex, Group, Text } from '@mantine/core'; +import React from 'react'; + +const dataTugas = [ + { + id: 1, + name: "Iqbal Ramadan", + image: "https://i.pravatar.cc/1000?img=5", + email: "iqbal.ramadan@gmail.com", + }, + { + id: 2, + name: "Doni Setiawan", + image: "https://i.pravatar.cc/1000?img=10", + email: "doni.setiawan@gmail.com", + }, + { + id: 3, + name: "Rangga Agung", + image: "https://i.pravatar.cc/1000?img=51", + email: "rangga.agung@gmail.com", + }, + { + id: 4, + name: "Ramadan Sananta", + image: "https://i.pravatar.cc/1000?img=15", + email: "ramadan@gmail.com", + }, + { + id: 5, + name: "Imam Baroni", + image: "https://i.pravatar.cc/1000?img=22", + email: "imam.baroni@gmail.com", + }, +]; + +export default function LiatAnggotaDetailProject() { + return ( + + + Anggota Terpilih + Total 10 Anggota + + + + + + Divisi Kerohanian + + {dataTugas.map((v, i) => { + return ( + + + + + + {v.name} + + + {v.email} + + + + + Anggota + + + ); + })} + + + + + ); +} + diff --git a/src/module/project/ui/list_file_detail_project.tsx b/src/module/project/ui/list_file_detail_project.tsx new file mode 100644 index 0000000..54fc7b6 --- /dev/null +++ b/src/module/project/ui/list_file_detail_project.tsx @@ -0,0 +1,22 @@ +'use client' +import { WARNA } from '@/module/_global'; +import { Box, Text } from '@mantine/core'; +import React from 'react'; + +export default function ListFileDetailProject() { + return ( + <> + + File + + Tidak ada file + + + + ); +} + diff --git a/src/module/project/ui/list_project.tsx b/src/module/project/ui/list_project.tsx index 4ab513a..f0569dd 100644 --- a/src/module/project/ui/list_project.tsx +++ b/src/module/project/ui/list_project.tsx @@ -1,158 +1,177 @@ "use client" import { WARNA } from '@/module/_global'; import { ActionIcon, Avatar, Badge, Box, Card, Center, Divider, Flex, Grid, Group, Text, TextInput, Title } from '@mantine/core'; -import { useRouter } from 'next/navigation'; +import { useRouter, useSearchParams } from 'next/navigation'; import React, { useState } from 'react'; import { HiMagnifyingGlass, HiMiniPresentationChartBar, HiOutlineListBullet, HiSquares2X2 } from 'react-icons/hi2'; import { MdAccountCircle } from 'react-icons/md'; import { RiCircleFill } from 'react-icons/ri'; +import { funGetAllProject } from '../lib/api_project'; +import toast from 'react-hot-toast'; +import { useShallowEffect } from '@mantine/hooks'; +import { IDataProject } from '../lib/type_project'; -const dataProject = [ - { - id: 1, - title: 'Project 1', - description: 'Tempat berkumpul semua anggota / staff perbekal darmasaba', - status: 'PROJECT PROSES', - }, - { - id: 2, - title: 'Project 2', - description: 'Tempat berkumpul semua anggota / staff perbekal darmasaba', - status: 'PROJECT PROSES', - }, - { - id: 3, - title: 'Project 3', - description: 'Tempat berkumpul semua anggota / staff perbekal darmasaba', - status: 'PROJECT PROSES', - }, - { - id: 4, - title: 'Project 4', - description: 'Tempat berkumpul semua anggota / staff perbekal darmasaba', - status: 'PROJECT PROSES', - }, - { - id: 5, - title: 'Project 5', - description: 'Tempat berkumpul semua anggota / staff perbekal darmasaba', - status: 'PROJECT PROSES', - }, - { - id: 6, - title: 'Project 6', - description: 'Tempat berkumpul semua anggota / staff perbekal darmasaba', - status: 'PROJECT PROSES', - }, -] export default function ListProject() { const [isList, setIsList] = useState(false) const router = useRouter() + const [isData, setData] = useState([]) + const [loading, setLoading] = useState(true); + const searchParams = useSearchParams() + const status = searchParams.get('status') + const [searchQuery, setSearchQuery] = useState('') + + const fetchData = async () => { + try { + setData([]); + setLoading(true); + + const response = await funGetAllProject('?status=' + status + '&search=' + searchQuery) + + if (response.success) { + setData(response?.data) + } else { + toast.error(response.message); + } + + setLoading(false); + } catch (error) { + toast.error("Gagal mendapatkan proyek, coba lagi nanti"); + console.error(error); + } finally { + setLoading(false); + } + }; + + + useShallowEffect(() => { + fetchData(); + }, [status, searchQuery]); const handleList = () => { setIsList(!isList) } + return ( - - - } - placeholder="Pencarian" - /> - - - - {isList ? ( - - ) : ( - - )} - - - - - - Total Proyek - - 35 - - - {isList ? ( - - {dataProject.map((v, i) => { - return ( - - router.push(`/project/${v.id}`)}> - -
- - - -
- {v.title} -
- - - -
- -
- ); - })} -
- ) : ( - - {dataProject.map((v, i) => { - return ( - - router.push(`/project/${v.id}`)}> - - - - {v.title} - - - - - {v.description} - - {v.status} - - - - - +5 - - - - - - ); - })} - - )} + + + } + placeholder="Pencarian" + onChange={(event) => setSearchQuery(event.currentTarget.value)} + value={searchQuery} + /> + + + + {isList ? ( + + ) : ( + + )} + + + + + + Total Proyek + + {isData.length} + - + {isList ? ( + + {isData.map((v, i) => { + return ( + + router.push(`/project/${v.id}`)}> + +
+ + + +
+ {v.name} +
+ + + +
+ +
+ ); + })} +
+ ) : ( + + {isData.map((v, i) => { + return ( + + router.push(`/project/${v.id}`)}> + + + + {v.name} + + + + + {v.desc} + + { + v.status === 0 ? 'Segera' : + v.status === 1 ? 'Dikerjakan' : + v.status === 2 ? 'Selesai' : + v.status === 3 ? 'Dibatalkan' : + "" + } + + + + + +{v.member - 1} + + + + + + ); + })} + + )} +
+
); } diff --git a/src/module/project/ui/list_tugas_detail_project.tsx b/src/module/project/ui/list_tugas_detail_project.tsx new file mode 100644 index 0000000..36f566a --- /dev/null +++ b/src/module/project/ui/list_tugas_detail_project.tsx @@ -0,0 +1,80 @@ +'use client' +import { WARNA } from '@/module/_global'; +import { Box, Center, Checkbox, Grid, Group, SimpleGrid, Text } from '@mantine/core'; +import React from 'react'; +import { AiOutlineFileSync } from 'react-icons/ai'; + +export default function ListTugasDetailProject() { + return ( + <> + + + Tanggal & Tugas + + + + +
+ +
+
+ + + + + Laporan Permasyarakatan + + + + + + Tanggal Mulai + + 16 Juni 2024 + + + + Tanggal Berakhir + + 20 Juni 2024 + + + + + +
+
+
+ + ); +} + diff --git a/src/module/project/ui/navbar_detail_project.tsx b/src/module/project/ui/navbar_detail_project.tsx new file mode 100644 index 0000000..0236ad5 --- /dev/null +++ b/src/module/project/ui/navbar_detail_project.tsx @@ -0,0 +1,104 @@ +'use client' +import { LayoutDrawer, LayoutNavbarNew, WARNA } from '@/module/_global'; +import { ActionIcon, Box, Flex, SimpleGrid, Stack, Text } from '@mantine/core'; +import { useParams, useRouter } from 'next/navigation'; +import React, { useState } from 'react'; +import { FaPencil, FaUsers } from 'react-icons/fa6'; +import { HiMenu } from 'react-icons/hi'; +import { IoAddCircle } from 'react-icons/io5'; +import { MdCancel } from 'react-icons/md'; + +export default function NavbarDetailProject() { + const router = useRouter() + const param = useParams<{ id: string }>() + const [name, setName] = useState('') + const [isOpen, setOpen] = useState(false) + return ( + <> + { setOpen(true) }} + > + + + } /> + + + setOpen(false)}> + + + + { + router.push(param.id + '/add-task') + }} + > + + + + + Tambah Tugas + + + + { + router.push(param.id + '/add-member') + }} + > + + + + + Tambah anggota + + + + { router.push(param.id + '/cancel') }} + > + + + + + Batal + + + + { router.push(param.id + '/edit') }} + > + + + + + Edit + + + + + + + + ); +} + diff --git a/src/module/project/ui/progress_detail_project.tsx b/src/module/project/ui/progress_detail_project.tsx new file mode 100644 index 0000000..e2a5551 --- /dev/null +++ b/src/module/project/ui/progress_detail_project.tsx @@ -0,0 +1,51 @@ +'use client' +import { WARNA } from '@/module/_global'; +import { ActionIcon, Box, Grid, Progress, Text } from '@mantine/core'; +import React from 'react'; +import { HiMiniPresentationChartBar } from 'react-icons/hi2'; + +export default function ProgressDetailProject() { + return ( + <> + + + + + + + + + + + Kemajuan Proyek 60% + + + + + + + + ); +} + diff --git a/src/module/project/ui/tab_project.tsx b/src/module/project/ui/tab_project.tsx index 04f2f34..0526b8d 100644 --- a/src/module/project/ui/tab_project.tsx +++ b/src/module/project/ui/tab_project.tsx @@ -5,8 +5,8 @@ import React, { useEffect, useState } from 'react'; import { HiMenu } from 'react-icons/hi'; import { HiMagnifyingGlass, HiMiniPresentationChartBar, HiOutlineListBullet, HiSquares2X2 } from 'react-icons/hi2'; import { MdAccountCircle } from 'react-icons/md'; -import { RiCircleFill } from "react-icons/ri"; -import { useRouter } from 'next/navigation'; +import { RiCircleFill, RiProgress3Line } from "react-icons/ri"; +import { useRouter, useSearchParams } from 'next/navigation'; import { TbClockPause } from 'react-icons/tb'; import { IoIosCheckmarkCircleOutline } from 'react-icons/io'; import { IoCloseCircleOutline } from 'react-icons/io5'; @@ -15,46 +15,11 @@ import MenuDrawerProject from './menu_drawer_project'; export default function TabProject() { const [openDrawer, setOpenDrawer] = useState(false) - + const router = useRouter() + const searchParams = useSearchParams() + const status = searchParams.get('status') const iconStyle = { width: rem(20), height: rem(20) }; - const tabsData = [ - { - value: 'segera', - label: 'Proyek Proses', - mobileLabel: 'Proses', - icon: , - }, - { - value: 'selesai', - label: 'Proyek Selesai', - mobileLabel: 'Selesai', - icon: , - }, - { - value: 'batal', - label: 'Proyek Batal', - mobileLabel: ' Batal', - icon: , - }, - ]; - const [isMobile, setIsMobile] = useState(false); - - useEffect(() => { - const handleResize = () => { - if (window.innerWidth < 495) { - setIsMobile(true); - } else { - setIsMobile(false); - } - }; - window.addEventListener('resize', handleResize); - handleResize(); - return () => { - window.removeEventListener('resize', handleResize); - }; - }, []); - return ( } /> - + - {tabsData.map((tab) => ( - - {isMobile ? tab.mobileLabel : tab.label} - - ))} + } + onClick={() => { router.push("?status=0") }} + color={WARNA.biruTua} + > + Segera + + } + onClick={() => { router.push("?status=1") }} + color={WARNA.biruTua} + > + Dikerjakan + + } + onClick={() => { router.push("?status=2") }} + color={WARNA.biruTua}> + Selesai + + } + onClick={() => { router.push("?status=3") }} + color={WARNA.biruTua}> + Batal + - - - - - - - - - - - - + setOpenDrawer(false)}> - + ); diff --git a/src/module/task/ui/list_division_task.tsx b/src/module/task/ui/list_division_task.tsx index 22ad55f..0979c0f 100644 --- a/src/module/task/ui/list_division_task.tsx +++ b/src/module/task/ui/list_division_task.tsx @@ -9,52 +9,6 @@ import { funGetAllTask } from "../lib/api_task"; import toast from "react-hot-toast"; import { useShallowEffect } from "@mantine/hooks"; - -const dataProject = [ - { - id: 1, - title: 'Project 1', - description: 'Tempat berkumpul semua anggota / staff perbekal darmasaba', - status: 'PROJECT SELESAI', - color: '#387529' - }, - { - id: 2, - title: 'Project 2', - description: 'Tempat berkumpul semua anggota / staff perbekal darmasaba', - status: 'PROJECT SELESAI', - color: '#387529' - }, - { - id: 3, - title: 'Project 3', - description: 'Tempat berkumpul semua anggota / staff perbekal darmasaba', - status: 'PROJECT SELESAI', - color: '#387529' - }, - { - id: 4, - title: 'Project 4', - description: 'Tempat berkumpul semua anggota / staff perbekal darmasaba', - status: 'PROSES', - color: '#C5771A' - }, - { - id: 5, - title: 'Project5', - description: 'Tempat berkumpul semua anggota / staff perbekal darmasaba', - status: 'PROSES', - color: '#C5771A' - }, - { - id: 6, - title: 'Project 6', - description: 'Tempat berkumpul semua anggota / staff perbekal darmasaba', - status: 'PROSES', - color: '#C5771A' - }, -] - export default function ListDivisionTask() { const [isList, setIsList] = useState(false) const router = useRouter() diff --git a/src/module/task/ui/navbar_detail_division_task.tsx b/src/module/task/ui/navbar_detail_division_task.tsx index 450048d..5940198 100644 --- a/src/module/task/ui/navbar_detail_division_task.tsx +++ b/src/module/task/ui/navbar_detail_division_task.tsx @@ -122,7 +122,6 @@ export default function NavbarDetailDivisionTask() { - ) } \ No newline at end of file