diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9421ad0..f111c5b 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -202,6 +202,7 @@ model Project { ProjectFile ProjectFile[] ProjectComment ProjectComment[] ProjectTask ProjectTask[] + ProjectLink ProjectLink[] } model ProjectMember { @@ -228,6 +229,16 @@ model ProjectFile { updatedAt DateTime @updatedAt } +model ProjectLink { + id String @id @default(cuid()) + Project Project @relation(fields: [idProject], references: [id]) + idProject String + link String @db.Text + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + model ProjectTask { id String @id @default(cuid()) Project Project @relation(fields: [idProject], references: [id]) @@ -280,6 +291,7 @@ model Division { DivisionCalendar DivisionCalendar[] DivisionCalendarReminder DivisionCalendarReminder[] ContainerFileDivision ContainerFileDivision[] + DivisionProjectLink DivisionProjectLink[] } model DivisionMember { @@ -309,6 +321,19 @@ model DivisionProject { DivisionProjectTask DivisionProjectTask[] DivisionProjectMember DivisionProjectMember[] DivisionProjectFile DivisionProjectFile[] + DivisionProjectLink DivisionProjectLink[] +} + +model DivisionProjectLink { + id String @id @default(cuid()) + Division Division @relation(fields: [idDivision], references: [id]) + idDivision String + DivisionProject DivisionProject @relation(fields: [idProject], references: [id]) + idProject String + link String @db.Text + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt } model DivisionProjectTask { diff --git a/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/page.tsx b/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/page.tsx index 293102f..37e2386 100644 --- a/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/page.tsx +++ b/src/app/(application)/division/[id]/(fitur-division)/task/[detail]/page.tsx @@ -1,4 +1,4 @@ -import { NavbarDetailDivisionTask, ProgressDetailTask, ListTugasDetailTask, ListFileDetailTask, ListAnggotaDetailTask } from "@/module/task" +import { ListAnggotaDetailTask, ListFileDetailTask, ListLinkDetailTask, ListTugasDetailTask, NavbarDetailDivisionTask, ProgressDetailTask } from "@/module/task" import { Box } from "@mantine/core" function Page() { @@ -9,6 +9,7 @@ function Page() { + diff --git a/src/app/(application)/project/[id]/page.tsx b/src/app/(application)/project/[id]/page.tsx index 26dfc03..c54659b 100644 --- a/src/app/(application)/project/[id]/page.tsx +++ b/src/app/(application)/project/[id]/page.tsx @@ -1,6 +1,5 @@ -import { ListAnggotaDetailProject, ListFileDetailProject, ListTugasDetailProject, NavbarDetailProject, ProgressDetailProject } from '@/module/project'; +import { ListAnggotaDetailProject, ListFileDetailProject, ListLinkDetailProject, ListTugasDetailProject, NavbarDetailProject, ProgressDetailProject } from '@/module/project'; import { Box } from '@mantine/core'; -import React from 'react'; function Page() { return ( @@ -10,6 +9,7 @@ function Page() { + diff --git a/src/app/api/project/[id]/link/route.ts b/src/app/api/project/[id]/link/route.ts new file mode 100644 index 0000000..a133801 --- /dev/null +++ b/src/app/api/project/[id]/link/route.ts @@ -0,0 +1,90 @@ +import { prisma } from "@/module/_global"; +import { funGetUserByCookies } from "@/module/auth"; +import { createLogUser } from "@/module/user"; +import { NextResponse } from "next/server"; + +// ADD LINK PROJECT +export async function POST(request: Request, context: { params: { id: string } }) { + try { + const user = await funGetUserByCookies() + if (user.id == undefined) { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + } + + const { id } = context.params + const { link } = (await request.json()) + + const data = await prisma.project.count({ + where: { + id: id + } + }) + + if (data == 0) { + return NextResponse.json( + { + success: false, message: "Gagal mendapatkan kegiatan, data tidak ditemukan", + }, + { status: 404 } + ); + } + + + const insertLink = await prisma.projectLink.create({ + data: { + idProject: id, + link: link + } + }) + + // create log user + const log = await createLogUser({ act: 'CREATE', desc: 'User menambah link kegiatan', table: 'projectLink', data: insertLink.id }) + return NextResponse.json({ success: true, message: "Berhasil menambahkan link kegiatan" }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal menambah link kegiatan, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} + + +// DELETE LINK PROJECT +export async function DELETE(request: Request, context: { params: { id: string } }) { + try { + const user = await funGetUserByCookies() + if (user.id == undefined) { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + } + + const { idLink } = (await request.json()) + + const data = await prisma.projectLink.count({ + where: { + id: idLink + } + }) + + if (data == 0) { + return NextResponse.json( + { + success: false, message: "Gagal mendapatkan link, data tidak ditemukan", + }, + { status: 404 } + ); + } + + const deleteLink = await prisma.projectLink.delete({ + where: { + id: idLink + } + }) + + // create log user + const log = await createLogUser({ act: 'DELETE', desc: 'User menghapus link kegiatan', table: 'projectLink', data: String(idLink) }) + return NextResponse.json({ success: true, message: "Berhasil menghapus link kegiatan" }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal menghapus link kegiatan, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} diff --git a/src/app/api/project/[id]/route.ts b/src/app/api/project/[id]/route.ts index 19bc836..ec851c0 100644 --- a/src/app/api/project/[id]/route.ts +++ b/src/app/api/project/[id]/route.ts @@ -133,6 +133,17 @@ export async function GET(request: Request, context: { params: { id: string } }) })) allData = fix + } else if (kategori == "link") { + const dataLink = await prisma.projectLink.findMany({ + where: { + isActive: true, + idProject: String(id) + }, + orderBy: { + createdAt: 'asc' + } + }) + allData = dataLink } return NextResponse.json({ success: true, message: "Berhasil mendapatkan kegiatan", data: allData, }, { status: 200 }); diff --git a/src/app/api/task/[id]/link/route.ts b/src/app/api/task/[id]/link/route.ts new file mode 100644 index 0000000..8f9146c --- /dev/null +++ b/src/app/api/task/[id]/link/route.ts @@ -0,0 +1,95 @@ +import { prisma } from "@/module/_global"; +import { funGetUserByCookies } from "@/module/auth"; +import { createLogUser } from "@/module/user"; +import { NextResponse } from "next/server"; + +// ADD LINK TASK DIVISI +export async function POST(request: Request, context: { params: { id: string } }) { + try { + const user = await funGetUserByCookies() + if (user.id == undefined) { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + } + + const { id } = context.params; + const { link, idDivision } = (await request.json()); + + const data = await prisma.divisionProject.count({ + where: { + id: id, + }, + }); + + if (data == 0) { + return NextResponse.json( + { + success: false, + message: "Tambah link tugas gagal, data tugas tidak ditemukan", + }, + { status: 404 } + ); + } + + + + const insertlink = await prisma.divisionProjectLink.create({ + data: { + idProject: id, + link, + idDivision, + } + }) + + + // create log user + const log = await createLogUser({ act: 'CREATE', desc: 'User menambahkan link tugas divisi', table: 'divisionProjectLink', data: insertlink.id }) + + + return NextResponse.json({ success: true, message: "Berhasil menambahkan link tugas", }, { status: 200 }); + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal menambah link tugas, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} + + +// DELETE LINK TASK DIVISI +export async function DELETE(request: Request, context: { params: { id: string } }) { + try { + const user = await funGetUserByCookies() + if (user.id == undefined) { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + } + + const { idLink } = (await request.json()) + + const data = await prisma.divisionProjectLink.count({ + where: { + id: idLink + } + }) + + if (data == 0) { + return NextResponse.json( + { + success: false, message: "Gagal mendapatkan link, data tidak ditemukan", + }, + { status: 404 } + ); + } + + const deleteLink = await prisma.divisionProjectLink.delete({ + where: { + id: idLink + } + }) + + // create log user + const log = await createLogUser({ act: 'DELETE', desc: 'User menghapus link tugas divisi', table: 'divisionProjectLink', data: String(idLink) }) + return NextResponse.json({ success: true, message: "Berhasil menghapus link tugas divisi" }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal menghapus link tugas divisi, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/task/[id]/route.ts b/src/app/api/task/[id]/route.ts index 07e9dc2..dbc2ef1 100644 --- a/src/app/api/task/[id]/route.ts +++ b/src/app/api/task/[id]/route.ts @@ -150,6 +150,18 @@ export async function GET(request: Request, context: { params: { id: string } }) })) allData = fix + } else if (kategori == "link") { + const dataLink = await prisma.divisionProjectLink.findMany({ + where: { + isActive: true, + idProject: String(id) + }, + orderBy: { + createdAt: 'asc' + } + }) + + allData = dataLink } return NextResponse.json({ success: true, message: "Berhasil mendapatkan tugas divisi", data: allData }, { status: 200 }); diff --git a/src/lib/urlCompleted.ts b/src/lib/urlCompleted.ts new file mode 100644 index 0000000..2af7aad --- /dev/null +++ b/src/lib/urlCompleted.ts @@ -0,0 +1,7 @@ +export function urlCompleted(url: string) { + if (url.startsWith("http://") || url.startsWith("https://")) { + return url + } else { + return `https://${url}` + } +} \ No newline at end of file diff --git a/src/module/project/index.ts b/src/module/project/index.ts index 1c33e04..e4b0d43 100644 --- a/src/module/project/index.ts +++ b/src/module/project/index.ts @@ -15,6 +15,7 @@ import AddMemberDetailProject from "./ui/add_member_detail_project"; import CreateProject from "./ui/create_project"; import AddFileDetailProject from "./ui/add_file_detail_project"; import WrapLayoutProject from "./ui/wrap_project"; +import ListLinkDetailProject from "./ui/list_link_detail_project"; export { ViewDateEndTask } export { CreateUsersProject } @@ -31,4 +32,5 @@ export { CancelProject } export { AddMemberDetailProject } export { CreateProject } export { AddFileDetailProject } -export { WrapLayoutProject } \ No newline at end of file +export { WrapLayoutProject } +export { ListLinkDetailProject } \ 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 255faae..b64563b 100644 --- a/src/module/project/lib/api_project.ts +++ b/src/module/project/lib/api_project.ts @@ -1,4 +1,4 @@ -import { IFormAddDetailproject, IFormAddMemberProject, IFormDateProject, NewIFormDateProject } from "./type_project"; +import { IFormAddDetailproject, IFormAddMemberProject, NewIFormDateProject } from "./type_project"; export const funGetAllProject = async (path?: string) => { @@ -149,6 +149,17 @@ export const funAddFileProject = async (path: string, data: FormData) => { return await response.json().catch(() => null); }; +export const funDeleteLinkProject = async (path: string, data: { idLink: string }) => { + const response = await fetch(`/api/project/${path}/link`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + return await response.json().catch(() => null); +}; + export const funDeleteProject = async (path: string) => { const response = await fetch(`/api/project/${path}/lainnya`, { method: "DELETE", @@ -159,3 +170,14 @@ export const funDeleteProject = async (path: string) => { return await response.json().catch(() => null); }; + +export const funAddLinkProject = async (path: string, data: {link: string}) => { + const response = await fetch(`/api/project/${path}/link`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + return await response.json().catch(() => null); +} diff --git a/src/module/project/lib/type_project.ts b/src/module/project/lib/type_project.ts index 8c1bb4c..4b26867 100644 --- a/src/module/project/lib/type_project.ts +++ b/src/module/project/lib/type_project.ts @@ -20,7 +20,13 @@ export interface IDataFileProject { id: string name: string extension: string - idStorage:string + idStorage: string +} + + +export interface IDataLinkProject { + id: string + link: string } export interface IDataMemberProject { @@ -46,7 +52,7 @@ export interface IFormDateProject { title: string, } -export interface NewIFormDateProject{ +export interface NewIFormDateProject { dateStart: string, dateEnd: string, title: string, diff --git a/src/module/project/ui/list_link_detail_project.tsx b/src/module/project/ui/list_link_detail_project.tsx new file mode 100644 index 0000000..eb2440b --- /dev/null +++ b/src/module/project/ui/list_link_detail_project.tsx @@ -0,0 +1,231 @@ +'use client' +import { urlCompleted } from '@/lib/urlCompleted'; +import { globalRole, keyWibu, LayoutDrawer, TEMA } from '@/module/_global'; +import LayoutModal from '@/module/_global/layout/layout_modal'; +import { useHookstate } from '@hookstate/core'; +import { Box, Flex, Grid, Group, SimpleGrid, Stack, Text } from '@mantine/core'; +import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; +import { useParams } from 'next/navigation'; +import { useState } from 'react'; +import toast from 'react-hot-toast'; +import { FaTrash } from 'react-icons/fa6'; +import { LuLink } from 'react-icons/lu'; +import { RiExternalLinkLine } from 'react-icons/ri'; +import { useWibuRealtime } from 'wibu-realtime'; +import { funDeleteLinkProject, funGetOneProjectById } from '../lib/api_project'; +import { IDataLinkProject } from '../lib/type_project'; +import { globalIsMemberProject } from '../lib/val_project'; + +export default function ListLinkDetailProject() { + const [isData, setData] = useState([]) + const param = useParams<{ id: string }>() + const [loading, setLoading] = useState(true) + const [loadingDelete, setLoadingDelete] = useState(false) + const [idData, setIdData] = useState('') + const [linkData, setLinkData] = useState('') + const [openDrawer, setOpenDrawer] = useState(false) + const [isOpenModal, setOpenModal] = useState(false) + const tema = useHookstate(TEMA) + const roleLogin = useHookstate(globalRole) + const memberProject = useHookstate(globalIsMemberProject) + const isMobile = useMediaQuery("(max-width: 350px)"); + const [reason, setReason] = useState("") + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) + + async function getOneDataCancel() { + try { + const res = await funGetOneProjectById(param.id, 'data'); + if (res.success) { + setReason(res.data.reason); + } else { + toast.error(res.message); + } + + } catch (error) { + console.error(error); + toast.error("Gagal mendapatkan data Kegiatan, coba lagi nanti"); + } + } + + useShallowEffect(() => { + getOneDataCancel(); + }, [param.id]) + + async function getOneData(loading: boolean) { + try { + setLoading(loading) + const res = await funGetOneProjectById(param.id, 'link'); + if (res.success) { + setData(res.data) + } else { + toast.error(res.message); + } + + } catch (error) { + console.error(error); + toast.error("Gagal mendapatkan link kegiatan, coba lagi nanti"); + } finally { + setLoading(false) + } + } + + useShallowEffect(() => { + getOneData(true); + }, [param.id]) + + + async function onDelete() { + try { + setLoadingDelete(true) + const res = await funDeleteLinkProject(param.id, { idLink: idData }); + if (res.success) { + setDataRealtime([{ + category: "project-detail-link", + id: param.id, + }]) + toast.success(res.message) + getOneData(false) + setIdData("") + setOpenDrawer(false) + } else { + toast.error(res.message); + } + } catch (error) { + console.error(error); + toast.error("Gagal menghapus link, coba lagi nanti"); + } finally { + setOpenModal(false) + setLoadingDelete(false) + } + + } + + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'project-detail-link' && i.id == param.id)) { + getOneData(false) + } else if (dataRealTime && dataRealTime.some((i: any) => i.category == 'project-detail-status' && i.id == param.id)) { + getOneDataCancel() + } + }, [dataRealTime]) + + return ( + <> + { + isData.length > 0 + && + + Link + + { + isData.map((item, index) => { + return ( + { + setLinkData(item.link) + setIdData(item.id) + setOpenDrawer(true) + }} + > + + + + + + {item.link} + + + + + + ) + }) + } + + + + Menu} onClose={() => setOpenDrawer(false)}> + + + + { + { window.open(urlCompleted(linkData), '_blank', 'noopener,noreferrer') }} justify={'center'} align={'center'} direction={'column'} > + + + + + Buka Link + + + } + + { + (roleLogin.get() == "user" || roleLogin.get() == "coadmin") && !memberProject.get() ? <> + : + { + reason == null ? + setOpenModal(true) + : setOpenModal(false) + }} justify={'center'} align={'center'} direction={'column'} > + + + + + Hapus + + + } + + + + + + + setOpenModal(false)} + description="Apakah Anda yakin ingin menghapus link ini? Link yang dihapus tidak dapat dikembalikan" + onYes={(val) => { + if (val) { + onDelete() + } else { + setOpenModal(false) + } + }} /> + + + } + + ); +} + diff --git a/src/module/project/ui/navbar_detail_project.tsx b/src/module/project/ui/navbar_detail_project.tsx index f3ff1ea..bbe5d7b 100644 --- a/src/module/project/ui/navbar_detail_project.tsx +++ b/src/module/project/ui/navbar_detail_project.tsx @@ -3,7 +3,7 @@ import { globalRole, keyWibu, LayoutDrawer, LayoutNavbarNew, TEMA } from '@/modu import LayoutModal from '@/module/_global/layout/layout_modal'; import { funGetUserByCookies } from '@/module/auth'; import { useHookstate } from '@hookstate/core'; -import { ActionIcon, Box, Flex, SimpleGrid, Stack, Text } from '@mantine/core'; +import { ActionIcon, Box, Button, Flex, Grid, Modal, SimpleGrid, Stack, Text, TextInput } from '@mantine/core'; import { useShallowEffect } from '@mantine/hooks'; import { useParams, useRouter } from 'next/navigation'; import { useState } from 'react'; @@ -11,9 +11,10 @@ import toast from 'react-hot-toast'; import { FaFileCirclePlus, FaPencil, FaTrash, FaUsers } from 'react-icons/fa6'; import { HiMenu } from 'react-icons/hi'; import { IoAddCircle } from 'react-icons/io5'; +import { LuLink } from 'react-icons/lu'; import { MdCancel } from 'react-icons/md'; import { useWibuRealtime } from 'wibu-realtime'; -import { funDeleteProject, funGetOneProjectById } from '../lib/api_project'; +import { funAddLinkProject, funDeleteProject, funGetOneProjectById } from '../lib/api_project'; import { globalIsMemberProject } from '../lib/val_project'; export default function NavbarDetailProject() { @@ -28,7 +29,10 @@ export default function NavbarDetailProject() { const tema = useHookstate(TEMA) const [reason, setReason] = useState("") const [openModal, setOpenModal] = useState(false) + const [openNewLink, setOpenNewLink] = useState(false) const [loadingModal, setLoadingModal] = useState(false) + const [loadingLink, setLoadingLink] = useState(false) + const [valLink, setValLink] = useState("") const [dataRealTime, setDataRealtime] = useWibuRealtime({ WIBU_REALTIME_TOKEN: keyWibu, project: "sdm" @@ -52,6 +56,30 @@ export default function NavbarDetailProject() { } } + async function addLinkProject() { + try { + setLoadingLink(true) + const res = await funAddLinkProject(param.id, { link: valLink }); + if (res.success) { + setDataRealtime([{ + category: "project-detail-link", + id: param.id, + user: res.user + }]) + toast.success(res.message) + } else { + toast.error(res.message) + } + } catch (error) { + console.error(error); + toast.error("Gagal menambahkan link, coba lagi nanti"); + } finally { + setLoadingLink(false) + setOpenNewLink(false) + setValLink("") + } + } + async function deleteDataProject() { try { setLoadingModal(true) @@ -157,6 +185,27 @@ export default function NavbarDetailProject() { + { + if (reason == null) { + setOpen(false) + setOpenNewLink(true) + } else { + null + } + }} + > + + + + + Tambah link + + + { (roleLogin.get() != "user" && roleLogin.get() != "coadmin") && <> @@ -229,11 +278,50 @@ export default function NavbarDetailProject() { - + setOpenModal(false)} description="Apakah Anda yakin ingin menghapus kegiatan ini?" onYes={(val) => { val ? deleteDataProject() : setOpenModal(false) }} /> + + setOpenNewLink(false)} centered withCloseButton={false}> + + Tambah Link + + setValLink(e.target.value)} + /> + + + + + + + + + + + + ); } diff --git a/src/module/task/index.ts b/src/module/task/index.ts index fcaa02e..48a67fc 100644 --- a/src/module/task/index.ts +++ b/src/module/task/index.ts @@ -7,6 +7,7 @@ import CreateTask from "./ui/create_task"; import CreateUsersProject from "./ui/create_users_project"; import ListAnggotaDetailTask from "./ui/detail_list_anggota_task"; import ListFileDetailTask from "./ui/detail_list_file_task"; +import ListLinkDetailTask from "./ui/detail_list_link_task"; import ListTugasDetailTask from "./ui/detail_list_tugas_task"; import ProgressDetailTask from "./ui/detail_progress_task"; import EditDetailTask from "./ui/edit_detail_task"; @@ -32,4 +33,5 @@ export { AddDetailTask } export { AddMemberDetailTask } export { CancelTask } export { EditTask } -export { AddFileDetailTask } \ No newline at end of file +export { AddFileDetailTask } +export { ListLinkDetailTask } \ No newline at end of file diff --git a/src/module/task/lib/api_task.ts b/src/module/task/lib/api_task.ts index 077bd98..1c8cba3 100644 --- a/src/module/task/lib/api_task.ts +++ b/src/module/task/lib/api_task.ts @@ -154,4 +154,27 @@ export const funDeleteTask = async (path: string) => { }, }); return await response.json().catch(() => null); -}; \ No newline at end of file +}; + +export const funAddLinkTask = async (path: string, data: { link: string, idDivision: string }) => { + const response = await fetch(`/api/task/${path}/link`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + return await response.json().catch(() => null); +}; + + +export const funDeleteLinkTask = async (path: string, data: { idLink: string }) => { + const response = await fetch(`/api/task/${path}/link`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + return await response.json().catch(() => null); +}; diff --git a/src/module/task/lib/type_task.ts b/src/module/task/lib/type_task.ts index ed88fb0..96dab26 100644 --- a/src/module/task/lib/type_task.ts +++ b/src/module/task/lib/type_task.ts @@ -70,4 +70,9 @@ export interface IDataFileTaskDivision { extension: string, nameInStorage: string, idStorage: string -} \ No newline at end of file +} + +export interface IDataLinkTaskDivision { + id: string + link: string +} diff --git a/src/module/task/ui/detail_list_link_task.tsx b/src/module/task/ui/detail_list_link_task.tsx new file mode 100644 index 0000000..2e84f45 --- /dev/null +++ b/src/module/task/ui/detail_list_link_task.tsx @@ -0,0 +1,230 @@ +'use client' +import { urlCompleted } from '@/lib/urlCompleted'; +import { globalRole, keyWibu, LayoutDrawer, TEMA } from '@/module/_global'; +import LayoutModal from '@/module/_global/layout/layout_modal'; +import { globalIsMemberDivision } from '@/module/division_new'; +import { useHookstate } from '@hookstate/core'; +import { Box, Flex, Grid, Group, SimpleGrid, Stack, Text } from '@mantine/core'; +import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; +import { useParams } from 'next/navigation'; +import { useState } from 'react'; +import toast from 'react-hot-toast'; +import { FaTrash } from 'react-icons/fa6'; +import { LuLink } from 'react-icons/lu'; +import { RiExternalLinkLine } from 'react-icons/ri'; +import { useWibuRealtime } from 'wibu-realtime'; +import { funDeleteLinkTask, funGetTaskDivisionById } from '../lib/api_task'; +import { IDataLinkTaskDivision } from '../lib/type_task'; + +export default function ListLinkDetailTask() { + const isMobile = useMediaQuery("(max-width: 350px)"); + const roleLogin = useHookstate(globalRole) + const memberDivision = useHookstate(globalIsMemberDivision) + const [isData, setData] = useState([]) + const [loading, setLoading] = useState(true) + const param = useParams<{ id: string, detail: string }>() + const [openDrawer, setOpenDrawer] = useState(false) + const [isOpenModal, setOpenModal] = useState(false) + const [loadingModal, setLoadingModal] = useState(false) + const [linkData, setLinkData] = useState('') + const [idData, setIdData] = useState('') + const tema = useHookstate(TEMA) + const [reason, setReason] = useState("") + const [dataRealTime, setDataRealtime] = useWibuRealtime({ + WIBU_REALTIME_TOKEN: keyWibu, + project: "sdm" + }) + + + async function getOneDataCancel() { + try { + const res = await funGetTaskDivisionById(param.detail, 'data'); + if (res.success) { + setReason(res.data.reason); + } else { + toast.error(res.message); + } + + } catch (error) { + console.error(error); + toast.error("Gagal mendapatkan data tugas divisi, coba lagi nanti"); + } + } + + useShallowEffect(() => { + getOneDataCancel(); + }, [param.detail]) + + async function getOneData(loading: boolean) { + try { + setLoading(loading) + const res = await funGetTaskDivisionById(param.detail, 'link'); + if (res.success) { + setData(res.data) + } else { + toast.error(res.message); + } + } catch (error) { + console.error(error); + toast.error("Gagal mendapatkan file tugas divisi, coba lagi nanti"); + } finally { + setLoading(false) + } + } + + useShallowEffect(() => { + getOneData(true); + }, [param.detail]) + + + async function onDelete() { + try { + setLoadingModal(true) + const res = await funDeleteLinkTask(param.detail, { idLink: idData }); + if (res.success) { + setDataRealtime([{ + category: "tugas-detail-link", + id: param.detail, + }]) + toast.success(res.message) + getOneData(false) + setIdData("") + setOpenDrawer(false) + } else { + toast.error(res.message); + } + } catch (error) { + console.error(error); + toast.error("Gagal menghapus file, coba lagi nanti"); + } finally { + setLoadingModal(false) + setOpenModal(false) + } + } + + useShallowEffect(() => { + if (dataRealTime && dataRealTime.some((i: any) => i.category == 'tugas-detail-link' && i.id == param.detail)) { + getOneData(false) + } else if (dataRealTime && dataRealTime.some((i: any) => i.category == 'tugas-detail-status' && i.id == param.detail)) { + getOneDataCancel() + } + }, [dataRealTime]) + + return ( + <> + { + isData.length > 0 + && + + Link + + { + isData.map((item, index) => { + return ( + { + setLinkData(item.link) + setIdData(item.id) + setOpenDrawer(true) + }} + > + + + + + + {item.link} + + + + + + ) + }) + } + + + + Menu} onClose={() => setOpenDrawer(false)}> + + + + { + { window.open(urlCompleted(linkData), '_blank', 'noopener,noreferrer') }} justify={'center'} align={'center'} direction={'column'} > + + + + + Buka Link + + + } + + { + (roleLogin.get() == "user" || roleLogin.get() == "coadmin") && !memberDivision.get() ? <> + : + { + reason == null ? + setOpenModal(true) + : setOpenModal(false) + }} justify={'center'} align={'center'} direction={'column'} > + + + + + Hapus + + + } + + + + + + + setOpenModal(false)} + description="Apakah Anda yakin ingin menghapus link ini? Link yang dihapus tidak dapat dikembalikan" + onYes={(val) => { + if (val) { + onDelete() + } else { + setOpenModal(false) + } + }} /> + + + } + + ); +} + diff --git a/src/module/task/ui/navbar_detail_division_task.tsx b/src/module/task/ui/navbar_detail_division_task.tsx index fb3131a..92f409d 100644 --- a/src/module/task/ui/navbar_detail_division_task.tsx +++ b/src/module/task/ui/navbar_detail_division_task.tsx @@ -1,8 +1,10 @@ 'use client' import { globalRole, keyWibu, LayoutDrawer, LayoutNavbarNew, TEMA } from "@/module/_global"; +import LayoutModal from "@/module/_global/layout/layout_modal"; +import { funGetUserByCookies } from "@/module/auth"; import { globalIsAdminDivision, globalIsMemberDivision } from "@/module/division_new"; import { useHookstate } from "@hookstate/core"; -import { ActionIcon, Box, Flex, SimpleGrid, Stack, Text } from "@mantine/core"; +import { ActionIcon, Box, Button, Flex, Grid, Modal, SimpleGrid, Stack, Text, TextInput } from "@mantine/core"; import { useShallowEffect } from "@mantine/hooks"; import { useParams, useRouter } from "next/navigation"; import { useState } from "react"; @@ -10,11 +12,10 @@ import toast from "react-hot-toast"; import { FaFileCirclePlus, FaPencil, FaTrash, FaUsers } from "react-icons/fa6"; import { HiMenu } from "react-icons/hi"; import { IoAddCircle } from "react-icons/io5"; +import { LuLink } from "react-icons/lu"; import { MdCancel } from "react-icons/md"; -import { funDeleteTask, funGetTaskDivisionById } from "../lib/api_task"; import { useWibuRealtime } from "wibu-realtime"; -import LayoutModal from "@/module/_global/layout/layout_modal"; -import { funGetUserByCookies } from "@/module/auth"; +import { funAddLinkTask, funDeleteTask, funGetTaskDivisionById } from "../lib/api_task"; export default function NavbarDetailDivisionTask() { const router = useRouter() @@ -29,6 +30,9 @@ export default function NavbarDetailDivisionTask() { const [isUser, setUser] = useState('') const [loadingModal, setLoadingModal] = useState(false) const [openModal, setOpenModal] = useState(false) + const [openNewLink, setOpenNewLink] = useState(false) + const [valLink, setValLink] = useState("") + const [loadingLink, setLoadingLink] = useState(false) const [dataRealTime, setDataRealtime] = useWibuRealtime({ WIBU_REALTIME_TOKEN: keyWibu, project: "sdm" @@ -80,6 +84,29 @@ export default function NavbarDetailDivisionTask() { getOneData(); }, [param.detail]) + async function addLinkProject() { + try { + setLoadingLink(true) + const res = await funAddLinkTask(param.detail, { link: valLink, idDivision: param.id }); + if (res.success) { + setDataRealtime([{ + category: "tugas-detail-link", + id: param.detail, + }]) + toast.success(res.message) + } else { + toast.error(res.message) + } + } catch (error) { + console.error(error); + toast.error("Gagal menambahkan link, coba lagi nanti"); + } finally { + setLoadingLink(false) + setOpenNewLink(false) + setValLink("") + } + } + useShallowEffect(() => { if (dataRealTime && dataRealTime.some((i: any) => (i.category == 'tugas-detail' || i.category == 'tugas-detail-status') && i.id == param.detail)) { @@ -158,6 +185,27 @@ export default function NavbarDetailDivisionTask() { + { + if (reason == null) { + setOpen(false) + setOpenNewLink(true) + } else { + null + } + }} + > + + + + + Tambah link + + + { (roleLogin.get() != "user" && roleLogin.get() != "coadmin") || adminLogin.get() ? <> @@ -239,7 +287,46 @@ export default function NavbarDetailDivisionTask() { setOpenModal(false)} description="Apakah Anda yakin ingin menghapus tugas divisi ini?" - onYes={(val) => { val ? deleteDataProject() : setOpenModal(false) }} /> + onYes={(val) => { val ? deleteDataProject() : setOpenModal(false) }} + /> + + setOpenNewLink(false)} centered withCloseButton={false}> + + Tambah Link + + setValLink(e.target.value)} + /> + + + + + + + + + + + ) } \ No newline at end of file