diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b8a6ce8..9a702b6 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -59,6 +59,7 @@ model Village { Project Project[] Division Division[] ColorTheme ColorTheme[] + BannerImage BannerImage[] } model Group { @@ -481,6 +482,18 @@ model ColorTheme { updatedAt DateTime @updatedAt } +model BannerImage { + id String @id @default(cuid()) + Village Village? @relation(fields: [idVillage], references: [id]) + idVillage String? + title String + extension String + image String + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + model Subscription { id String @id @default(cuid()) data Json diff --git a/public/icon-192x192.png b/public/icon-192x192.png index 807489a..4a2d41b 100644 Binary files a/public/icon-192x192.png and b/public/icon-192x192.png differ diff --git a/public/icon2-192x192.png b/public/icon2-192x192.png new file mode 100644 index 0000000..807489a Binary files /dev/null and b/public/icon2-192x192.png differ diff --git a/src/app/(application)/banner/edit/[id]/page.tsx b/src/app/(application)/banner/edit/[id]/page.tsx index 6dea5fd..1df7437 100644 --- a/src/app/(application)/banner/edit/[id]/page.tsx +++ b/src/app/(application)/banner/edit/[id]/page.tsx @@ -1,3 +1,7 @@ -export default function Page({ params }: { params: { id: string } }) { - return
Edit Banner
; +import { EditBanner } from "@/module/banner"; + +export default function Page() { + return ( + + ); } \ No newline at end of file diff --git a/src/app/(application)/banner/viewfile/page.tsx b/src/app/(application)/banner/viewfile/page.tsx new file mode 100644 index 0000000..768f17e --- /dev/null +++ b/src/app/(application)/banner/viewfile/page.tsx @@ -0,0 +1,10 @@ +import { ViewfileBanner } from '@/module/banner'; +import React from 'react'; + +function Page() { + return ( + + ); +} + +export default Page; diff --git a/src/app/(application)/web-push/page.tsx b/src/app/(application)/web-push/page.tsx index 24e988c..0162ceb 100644 --- a/src/app/(application)/web-push/page.tsx +++ b/src/app/(application)/web-push/page.tsx @@ -2,10 +2,10 @@ import { NotificationManager } from "@/module/_global/components/notification_ma const publicKey = process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY!; -console.log( - process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY, - process.env.VAPID_PRIVATE_KEY -); +// console.log( +// process.env.NEXT_PUBLIC_VAPID_PUBLIC_KEY, +// process.env.VAPID_PRIVATE_KEY +// ); export default function Page() { return ( diff --git a/src/app/api/.gitkeep b/src/app/api/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/api/banner/[id]/route.ts b/src/app/api/banner/[id]/route.ts new file mode 100644 index 0000000..22ec8d2 --- /dev/null +++ b/src/app/api/banner/[id]/route.ts @@ -0,0 +1,113 @@ +import { DIR, funDeleteFile, funUploadFile, prisma } from "@/module/_global"; +import { funGetUserByCookies } from "@/module/auth"; +import { createLogUser } from "@/module/user"; +import { NextResponse } from "next/server"; + + +// GET ONE BANNER +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.bannerImage.findUnique({ + where: { + id: String(id) + } + }) + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan banner", data }, { status: 200 }); + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan banner, coba lagi nanti", reason: (error as Error).message, }, { status: 500 }); + } +} + + +// DELETE BANNER +export async function DELETE(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 upd = await prisma.bannerImage.update({ + where: { + id: String(id) + }, + data: { + isActive: false + } + }) + + + // create log user + const log = await createLogUser({ act: 'DELETE', desc: 'User menghapus banner', table: 'bannerImage', data: id }) + + return NextResponse.json({ success: true, message: "Berhasil menghapus banner" }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal menghapus banner, coba lagi nanti", reason: (error as Error).message, }, { status: 500 }); + } +} + + +// UPDATE BANNER +export async function PUT(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 body = await request.formData() + const file = body.get("file") as File + const data = body.get("data") + const { title } = JSON.parse(data as string) + + + const upd = await prisma.bannerImage.update({ + where: { + id: String(id) + }, + data: { + title + }, + select: { + image: true + } + }) + + if (String(file) != "undefined" && String(file) != "null") { + const fExt = file.name.split(".").pop() + const fileName = id + '.' + fExt; + const newFile = new File([file], fileName, { type: file.type }); + await funDeleteFile({ fileId: String(upd.image) }) + const upload = await funUploadFile({ file: newFile, dirId: DIR.banner }) + await prisma.bannerImage.update({ + where: { + id: id + }, + data: { + image: upload.data.id + } + }) + } + + // create log user + const log = await createLogUser({ act: 'UPDATE', desc: 'User mengupdate data banner', table: 'bannerImage', data: user.id }) + + return NextResponse.json({ success: true, message: "Berhasil mengupdate banner" }, { status: 200 }); + + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mengupdate banner, coba lagi nanti", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/banner/route.ts b/src/app/api/banner/route.ts new file mode 100644 index 0000000..a8010ae --- /dev/null +++ b/src/app/api/banner/route.ts @@ -0,0 +1,74 @@ +import { DIR, funUploadFile, funViewDir, prisma } from "@/module/_global"; +import { funGetUserByCookies } from "@/module/auth"; +import { createLogUser } from "@/module/user"; +import { NextResponse } from "next/server"; + + +// GET ALL BANNER +export async function GET() { + try { + 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.bannerImage.findMany({ + where: { + isActive: true, + idVillage: user.idVillage + }, + orderBy: { + createdAt: 'desc' + } + }); + + return NextResponse.json({ success: true, message: "Berhasil mendapatkan banner", data }, { status: 200 }); + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal mendapatkan data banner, coba lagi nanti", reason: (error as Error).message, }, { status: 500 }); + } +} + + +// CREATE BANNER +export async function POST(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 body = await request.formData() + const file = body.get("file") as File; + const data = body.get("data"); + + const { title } = JSON.parse(data as string) + + const fExt = file.name.split(".").pop() + const fName = file.name.replace("." + fExt, "") + const newFile = new File([file], file.name, { type: file.type }); + + const ini = funViewDir({ dirId: DIR.user }) + const upload = await funUploadFile({ file: newFile, dirId: DIR.banner }) + if (upload.success) { + const create = await prisma.bannerImage.create({ + data: { + title: title, + idVillage: user.idVillage, + extension: String(fExt), + image: upload.data.id + } + }) + + // create log user + const log = await createLogUser({ act: 'CREATE', desc: 'User menambah data banner baru', table: 'bannerImage', data: user.id }) + + return Response.json({ success: true, message: 'Sukses menambah data banner' }, { status: 200 }); + } else { + return Response.json({ success: false, message: 'Gagal menambah data banner' }, { status: 200 }); + } + } catch (error) { + console.error(error); + return NextResponse.json({ success: false, message: "Gagal menambahkan banner, coba lagi nanti", reason: (error as Error).message, }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/home/route.ts b/src/app/api/home/route.ts index a447f50..6f6b3d2 100644 --- a/src/app/api/home/route.ts +++ b/src/app/api/home/route.ts @@ -1,6 +1,6 @@ import { prisma } from "@/module/_global"; import { funGetUserByCookies } from "@/module/auth"; -import _, { ceil } from "lodash"; +import _, { ceil, some } from "lodash"; import moment from "moment"; import "moment/locale/id"; import { NextResponse } from "next/server"; @@ -35,11 +35,21 @@ export async function GET(request: Request) { isActive: true, } } - } else { + } else if (roleUser == "admin" || roleUser == "cosupadmin") { kondisi = { isActive: true, idGroup: idGroup } + } else { + kondisi = { + isActive: true, + idGroup: idGroup, + ProjectMember: { + some: { + idUser: user.id + } + } + } } const data = await prisma.project.findMany({ @@ -133,17 +143,31 @@ export async function GET(request: Request) { kondisi = { isActive: true, Division: { + isActive: true, idVillage: idVillage, Group: { isActive: true, } } } + } else if (roleUser == "admin" || roleUser == "cosupadmin") { + kondisi = { + isActive: true, + Division: { + isActive: true, + idGroup: idGroup + } + } } else { kondisi = { isActive: true, Division: { - idGroup: idGroup + isActive: true, + DivisionMember: { + some: { + idUser: user.id + } + } } } } @@ -185,18 +209,33 @@ export async function GET(request: Request) { isActive: true, category: 'FILE', Division: { + isActive: true, idVillage: idVillage, Group: { isActive: true, } } } + } else if (roleUser == "admin" || roleUser == "cosupadmin") { + kondisi = { + isActive: true, + category: 'FILE', + Division: { + isActive: true, + idGroup: idGroup + } + } } else { kondisi = { isActive: true, category: 'FILE', Division: { - idGroup: idGroup + isActive: true, + DivisionMember: { + some: { + idUser: user.id + } + } } } } @@ -284,7 +323,7 @@ export async function GET(request: Request) { dateEnd: true, createdAt: true, status: true, - idDivision:true, + idDivision: true, DivisionCalendar: { select: { title: true, @@ -327,18 +366,33 @@ export async function GET(request: Request) { isActive: true, status: 1, Division: { + isActive: true, idVillage: idVillage, Group: { isActive: true, } } } + } else if (roleUser == "admin" || roleUser == "cosupadmin") { + kondisi = { + isActive: true, + status: 1, + Division: { + idGroup: idGroup, + isActive: true + } + } } else { kondisi = { isActive: true, status: 1, Division: { - idGroup: idGroup + isActive: true, + DivisionMember: { + some: { + idUser: user.id + } + } } } } diff --git a/src/app/api/send-notification/route.ts b/src/app/api/send-notification/route.ts index 81fc90e..8e6bea9 100644 --- a/src/app/api/send-notification/route.ts +++ b/src/app/api/send-notification/route.ts @@ -36,9 +36,9 @@ export async function POST() { const subscriptionData = sub.data as any; await webpush.sendNotification(subscriptionData, notificationPayload); - console.log( - `Notification sent successfully to ${subscriptionData.endpoint}` - ); + // console.log( + // `Notification sent successfully to ${subscriptionData.endpoint}` + // ); successCount++; } catch (error: any) { console.error( diff --git a/src/app/api/user/route.ts b/src/app/api/user/route.ts index 6fed7bf..80ea26a 100644 --- a/src/app/api/user/route.ts +++ b/src/app/api/user/route.ts @@ -167,7 +167,7 @@ export async function POST(request: Request) { const cekPhone = await prisma.user.count({ where: { - phone: data.phone + phone: "62" + data.phone }, }); @@ -177,7 +177,7 @@ export async function POST(request: Request) { data: { nik: data.nik, name: data.name, - phone: data.phone, + phone: "62" + data.phone, email: data.email, gender: data.gender, idGroup: groupFix, diff --git a/src/module/_global/bin/val_global.ts b/src/module/_global/bin/val_global.ts index 2442734..906b7d7 100644 --- a/src/module/_global/bin/val_global.ts +++ b/src/module/_global/bin/val_global.ts @@ -5,15 +5,16 @@ import { RefObject } from "react"; export const pwd_key_config = "fchgvjknlmdfnbvghhujlaknsdvjbhknlkmsdbdyu567t8y9u30r4587638y9uipkoeghjvuyi89ipkoefmnrjbhtiu4or9ipkoemnjfbhjiuoijdklnjhbviufojkejnshbiuojijknehgruyu" export const globalRole = hookstate('') export const DIR = { + parentDir: "cm0x8a1as0001bp5te7354yrp", task: "cm0xhcqf0000dacbbixjb09yn", project: "cm0xhc9sv000bacbb7rfikw1k", document: "cm0xhbkf50009acbbtw03qo4l", village: "cm0xhb91o0007acbbkx8rk8hj", user: "cm0x8dbwn0005bp5tgmfcthzw", - + banner: "cm1sxex19004938bjvyaq8vta" } -export const keyWibu= 'padahariminggukuturutayahkekotanaikdelmanistimewakududukdimuka' +export const keyWibu = 'padahariminggukuturutayahkekotanaikdelmanistimewakududukdimuka' export const TEMA = hookstate({ utama: "#19345E", diff --git a/src/module/_global/fun/view_dir.ts b/src/module/_global/fun/view_dir.ts new file mode 100644 index 0000000..0005934 --- /dev/null +++ b/src/module/_global/fun/view_dir.ts @@ -0,0 +1,22 @@ +export async function funViewDir({ dirId }: { dirId: string }) { + + try { + const res = await fetch("https://wibu-storage.wibudev.com/api/dir/" + dirId + "/tree", { + method: "GET", + headers: { + Authorization: `Bearer ${process.env.WS_APIKEY}` + } + }); + + if (res.ok) { + const hasil = await res.json() + return { success: true, data: hasil.data } + } else { + const errorText = await res.text(); + return { success: false, data: {} } + } + } catch (error) { + console.error("Upload error:", error); + return { success: false, data: {} } + } +} \ No newline at end of file diff --git a/src/module/_global/index.ts b/src/module/_global/index.ts index 0e87f99..d9ee900 100644 --- a/src/module/_global/index.ts +++ b/src/module/_global/index.ts @@ -25,6 +25,7 @@ import NotificationCustome from "./components/notification_custome"; import { ScrollProvider } from "./components/scroll_provider"; import SkeletonUser from "./components/skeleton_user"; import SkeletonList from "./components/skeleton_list"; +import { funViewDir } from "./fun/view_dir"; export { WARNA }; export { LayoutLogin }; @@ -59,3 +60,4 @@ export { currentScroll } export { SkeletonUser } export { SkeletonList } export { keyWibu } +export { funViewDir } diff --git a/src/module/announcement/ui/create_announcement.tsx b/src/module/announcement/ui/create_announcement.tsx index 5dbe027..25d72cf 100644 --- a/src/module/announcement/ui/create_announcement.tsx +++ b/src/module/announcement/ui/create_announcement.tsx @@ -19,14 +19,13 @@ export default function CreateAnnouncement() { const memberGroup = useHookstate(globalMemberAnnouncement) const memberValue = memberGroup.get() as GroupData[] const [selectedFiles, setSelectedFiles] = useState([]) + const [loadingKonfirmasi, setLoadingKonfirmasi] = useState(false) const router = useRouter() const tema = useHookstate(TEMA) const [data, setData] = useWibuRealtime({ WIBU_REALTIME_TOKEN: keyWibu, project: "sdm" }) - - const [isChooseMember, setIsChooseMember] = useState(false) const [isData, setisData] = useState({ title: "", @@ -37,8 +36,10 @@ export default function CreateAnnouncement() { desc: false }); + async function onSubmit() { try { + setLoadingKonfirmasi(true) const response = await funCreateAnnouncement({ title: isData.title, desc: isData.desc, @@ -56,9 +57,11 @@ export default function CreateAnnouncement() { } catch (error) { console.error(error) toast.error("Gagal menambahkan pengumuman, coba lagi nanti"); + } finally { + setLoadingKonfirmasi(false) + setOpen(false) } - setOpen(false) } async function loadData() { @@ -69,18 +72,31 @@ export default function CreateAnnouncement() { loadData() }, []) - function onToChooseMember() { - setIsChooseMember(true) - } - - if (isChooseMember) return { setIsChooseMember(false) }} /> function onCheck() { - if (Object.values(touched).some((v) => v == true)) + const cek = checkAll() + if (!cek) return false + + if (memberValue.length == 0) + return toast.error("Error! silahkan pilih divisi") + setOpen(true) } + function checkAll() { + let nilai = true + if (isData.title === "") { + setTouched(touched => ({ ...touched, title: true })) + nilai = false + } + if (isData.desc === "") { + setTouched(touched => ({ ...touched, desc: true })) + nilai = false + } + return nilai + } + function onValidation(kategori: string, val: string) { if (kategori == 'title') { @@ -100,6 +116,8 @@ export default function CreateAnnouncement() { } } + if (isChooseMember) return { setIsChooseMember(false) }} /> + return ( } /> @@ -151,7 +169,7 @@ export default function CreateAnnouncement() { padding: 10, borderRadius: 10 }} - onClick={() => { onToChooseMember() }} + onClick={() => { setIsChooseMember(true) }} > Tambah divisi penerima pengumuman @@ -162,7 +180,7 @@ export default function CreateAnnouncement() { Divisi Terpilih {(memberGroup.length === 0) ? ( - Belum ada anggota + Belum ada divisi yang dipilih ) : memberGroup.get().map((v: any, i: any) => { return ( @@ -195,13 +213,14 @@ export default function CreateAnnouncement() { Simpan - setOpen(false)} + setOpen(false)} description="Apakah Anda yakin ingin menambahkan data?" onYes={(val) => { if (val) { onSubmit() + } else { + setOpen(false) } - setOpen(false) }} /> diff --git a/src/module/announcement/ui/create_users_announcement.tsx b/src/module/announcement/ui/create_users_announcement.tsx index b4c1037..7d2e7e5 100644 --- a/src/module/announcement/ui/create_users_announcement.tsx +++ b/src/module/announcement/ui/create_users_announcement.tsx @@ -1,15 +1,16 @@ "use client"; -import { LayoutNavbarNew, TEMA, WARNA } from '@/module/_global'; +import { LayoutNavbarNew, TEMA } from '@/module/_global'; import { funGetGroupDivision } from '@/module/group/lib/api_group'; -import { Box, Button, Divider, Flex, Group, rem, Skeleton, Stack, Text } from '@mantine/core'; -import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; -import React, { useState } from 'react'; -import { FaCheck } from 'react-icons/fa'; -import { GroupData } from '../lib/type_announcement'; import { useHookstate } from '@hookstate/core'; -import { globalMemberAnnouncement } from '../lib/val_announcement'; -import { FaMinus } from 'react-icons/fa6'; +import { ActionIcon, Box, Button, Divider, Group, rem, Skeleton, Stack, Text } from '@mantine/core'; +import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; +import { useState } from 'react'; import toast from 'react-hot-toast'; +import { FaCheck } from 'react-icons/fa'; +import { FaMinus } from 'react-icons/fa6'; +import { HiChevronLeft } from 'react-icons/hi2'; +import { GroupData } from '../lib/type_announcement'; +import { globalMemberAnnouncement } from '../lib/val_announcement'; @@ -112,7 +113,13 @@ export default function CreateUsersAnnouncement({ onClose }: { onClose: (val: an return (
- } /> + + { onClose(true) }} bg={tema.get().bgIcon} size="lg" radius="lg" aria-label="Settings"> + + + + } title="Tambah Divisi Penerima Pengumuman" menu={<>} /> () const [loading, setLoading] = useState(true) @@ -75,6 +75,7 @@ export default function EditAnnouncement() { toast.error("Gagal mendapatkan pengumuman, coba lagi nanti") } finally { setLoading(false) + setOpen(false) } } @@ -94,30 +95,24 @@ export default function EditAnnouncement() { if (response.success) { toast.success(response.message) - setLoadingSubmit(false) router.push(`/announcement/${param.id}`) } else { toast.error(response.message) } - setLoadingSubmit(false) } catch (error) { console.error(error) toast.error("Gagal mengedit pengumuman, coba lagi nanti"); } finally { setLoadingSubmit(false) + setOpen(false) } - setOpen(false) } - - - - if (isChooseDivisi) return { setChooseDivisi(false) }} /> - - function onCheck() { if (Object.values(touched).some((v) => v == true)) return false + if (memberGroup.get().length == 0) + return toast.error("Error! silahkan pilih divisi") setOpen(true) } @@ -140,6 +135,8 @@ export default function EditAnnouncement() { } } + if (isChooseDivisi) return { setChooseDivisi(false) }} /> + return ( <> } /> @@ -160,7 +157,7 @@ export default function EditAnnouncement() { : <> { - return ( - - {v.name} - - - - { - v.Division.map((division: any) => { - return - {division.name} - - }) - } - - - - - ); + <> + Divisi Terpilih + { + memberGroup.get().length == 0 ? Belum ada divisi yang dipilih : + memberGroup.get().map((v: any, i: any) => { + return ( + + {v.name} + + + + { + v.Division.map((division: any) => { + return + {division.name} + + }) + } + + + + + ); + + }) + } + - } - ) } @@ -282,8 +285,9 @@ export default function EditAnnouncement() { onYes={(val) => { if (val) { onSubmit() + } else { + setOpen(false) } - setOpen(false) }} /> ) diff --git a/src/module/announcement/ui/edit_choose_member.tsx b/src/module/announcement/ui/edit_choose_member.tsx index 5f234d0..642883e 100644 --- a/src/module/announcement/ui/edit_choose_member.tsx +++ b/src/module/announcement/ui/edit_choose_member.tsx @@ -1,17 +1,16 @@ "use client"; -import { LayoutNavbarNew, TEMA, WARNA } from '@/module/_global'; +import { LayoutNavbarNew, TEMA } from '@/module/_global'; import { funGetGroupDivision } from '@/module/group/lib/api_group'; -import { Box, Button, Divider, Flex, Group, rem, Skeleton, Stack, Text } from '@mantine/core'; -import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; -import React, { useState } from 'react'; -import { FaCheck } from 'react-icons/fa'; -import { GroupData, GroupDataEditAnnouncement } from '../lib/type_announcement'; import { useHookstate } from '@hookstate/core'; -import { globalMemberEditAnnouncement } from '../lib/val_announcement'; -import { FaMinus } from 'react-icons/fa6'; +import { ActionIcon, Box, Button, Divider, Group, rem, Skeleton, Stack, Text } from '@mantine/core'; +import { useMediaQuery, useShallowEffect } from '@mantine/hooks'; +import { useState } from 'react'; import toast from 'react-hot-toast'; - - +import { FaCheck } from 'react-icons/fa'; +import { FaMinus } from 'react-icons/fa6'; +import { HiChevronLeft } from 'react-icons/hi2'; +import { GroupData } from '../lib/type_announcement'; +import { globalMemberEditAnnouncement } from '../lib/val_announcement'; interface CheckedState { [key: string]: string[]; @@ -111,7 +110,13 @@ export default function EditChooseMember({ onClose }: { onClose: (val: any) => v return (
- } /> + + { onClose(true) }} bg={tema.get().bgIcon} size="lg" radius="lg" aria-label="Settings"> + + + + } title="Tambah Divisi Penerima Pengumuman" menu={<>} /> { - if (res.status == 200) { - setValPhone(cekLogin.phone) - setOTP(code) - setUser(cekLogin.id) - setVerif(true) - setLoading(false) - toast.success('Kode verifikasi telah dikirim') - } else { - toast.error('Internal Server Error') - setLoading(false) + try { + const res = await fetch(`https://wa.wibudev.com/code?nom=${cekLogin.phone}&text=*DARMASABA*%0A%0A + JANGAN BERIKAN KODE RAHASIA ini kepada siapa pun TERMASUK PIHAK DARMASABA. Masukkan otentikasi: *${encodeURIComponent(code)}*`).then( + async (res) => { + if (res.status == 200) { + setValPhone(cekLogin.phone) + setOTP(code) + setUser(cekLogin.id) + setVerif(true) + setLoading(false) + toast.success('Kode verifikasi telah dikirim') + } else { + console.error(res.status) + toast.error('Internal Server Error') + setLoading(false) + } } - } - ) + ) + } catch (error) { + console.error(error) + toast.error('Internal Server Error') + }finally{ + setLoading(false) + } } else { return toast.error(cekLogin.message) } diff --git a/src/module/auth/varification/view/view_verification.tsx b/src/module/auth/varification/view/view_verification.tsx index 374c099..d345b4c 100644 --- a/src/module/auth/varification/view/view_verification.tsx +++ b/src/module/auth/varification/view/view_verification.tsx @@ -14,26 +14,30 @@ export default function ViewVerification({ phone, otp, user }: IVerification) { const [isLoading, setLoading] = useState(false) async function onResend() { - const code = Math.floor(Math.random() * 1000) + 1000 - - const res = await fetch(`https://wa.wibudev.com/code?nom=${phone}&text=*DARMASABA*%0A%0A -JANGAN BERIKAN KODE RAHASIA ini kepada siapa pun TERMASUK PIHAK DARMASABA. Masukkan otentikasi: *${encodeURIComponent(code)}*`) - .then( - async (res) => { - if (res.status == 200) { - toast.success('Kode verifikasi telah dikirim') - setOTP(code) - } else { - toast.error('Internal Server Error') + try { + const code = Math.floor(Math.random() * 1000) + 1000 + const res = await fetch(`https://wa.wibudev.com/code?nom=${phone}&text=*DARMASABA*%0A%0A + JANGAN BERIKAN KODE RAHASIA ini kepada siapa pun TERMASUK PIHAK DARMASABA. Masukkan otentikasi: *${encodeURIComponent(code)}*`) + .then( + async (res) => { + if (res.status == 200) { + toast.success('Kode verifikasi telah dikirim') + setOTP(code) + } else { + toast.error('Internal Server Error') + } } - } - ); + ); + } catch (error) { + console.error(error) + toast.error('Internal Server Error') + } } async function getVerification() { setLoading(true) if (isOTP == inputOTP) { - const setCookies = await funSetCookies({ user: user }) + const setCookies: any = await funSetCookies({ user: user }) if (setCookies.success) { toast.success(setCookies.message) diff --git a/src/module/banner/index.ts b/src/module/banner/index.ts index 9b3343f..1aa792a 100644 --- a/src/module/banner/index.ts +++ b/src/module/banner/index.ts @@ -1,4 +1,6 @@ import NavbarBanner from "./ui/navbar_banner"; import ListBanner from "./ui/list_banner"; import CreateBanner from "./ui/create_banner"; -export {NavbarBanner, ListBanner, CreateBanner} \ No newline at end of file +import EditBanner from "./ui/edit_banner"; +import ViewfileBanner from "./ui/viewfile_banner"; +export { NavbarBanner, ListBanner, CreateBanner, EditBanner, ViewfileBanner } diff --git a/src/module/banner/lib/api_banner.ts b/src/module/banner/lib/api_banner.ts new file mode 100644 index 0000000..54dd40a --- /dev/null +++ b/src/module/banner/lib/api_banner.ts @@ -0,0 +1,35 @@ +export const funGetAllBanner = async (path?: string) => { + const response = await fetch(`/api/banner${(path) ? path : ''}`, { next: { tags: ['banner'] } }); + return await response.json().catch(() => null); +} + +export const funDeleteBanner = async (path: string) => { + const response = await fetch(`/api/banner/${path}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + }); + return await response.json().catch(() => null); +}; + +export const funCreateBanner = async (data: FormData) => { + const response = await fetch(`/api/banner`, { + method: "POST", + body: data, + }); + return await response.json().catch(() => null); +} + +export const funGetOneBanner = async (path: string) => { + const response = await fetch(`/api/banner/${path}`) + return await response.json().catch(() => null); +} + +export const funEditBanner = async (path: string, data: FormData) => { + const response = await fetch(`/api/banner/${path}`, { + method: "PUT", + body: data, + }); + return await response.json().catch(() => null); +} diff --git a/src/module/banner/lib/git.keep b/src/module/banner/lib/git.keep deleted file mode 100644 index e69de29..0000000 diff --git a/src/module/banner/lib/type_banner.ts b/src/module/banner/lib/type_banner.ts new file mode 100644 index 0000000..82b03e2 --- /dev/null +++ b/src/module/banner/lib/type_banner.ts @@ -0,0 +1,19 @@ +export interface IDataBanner { + id: string + title: string + extension: string + image: string +}[] + +export interface IEditDataBanner { + id: string + title: string + extension: string + image: string +} +export interface ICreateDataBanner { + id: string + title: string + extension: string + image: string +} diff --git a/src/module/banner/ui/create_banner.tsx b/src/module/banner/ui/create_banner.tsx index 954cf34..25e938d 100644 --- a/src/module/banner/ui/create_banner.tsx +++ b/src/module/banner/ui/create_banner.tsx @@ -1,71 +1,206 @@ 'use client' -import { LayoutNavbarNew, WARNA } from '@/module/_global'; -import { Box, Button, FileInput, Group, Paper, rem, Text, TextInput } from '@mantine/core'; -import { IconUpload, IconPhoto, IconX } from '@tabler/icons-react'; -import { Dropzone, DropzoneProps, IMAGE_MIME_TYPE } from '@mantine/dropzone'; -import React from 'react'; +import { LayoutNavbarNew, TEMA } from '@/module/_global'; +import LayoutModal from '@/module/_global/layout/layout_modal'; +import { useHookstate } from '@hookstate/core'; +import { Box, Button, Group, Image, Paper, rem, Text, TextInput } from '@mantine/core'; +import { Dropzone } from '@mantine/dropzone'; +import { IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; +import _ from 'lodash'; +import { useRouter } from 'next/navigation'; +import { useRef, useState } from 'react'; +import toast from 'react-hot-toast'; +import { funCreateBanner } from '../lib/api_banner'; +import { title } from 'process'; + +function CreateBanner() { + const router = useRouter(); + const [isModal, setModal] = useState(false); + const tema = useHookstate(TEMA) + const [loadingKonfirmasi, setLoadingKonfirmasi] = useState(false) + const [listData, setListData] = useState({ + title: "", + image: "" + + }); + const [imgForm, setImgForm] = useState() + const openRef = useRef<() => void>(null) + const [img, setIMG] = useState() + const [touched, setTouched] = useState({ + title: false, + image: false + }) + + + function onValidation(kategori: string, val: any) { + if (kategori == 'title') { + setListData({ ...listData, title: val }) + if (val === "") { + setTouched({ ...touched, title: true }) + } else { + setTouched({ ...touched, title: false }) + } + } else if (kategori == 'image') { + if (imgForm) { + setTouched({ ...touched, image: false }) + } else { + setTouched({ ...touched, image: true }) + } + } + } + + + async function onSubmit(val: boolean) { + if (!imgForm || !listData.title) { + toast.error("Mohon lengkapi semua data"); + } + try { + setLoadingKonfirmasi(true) + const fd = new FormData() + fd.append("file", imgForm) + fd.append("data", JSON.stringify( + { + title: listData.title, + image: listData.image + } + )) + const res = await funCreateBanner(fd); + if (res.success) { + toast.success(res.message); + router.push('/banner') + } else { + toast.error(res.message); + } + setModal(false); + } catch (error) { + toast.error("Error"); + } finally { + setLoadingKonfirmasi(false) + setModal(false); + } + } + + // async function loadData() { + // const + // } -function CreateBanner(props: Partial) { return ( - } /> + console.log('accepted files', files)} - onReject={(files) => console.log('rejected files', files)} - maxSize={5 * 1024 ** 2} - accept={IMAGE_MIME_TYPE} - {...props} + openRef={openRef} + onDrop={async (files) => { + if (!files || _.isEmpty(files)) + return toast.error('Tidak ada gambar yang dipilih') + const file = files[0] + setImgForm(file) + const buffer = URL.createObjectURL(new Blob([new Uint8Array(await files[0].arrayBuffer())])) + setIMG(buffer) + onValidation('image', files[0]) + }} + activateOnClick={false} + maxSize={1 * 1024 ** 2} + accept={['image/png', 'imagfe/jpeg', 'image/heic']} + onReject={(files) => { + return toast.error('File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 1 MB') + }} + onClick={() => openRef.current?.()} > - - - - - - - - - - - - - Upload File - - - File Tidak Boleh Melebihi 500mb - - - + { + img ? + + : + + + + + + + + + + + +
+ + Klik Untuk Upload Image + + + Ukuran Foto Tidak Boleh Lebih Dari 1MB + +
+
+ } +
Judul Banner
} + label={Judul Banner} + value={listData.title} placeholder='Judul Banner' + onChange={(e) => { + setListData({ ...listData, title: e.target.value }) + onValidation('title', e.target.value) + }} styles={{ input: { - border: `1px solid ${"#D6D8F6"}`, + border: `1px solid ${touched.title ? 'red' : "#D6D8F6"}`, borderRadius: 10, }, }} + + />
- + + + + setModal(false)} + description="Apakah anda yakin ingin menambahkan banner ini?" + onYes={(val) => { + if (val) { + onSubmit(val); + } else { + setModal(false); + } + }} + + /> ); } diff --git a/src/module/banner/ui/edit_banner.tsx b/src/module/banner/ui/edit_banner.tsx new file mode 100644 index 0000000..411c731 --- /dev/null +++ b/src/module/banner/ui/edit_banner.tsx @@ -0,0 +1,188 @@ +'use client' +import { LayoutNavbarNew, TEMA, WARNA } from '@/module/_global'; +import LayoutModal from '@/module/_global/layout/layout_modal'; +import { useHookstate } from '@hookstate/core'; +import { Box, Button, Image, Paper, rem, TextInput } from '@mantine/core'; +import { Dropzone } from '@mantine/dropzone'; +import { useShallowEffect } from '@mantine/hooks'; +import _ from 'lodash'; +import { useParams, useRouter } from 'next/navigation'; +import { useRef, useState } from 'react'; +import toast from 'react-hot-toast'; +import { funEditBanner, funGetOneBanner } from '../lib/api_banner'; +import { IEditDataBanner } from '../lib/type_banner'; + + +export default function EditBanner() { + const router = useRouter() + const param = useParams<{ id: string }>() + const tema = useHookstate(TEMA) + const [isModal, setModal] = useState(false) + const [data, setData] = useState({ + id: "", + title: "", + extension: "", + image: "", + }); + const openRef = useRef<() => void>(null) + const [img, setIMG] = useState() + const [imgForm, setImgForm] = useState() + const [loading, setLoading] = useState(false) + const [touched, setTouched] = useState({ + title: false, + image: false + }) + + + function onValidation(kategori: string, val: any) { + if (kategori == 'title') { + setData({ ...data, title: val }) + if (val === "") { + setTouched({ ...touched, title: true }) + } else { + setTouched({ ...touched, title: false }) + } + } else if (kategori == 'image') { + if (imgForm) { + setTouched({ ...touched, image: false }) + } else { + setTouched({ ...touched, image: true }) + } + } + } + + + async function getOneData() { + try { + const res = await funGetOneBanner(param.id) + console.log(res) + setData(res.data) + setIMG(`https://wibu-storage.wibudev.com/api/files/${res.data.image}`) + } catch (error) { + console.error(error) + } + } + + async function onSubmit(val: boolean) { + try { + setLoading(true) + const fd = new FormData() + fd.append("file", imgForm) + fd.append("data", JSON.stringify( + { + id: data.id, + title: data.title, + image: data.image, + extension: data.extension + } + )) + + const res = await funEditBanner(param.id, fd) + + if (res.success) { + toast.success(res.message) + router.push('/banner') + } else { + toast.error(res.message) + } + } catch (error) { + toast.error("Error"); + } finally { + setLoading(false) + setModal(false) + } + } + + useShallowEffect(() => { + getOneData() + }, []) + + return ( + + } /> + + + + { + if (!files || _.isEmpty(files)) + return toast.error("Tidak Ada Gambar Yang Dipilih") + setImgForm(files[0]) + const buffer = URL.createObjectURL(new Blob([new Uint8Array(await files[0].arrayBuffer())])) + setIMG(buffer) + onValidation('image', files[0]) + }} + activateOnClick={false} + maxSize={1 * 1024 ** 2} + accept={['image/png', 'image/jpeg', 'image/heic']} + onReject={(files) => { + return toast.error('File yang diizinkan: .png, .jpg, dan .heic dengan ukuran maksimal 1 MB') + }} + onClick={() => openRef.current?.()} + > + + + + + { + setData({ ...data, title: e.target.value }) + onValidation('title', e.target.value) + }} + styles={{ + input: { + border: `1px solid ${touched.title ? 'red' : "#D6D8F6"}`, + borderRadius: 10, + }, + }} + required + size='md' + /> + + + + + setModal(false)} + description="Apakah Anda yakin ingin mengedit banner ini?" + onYes={(val) => { + if (val) { + onSubmit(val) + } else { + setModal(false) + } + }} + /> + + + + + ); +} + diff --git a/src/module/banner/ui/list_banner.tsx b/src/module/banner/ui/list_banner.tsx index 3283170..4d8de51 100644 --- a/src/module/banner/ui/list_banner.tsx +++ b/src/module/banner/ui/list_banner.tsx @@ -1,77 +1,237 @@ 'use client' -import { LayoutDrawer, SkeletonSingle, TEMA, WARNA } from '@/module/_global'; +import { currentScroll, LayoutDrawer, LayoutModalViewFile, TEMA, WARNA } from '@/module/_global'; +import LayoutModal from '@/module/_global/layout/layout_modal'; import { useHookstate } from '@hookstate/core'; -import { Anchor, Box, Flex, Group, Image, SimpleGrid, Stack, Text, TextInput } from '@mantine/core'; -import _ from 'lodash'; -import { useRouter } from 'next/navigation'; -import React, { useState } from 'react'; +import { ActionIcon, Anchor, Box, Flex, Group, Image, Paper, SimpleGrid, Stack, Text, TextInput } from '@mantine/core'; +import { useParams, useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; import { FaFile, FaPencil, FaTrash } from 'react-icons/fa6'; +import { IDataBanner } from '../lib/type_banner'; +import toast from 'react-hot-toast'; +import { useShallowEffect } from '@mantine/hooks'; +import { funDeleteBanner, funGetAllBanner, funGetOneBanner } from '../lib/api_banner'; import { HiMagnifyingGlass } from 'react-icons/hi2'; -import { IoAddCircle } from 'react-icons/io5'; function ListBanner() { + const [isList, setIsList] = useState(false) const tema = useHookstate(TEMA) - const [searchQuery, setSearchQuery] = useState('') - const [loading, setLoading] = useState(true); - const [isData, setData] = useState([]); - const [valChoose, setValChoose] = useState(""); const router = useRouter(); - - + const param = useParams<{ id: string }>() + const [isOpenModalView, setOpenModalView] = useState(false) + const [isOpenModal, setOpenModal] = useState(false) const [openDrawer, setOpenDrawer] = useState(false); + const [idDataStorage, setIdDataStorage] = useState('') + const [isExtension, setExtension] = useState('') + const [loading, setLoading] = useState(true) + const [isData, setData] = useState([]) + const [idData, setIdData] = useState('') + const [isPage, setPage] = useState(1) + const [searchQuerry, setSearchQuerry] = useState('') + // const { value: containerRef } = useHookstate(currentScroll); + + const handleList = () => { + setIsList(!isList) + } + + const fetchData = async (loading: boolean) => { + try { + if (loading) + setLoading(true) + const response = await funGetAllBanner('?search=' + searchQuerry) + if (response.success) { + setData(response.data.map((banner: { image: any; }) => ({ ...banner, image: banner.image }))); + } else { + toast.error(response.message) + } + setLoading(false) + } catch (error) { + console.error(error) + toast.error("Gagal mendapatkan banner, coba lagi nanti"); + } finally { + setLoading(false) + } + } + + function searchBanner(search: string) { + setSearchQuerry(search) + setPage(1) + } + + + useShallowEffect(() => { + fetchData(true) + }, [searchQuerry]) + + useShallowEffect(() => { + fetchData(false) + }, [isPage]) + + + async function onDelete(id: string) { + try { + const res = await funDeleteBanner(id); + if (res.success) { + toast.success(res.message) + setData(isData.filter((banner) => banner.id !== id)); + setIdData("") + setIdDataStorage("") + setOpenDrawer(false) + } else { + toast.error(res.message); + } + } catch (error) { + console.error(error); + toast.error("Gagal menghapus banner, coba lagi nanti"); + } + + } + + + return ( - - + + + } + placeholder='pencarian' + value={searchQuerry} + onChange={(val) => { searchBanner(val.target.value) }} + /> + + + + - {[...Array(5)].map((_, index) => ( -
{ setOpenDrawer(true) }}> - Banner {index + 1} - -
+ {isData.map((v, index) => ( + { + setIdData(v.id); + setIdDataStorage(v.image); + setExtension(v.extension); + setOpenDrawer(true) + } + } + style={{ + width: '100%', + maxWidth: 550, + height: 85, + backgroundColor: 'transparent', + border: `1px solid ${tema.get().bgTotalKegiatan}` + + }}> + + + + + + {v.title} + + + ))}
- setOpenDrawer(false)} - title={{"Menu"}} - > - - - - - - Edit - - - - - - - - View File - - - - - - - - Hapus - - - - + + setOpenDrawer(false)}> + + + + router.push(`/banner/edit/${idData}`)} direction="column" align="center" justify="center" pb={20}> + + + + + Edit + + + + + { setOpenModalView(true) }} direction={"column"} align={"center"} justify={"center"}> + + + + + Lihat File + + + + { setOpenModal(true) }} direction={"column"} align={"center"} justify={"center"}> + + + + + Hapus + + + + + + + setOpenModal(false)} + description='Apakah Anda yakin ingin menghapus banner?' + onYes={(val) => { + if (val) { + onDelete(idData) + } + setOpenModal(false) + }} /> + + setOpenModalView(false)} file={idDataStorage} extension={isExtension} fitur="image" />
); } - export default ListBanner; + + + + + +// useEffect(() => { +// const handleScroll = async () => { +// if (containerRef && containerRef.current) { +// const scrollTop = containerRef.current.scrollTop; +// const containerHeight = containerRef.current.clientHeight; +// const scrollHeight = containerRef.current.scrollHeight; + +// if (scrollTop + containerHeight + 1 >= scrollHeight) { +// setPage(isPage + 1) +// } +// } +// }; + +// const container = containerRef?.current; +// container?.addEventListener("scroll", handleScroll); + +// return () => { +// container?.removeEventListener("scroll", handleScroll); +// }; +// }, [containerRef, isPage]); + diff --git a/src/module/banner/ui/viewfile_banner.tsx b/src/module/banner/ui/viewfile_banner.tsx new file mode 100644 index 0000000..ea022a3 --- /dev/null +++ b/src/module/banner/ui/viewfile_banner.tsx @@ -0,0 +1,13 @@ +import { LayoutNavbarNew } from '@/module/_global'; +import { Box } from '@mantine/core'; +import React from 'react'; + +function ViewfileBanner() { + return ( + + }/> + + ); +} + +export default ViewfileBanner; diff --git a/src/module/calender/ui/create_calender_division_caleder.tsx b/src/module/calender/ui/create_calender_division_caleder.tsx index 2cfc315..9ec3e2a 100644 --- a/src/module/calender/ui/create_calender_division_caleder.tsx +++ b/src/module/calender/ui/create_calender_division_caleder.tsx @@ -1,11 +1,428 @@ -import { Box } from '@mantine/core'; -import React from 'react'; -import NavbarCreateDivisionCalender from './navbar_create_division_calender'; +'use client' +import { LayoutNavbarNew, TEMA } from '@/module/_global'; +import LayoutModal from '@/module/_global/layout/layout_modal'; +import { useHookstate } from '@hookstate/core'; +import { Avatar, Box, Button, Divider, Grid, Group, rem, Select, SimpleGrid, Stack, Text, Textarea, TextInput } from '@mantine/core'; +import { DateInput, TimeInput } from '@mantine/dates'; +import { useMediaQuery } from '@mantine/hooks'; +import moment from 'moment'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; +import toast from 'react-hot-toast'; +import { IoIosArrowDropright } from 'react-icons/io'; +import { funCreateCalender } from '../lib/api_calender'; +import { IFormMemberCalender } from '../lib/type_calender'; +import { globalCalender } from '../lib/val_calender'; +import CreateUserCalender from './create_user_calender'; export default function CreateCalenderDivisionCaleder() { + const [value, setValue] = useState(null); + const router = useRouter() + const [isModal, setModal] = useState(false) + const [loadingModal, setLoadingModal] = useState(false) + const member = useHookstate(globalCalender) + const memberValue = member.get() as IFormMemberCalender[] + const [openMember, setOpenMember] = useState(false) + const param = useParams<{ id: string, detail: string }>() + const tema = useHookstate(TEMA) + const isMobile = useMediaQuery('(max-width: 369px)'); + const isMobile2 = useMediaQuery("(max-width: 438px)"); + const [touched, setTouched] = useState({ + title: false, + dateStart: false, + timeStart: false, + timeEnd: false, + repeatEventTyper: false, + desc: false, + repeatValue: false + }) + const [isData, setData] = useState({ + idDivision: "", + title: "", + dateStart: "", + timeStart: "", + timeEnd: "", + linkMeet: "", + repeatEventTyper: "", + desc: "", + repeatValue: "1" + }) + + async function onSubmit(val: boolean) { + try { + setLoadingModal(true) + const response = await funCreateCalender({ + idDivision: param.id, + title: isData.title, + dateStart: isData.dateStart, + timeStart: isData.timeStart, + timeEnd: isData.timeEnd, + linkMeet: isData.linkMeet, + repeatEventTyper: isData.repeatEventTyper, + desc: isData.desc, + repeatValue: isData.repeatValue, + member: memberValue + }) + + if (response.success) { + setModal(false) + router.push(`/division/${param.id}/calender`) + toast.success(response.message) + member.set([]) + } else { + toast.error(response.message) + setModal(false) + } + } catch (error) { + console.error(error) + toast.error("Gagal menambahkan pengumuman, coba lagi nanti"); + } finally { + setModal(false) + setLoadingModal(false) + } + } + + function onCheck() { + const cek = checkAll() + if (!cek) + return false + + if (memberValue.length == 0) + return toast.error("Error! silahkan pilih anggota") + + setModal(true) + } + + function checkAll() { + let nilai = true + if (isData.title === "") { + setTouched(touched => ({ ...touched, title: true })) + nilai = false + } + if (isData.dateStart === "") { + setTouched(touched => ({ ...touched, dateStart: true })) + nilai = false + } + if (isData.timeStart == "") { + setTouched(touched => ({ ...touched, timeStart: true })) + nilai = false + } + if (isData.timeEnd == "" || isData.timeStart > isData.timeEnd) { + setTouched(touched => ({ ...touched, timeEnd: true })) + nilai = false + } + if (isData.repeatEventTyper == "" || String(isData.repeatEventTyper) == "null") { + setTouched(touched => ({ ...touched, repeatEventTyper: true })) + nilai = false + } + if (isData.repeatValue === "" || Number(isData.repeatValue) <= 0) { + setTouched(touched => ({ ...touched, repeatValue: true })) + nilai = false + } + + return nilai + } + + function onValidation(kategori: string, val: any) { + if (kategori == 'title') { + setData({ ...isData, title: val }) + if (val === "") { + setTouched({ ...touched, title: true }) + } else { + setTouched({ ...touched, title: false }) + } + } else if (kategori == 'dateStart') { + setValue(val) + setData({ ...isData, dateStart: moment(val).format("YYYY-MM-DD") }) + if (val === "") { + setTouched({ ...touched, dateStart: true }) + } else { + setTouched({ ...touched, dateStart: false }) + } + } else if (kategori == 'timeStart') { + setData({ ...isData, timeStart: val }) + if (val == "") { + setTouched({ ...touched, timeStart: true }) + } else { + setTouched({ ...touched, timeStart: false }) + } + } else if (kategori == 'timeEnd') { + setData({ ...isData, timeEnd: val }) + if (val == "" || isData.timeStart > val) { + setTouched({ ...touched, timeEnd: true }) + } else { + setTouched({ ...touched, timeEnd: false }) + } + } else if (kategori == 'repeatEventTyper') { + setData(isData => ({ ...isData, repeatEventTyper: val })) + if (val == "once") { + setData(isData => ({ ...isData, repeatValue: "1" })) + } + if (val == "" || String(val) == "null") { + setTouched({ ...touched, repeatEventTyper: true }) + } else { + setTouched({ ...touched, repeatEventTyper: false }) + } + } else if (kategori == 'repeatValue') { + setData(isData => ({ ...isData, repeatValue: val, })) + if (val === "" || Number(val) <= 0) { + setTouched(touched => ({ ...touched, repeatValue: true })) + } else { + setTouched({ ...touched, repeatValue: false }) + } + } + } + + if (openMember) return setOpenMember(false)} /> + return ( - + + + + { onValidation('title', event.target.value) }} + error={ + touched.title && ( + isData.title == "" ? "Nama Acara Tidak Boleh Kosong" : null + ) + } + /> + { onValidation('dateStart', val) }} + placeholder="Input Tanggal" + label="Tanggal" + error={ + touched.dateStart && ( + isData.dateStart == "" ? "Tanggal Tidak Boleh Kosong" : null + ) + } + /> + + onValidation('timeStart', event.target.value)} + error={ + touched.timeStart && ( + isData.timeStart == "" ? "Waktu Awal Tidak Boleh Kosong" : null + ) + } + /> + onValidation('timeEnd', event.target.value)} + error={ + touched.timeEnd && ( + isData.timeEnd == "" ? "Waktu Akhir Tidak Boleh Kosong" : null + ) || + (isData.timeStart > isData.timeEnd ? "Waktu Akhir Tidak Tepat" : null) + } + /> + + setData({ ...isData, linkMeet: event.target.value })} + /> +