Merge pull request #294 from bipproduction/join

Join
This commit is contained in:
Amalia
2024-10-09 10:13:25 +08:00
committed by GitHub
49 changed files with 2318 additions and 826 deletions

View File

@@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 34 KiB

BIN
public/icon2-192x192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -1,3 +1,7 @@
export default function Page({ params }: { params: { id: string } }) {
return <div>Edit Banner</div>;
import { EditBanner } from "@/module/banner";
export default function Page() {
return (
<EditBanner />
);
}

View File

@@ -0,0 +1,10 @@
import { ViewfileBanner } from '@/module/banner';
import React from 'react';
function Page() {
return (
<ViewfileBanner/>
);
}
export default Page;

View File

@@ -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 (

View File

View File

@@ -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 });
}
}

View File

@@ -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 });
}
}

View File

@@ -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
}
}
}
}
}

View File

@@ -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(

View File

@@ -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,

View File

@@ -5,15 +5,16 @@ import { RefObject } from "react";
export const pwd_key_config = "fchgvjknlmdfnbvghhujlaknsdvjbhknlkmsdbdyu567t8y9u30r4587638y9uipkoeghjvuyi89ipkoefmnrjbhtiu4or9ipkoemnjfbhjiuoijdklnjhbviufojkejnshbiuojijknehgruyu"
export const globalRole = hookstate<string>('')
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<IGlobalTema>({
utama: "#19345E",

View File

@@ -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: {} }
}
}

View File

@@ -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 }

View File

@@ -19,14 +19,13 @@ export default function CreateAnnouncement() {
const memberGroup = useHookstate(globalMemberAnnouncement)
const memberValue = memberGroup.get() as GroupData[]
const [selectedFiles, setSelectedFiles] = useState<any>([])
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 <CreateUsersAnnouncement onClose={() => { 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 <CreateUsersAnnouncement onClose={() => { setIsChooseMember(false) }} />
return (
<Box>
<LayoutNavbarNew back="/announcement/" title="Tambah Pengumuman" menu={<></>} />
@@ -151,7 +169,7 @@ export default function CreateAnnouncement() {
padding: 10,
borderRadius: 10
}}
onClick={() => { onToChooseMember() }}
onClick={() => { setIsChooseMember(true) }}
>
<Text size="sm">
Tambah divisi penerima pengumuman
@@ -162,7 +180,7 @@ export default function CreateAnnouncement() {
<Box pt={20} mb={100}>
<Text c={tema.get().utama} mb={10}>Divisi Terpilih</Text>
{(memberGroup.length === 0) ? (
<Text c="dimmed" ta={"center"} fs={"italic"}>Belum ada anggota</Text>
<Text c="dimmed" ta={"center"} fs={"italic"}>Belum ada divisi yang dipilih</Text>
) : memberGroup.get().map((v: any, i: any) => {
return (
<Box key={i} mt={10}>
@@ -195,13 +213,14 @@ export default function CreateAnnouncement() {
Simpan
</Button>
</Box>
<LayoutModal opened={isOpen} onClose={() => setOpen(false)}
<LayoutModal loading={loadingKonfirmasi} opened={isOpen} onClose={() => setOpen(false)}
description="Apakah Anda yakin ingin menambahkan data?"
onYes={(val) => {
if (val) {
onSubmit()
} else {
setOpen(false)
}
setOpen(false)
}} />
</Box>

View File

@@ -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 (
<div>
<LayoutNavbarNew back="" title="Tambah Divisi Penerima Pengumuman" menu={<></>} />
<LayoutNavbarNew state={
<Box>
<ActionIcon variant="light" onClick={() => { onClose(true) }} bg={tema.get().bgIcon} size="lg" radius="lg" aria-label="Settings">
<HiChevronLeft size={20} color='white' />
</ActionIcon>
</Box>
} title="Tambah Divisi Penerima Pengumuman" menu={<></>} />
<Box p={20} pb={100}>
<Group justify='flex-end' mb={20}>
<Text

View File

@@ -1,21 +1,21 @@
'use client'
import { LayoutNavbarNew, TEMA, WARNA } from "@/module/_global";
import { LayoutNavbarNew, TEMA } from "@/module/_global";
import LayoutModal from "@/module/_global/layout/layout_modal";
import { useHookstate } from "@hookstate/core";
import { Box, Button, Flex, Group, List, rem, Skeleton, Stack, Text, Textarea, TextInput } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
import toast from "react-hot-toast";
import { HiOutlineChevronRight } from "react-icons/hi2";
import { funEditAnnouncement, funGetAnnouncementById } from "../lib/api_announcement";
import { useParams, useRouter } from "next/navigation";
import { useHookstate } from "@hookstate/core";
import { globalMemberEditAnnouncement } from "../lib/val_announcement";
import { GroupData, GroupDataEditAnnouncement } from "../lib/type_announcement";
import EditChooseMember from "./edit_choose_member";
import { IoIosArrowForward } from "react-icons/io";
import { funEditAnnouncement, funGetAnnouncementById } from "../lib/api_announcement";
import { GroupData } from "../lib/type_announcement";
import { globalMemberEditAnnouncement } from "../lib/val_announcement";
import EditChooseMember from "./edit_choose_member";
export default function EditAnnouncement() {
const [isOpen, setOpen] = useState(false)
const [loadingKonfirmasi, setLoadingKonfirmasi] = useState(false)
const [isChooseDivisi, setChooseDivisi] = useState(false)
const param = useParams<{ id: string }>()
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 <EditChooseMember onClose={() => { 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 <EditChooseMember onClose={() => { setChooseDivisi(false) }} />
return (
<>
<LayoutNavbarNew back="" title="Edit Pengumuman" menu={<></>} />
@@ -160,7 +157,7 @@ export default function EditAnnouncement() {
:
<>
<TextInput
size="md" type="text" radius={30} placeholder="Judul Pengumuman" withAsterisk label="Judul" w={"100%"}
size="md" type="text" radius={10} placeholder="Judul Pengumuman" withAsterisk label="Judul" w={"100%"}
styles={{
input: {
color: tema.get().utama,
@@ -231,28 +228,34 @@ export default function EditAnnouncement() {
))
:
memberGroup.get().map((v: any, i: any) => {
return (
<Box key={i} mt={10}>
<Text fw={"bold"}>{v.name}</Text>
<Box pl={20} pr={10}>
<Flex direction={"column"} gap={"md"}>
<List>
{
v.Division.map((division: any) => {
return <List.Item key={division.id}>
<Text lineClamp={1}>{division.name}</Text>
</List.Item>
})
}
</List>
</Flex>
</Box>
</Box>
);
<>
<Text c={tema.get().utama} mb={10}>Divisi Terpilih</Text>
{
memberGroup.get().length == 0 ? <Text c="dimmed" ta={"center"} fs={"italic"}>Belum ada divisi yang dipilih</Text> :
memberGroup.get().map((v: any, i: any) => {
return (
<Box key={i} mt={10}>
<Text fw={"bold"}>{v.name}</Text>
<Box pl={20} pr={10}>
<Flex direction={"column"} gap={"md"}>
<List>
{
v.Division.map((division: any) => {
return <List.Item key={division.id}>
<Text lineClamp={1}>{division.name}</Text>
</List.Item>
})
}
</List>
</Flex>
</Box>
</Box>
);
})
}
</>
}
)
}
</Box>
@@ -282,8 +285,9 @@ export default function EditAnnouncement() {
onYes={(val) => {
if (val) {
onSubmit()
} else {
setOpen(false)
}
setOpen(false)
}} />
</>
)

View File

@@ -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 (
<div>
<LayoutNavbarNew back="" title="Tambah Divisi Penerima Pengumuman" menu={<></>} />
<LayoutNavbarNew state={
<Box>
<ActionIcon variant="light" onClick={() => { onClose(true) }} bg={tema.get().bgIcon} size="lg" radius="lg" aria-label="Settings">
<HiChevronLeft size={20} color='white' />
</ActionIcon>
</Box>
} title="Tambah Divisi Penerima Pengumuman" menu={<></>} />
<Box p={20} pb={100}>
<Group justify='flex-end' mb={20}>
<Text

View File

@@ -49,23 +49,30 @@ function ViewLogin() {
if (cekLogin.success) {
const code = Math.floor(Math.random() * 1000) + 1000
setLoading(true)
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 {
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)
}

View File

@@ -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)

View File

@@ -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}
import EditBanner from "./ui/edit_banner";
import ViewfileBanner from "./ui/viewfile_banner";
export { NavbarBanner, ListBanner, CreateBanner, EditBanner, ViewfileBanner }

View File

@@ -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);
}

View File

@@ -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
}

View File

@@ -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<any>()
const openRef = useRef<() => void>(null)
const [img, setIMG] = useState<any | null>()
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<DropzoneProps>) {
return (
<Box>
<LayoutNavbarNew back='/banner' title='Tambah Banner' menu={<></>} />
<LayoutNavbarNew back='/banner' title='Tambah Banner' menu />
<Box p={20}>
<Box>
<Paper withBorder radius={20}>
<Dropzone
onDrop={(files) => 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?.()}
>
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
<Dropzone.Accept>
<IconUpload
style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-blue-6)' }}
stroke={1.5}
/>
</Dropzone.Accept>
<Dropzone.Reject>
<IconX
style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-red-6)' }}
stroke={1.5}
/>
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto
style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-dimmed)' }}
stroke={1.5}
/>
</Dropzone.Idle>
<Box>
<Text size="xl" inline>
Upload File
</Text>
<Text size="sm" c="dimmed" inline mt={7}>
File Tidak Boleh Melebihi 500mb
</Text>
</Box>
</Group>
{
img ?
<Image radius="md" src={img} alt='' />
:
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
<Dropzone.Accept>
<IconUpload
style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-blue-6)' }}
stroke={1.5}
/>
</Dropzone.Accept>
<Dropzone.Reject>
<IconX
style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-red-6)' }}
stroke={1.5}
/>
</Dropzone.Reject>
<Dropzone.Idle>
<IconPhoto
style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-dimmed)' }}
stroke={1.5}
/>
</Dropzone.Idle>
<div>
<Text size="xl" inline>
Klik Untuk Upload Image
</Text>
<Text size="sm" c="dimmed" inline mt={7}>
Ukuran Foto Tidak Boleh Lebih Dari 1MB
</Text>
</div>
</Group>
}
</Dropzone>
</Paper>
<Box>
<TextInput
mt={10}
label={ <Text>Judul Banner</Text>}
label={<Text >Judul Banner</Text>}
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,
},
}}
/>
</Box>
<Button size='lg' bg={WARNA.biruTua} radius={30} fullWidth mt={20}>Simpan</Button>
<Box pos={"fixed"} bottom={0} p={rem(20)} w={"100%"} style={{
maxWidth: rem(510),
zIndex: 999,
backgroundColor: `${tema.get().bgUtama}`
}}>
<Button
size='lg'
color='white'
bg={tema.get().utama} radius={30} fullWidth
onClick={() => {
if (touched.title || touched.image) {
toast.error("Mohon Isi Semua Data")
} else {
setModal(true)
}
}}
>Simpan</Button>
</Box>
</Box>
</Box>
<LayoutModal
loading={loadingKonfirmasi}
opened={isModal}
onClose={() => setModal(false)}
description="Apakah anda yakin ingin menambahkan banner ini?"
onYes={(val) => {
if (val) {
onSubmit(val);
} else {
setModal(false);
}
}}
/>
</Box>
);
}

View File

@@ -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<IEditDataBanner>({
id: "",
title: "",
extension: "",
image: "",
});
const openRef = useRef<() => void>(null)
const [img, setIMG] = useState<any | null>()
const [imgForm, setImgForm] = useState<any>()
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 (
<Box>
<LayoutNavbarNew back='/banner' title='Edit Banner' menu={<></>} />
<Box p={20}>
<Box>
<Paper withBorder radius={20}>
<Dropzone
openRef={openRef}
onDrop={async (files) => {
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?.()}
>
<Image radius={"md"} src={img} alt="" />
</Dropzone>
</Paper>
<Box>
<TextInput
mt={10}
label="Judul Banner"
placeholder='Banner'
value={data.title}
onChange={(e) => {
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'
/>
</Box>
<Box pos={"fixed"} bottom={0} p={rem(20)} w={"100%"} style={{
maxWidth: rem(510),
zIndex: 999,
backgroundColor: `${tema.get().bgUtama}`
}}>
<Button
size='lg'
color='white'
bg={tema.get().utama}
radius={30}
fullWidth
onClick={() => {
if (touched.title || touched.image) {
toast.error('Mohon Isi Semua Data')
} else {
setModal(true)
}
}}
>
Simpan
</Button>
<LayoutModal
loading={loading}
opened={isModal}
onClose={() => setModal(false)}
description="Apakah Anda yakin ingin mengedit banner ini?"
onYes={(val) => {
if (val) {
onSubmit(val)
} else {
setModal(false)
}
}}
/>
</Box>
</Box>
</Box>
</Box>
);
}

View File

@@ -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<IDataBanner[]>([])
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 (
<Box pt={20}>
<Box>
<Box pt={2}>
<Box p={20}>
<TextInput
styles={{
input: {
color: tema.get().utama,
borderRadius: '#A3A3A3',
borderColor: '#A3A3A3',
},
}}
size='md'
radius={30}
leftSection={<HiMagnifyingGlass size={20} />}
placeholder='pencarian'
value={searchQuerry}
onChange={(val) => { searchBanner(val.target.value) }}
/>
</Box>
<Box p={20}>
<Anchor underline='never'>
<Stack align='center' justify='center'>
{[...Array(5)].map((_, index) => (
<div key={index} onClick={() => { setOpenDrawer(true) }}>
<Text c={"dark"} ta={"center"}>Banner {index + 1}</Text>
<Image radius={"lg"} src={`/assets/img/banner/Banner-${index + 1}.png`} alt='' w={380} h={194.5} />
</div>
{isData.map((v, index) => (
<Paper radius={'md'} withBorder key={index} onClick={() => {
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}`
}}>
<Group mt={"15"}>
<ActionIcon variant='transparent' w={"100"}>
<Image
radius={"xs"}
src={`https://wibu-storage.wibudev.com/api/files/${v.image}`}
alt=''
w={76}
h={38.9}
/>
</ActionIcon>
<Flex direction={"column"}>
<Text c={"dark"} fz={"h4"}>{v.title}</Text>
</Flex>
</Group>
</Paper>
))}
</Stack>
</Anchor>
</Box>
<LayoutDrawer
opened={openDrawer}
onClose={() => setOpenDrawer(false)}
title={<Text lineClamp={1}>{"Menu"}</Text>}
>
<SimpleGrid
cols={{ base: 3, sm: 3, lg: 3 }}
>
<Box>
<Anchor underline='never'>
<Flex direction={"column"}>
<FaPencil size={30} color={WARNA.biruTua} />
<Text c={"dark"} mt={10}>Edit</Text>
</Flex>
</Anchor>
</Box>
<Box>
<Anchor underline='never'>
<Flex direction={"column"}>
<FaFile size={30} color={WARNA.biruTua} />
<Text c={"dark"} mt={10}>View File</Text>
</Flex>
</Anchor>
</Box>
<Box >
<Anchor underline='never'>
<Flex direction={"column"}>
<FaTrash size={30} color={WARNA.biruTua} />
<Text ta="center" c={"dark"} mt={10}>Hapus</Text>
</Flex>
</Anchor>
</Box>
</SimpleGrid>
<LayoutDrawer opened={openDrawer} title={'Menu'} onClose={() => setOpenDrawer(false)}>
<Box>
<Stack pt={10}>
<SimpleGrid
cols={{ base: 2, sm: 3, lg: 3 }}
style={{
alignContent: "flex-start",
alignItems: "flex-start"
}}
>
<Flex onClick={() => router.push(`/banner/edit/${idData}`)} direction="column" align="center" justify="center" pb={20}>
<Box>
<FaPencil size={30} color={WARNA.biruTua} />
</Box>
<Box>
<Text c={tema.get().utama} fz={{ base: 'sm', md: 'md' }}>Edit</Text>
</Box>
</Flex>
<Flex onClick={() => { setOpenModalView(true) }} direction={"column"} align={"center"} justify={"center"}>
<Box>
<FaFile size={30} color={tema.get().utama} />
</Box>
<Box>
<Text c={tema.get().utama} fz={{ base: 'sm', md: 'md'}}>Lihat File</Text>
</Box>
</Flex>
<Flex onClick={() => { setOpenModal(true) }} direction={"column"} align={"center"} justify={"center"}>
<Box>
<FaTrash size={30} color={tema.get().utama} />
</Box>
<Box>
<Text c={tema.get().utama} fz={{ base: 'sm', md: 'md'}}>Hapus</Text>
</Box>
</Flex>
</SimpleGrid>
</Stack>
</Box>
</LayoutDrawer>
<LayoutModal
opened={isOpenModal}
onClose={() => setOpenModal(false)}
description='Apakah Anda yakin ingin menghapus banner?'
onYes={(val) => {
if (val) {
onDelete(idData)
}
setOpenModal(false)
}} />
<LayoutModalViewFile opened={isOpenModalView} onClose={() => setOpenModalView(false)} file={idDataStorage} extension={isExtension} fitur="image" />
</Box>
);
}
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]);

View File

@@ -0,0 +1,13 @@
import { LayoutNavbarNew } from '@/module/_global';
import { Box } from '@mantine/core';
import React from 'react';
function ViewfileBanner() {
return (
<Box>
<LayoutNavbarNew back='/banner' title='View File Banner' menu={<></>}/>
</Box>
);
}
export default ViewfileBanner;

View File

@@ -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<Date | null>(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 <CreateUserCalender onClose={() => setOpenMember(false)} />
return (
<Box>
<NavbarCreateDivisionCalender />
<LayoutNavbarNew back={`/division/${param.id}/calender/`} title="Tambah Acara" menu />
<Box p={20}>
<Stack pb={100}>
<TextInput
required
styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
size="md"
placeholder="Nama Acara"
label="Nama Acara"
value={isData.title}
onChange={(event) => { onValidation('title', event.target.value) }}
error={
touched.title && (
isData.title == "" ? "Nama Acara Tidak Boleh Kosong" : null
)
}
/>
<DateInput
required
styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
size="md"
value={value}
onChange={(val) => { onValidation('dateStart', val) }}
placeholder="Input Tanggal"
label="Tanggal"
error={
touched.dateStart && (
isData.dateStart == "" ? "Tanggal Tidak Boleh Kosong" : null
)
}
/>
<SimpleGrid
cols={{ base: 2, sm: 2, lg: 2 }}
>
<TimeInput
required
styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
size="md"
label="Waktu Awal"
value={isData.timeStart}
onChange={(event) => onValidation('timeStart', event.target.value)}
error={
touched.timeStart && (
isData.timeStart == "" ? "Waktu Awal Tidak Boleh Kosong" : null
)
}
/>
<TimeInput
required
styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
size="md"
label="Waktu Akhir"
value={isData.timeEnd}
onChange={(event) => 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)
}
/>
</SimpleGrid>
<TextInput
styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
size="md"
placeholder="Link Meet"
label="Link Meet"
value={isData.linkMeet}
onChange={(event) => setData({ ...isData, linkMeet: event.target.value })}
/>
<Select
required
styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
size="md"
placeholder="Ulangi Acara"
label="Ulangi Acara"
data={[
{ value: 'once', label: 'Acara 1 Kali' },
{ value: 'daily', label: 'Setiap Hari' },
{ value: 'weekly', label: 'Mingguan' },
{ value: 'monthly', label: 'Bulanan' },
{ value: 'yearly', label: 'Tahunan' },
]}
value={isData.repeatEventTyper}
onChange={(val: any) => { onValidation('repeatEventTyper', val) }}
error={
touched.repeatEventTyper && (
isData.repeatEventTyper == "" || String(isData.repeatEventTyper) == "null" ? "Ulangi Acara Tidak Boleh Kosong" : null
)
}
/>
<TextInput styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
type='number'
required
label="Jumlah pengulangan"
size="md"
placeholder='Jumlah pengulangan'
value={isData.repeatValue}
min={1}
disabled={(isData.repeatEventTyper == "once") ? true : false}
onChange={(event) => { onValidation('repeatValue', event.currentTarget.value) }}
error={
touched.repeatValue && (
isData.repeatValue == "" ? "Jumlah pengulangan tidak boleh kosong" :
Number(isData.repeatValue) <= 0 ? "Jumlah pengulangan tidak boleh di bawah 1" : ""
)
}
/>
<Textarea styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
value={isData.desc}
size="md" placeholder='Deskripsi' label="Deskripsi"
onChange={(event) => setData({ ...isData, desc: event.target.value })}
/>
<Box mt={5} onClick={() => setOpenMember(true)}>
<Group
justify="space-between"
p={10}
style={{
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
}}
>
<Text>Tambah Anggota</Text>
<IoIosArrowDropright size={25} />
</Group>
</Box>
{
member.length > 0 &&
<Box pt={30} mb={60}>
<Group justify="space-between">
<Text c={tema.get().utama}>Anggota Terpilih</Text>
<Text c={tema.get().utama}>Total {member.length} Anggota</Text>
</Group>
<Box pt={10}>
<Box mb={20}>
<Box
style={{
border: `1px solid ${"#C7D6E8"}`,
borderRadius: 10,
}}
px={20}
py={10}
>
{member.length == 0 ?
<Box style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '10vh' }}>
<Text c="dimmed" ta={"center"} fs={"italic"}>Tidak ada Anggota</Text>
</Box>
:
member.get().map((v: any, i: any) => {
return (
<Box key={i}>
<Grid align='center' mt={10}
>
<Grid.Col span={1}>
<Avatar src={`https://wibu-storage.wibudev.com/api/files/${v.img}`} alt="it's me" size={'lg'} />
</Grid.Col>
<Grid.Col span={8}>
<Text c={tema.get().utama} fw={"bold"} lineClamp={1} pl={isMobile2 ? 40 : 30} fz={isMobile ? 14 : 16} >
{v.name}
</Text>
</Grid.Col>
<Grid.Col span={3}>
<Text c={tema.get().utama} fw={"bold"} ta={'end'} fz={isMobile ? 13 : 16}>
Anggota
</Text>
</Grid.Col>
</Grid>
<Box mt={10}>
<Divider size={"xs"} />
</Box>
</Box>
);
})}
</Box>
</Box>
</Box>
</Box>
}
</Stack>
</Box>
<Box pos={'fixed'} bottom={0} p={rem(20)} w={"100%"} style={{
maxWidth: rem(550),
zIndex: 999,
backgroundColor: `${tema.get().bgUtama}`,
}}>
<Button
c={"white"}
bg={tema.get().utama}
size="lg"
radius={30}
fullWidth
onClick={() => { onCheck() }}
>
Simpan
</Button>
</Box>
<LayoutModal loading={loadingModal} opened={isModal} onClose={() => setModal(false)}
description="Apakah Anda yakin ingin menambahkan data?"
onYes={(val) => {
if (val) {
onSubmit(val)
} else {
setModal(false)
}
}} />
</Box>
);
}
}

View File

@@ -1,21 +1,22 @@
"use client"
import { LayoutNavbarNew, TEMA } from '@/module/_global';
import { Box, Button, Group, rem, Select, SimpleGrid, Skeleton, Stack, Text, Textarea, TextInput } from '@mantine/core';
import { DateInput, TimeInput } from '@mantine/dates';
import React, { useState } from 'react';
import { useParams, useRouter } from 'next/navigation';
import LayoutModal from '@/module/_global/layout/layout_modal';
import toast from 'react-hot-toast';
import { funEditCalenderById, funGetOneCalender, funGetOneCalenderByIdCalendar, } from '../lib/api_calender';
import { useHookstate } from '@hookstate/core';
import { Box, Button, Group, rem, Select, SimpleGrid, Skeleton, Stack, Textarea, TextInput } from '@mantine/core';
import { DateInput, TimeInput } from '@mantine/dates';
import { useShallowEffect } from '@mantine/hooks';
import { IDetailByIdCalender } from '../lib/type_calender';
import moment from 'moment';
import "moment/locale/id";
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import toast from 'react-hot-toast';
import { funEditCalenderById, funGetOneCalenderByIdCalendar } from '../lib/api_calender';
import { IDetailByIdCalender } from '../lib/type_calender';
import UpdateListUsers from './update_list_users';
import { useHookstate } from '@hookstate/core';
export default function UpdateDivisionCalender() {
const [isModal, setModal] = useState(false)
const [loadingModal, setLoadingModal] = useState(false)
const param = useParams<{ id: string, detail: string }>()
const [isDataCalender, setDataCalender] = useState<IDetailByIdCalender>()
const [openMember, setOpenMember] = useState(false)
@@ -54,35 +55,89 @@ export default function UpdateDivisionCalender() {
const [value, setValue] = useState<Date | null>(null);
const router = useRouter()
async function onSubmit(val: boolean) {
try {
if (val) {
const response = await funEditCalenderById(param.detail, {
title: isDataCalender?.title,
dateStart: isDataCalender?.dateStart,
timeStart: isDataCalender?.timeStart,
timeEnd: isDataCalender?.timeEnd,
linkMeet: isDataCalender?.linkMeet,
repeatEventTyper: isDataCalender?.repeatEventTyper,
desc: isDataCalender?.desc,
repeatValue: isDataCalender?.repeatValue
})
if (response.success) {
setModal(false)
router.push(`/division/${param.id}/calender`)
toast.success(response.message)
} else {
toast.error(response.message)
}
function onCheck() {
if (Object.values(touched).some((v) => v == true))
return false
setModal(true)
}
async function onSubmit() {
try {
setLoadingModal(true)
const response = await funEditCalenderById(param.detail, {
title: isDataCalender?.title,
dateStart: isDataCalender?.dateStart,
timeStart: isDataCalender?.timeStart,
timeEnd: isDataCalender?.timeEnd,
linkMeet: isDataCalender?.linkMeet,
repeatEventTyper: isDataCalender?.repeatEventTyper,
desc: isDataCalender?.desc,
repeatValue: isDataCalender?.repeatValue
})
if (response.success) {
router.push(`/division/${param.id}/calender`)
toast.success(response.message)
} else {
toast.error(response.message)
}
setModal(false)
} catch (error) {
console.error(error)
toast.error("Terjadi kesalahan! Silahkan coba kembali");
setModal(false)
} finally {
setModal(false)
setLoadingModal(false)
}
}
function onValidation(kategori: string, val: any) {
if (kategori == 'title') {
setDataCalender({ ...isDataCalender, title: val })
if (val === "") {
setTouched({ ...touched, title: true })
} else {
setTouched({ ...touched, title: false })
}
} else if (kategori == 'dateStart') {
setValue(val)
setDataCalender({ ...isDataCalender, dateStart: moment(val).format("YYYY-MM-DD") })
if (val === "") {
setTouched({ ...touched, dateStart: true })
} else {
setTouched({ ...touched, dateStart: false })
}
} else if (kategori == 'timeStart') {
setDataCalender({ ...isDataCalender, timeStart: val })
if (val == "") {
setTouched({ ...touched, timeStart: true })
} else {
setTouched({ ...touched, timeStart: false })
}
} else if (kategori == 'timeEnd') {
setDataCalender({ ...isDataCalender, timeEnd: val })
if (val == "" || String(isDataCalender?.timeStart) > String(val)) {
setTouched({ ...touched, timeEnd: true })
} else {
setTouched({ ...touched, timeEnd: false })
}
} else if (kategori == 'repeatEventTyper') {
setDataCalender(isDataCalender => ({ ...isDataCalender, repeatEventTyper: val }))
if (val == "once") {
setDataCalender(isDataCalender => ({ ...isDataCalender, repeatValue: "1" }))
}
if (val == "" || String(val) == "null") {
setTouched({ ...touched, repeatEventTyper: true })
} else {
setTouched({ ...touched, repeatEventTyper: false })
}
} else if (kategori == 'repeatValue') {
setDataCalender(isDataCalender => ({ ...isDataCalender, repeatValue: val, }))
if (val === "" || Number(val) <= 0) {
setTouched(touched => ({ ...touched, repeatValue: true }))
} else {
setTouched({ ...touched, repeatValue: false })
}
}
}
@@ -117,16 +172,7 @@ export default function UpdateDivisionCalender() {
placeholder="Acara"
label="Acara"
defaultValue={isDataCalender?.title}
onChange={
(event) => {
setDataCalender({
...isDataCalender,
title: event.target.value
})
setTouched({ ...touched, title: false })
}
}
onBlur={() => setTouched({ ...touched, title: true })}
onChange={(event) => { onValidation('title', event.target.value) }}
required
error={
touched.title && (
@@ -145,20 +191,10 @@ export default function UpdateDivisionCalender() {
value={
(isDataCalender?.dateStart == '' || isDataCalender?.dateStart == null) ? null : new Date(isDataCalender.dateStart)
}
onChange={
(val) => {
setValue(val);
setDataCalender({
...isDataCalender,
dateStart: moment(val).format("YYYY-MM-DD")
})
setTouched({ ...touched, dateStart: false })
}
}
onChange={(val) => { onValidation('dateStart', val) }}
placeholder="Input Tanggal"
label="Tanggal"
minDate={new Date()}
onBlur={() => setTouched({ ...touched, dateStart: true })}
error={
touched.dateStart && (
isDataCalender?.dateStart == "" ? "Tanggal Tidak Boleh Kosong" : null
@@ -178,17 +214,8 @@ export default function UpdateDivisionCalender() {
}}
size="md"
label="Waktu Awal"
// value={isDataCalender?.timeStart}
defaultValue={isDataCalender?.timeStart}
onChange={
(event) => {
setDataCalender({
...isDataCalender,
timeStart: event.target.value
})
}
}
onBlur={() => setTouched({ ...touched, timeStart: true })}
onChange={(event) => { onValidation('timeStart', event.target.value) }}
error={touched.timeStart && !isDataCalender?.timeStart ? "Waktu Awal Tidak Boleh Kosong" : null}
required
/>
@@ -201,17 +228,8 @@ export default function UpdateDivisionCalender() {
}}
size="md"
label="Waktu Akhir"
// value={isDataCalender?.timeEnd}
defaultValue={isDataCalender?.timeEnd}
onChange={
(event) => {
setDataCalender({
...isDataCalender,
timeEnd: event.target.value
})
}
}
onBlur={() => setTouched({ ...touched, timeEnd: true })}
onChange={(event) => { onValidation('timeEnd', event.target.value) }}
required
error={
touched.timeEnd && (
@@ -232,14 +250,7 @@ export default function UpdateDivisionCalender() {
placeholder="Link Meet"
label="Link Meet"
defaultValue={isDataCalender?.linkMeet}
onChange={
(event) => {
setDataCalender({
...isDataCalender,
linkMeet: event.target.value
})
}
}
onChange={(event) => { setDataCalender({ ...isDataCalender, linkMeet: event.target.value }) }}
/>
<Select
styles={{
@@ -260,76 +271,36 @@ export default function UpdateDivisionCalender() {
]}
value={isDataCalender?.repeatEventTyper}
defaultValue={isDataCalender?.repeatEventTyper}
onChange={
(val: any) => {
setDataCalender({
...isDataCalender,
repeatEventTyper: val
})
setTouched({ ...touched, repeatEventTyper: false })
}
}
onBlur={() => setTouched({ ...touched, repeatEventTyper: true })}
onChange={(val: any) => { onValidation('repeatEventTyper', val) }}
error={
touched.repeatEventTyper && (
isDataCalender?.repeatEventTyper == "" ? "Ulangi Acara Tidak Boleh Kosong" : null
isDataCalender?.repeatEventTyper == "" || String(isDataCalender?.repeatEventTyper) == "null" ? "Ulangi Acara Tidak Boleh Kosong" : null
)
}
required
/>
{isDataCalender?.repeatEventTyper == "once" ?
<TextInput styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
type='number'
required
label="Jumlah pengulangan"
size="md"
disabled
placeholder='Jumlah pengulangan'
defaultValue={"1"}
onChange={(event) => {
setDataCalender({ ...isDataCalender, repeatValue: String(event.currentTarget.value) })
setTouched({ ...touched, repeatValue: false })
}}
onBlur={() => setTouched({ ...touched, repeatValue: true })}
error={
touched.repeatValue && (
isDataCalender?.repeatValue == "" ? "Jumlah pengulangan tidak boleh kosong" : null
// || Number(isDataCalender?.repeatValue) <= 0 ? "Jumlah pengulangan tidak boleh dibawah 1" : null
)
}
/>
:
<TextInput styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
type='number'
required
label="Jumlah pengulangan"
size="md"
min={1}
placeholder='Jumlah pengulangan'
defaultValue={isDataCalender?.repeatValue}
onChange={(event) => {
setDataCalender({ ...isDataCalender, repeatValue: String(event.currentTarget.value) })
setTouched({ ...touched, repeatValue: false })
}}
onBlur={() => setTouched({ ...touched, repeatValue: true })}
error={
touched.repeatValue && (
isDataCalender?.repeatValue == "" ? "Jumlah pengulangan tidak boleh kosong" :
Number(isDataCalender?.repeatValue) <= 0 ? "Jumlah pengulangan tidak boleh di bawah 1" : ""
)
}
/>
}
<TextInput styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
type='number'
required
label="Jumlah pengulangan"
size="md"
min={1}
disabled={(isDataCalender?.repeatEventTyper == "once") ? true : false}
placeholder='Jumlah pengulangan'
defaultValue={isDataCalender?.repeatValue}
onChange={(event) => { onValidation('repeatValue', event.currentTarget.value) }}
error={
touched.repeatValue && (
isDataCalender?.repeatValue == "" ? "Jumlah pengulangan tidak boleh kosong" :
Number(isDataCalender?.repeatValue) <= 0 ? "Jumlah pengulangan tidak boleh di bawah 1" : ""
)
}
/>
<Textarea styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
@@ -365,15 +336,21 @@ export default function UpdateDivisionCalender() {
size="lg"
radius={30}
fullWidth
onClick={() => setModal(true)}
onClick={() => onCheck()}
>
Simpan
</Button>
}
</Box>
<LayoutModal opened={isModal} onClose={() => setModal(false)}
<LayoutModal loading={loadingModal} opened={isModal} onClose={() => setModal(false)}
description="Apakah Anda yakin ingin mengubah data acara ini? Data ini akan mempengaruhi semua data yang terkait"
onYes={(val) => { onSubmit(val) }} />
onYes={(val) => {
if (val) {
onSubmit()
} else {
setModal(false)
}
}} />
</Box>
);
}

View File

@@ -1,13 +1,12 @@
"use client"
import { LayoutNavbarNew, TEMA } from '@/module/_global';
import { useHookstate } from '@hookstate/core';
import { Badge, Box, Button, Center, ColorInput, Flex, Pill, rem, SimpleGrid, Stack, Text, TextInput } from '@mantine/core';
import React, { useState } from 'react';
import { funCreateTheme } from '../lib/api_theme';
import toast from 'react-hot-toast';
import { useRouter } from 'next/navigation';
import LayoutModal from '@/module/_global/layout/layout_modal';
import _ from 'lodash';
import { useHookstate } from '@hookstate/core';
import { Box, Button, Center, ColorInput, Flex, Pill, rem, SimpleGrid, Stack, TextInput } from '@mantine/core';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import toast from 'react-hot-toast';
import { funCreateTheme } from '../lib/api_theme';
export default function CreatePaletteColor() {
const [isValModal, setValModal] = useState(false);
@@ -56,11 +55,46 @@ export default function CreatePaletteColor() {
}
function onCheck() {
if (Object.values(touched).some((v) => v == true))
const cek = checkAll()
if (!cek)
return false
setValModal(true)
}
function checkAll() {
let nilai = true
if (isWarna.name === "") {
setTouched(touched => ({ ...touched, name: true }))
nilai = false
}
if (isWarna.utama === "" || isWarna.utama.substring(0, 1) !== '#') {
setTouched(touched => ({ ...touched, utama: true }))
nilai = false
}
if (isWarna.bgUtama === "" || isWarna.bgUtama.substring(0, 1) !== '#') {
setTouched(touched => ({ ...touched, bgUtama: true }))
nilai = false
}
if (isWarna.bgIcon === "" || isWarna.bgIcon.substring(0, 1) !== '#') {
setTouched(touched => ({ ...touched, bgIcon: true }))
nilai = false
}
if (isWarna.bgFiturHome === "" || isWarna.bgFiturHome.substring(0, 1) !== '#') {
setTouched(touched => ({ ...touched, bgFiturHome: true }))
nilai = false
}
if (isWarna.bgFiturDivision === "" || isWarna.bgFiturDivision.substring(0, 1) !== '#') {
setTouched(touched => ({ ...touched, bgFiturDivision: true }))
nilai = false
}
if (isWarna.bgTotalKegiatan === "" || isWarna.bgTotalKegiatan.substring(0, 1) !== '#') {
setTouched(touched => ({ ...touched, bgTotalKegiatan: true }))
nilai = false
}
return nilai
}
function onValidation(kategori: string, val: string) {
if (kategori == 'name') {
setWarna({ ...isWarna, name: val })
@@ -239,8 +273,8 @@ export default function CreatePaletteColor() {
}
error={
touched.bgTotalKegiatan && (
isWarna.bgTotalKegiatan == "" ? "Background Total Kegiatan Tidak Boleh Kosong" :
isWarna.bgTotalKegiatan.substring(0, 1) != "#" ? "Kode Warna Tidak Valid" : null
isWarna.bgTotalKegiatan == "" ? "Background Total Kegiatan Tidak Boleh Kosong" :
isWarna.bgTotalKegiatan.substring(0, 1) != "#" ? "Kode Warna Tidak Valid" : null
)
}
/>

View File

@@ -1,14 +1,14 @@
"use client"
import { LayoutNavbarNew, TEMA } from '@/module/_global';
import LayoutModal from "@/module/_global/layout/layout_modal";
import { useHookstate } from '@hookstate/core';
import { Badge, Box, Button, Center, ColorInput, Flex, Pill, rem, SimpleGrid, Skeleton, Stack, Text, TextInput } from '@mantine/core';
import React, { useState } from 'react';
import { IEditTheme } from '../lib/type_theme';
import { Box, Button, Center, ColorInput, Flex, Pill, rem, SimpleGrid, Skeleton, Stack, TextInput } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import toast from 'react-hot-toast';
import { funEditTheme, funGetThemeById } from '../lib/api_theme';
import { useParams, useRouter } from 'next/navigation';
import { useShallowEffect } from '@mantine/hooks';
import LayoutModal from "@/module/_global/layout/layout_modal";
import { IEditTheme } from '../lib/type_theme';
export default function EditPaletteColor() {
const tema = useHookstate(TEMA)
@@ -16,6 +16,7 @@ export default function EditPaletteColor() {
const [isModal, setModal] = useState(false)
const param = useParams<{ id: string }>()
const [loading, setLoading] = useState(true)
const [loadingKonfirmasi, setLoadingKonfirmasi] = useState(false);
const [touched, setTouched] = useState({
name: false,
utama: false,
@@ -62,6 +63,7 @@ export default function EditPaletteColor() {
async function onSubmit() {
try {
setLoadingKonfirmasi(true)
const res = await funEditTheme(param.id, isWarna)
if (res.success) {
toast.success(res.message);
@@ -72,6 +74,9 @@ export default function EditPaletteColor() {
} catch (error) {
console.error(error)
toast.error("Gagal mengedit tema, coba lagi nanti");
} finally {
setLoadingKonfirmasi(false)
setModal(false)
}
}
@@ -381,11 +386,13 @@ export default function EditPaletteColor() {
</Box>
<LayoutModal opened={isModal} onClose={() => setModal(false)} description="Apakah Anda yakin ingin mengubah data?"
<LayoutModal loading={loadingKonfirmasi} opened={isModal} onClose={() => setModal(false)} description="Apakah Anda yakin ingin mengubah data?"
onYes={(val) => {
if (val)
if (val) {
onSubmit()
setModal(false)
} else {
setModal(false)
}
}
} />
</Box>

View File

@@ -13,6 +13,7 @@ import { funCreateDiscussion } from "../lib/api_discussion";
export default function FormCreateDiscussion({ id }: { id: string }) {
const [isValModal, setValModal] = useState(false)
const [loadingModal, setLoadingModal] = useState(false)
const router = useRouter()
const [isImg, setImg] = useState("")
const param = useParams<{ id: string, detail: string }>()
@@ -50,26 +51,25 @@ export default function FormCreateDiscussion({ id }: { id: string }) {
async function createDiscussion(val: boolean) {
try {
if (val) {
const response = await funCreateDiscussion({
desc: isData.desc,
idDivision: id
})
setLoadingModal(true)
const response = await funCreateDiscussion({
desc: isData.desc,
idDivision: id
})
if (response.success) {
setDataRealtime(response.notif)
toast.success(response.message)
router.push(`/division/${param.id}/discussion/`)
setValModal(false)
} else {
toast.error(response.message)
}
if (response.success) {
setDataRealtime(response.notif)
toast.success(response.message)
router.push(`/division/${param.id}/discussion/`)
} else {
toast.error(response.message)
}
} catch (error) {
console.error(error);
toast.error("Gagal menambahkan diskusi, coba lagi nanti");
} finally {
setValModal(false)
setLoadingModal(false)
}
}
@@ -94,12 +94,12 @@ export default function FormCreateDiscussion({ id }: { id: string }) {
}}
value={isData.desc}
onChange={(e) => setData({ ...isData, desc: e.target.value })}
onBlur={() => setTouched({ ...touched, desc: true })}
error={
touched.desc && (
isData.desc == "" ? "Form Tidak Boleh Kosong" : null
)
}
// onBlur={() => setTouched({ ...touched, desc: true })}
// error={
// touched.desc && (
// isData.desc == "" ? "Form Tidak Boleh Kosong" : null
// )
// }
/>
</Box>
</Grid.Col>
@@ -130,9 +130,11 @@ export default function FormCreateDiscussion({ id }: { id: string }) {
</Button>
</Box>
<LayoutModal opened={isValModal} onClose={() => setValModal(false)}
<LayoutModal loading={loadingModal} opened={isValModal} onClose={() => setValModal(false)}
description="Apakah Anda yakin ingin menambah data?"
onYes={(val) => { createDiscussion(val) }} />
onYes={(val) => {
createDiscussion(val)
}} />
</Box>
)
}

View File

@@ -12,6 +12,7 @@ import { useHookstate } from "@hookstate/core"
export default function FormEditDiscussion() {
const [isValModal, setValModal] = useState(false)
const [loadingModal, setLoadingModal] = useState(false)
const router = useRouter()
const param = useParams<{ id: string, detail: string }>()
const [isDataOne, setDataOne] = useState("")
@@ -35,43 +36,43 @@ export default function FormEditDiscussion() {
}
}
async function fetchEditDiscussion(val: boolean) {
async function fetchEditDiscussion() {
try {
if (val) {
const response = await funEditDiscussion(param.detail, {
desc: isDataOne
})
if (response.success) {
toast.success(response.message)
setValModal(false)
router.push(`/division/${param.id}/discussion/${param.detail}`)
} else {
toast.error(response.message)
}
setLoadingModal(true)
const response = await funEditDiscussion(param.detail, {
desc: isDataOne
})
if (response.success) {
toast.success(response.message)
setValModal(false)
router.push(`/division/${param.id}/discussion/${param.detail}`)
} else {
toast.error(response.message)
}
setValModal(false)
} catch (error) {
console.error(error);
setValModal(false)
toast.error("Gagal menambahkan diskusi, coba lagi nanti");
} finally {
setValModal(false)
setLoadingModal(false)
}
}
async function getData() {
try {
setLoading(true)
const res = await funGetProfileByCookies()
setIMG(`https://wibu-storage.wibudev.com/api/files/${res.data.img}`)
setLoading(false)
setLoading(true)
const res = await funGetProfileByCookies()
setIMG(`https://wibu-storage.wibudev.com/api/files/${res.data.img}`)
setLoading(false)
} catch (error) {
console.error(error);
console.error(error);
} finally {
setLoading(false)
setLoading(false)
}
}
}
useShallowEffect(() => {
fetchGetOneDiscussion()
getData()
@@ -84,43 +85,43 @@ export default function FormEditDiscussion() {
<Box p={20}>
<Grid gutter={0} pt={10}>
<Grid.Col span={2}>
{loading ?
<Skeleton height={60} width={60} radius={100} />
:
<Avatar src={img} alt="it's me" size="lg" />
}
{loading ?
<Skeleton height={60} width={60} radius={100} />
:
<Avatar src={img} alt="it's me" size="lg" />
}
</Grid.Col>
<Grid.Col span={10}>
{loading ?
Array(10)
.fill(null)
.map((_, i) => (
<Box key={i} mb={20}>
<Skeleton height={20} radius={10} />
</Box>
))
:
<Box>
<Textarea
placeholder="Tuliskan apa yang ingin anda diskusikan"
styles={{
input: {
border: 'none',
backgroundColor: 'transparent',
height: "50vh"
}
}}
value={isDataOne}
onChange={(e) => setDataOne(e.target.value)}
onBlur={() => setTouched({ ...touched, desc: true })}
error={
touched.desc && (
isDataOne == "" ? "Form Tidak Boleh Kosong" : null
)
}
/>
</Box>
}
Array(10)
.fill(null)
.map((_, i) => (
<Box key={i} mb={20}>
<Skeleton height={20} radius={10} />
</Box>
))
:
<Box>
<Textarea
placeholder="Tuliskan apa yang ingin anda diskusikan"
styles={{
input: {
border: 'none',
backgroundColor: 'transparent',
height: "50vh"
}
}}
value={isDataOne}
onChange={(e) => setDataOne(e.target.value)}
// onBlur={() => setTouched({ ...touched, desc: true })}
// error={
// touched.desc && (
// isDataOne == "" ? "Form Tidak Boleh Kosong" : null
// )
// }
/>
</Box>
}
</Grid.Col>
</Grid>
</Box>
@@ -153,9 +154,15 @@ export default function FormEditDiscussion() {
}
</Box>
<LayoutModal opened={isValModal} onClose={() => setValModal(false)}
<LayoutModal loading={loadingModal} opened={isValModal} onClose={() => setValModal(false)}
description="Apakah Anda yakin ingin mengubah data?"
onYes={(val) => { fetchEditDiscussion(val) }} />
onYes={(val) => {
if (val) {
fetchEditDiscussion()
} else {
setValModal(false)
}
}} />
</Box>
)
}

View File

@@ -1,29 +1,15 @@
"use client";
import { LayoutNavbarNew, TEMA } from "@/module/_global";
import { useHookstate } from "@hookstate/core";
import {
Avatar,
Box,
Button,
Divider,
Flex,
Grid,
Group,
rem,
Select,
Stack,
Text,
Textarea,
TextInput,
} from "@mantine/core";
import { useMediaQuery, useShallowEffect } from "@mantine/hooks";
import { useRouter } from "next/navigation";
import React, { useState } from "react";
import { IoIosArrowDropright } from "react-icons/io";
import { globalMemberDivision } from "../lib/val_division";
import toast from "react-hot-toast";
import { funGetUserByCookies } from "@/module/auth";
import { funGetAllGroup, IDataGroup } from "@/module/group";
import { useHookstate } from "@hookstate/core";
import { Avatar, Box, Button, Divider, Grid, Group, rem, Select, Stack, Text, Textarea, TextInput } from "@mantine/core";
import { useMediaQuery, useShallowEffect } from "@mantine/hooks";
import { useRouter } from "next/navigation";
import { useState } from "react";
import toast from "react-hot-toast";
import { IoIosArrowDropright } from "react-icons/io";
import { globalMemberDivision } from "../lib/val_division";
import NavbarAdminDivision from "./navbar_admin_division";
import NavbarCreateUsers from "./navbar_create_users";
@@ -58,23 +44,14 @@ export default function CreateDivision() {
setRoleUser(loadUser.idUserRole)
}
function onSubmit() {
if (roleUser == "supadmin" && (body.idGroup == "" || body.idGroup == null)) {
return toast.error("Error! grup harus diisi")
}
if (body.name == "") {
return toast.error("Error! nama divisi harus diisi")
}
if (member.length == 0) {
function onCheck() {
const cek = checkAll()
if (!cek)
return false
if (member.length == 0)
return toast.error("Error! belum ada anggota yang terdaftar")
}
setChooseAdmin(true)
}
function onToChooseAnggota() {
@@ -86,7 +63,7 @@ export default function CreateDivision() {
function onChooseGroup(val: any) {
member.set([])
setBody({ ...body, idGroup: val })
onValidation('idGroup', val)
}
@@ -95,6 +72,39 @@ export default function CreateDivision() {
loadData();
}, []);
function onValidation(kategori: string, val: any) {
if (kategori == 'name') {
setBody({ ...body, name: val })
if (val === "") {
setTouched({ ...touched, name: true })
} else {
setTouched({ ...touched, name: false })
}
} else if (kategori == 'idGroup') {
setBody({ ...body, idGroup: val, })
if (val === "" || String(val) == "null") {
setTouched(touched => ({ ...touched, idGroup: true }))
} else {
setTouched({ ...touched, idGroup: false })
}
} else if (kategori == 'desc') {
setBody({ ...body, desc: val })
}
}
function checkAll() {
let nilai = true
if (roleUser == "supadmin" && (body.idGroup === "" || String(body.idGroup) == "null")) {
setTouched(touched => ({ ...touched, idGroup: true }))
nilai = false
}
if (body.name === "") {
setTouched(touched => ({ ...touched, name: true }))
nilai = false
}
return nilai
}
if (isChooseAdmin) return <NavbarAdminDivision data={body} onSuccess={(val) => {
@@ -133,10 +143,9 @@ export default function CreateDivision() {
onChange={(val) => {
onChooseGroup(val)
}}
onBlur={() => setTouched({ ...touched, idGroup: true })}
error={
touched.idGroup && (
body.idGroup == "" ? "Grup Tidak Boleh Kosong" : null
body.idGroup == "" || String(body.idGroup) == "null" ? "Grup Tidak Boleh Kosong" : null
)
}
value={body.idGroup}
@@ -150,11 +159,10 @@ export default function CreateDivision() {
required
radius={10}
value={body.name}
onChange={(val) => { setBody({ ...body, name: val.target.value }) }}
onBlur={() => setTouched({ ...touched, name: true })}
onChange={(val) => { onValidation('name', val.target.value) }}
error={
touched.name && (
body.name == "" ? "Nama Tidak Boleh Kosong" : null
body.name == "" ? "Nama Divisi Tidak Boleh Kosong" : null
)
}
/>
@@ -230,16 +238,7 @@ export default function CreateDivision() {
zIndex: 999,
backgroundColor: `${tema.get().bgUtama}`,
}}>
<Button
color="white"
bg={tema.get().utama}
size="lg"
radius={30}
fullWidth
onClick={() => {
onSubmit()
}}
>
<Button color="white" bg={tema.get().utama} size="lg" radius={30} fullWidth onClick={() => { onCheck() }} >
Simpan
</Button>
</Box>

View File

@@ -1,25 +1,22 @@
"use client"
import { LayoutNavbarNew, TEMA } from '@/module/_global';
import LayoutModal from '@/module/_global/layout/layout_modal';
import { Box, Button, rem, Select, Skeleton, Stack, Textarea, TextInput } from '@mantine/core';
import { useHookstate } from '@hookstate/core';
import { Box, Button, rem, Skeleton, Stack, Textarea, TextInput } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useParams, useRouter } from 'next/navigation';
import React, { useState } from 'react';
import { useState } from 'react';
import toast from 'react-hot-toast';
import { funEditDivision, funGetDivisionById } from '../lib/api_division';
import { funGetAllGroup, IDataGroup } from '@/module/group';
import { funGetUserByCookies } from '@/module/auth';
import { useHookstate } from '@hookstate/core';
export default function EditDivision() {
const [openModal, setOpenModal] = useState(false)
const router = useRouter()
const param = useParams<{ id: string }>()
const tema = useHookstate(TEMA)
const [loadingModal, setLoadingModal] = useState(false)
const [loading, setLoading] = useState(false)
const [body, setBody] = useState<any>({
idGroup: "",
name: "",
desc: "",
});
@@ -28,6 +25,36 @@ export default function EditDivision() {
name: false,
});
function onValidation(kategori: string, val: any) {
if (kategori == 'name') {
setBody({ ...body, name: val })
if (val === "") {
setTouched({ ...touched, name: true })
} else {
setTouched({ ...touched, name: false })
}
} else if (kategori == "desc") {
setBody({ ...body, desc: val })
}
}
function onCheck() {
const cek = checkAll()
if (!cek)
return false
setOpenModal(true)
}
function checkAll() {
let nilai = true
if (body.name === "") {
setTouched(touched => ({ ...touched, name: true }))
nilai = false
}
return nilai
}
async function getOneData() {
try {
@@ -56,17 +83,19 @@ export default function EditDivision() {
async function onUpdate() {
try {
setLoadingModal(true)
const res = await funEditDivision(param.id, body)
if (res.success) {
toast.success(res.message)
} else {
toast.error(res.message)
}
setOpenModal(false)
} catch (error) {
console.error(error)
setOpenModal(false)
toast.error("Gagal mengedit divisi, coba lagi nanti");
} finally {
setLoadingModal(false)
setOpenModal(false)
}
}
@@ -88,26 +117,22 @@ export default function EditDivision() {
:
<>
<TextInput
placeholder="Judul"
label="Judul"
placeholder="Nama Divisi"
label="Nama Divisi"
size="md"
required
radius={40}
radius={10}
value={body.name}
onChange={(e) => {
setBody({ ...body, name: e.target.value })
setTouched({ ...touched, name: false })
}}
onBlur={() => setTouched({ ...touched, name: true })}
onChange={(e) => { onValidation('name', e.currentTarget.value) }}
error={
touched.name && (
body.name == "" ? "Judul Tidak Boleh Kosong" : null
body.name == "" ? "Nama Divisi Tidak Boleh Kosong" : null
)
}
/>
<Textarea placeholder="Deskripsi" label="Deskripsi" size="md" radius={10}
value={body.desc}
onChange={(e) => { setBody({ ...body, desc: e.currentTarget.value }) }}
onChange={(e) => { onValidation('desc', e.currentTarget.value) }}
styles={{
input: {
height: "40vh"
@@ -132,21 +157,13 @@ export default function EditDivision() {
size="lg"
radius={30}
fullWidth
onClick={() => {
if (
body.name !== ""
) {
setOpenModal(true)
} else {
toast.error("Judul Tidak Boleh Kosong")
}
}}
onClick={() => { onCheck() }}
>
Simpan
</Button>
}
</Box>
<LayoutModal opened={openModal} onClose={() => setOpenModal(false)} description='Apakah Anda yakin ingin edit data'
<LayoutModal loading={loadingModal} opened={openModal} onClose={() => setOpenModal(false)} description='Apakah Anda yakin ingin edit data'
onYes={(val) => {
if (val) {
onUpdate()

View File

@@ -135,9 +135,9 @@ export default function InformationDivision() {
</Stack>
))
:
(deskripsi != null && deskripsi != undefined) ?
(deskripsi != null && deskripsi != undefined && deskripsi != "") ?
<Text ta={"justify"}>{deskripsi}</Text>
: <></>
: <Text ta={"center"} c={"dimmed"} fs={"italic"}>Tidak ada deskripsi</Text>
}
</Box>
</Box>
@@ -195,12 +195,12 @@ export default function InformationDivision() {
}}
>
<Grid.Col span={1}>
<Avatar src={`https://wibu-storage.wibudev.com/api/files/${v.img}`} alt="it's me" size={'lg'} />
<Avatar src={`https://wibu-storage.wibudev.com/api/files/${v.img}`} alt="it's me" size={'lg'} />
</Grid.Col>
<Grid.Col span={8}>
<Text c={tema.get().utama} fw={"bold"} truncate="end" pl={isMobile2 ? 40 : 30} fz={isMobile ? 14 : 16}>
{v.name}
</Text>
<Text c={tema.get().utama} fw={"bold"} truncate="end" pl={isMobile2 ? 40 : 30} fz={isMobile ? 14 : 16}>
{v.name}
</Text>
</Grid.Col>
<Grid.Col span={3}>
<Text c={tema.get().utama} fw={"bold"} ta={'end'} fz={isMobile ? 13 : 16}>

View File

@@ -45,8 +45,10 @@ export default function DrawerGroup({ onSuccess, }: { onSuccess: (val: boolean)
}
function onCheck() {
if (Object.values(touched).some((v) => v == true))
const cek = checkAll()
if (!cek) {
return false
}
createData()
}
@@ -58,7 +60,15 @@ export default function DrawerGroup({ onSuccess, }: { onSuccess: (val: boolean)
} else {
setTouched({ ...touched, name: false })
}
}
}
}
function checkAll() {
if (namaGroup == "" || namaGroup.length < 3) {
setTouched({ ...touched, name: true })
return false
}
return true
}
return (
@@ -102,7 +112,7 @@ export default function DrawerGroup({ onSuccess, }: { onSuccess: (val: boolean)
}}
error={
touched.name &&
(namaGroup == "" ? "Error! harus memasukkan grup" :
(namaGroup == "" ? "Grup Tidak Boleh Kosong" :
namaGroup.length < 3 ? "Masukkan Minimal 3 karakter" : ""
)
}

View File

@@ -162,7 +162,7 @@ export default function EditDrawerGroup({ onUpdated, id, isActive, }: { onUpdate
}}
error={
touched.name &&
(name == "" ? "Error! harus memasukkan grup" :
(name == "" ? "Grup Tidak Boleh Kosong" :
name.length < 3 ? "Masukkan Minimal 3 karakter" : ""
)
}

View File

@@ -18,6 +18,7 @@ export default function DrawerDetailPosition({ onUpdated, id, isActive }: {
const [isModal, setModal] = useState(false)
const refresh = useHookstate(globalRefreshPosition)
const [loading, setLoading] = useState(true)
const [loadingEdit, setLoadingEdit] = useState(false)
const tema = useHookstate(TEMA)
const [data, setData] = useState<any>({
id: id,
@@ -27,7 +28,6 @@ export default function DrawerDetailPosition({ onUpdated, id, isActive }: {
const [listGroup, setListGorup] = useState<IDataPosition[]>([])
const [touched, setTouched] = useState({
name: false,
idGroup: false
});
function onCLose() {
@@ -70,6 +70,7 @@ export default function DrawerDetailPosition({ onUpdated, id, isActive }: {
async function onSubmit() {
try {
setLoadingEdit(true)
const res = await funEditPosition(id, {
name: data.name,
idGroup: data.idGroup
@@ -88,6 +89,8 @@ export default function DrawerDetailPosition({ onUpdated, id, isActive }: {
} catch (error) {
toast.error('Error');
toast.error("Edit jabatan gagal, coba lagi nanti");
} finally {
setLoadingEdit(false)
}
}
@@ -98,20 +101,20 @@ export default function DrawerDetailPosition({ onUpdated, id, isActive }: {
function onCheck() {
if (Object.values(touched).some((v) => v == true))
return false
return false
onSubmit()
}
function onValidation(kategori: string, val: string) {
if (kategori == 'name') {
setData({...data, name: val})
if (val == "" || val.length < 3) {
setTouched({ ...touched, name: true })
} else {
setTouched({ ...touched, name: false })
}
setData({ ...data, name: val })
if (val == "" || val.length < 3) {
setTouched({ ...touched, name: true })
} else {
setTouched({ ...touched, name: false })
}
}
}
}
async function nonActive(val: boolean) {
try {
@@ -189,13 +192,12 @@ export default function DrawerDetailPosition({ onUpdated, id, isActive }: {
size="md"
value={String(data.name)}
onChange={(e) => { onValidation('name', e.target.value) }}
onBlur={() => setTouched({ ...touched, name: true })}
error={
touched.name &&
(data.name == "" ? "Error! harus memasukkan Nama Jabatan" :
data.name.length < 3 ? "Masukkan Minimal 3 karakter" : ""
)
}
}
radius={10}
placeholder="Nama Jabatan"
/>
@@ -208,7 +210,8 @@ export default function DrawerDetailPosition({ onUpdated, id, isActive }: {
size="lg"
radius={30}
fullWidth
onClick={onSubmit}
onClick={() => { onCheck() }}
loading={loadingEdit}
>
EDIT
</Button>

View File

@@ -15,6 +15,7 @@ import { globalRefreshPosition } from "../lib/val_posisition";
export default function DrawerListPosition({ onCreated }: { onCreated: (val: boolean) => void }) {
const roleLogin = useHookstate(globalRole)
const [openDrawerGroup, setOpenDrawerGroup] = useState(false)
const [loadingSave, setLoadingSave] = useState(false)
const router = useRouter()
const [listGroup, setListGorup] = useState<IDataGroup[]>([])
const refresh = useHookstate(globalRefreshPosition)
@@ -52,6 +53,7 @@ export default function DrawerListPosition({ onCreated }: { onCreated: (val: boo
async function onSubmit() {
try {
setLoadingSave(true)
const res = await funCreatePosition({
name: listData.name,
idGroup: listData.idGroup
@@ -70,23 +72,41 @@ export default function DrawerListPosition({ onCreated }: { onCreated: (val: boo
} catch (error) {
toast.error('Error')
} finally {
setLoadingSave(false)
}
}
function onCheck() {
if (Object.values(touched).some((v) => v == true))
return false
const check = checkAll()
if (!check)
return false
onSubmit()
}
function checkAll() {
let nilai = true
if (listData.name == "" || listData.name.length < 3) {
setTouched(touched => ({ ...touched, name: true }))
nilai = false
}
if (roleLogin.get() == "supadmin" && listData.idGroup == "") {
setTouched(touched => ({ ...touched, idGroup: true }))
nilai = false
}
return nilai
}
function onValidation(kategori: string, val: string) {
if (kategori == 'name') {
setListData({...listData, name: val})
if (val == "" || val.length < 3) {
setTouched({ ...touched, name: true })
} else {
setTouched({ ...touched, name: false })
}
setListData({ ...listData, name: val })
if (val == "" || val.length < 3) {
setTouched({ ...touched, name: true })
} else {
setTouched({ ...touched, name: false })
}
} else if (kategori == 'idGroup') {
setListData({ ...listData, idGroup: val })
if (val == "") {
@@ -95,7 +115,7 @@ export default function DrawerListPosition({ onCreated }: { onCreated: (val: boo
setTouched({ ...touched, idGroup: false })
}
}
}
}
return (
<Box>
@@ -130,7 +150,7 @@ export default function DrawerListPosition({ onCreated }: { onCreated: (val: boo
sm: "67vh",
lg: "67vh",
xl: "70vh"
}}>
{
roleLogin.get() == "supadmin" &&
@@ -149,8 +169,7 @@ export default function DrawerListPosition({ onCreated }: { onCreated: (val: boo
radius={10}
mb={5}
withAsterisk
onChange={(e: any) =>
{ onValidation('idGroup', e) }
onChange={(e: any) => { onValidation('idGroup', e) }
}
styles={{
input: {
@@ -185,7 +204,7 @@ export default function DrawerListPosition({ onCreated }: { onCreated: (val: boo
(listData.name == "" ? "Error! harus memasukkan Nama Jabatan" :
listData.name.length < 3 ? "Masukkan Minimal 3 karakter" : ""
)
}
}
required
/>
<Box pos={"absolute"} bottom={10} left={0} right={0}>
@@ -196,6 +215,7 @@ export default function DrawerListPosition({ onCreated }: { onCreated: (val: boo
radius={30}
fullWidth
onClick={() => { onCheck() }}
loading={loadingSave}
>
SIMPAN
</Button>

View File

@@ -24,7 +24,6 @@ import ResultsFile from "./results_file";
export default function CreateProject() {
const router = useRouter();
const [openDrawer, setOpenDrawer] = useState(false)
const [openDrawerFile, setOpenDrawerFile] = useState(false)
const [openDrawerTask, setOpenDrawerTask] = useState(false)
const [isModal, setModal] = useState(false)
@@ -42,6 +41,7 @@ export default function CreateProject() {
const roleLogin = useHookstate(globalRole)
const isMobile = useMediaQuery('(max-width: 369px)');
const tema = useHookstate(TEMA)
const [loadingModal, setLoadingModal] = useState(false)
const [body, setBody] = useState<any>({
idGroup: "",
@@ -50,13 +50,12 @@ export default function CreateProject() {
const [touched, setTouched] = useState({
title: false,
idGroup: false,
desc: false
});
const [data, setDataRealtime] = useWibuRealtime({
WIBU_REALTIME_TOKEN: keyWibu,
project: "sdm"
})
})
function deleteFile(index: number) {
setListFile([...listFile.filter((val, i) => i !== index)])
@@ -92,7 +91,7 @@ export default function CreateProject() {
function onChooseGroup(val: any) {
member.set([])
setBody({ ...body, idGroup: val })
onValidation('idGroup', val)
}
useShallowEffect(() => {
@@ -102,6 +101,7 @@ export default function CreateProject() {
async function onSubmit() {
try {
setLoadingModal(true)
const fd = new FormData();
for (let i = 0; i < fileForm.length; i++) {
fd.append(`file${i}`, fileForm[i]);
@@ -130,6 +130,54 @@ export default function CreateProject() {
} catch (error) {
console.error(error)
toast.error("Gagal menambahkan kegiatan, coba lagi nanti");
} finally {
setLoadingModal(false)
setModal(false)
}
}
function onCheck() {
const cek = checkAll()
if (!cek)
return false
if (dataTask.length == 0)
return toast.error("Error! silahkan tambahkan tugas")
if (memberValue.length == 0)
return toast.error("Error! silahkan pilih anggota")
setModal(true)
}
function checkAll() {
let nilai = true
if (body.idGroup === "" || String(body.idGroup) == "null") {
setTouched(touched => ({ ...touched, idGroup: true }))
nilai = false
}
if (body.title === "") {
setTouched(touched => ({ ...touched, title: true }))
nilai = false
}
return nilai
}
function onValidation(kategori: string, val: string) {
if (kategori == 'idGroup') {
setBody({ ...body, idGroup: val })
if (val === "" || String(val) == "null") {
setTouched({ ...touched, idGroup: true })
} else {
setTouched({ ...touched, idGroup: false })
}
} else if (kategori == 'title') {
setBody({ ...body, title: val })
if (val === "") {
setTouched({ ...touched, title: true })
} else {
setTouched({ ...touched, title: false })
}
}
}
@@ -164,14 +212,12 @@ export default function CreateProject() {
}))}
onChange={(val) => {
onChooseGroup(val)
setTouched({ ...touched, idGroup: false })
}}
value={(body.idGroup == "") ? null : body.idGroup}
onBlur={() => setTouched({ ...touched, idGroup: true })}
error={
touched.idGroup && (
body.idGroup == "" ? "Grup Tidak Boleh Kosong" : null
body.idGroup == "" || String(body.idGroup) == "null" ? "Grup Tidak Boleh Kosong" : null
)
}
/>
@@ -190,11 +236,7 @@ export default function CreateProject() {
placeholder="Nama Kegiatan"
size="md"
value={body.title}
onChange={(e) => {
setBody({ ...body, title: e.target.value })
setTouched({ ...touched, title: false })
}}
onBlur={() => setTouched({ ...touched, title: true })}
onChange={(e) => { onValidation('title', e.target.value) }}
error={
touched.title && (
body.title == "" ? "Kegiatan Tidak Boleh Kosong" : null
@@ -356,16 +398,7 @@ export default function CreateProject() {
size="lg"
radius={30}
fullWidth
onClick={() => {
if (
body.title !== "" &&
body.idGroup !== ""
) {
setModal(true)
} else {
toast.error("Mohon lengkapi data terlebih dahulu");
}
}}>
onClick={() => { onCheck() }}>
Simpan
</Button>
</Box>
@@ -494,13 +527,15 @@ export default function CreateProject() {
</LayoutDrawer>
<LayoutModal opened={isModal} onClose={() => setModal(false)}
<LayoutModal loading={loadingModal} opened={isModal} onClose={() => setModal(false)}
description="Apakah Anda yakin ingin menambahkan data?"
onYes={(val) => {
if (val) {
onSubmit()
} else {
setModal(false)
}
setModal(false)
}} />
</Box >
);

View File

@@ -1,13 +1,13 @@
"use client"
import { useParams, useRouter } from 'next/navigation';
import React, { useState } from 'react';
import toast from 'react-hot-toast';
import { funEditProject, funGetOneProjectById } from '../lib/api_project';
import { useShallowEffect } from '@mantine/hooks';
import { Box, Button, Input, rem, Skeleton, Stack, TextInput } from '@mantine/core';
import { LayoutNavbarNew, TEMA} from '@/module/_global';
import { LayoutNavbarNew, TEMA } from '@/module/_global';
import LayoutModal from '@/module/_global/layout/layout_modal';
import { useHookstate } from '@hookstate/core';
import { Box, Button, rem, Skeleton, Stack, TextInput } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import toast from 'react-hot-toast';
import { funEditProject, funGetOneProjectById } from '../lib/api_project';
export default function EditTaskProject() {
const router = useRouter()
@@ -20,13 +20,6 @@ export default function EditTaskProject() {
name: false,
});
function onVerification() {
if (name == "")
return toast.error("Error! harus memasukkan judul Kegiatan")
setOpenModal(true)
}
async function onSubmit() {
try {
const res = await funEditProject(param.id, { name })
@@ -42,6 +35,27 @@ export default function EditTaskProject() {
}
}
function onCheck() {
if (name == "") {
setTouched({ ...touched, name: true })
return false
}
setOpenModal(true)
}
function onValidation(kategori: string, val: string) {
if (kategori == 'title') {
setName(val)
if (val === "") {
setTouched({ ...touched, name: true })
} else {
setTouched({ ...touched, name: false })
}
}
}
async function getOneData() {
try {
setLoading(true)
@@ -69,32 +83,28 @@ export default function EditTaskProject() {
<LayoutNavbarNew back="" title={"Edit Judul Kegiatan"} menu />
<Box p={20}>
<Stack pt={15}>
{loading ?
<Skeleton height={40} mt={20} radius={10} />
{loading ?
<Skeleton height={40} mt={20} radius={10} />
:
<TextInput
styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
placeholder="Input Kegiatan"
label="Judul Kegiatan"
required
size="md"
value={name}
onChange={(e) => {
setName(e.target.value)
setTouched({ ...touched, name: false })
}}
error={
touched.name && (
name == "" ? "Judul Kegiatan Tidak Boleh Kosong" : null
)
}
onBlur={() => setTouched({ ...touched, name: true })}
/>
<TextInput
styles={{
input: {
border: `1px solid ${"#D6D8F6"}`,
borderRadius: 10,
},
}}
placeholder="Nama Kegiatan"
label="Kegiatan"
required
size="md"
value={name}
onChange={(e) => { onValidation('title', e.target.value) }}
error={
touched.name && (
name == "" ? "Kegiatan Tidak Boleh Kosong" : null
)
}
/>
}
</Stack>
</Box>
@@ -104,19 +114,19 @@ export default function EditTaskProject() {
backgroundColor: `${tema.get().bgUtama}`,
}}>
{loading ?
<Skeleton height={50} radius={30} />
:
<Button
c={"white"}
bg={tema.get().utama}
size="lg"
radius={30}
fullWidth
onClick={() => { onVerification() }}
>
Simpan
</Button>
}
<Skeleton height={50} radius={30} />
:
<Button
c={"white"}
bg={tema.get().utama}
size="lg"
radius={30}
fullWidth
onClick={() => { onCheck() }}
>
Simpan
</Button>
}
</Box>

View File

@@ -1,30 +1,29 @@
"use client";
import { keyWibu, LayoutDrawer, LayoutNavbarNew, TEMA } from "@/module/_global";
import { Avatar, Box, Button, Center, Divider, Flex, Grid, Group, Input, rem, SimpleGrid, Stack, Text, TextInput } from "@mantine/core";
import LayoutModal from "@/module/_global/layout/layout_modal";
import { useHookstate } from "@hookstate/core";
import { Avatar, Box, Button, Divider, Flex, Grid, Group, rem, SimpleGrid, Stack, Text, TextInput } from "@mantine/core";
import { Dropzone } from '@mantine/dropzone';
import { useMediaQuery } from "@mantine/hooks";
import _ from "lodash";
import { useParams, useRouter } from "next/navigation";
import React, { useRef, useState } from "react";
import { useRef, useState } from "react";
import toast from "react-hot-toast";
import { FaTrash } from "react-icons/fa6";
import { IoIosArrowDropright } from "react-icons/io";
import { BsFiletypeCsv } from "react-icons/bs";
import { useWibuRealtime } from "wibu-realtime";
import { funCreateTask } from "../lib/api_task";
import { IFormDateTask, IFormMemberTask, IListFileTask } from "../lib/type_task";
import { globalMemberTask } from "../lib/val_task";
import ViewDateEndTask from "./create_date_end_task";
import CreateUsersProject from "./create_users_project";
import ResultsDateAndTask from "./results_date-and_task";
import ResultsFile from "./results_file";
import { useHookstate } from "@hookstate/core";
import { globalMemberTask } from "../lib/val_task";
import ViewDateEndTask from "./create_date_end_task";
import { IFormDateTask, IFormMemberTask, IListFileTask } from "../lib/type_task";
import { Dropzone } from '@mantine/dropzone';
import toast from "react-hot-toast";
import _ from "lodash";
import { FaTrash } from "react-icons/fa6";
import LayoutModal from "@/module/_global/layout/layout_modal";
import { funCreateTask } from "../lib/api_task";
import { useMediaQuery } from "@mantine/hooks";
import { useWibuRealtime } from "wibu-realtime";
export default function CreateTask() {
const router = useRouter()
const param = useParams<{ id: string }>()
const [openDrawer, setOpenDrawer] = useState(false)
const [loadingModal, setLoadingModal] = useState(false)
const [openDrawerFile, setOpenDrawerFile] = useState(false)
const [openDrawerTask, setOpenDrawerTask] = useState(false)
const [openMember, setOpenMember] = useState(false)
@@ -44,8 +43,6 @@ export default function CreateTask() {
const tema = useHookstate(TEMA)
const [touched, setTouched] = useState({
title: false,
task: false,
member: false
});
const [data, setData] = useWibuRealtime({
WIBU_REALTIME_TOKEN: keyWibu,
@@ -66,6 +63,7 @@ export default function CreateTask() {
async function onSubmit() {
try {
setLoadingModal(true)
const fd = new FormData();
for (let i = 0; i < fileForm.length; i++) {
fd.append(`file${i}`, fileForm[i]);
@@ -95,10 +93,51 @@ export default function CreateTask() {
} catch (error) {
console.error(error)
toast.error("Gagal menambahkan tugas divisi, coba lagi nanti");
} finally {
setLoadingModal(false)
setOpenModal(false)
}
}
function onCheck() {
const cek = checkAll()
if (!cek)
return false
if (dataTask.length == 0)
return toast.error("Error! silahkan tambahkan tugas")
if (memberValue.length == 0)
return toast.error("Error! silahkan pilih anggota")
setOpenModal(true)
}
function checkAll() {
let nilai = true
if (title === "") {
setTouched(touched => ({ ...touched, title: true }))
nilai = false
}
return nilai
}
function onValidation(kategori: string, val: string) {
if (kategori == 'title') {
setTitle(val)
if (val === "") {
setTouched({ ...touched, title: true })
} else {
setTouched({ ...touched, title: false })
}
}
}
if (openTugas) return <ViewDateEndTask onClose={(val) => { setOpenTugas(false) }} onSet={(val) => {
setDataTask([...dataTask, val])
setOpenTugas(false)
@@ -123,11 +162,7 @@ export default function CreateTask() {
size="md"
label="Judul Tugas"
value={title}
onChange={(e) => {
setTitle(e.target.value)
setTouched({ ...touched, title: false })
}}
onBlur={() => setTouched({ ...touched, title: true })}
onChange={(e) => { onValidation('title', e.target.value) }}
required
error={
touched.title && (
@@ -287,15 +322,7 @@ export default function CreateTask() {
bg={tema.get().utama}
size="lg" radius={30}
fullWidth
onClick={() => {
if (
title !== ""
) {
setOpenModal(true)
} else {
toast.error("Semua form harus diisi")
}
}}>
onClick={() => { onCheck() }}>
Simpan
</Button>
</Box>
@@ -412,13 +439,14 @@ export default function CreateTask() {
<LayoutModal opened={openModal} onClose={() => setOpenModal(false)}
<LayoutModal loading={loadingModal} opened={openModal} onClose={() => setOpenModal(false)}
description="Apakah Anda yakin ingin menambahkan data?"
onYes={(val) => {
if (val) {
onSubmit()
} else {
setOpenModal(false)
}
setOpenModal(false)
}} />
</Box >
);

View File

@@ -1,28 +1,15 @@
"use client";
import { LayoutNavbarNew, TEMA } from "@/module/_global";
import {
Avatar,
Box,
Button,
Flex,
Group,
Input,
rem,
SimpleGrid,
Skeleton,
Stack,
Text,
TextInput,
} from "@mantine/core";
import React, { useState } from "react";
import { DatePicker } from "@mantine/dates";
import { useParams, useRouter } from "next/navigation";
import toast from "react-hot-toast";
import moment from "moment";
import { funEditDetailTask, funGetDetailTask } from "../lib/api_task";
import { useShallowEffect } from "@mantine/hooks";
import LayoutModal from "@/module/_global/layout/layout_modal";
import { useHookstate } from "@hookstate/core";
import { Box, Button, Group, rem, SimpleGrid, Skeleton, Stack, Text, TextInput } from "@mantine/core";
import { DatePicker } from "@mantine/dates";
import { useShallowEffect } from "@mantine/hooks";
import moment from "moment";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
import toast from "react-hot-toast";
import { funEditDetailTask, funGetDetailTask } from "../lib/api_task";
export default function EditDetailTask() {
@@ -185,10 +172,10 @@ export default function EditDetailTask() {
touched.title &&
(title == "" ? "Error! harus memasukkan Judul Tahapan" : ""
)
}
}
onChange={(e) => {
onValidation('title', e.target.value)
}}
}}
/>
}
</Stack>

View File

@@ -1,22 +1,13 @@
"use client";
import { LayoutNavbarNew, TEMA } from "@/module/_global";
import {
Box,
Button,
Input,
rem,
Skeleton,
Stack,
Textarea,
TextInput,
} from "@mantine/core";
import React, { useState } from "react";
import { useParams, useRouter } from "next/navigation";
import toast from "react-hot-toast";
import LayoutModal from "@/module/_global/layout/layout_modal";
import { funEditTask, funGetTaskDivisionById } from "../lib/api_task";
import { useShallowEffect } from "@mantine/hooks";
import { useHookstate } from "@hookstate/core";
import { Box, Button, rem, Skeleton, Stack, TextInput } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { useParams, useRouter } from "next/navigation";
import { useState } from "react";
import toast from "react-hot-toast";
import { funEditTask, funGetTaskDivisionById } from "../lib/api_task";
export default function EditTask() {
@@ -31,12 +22,22 @@ export default function EditTask() {
});
function onVerification() {
if (title == "")
return toast.error("Error! harus memasukkan judul tugas")
if (Object.values(touched).some((v) => v == true))
return false
setOpenModal(true)
}
function onValidation(kategori: string, val: string) {
if (kategori == 'title') {
setTitle(val)
if (val === "") {
setTouched({ ...touched, title: true })
} else {
setTouched({ ...touched, title: false })
}
}
}
async function onSubmit() {
try {
const res = await funEditTask(param.detail, { title })
@@ -96,16 +97,12 @@ export default function EditTask() {
label="Judul Tugas"
size="md"
value={title}
onChange={(e) => {
setTitle(e.target.value)
setTouched({ ...touched, title: false })
}}
onChange={(e) => { onValidation('title', e.target.value)}}
error={
touched.title && (
title == "" ? "Error! harus memasukkan judul tugas" : null
)
}
onBlur={() => setTouched({ ...touched, title: true })}
/>
}
</Stack>

View File

@@ -1,25 +1,26 @@
"use client";
import { globalRole, TEMA, WARNA } from "@/module/_global";
import { globalRole, TEMA } from "@/module/_global";
import LayoutModal from "@/module/_global/layout/layout_modal";
import { funGetUserByCookies } from "@/module/auth";
import { funGetAllGroup, IDataGroup } from "@/module/group";
import { funGetAllPosition } from "@/module/position/lib/api_position";
import { useHookstate } from "@hookstate/core";
import { Avatar, Box, Button, Indicator, rem, Select, Stack, Text, TextInput } from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import { useShallowEffect } from "@mantine/hooks";
import _ from "lodash";
import { useRouter } from "next/navigation";
import { useRef, useState } from "react";
import toast from "react-hot-toast";
import { IDataPositionMember, IDataROleMember } from "../lib/type_member";
import { funGetAllPosition } from "@/module/position/lib/api_position";
import { funCreateMember } from "../lib/api_member";
import _ from "lodash";
import { useHookstate } from "@hookstate/core";
import { useShallowEffect } from "@mantine/hooks";
import { funGetUserByCookies } from "@/module/auth";
import { valueRoleUser } from "../../lib/val_user";
import { FaCamera } from "react-icons/fa6";
import { Dropzone } from "@mantine/dropzone";
import { valueRoleUser } from "../../lib/val_user";
import { funCreateMember } from "../lib/api_member";
import { IDataPositionMember, IDataROleMember } from "../lib/type_member";
export default function CreateMember() {
const router = useRouter();
const [isModal, setModal] = useState(false);
const [loadingKonfirmasi, setLoadingKonfirmasi] = useState(false);
const [listGroup, setListGorup] = useState<IDataGroup[]>([]);
const [listPosition, setListPosition] = useState<IDataPositionMember[]>([]);
const [listUserRole, setListUserRole] = useState<IDataROleMember[]>([]);
@@ -100,37 +101,33 @@ export default function CreateMember() {
async function onSubmit(val: boolean) {
try {
if (_.isEmpty(listData)) {
return;
}
if (val) {
const fd = new FormData()
fd.append("file", imgForm)
fd.append("data", JSON.stringify(
{
nik: listData.nik,
name: listData.name,
phone: listData.phone,
email: listData.email,
gender: listData.gender,
idGroup: listData.idGroup,
idPosition: listData.idPosition,
idUserRole: listData.idUserRole,
}
))
const res = await funCreateMember(fd);
if (res.success) {
toast.success(res.message);
setModal(false);
router.push("/member?active=true");
} else {
toast.error(res.message);
setLoadingKonfirmasi(true)
const fd = new FormData()
fd.append("file", imgForm)
fd.append("data", JSON.stringify(
{
nik: listData.nik,
name: listData.name,
phone: listData.phone,
email: listData.email,
gender: listData.gender,
idGroup: listData.idGroup,
idPosition: listData.idPosition,
idUserRole: listData.idUserRole,
}
))
const res = await funCreateMember(fd);
if (res.success) {
toast.success(res.message);
router.push("/member?active=true");
} else {
toast.error(res.message);
}
setModal(false);
} catch (error) {
toast.error("Error");
} finally {
setLoadingKonfirmasi(false)
setModal(false);
}
}
@@ -142,12 +139,61 @@ export default function CreateMember() {
}, []);
function onCheck() {
if (Object.values(touched).some((v) => v == true))
const cek = checkAll()
if (!cek)
return false
setModal(true)
}
function onValidation(kategori: string, val: string) {
function checkAll() {
let nilai = true
if (listData.nik === "" || listData.nik.length !== 16) {
setTouched(touched => ({ ...touched, nik: true }))
nilai = false
}
if (listData.name === "") {
setTouched(touched => ({ ...touched, name: true }))
nilai = false
}
if (listData.phone == "" || !(listData.phone.length >= 10 && listData.phone.length <= 15)) {
setTouched(touched => ({ ...touched, phone: true }))
nilai = false
}
if (listData.email == "" || !/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(listData.email)) {
setTouched(touched => ({ ...touched, email: true }))
nilai = false
}
if (listData.gender == "" || String(listData.gender) == "null") {
setTouched(touched => ({ ...touched, gender: true }))
nilai = false
}
if (roleLogin.get() == "supadmin" && (listData.idGroup == "" || String(listData.idGroup) == "null")) {
setTouched(touched => ({ ...touched, idGroup: true }))
nilai = false
}
if (listData.idPosition === "" || String(listData.idPosition) == "null") {
setTouched(touched => ({ ...touched, idPosition: true }))
nilai = false
}
if (listData.idUserRole === "" || String(listData.idUserRole) == "null") {
setTouched(touched => ({ ...touched, idUserRole: true }))
nilai = false
}
return nilai
}
function onValidation(kategori: string, val: any) {
if (kategori == 'nik') {
setListData({ ...listData, nik: val })
if (val === "" || val.length !== 16) {
@@ -178,28 +224,28 @@ export default function CreateMember() {
}
} else if (kategori == 'gender') {
setListData({ ...listData, gender: val })
if (val == "" || val == "null") {
if (val == "" || String(val) == "null") {
setTouched({ ...touched, gender: true })
} else {
setTouched({ ...touched, gender: false })
}
} else if (kategori == 'idGroup') {
setListData({ ...listData, idGroup: val, idPosition: "", })
if (val === "") {
setTouched({ ...touched, idGroup: true })
setListData(listData => ({ ...listData, idGroup: val, }))
if (val === "" || String(val) == "null") {
setTouched(touched => ({ ...touched, idGroup: true }))
} else {
setTouched({ ...touched, idGroup: false })
}
} else if (kategori == 'idPosition') {
setListData({ ...listData, idPosition: val })
if (val === "") {
setTouched({ ...touched, idPosition: true })
setListData(listData => ({ ...listData, idPosition: val }))
if (val === "" || String(val) == "null") {
setTouched(touched => ({ ...touched, idPosition: true }))
} else {
setTouched({ ...touched, idPosition: false })
}
} else if (kategori == 'idUserRole') {
setListData({ ...listData, idUserRole: val })
if (val === "") {
if (val === "" || String(val) == "null") {
setTouched({ ...touched, idUserRole: true })
} else {
setTouched({ ...touched, idUserRole: false })
@@ -210,6 +256,7 @@ export default function CreateMember() {
async function changeGrup(val: any) {
setListPosition([]);
onValidation('idGroup', val)
onValidation('idPosition', '')
getAllPosition(val);
}
@@ -269,7 +316,7 @@ export default function CreateMember() {
onChange={(val: any) => { changeGrup(val) }}
error={
touched.idGroup && (
listData.idGroup == "" ? "Grup Tidak Boleh Kosong" : null
listData.idGroup == "" || String(listData.idGroup) == "null" ? "Grup Tidak Boleh Kosong" : null
)
}
/>
@@ -301,7 +348,7 @@ export default function CreateMember() {
value={listData.idPosition == "" ? null : listData.idPosition}
error={
touched.idPosition && (
listData.idPosition == "" ? "Jabatan Tidak Boleh Kosong" : null
listData.idPosition == "" || String(listData.idPosition) == "null" ? "Jabatan Tidak Boleh Kosong" : null
)
}
/>
@@ -331,7 +378,7 @@ export default function CreateMember() {
onChange={(val: any) => { onValidation('idUserRole', val) }}
error={
touched.idUserRole && (
listData.idUserRole == "" ? "Role Tidak Boleh Kosong" : null
listData.idUserRole == "" || String(listData.idUserRole) == "null" ? "Role Tidak Boleh Kosong" : null
)
}
/>
@@ -423,7 +470,7 @@ export default function CreateMember() {
error={
touched.phone && (
listData.phone == "" ? "Nomor Telepon Tidak Boleh Kosong" :
listData.phone.length < 10 ? "Nomor Telepon harus 10 digit" : null
listData.phone.length < 10 ? "Nomor Telepon Tidak Valid" : null
)
}
/>
@@ -449,7 +496,7 @@ export default function CreateMember() {
onChange={(val: any) => { onValidation('gender', val) }}
error={
touched.gender && (
listData.gender == "" ? "Jenis Kelamin Tidak Boleh Kosong" : null
listData.gender == "" || String(listData.gender == "null") ? "Jenis Kelamin Tidak Boleh Kosong" : null
)
}
/>
@@ -471,11 +518,16 @@ export default function CreateMember() {
</Button>
</Box>
<LayoutModal
loading={loadingKonfirmasi}
opened={isModal}
onClose={() => setModal(false)}
description="Apakah Anda yakin ingin menambahkan data?"
onYes={(val) => {
onSubmit(val);
if (val) {
onSubmit(val);
} else {
setModal(false);
}
}}
/>
</Box>

View File

@@ -1,25 +1,25 @@
'use client'
import { globalRole, TEMA, WARNA } from "@/module/_global";
import { globalRole, TEMA } from "@/module/_global";
import LayoutModal from "@/module/_global/layout/layout_modal";
import { funGetAllGroup, IDataGroup } from "@/module/group";
import { funGetAllPosition } from "@/module/position/lib/api_position";
import { useHookstate } from "@hookstate/core";
import { Avatar, Box, Button, Indicator, rem, Select, Skeleton, Stack, Text, TextInput } from "@mantine/core";
import { Dropzone } from "@mantine/dropzone";
import { useShallowEffect } from "@mantine/hooks";
import _ from "lodash";
import { useRouter } from "next/navigation";
import { useRef, useState } from "react";
import toast from "react-hot-toast";
import { HiUser } from "react-icons/hi2";
import { IDataPositionMember, IDataROleMember, IEditDataMember, IFormMember } from "../lib/type_member";
import { funEditMember, funGetOneMember, funGetRoleUser } from "../lib/api_member";
import _ from "lodash";
import { Dropzone } from "@mantine/dropzone";
import { FaCamera } from "react-icons/fa6";
import { useHookstate } from "@hookstate/core";
import { valueRoleUser } from "../../lib/val_user";
import { funEditMember, funGetOneMember } from "../lib/api_member";
import { IDataPositionMember, IDataROleMember, IEditDataMember } from "../lib/type_member";
export default function EditMember({ id }: { id: string }) {
const [isModal, setModal] = useState(false)
const [loadingKonfirmasi, setLoadingKonfirmasi] = useState(false)
const router = useRouter()
const [listGroup, setListGorup] = useState<IDataGroup[]>([])
const [listPosition, setListPosition] = useState<IDataPositionMember[]>([])
@@ -111,26 +111,24 @@ export default function EditMember({ id }: { id: string }) {
async function onSubmit(val: boolean) {
try {
if (_.isEmpty(data)) {
return
setLoadingKonfirmasi(true)
const fd = new FormData()
fd.append("file", imgForm)
fd.append("data", JSON.stringify(data))
const res = await funEditMember(id, fd)
if (res.success) {
toast.success(res.message)
router.push(`/member?active=true`)
} else {
toast.error(res.message)
}
if (val) {
const fd = new FormData()
fd.append("file", imgForm)
fd.append("data", JSON.stringify(data))
const res = await funEditMember(id, fd)
if (res.success) {
toast.success(res.message)
router.push(`/member?active=true`)
} else {
toast.error(res.message)
}
}
} catch (error) {
toast.error('Error');
} finally {
setLoadingKonfirmasi(false)
setModal(false)
}
}
@@ -171,21 +169,21 @@ export default function EditMember({ id }: { id: string }) {
}
} else if (kategori == 'gender') {
setData({ ...data, gender: val })
if (val == "" || val == "null") {
if (val == "" || String(val) == "null") {
setTouched({ ...touched, gender: true })
} else {
setTouched({ ...touched, gender: false })
}
} else if (kategori == 'idPosition') {
setData({ ...data, idPosition: val })
if (val === "") {
if (val === "" || String(val) == "null") {
setTouched({ ...touched, idPosition: true })
} else {
setTouched({ ...touched, idPosition: false })
}
} else if (kategori == 'idUserRole') {
setData({ ...data, idUserRole: val })
if (val === "") {
if (val === "" || String(val) == "null") {
setTouched({ ...touched, idUserRole: true })
} else {
setTouched({ ...touched, idUserRole: false })
@@ -267,7 +265,7 @@ export default function EditMember({ id }: { id: string }) {
value={(data?.idPosition == "") ? null : data.idPosition}
error={
touched.idPosition && (
data.idPosition == "" ? "Jabatan Tidak Boleh Kosong" : null
data.idPosition == "" || String(data.idPosition) == "null" ? "Jabatan Tidak Boleh Kosong" : null
)
}
/>
@@ -292,7 +290,7 @@ export default function EditMember({ id }: { id: string }) {
value={data?.idUserRole}
error={
touched.idUserRole && (
data.idUserRole == "" ? "Role Tidak Boleh Kosong" : null
data.idUserRole == "" || String(data.idUserRole) == "null" ? "Role Tidak Boleh Kosong" : null
)
}
/>
@@ -325,7 +323,6 @@ export default function EditMember({ id }: { id: string }) {
}}
onChange={(e) => { onValidation('name', e.target.value) }}
value={data.name}
onBlur={() => setTouched({ ...touched, name: true })}
error={
touched.name && (
data.name == "" ? "Nama Tidak Boleh Kosong" : null
@@ -366,7 +363,7 @@ export default function EditMember({ id }: { id: string }) {
error={
touched.phone && (
data.phone == "" ? "Nomor Telepon Tidak Boleh Kosong" :
data.phone.length < 10 ? "Nomor Telepon harus 10 digit" : null
data.phone.length < 10 ? "Nomor Telepon Tidak Valid" : null
)
}
/>
@@ -395,7 +392,7 @@ export default function EditMember({ id }: { id: string }) {
value={data.gender}
error={
touched.gender && (
data.gender == "" ? "Gender Tidak Boleh Kosong" : null
data.gender == "" || String(data.gender) == "null" ? "Gender Tidak Boleh Kosong" : null
)
}
/>
@@ -422,12 +419,14 @@ export default function EditMember({ id }: { id: string }) {
</Button>
}
</Box>
<LayoutModal opened={isModal} onClose={() => setModal(false)}
<LayoutModal loading={loadingKonfirmasi} opened={isModal} onClose={() => setModal(false)}
description="Apakah Anda yakin ingin mengubah data?"
onYes={(val) => {
if (val)
if (val) {
onSubmit(val)
setModal(false)
} else {
setModal(false)
}
}
} />
</Box>