From a2e25a3e3a038941babc957218ebd261266caebb Mon Sep 17 00:00:00 2001 From: nico Date: Mon, 7 Jul 2025 17:14:44 +0800 Subject: [PATCH] API & UI Menu Struktur bagian pegawai --- .../ekonomi/struktur-organisasi/pegawai.json | 4 +- .../migration.sql | 78 +++++ prisma/schema.prisma | 2 +- prisma/seed.ts | 2 + .../struktur-organisasi.ts | 246 ++++++++++------ .../pegawai/[id]/edit/page.tsx | 276 ++++++++++++++++++ .../pegawai/[id]/page.tsx | 148 ++++++++++ .../pegawai/create/page.tsx | 200 +++++++++++++ .../pegawai/page.tsx | 134 ++++++++- .../struktur-organisasi/pegawai/create.ts | 26 +- .../struktur-organisasi/pegawai/del.ts | 2 +- .../struktur-organisasi/pegawai/findMany.ts | 2 +- .../struktur-organisasi/pegawai/index.ts | 2 +- .../struktur-organisasi/pegawai/updt.ts | 8 +- 14 files changed, 1029 insertions(+), 101 deletions(-) create mode 100644 prisma/migrations/20250707074426_add_is_active_to_pegawai/migration.sql create mode 100644 src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/edit/page.tsx create mode 100644 src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/page.tsx create mode 100644 src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create/page.tsx diff --git a/prisma/data/ekonomi/struktur-organisasi/pegawai.json b/prisma/data/ekonomi/struktur-organisasi/pegawai.json index 80578dad..9d7a6437 100644 --- a/prisma/data/ekonomi/struktur-organisasi/pegawai.json +++ b/prisma/data/ekonomi/struktur-organisasi/pegawai.json @@ -8,7 +8,7 @@ "telepon": "081234567891", "alamat": "Jl. Raya Desa No. 1", "posisiId": "kepala_desa", - "aktif": true + "isActive": true }, { "id": "550e8400-e29b-41d4-a716-446655440002", @@ -19,6 +19,6 @@ "telepon": "081234567892", "alamat": "Jl. Raya Desa No. 2", "posisiId": "sekretaris_desa", - "aktif": true + "isActive": true } ] \ No newline at end of file diff --git a/prisma/migrations/20250707074426_add_is_active_to_pegawai/migration.sql b/prisma/migrations/20250707074426_add_is_active_to_pegawai/migration.sql new file mode 100644 index 00000000..aa41c6e8 --- /dev/null +++ b/prisma/migrations/20250707074426_add_is_active_to_pegawai/migration.sql @@ -0,0 +1,78 @@ +-- CreateTable +CREATE TABLE "posisi_organisasi" ( + "id" VARCHAR(50) NOT NULL, + "nama" VARCHAR(100) NOT NULL, + "deskripsi" TEXT, + "hierarki" INTEGER NOT NULL, + + CONSTRAINT "posisi_organisasi_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "pegawai" ( + "id" UUID NOT NULL, + "namaLengkap" VARCHAR(255) NOT NULL, + "gelarAkademik" VARCHAR(100), + "imageId" TEXT, + "tanggalMasuk" DATE, + "email" VARCHAR(255), + "telepon" VARCHAR(20), + "alamat" TEXT, + "posisiId" VARCHAR(50) NOT NULL, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "pegawai_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "hubungan_organisasi" ( + "id" UUID NOT NULL, + "atasanId" UUID NOT NULL, + "bawahanId" UUID NOT NULL, + "tipe" VARCHAR(50), + + CONSTRAINT "hubungan_organisasi_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "struktur_organisasi" ( + "id" TEXT NOT NULL, + "posisiOrganisasiId" VARCHAR(50) NOT NULL, + "pegawaiId" UUID NOT NULL, + "hubunganOrganisasiId" UUID NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "deletedAt" TIMESTAMP(3), + "isActive" BOOLEAN NOT NULL DEFAULT true, + + CONSTRAINT "struktur_organisasi_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "pegawai_email_key" ON "pegawai"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "hubungan_organisasi_atasanId_bawahanId_key" ON "hubungan_organisasi"("atasanId", "bawahanId"); + +-- AddForeignKey +ALTER TABLE "pegawai" ADD CONSTRAINT "pegawai_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "pegawai" ADD CONSTRAINT "pegawai_posisiId_fkey" FOREIGN KEY ("posisiId") REFERENCES "posisi_organisasi"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "hubungan_organisasi" ADD CONSTRAINT "hubungan_organisasi_atasanId_fkey" FOREIGN KEY ("atasanId") REFERENCES "pegawai"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "hubungan_organisasi" ADD CONSTRAINT "hubungan_organisasi_bawahanId_fkey" FOREIGN KEY ("bawahanId") REFERENCES "pegawai"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "struktur_organisasi" ADD CONSTRAINT "struktur_organisasi_posisiOrganisasiId_fkey" FOREIGN KEY ("posisiOrganisasiId") REFERENCES "posisi_organisasi"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "struktur_organisasi" ADD CONSTRAINT "struktur_organisasi_pegawaiId_fkey" FOREIGN KEY ("pegawaiId") REFERENCES "pegawai"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "struktur_organisasi" ADD CONSTRAINT "struktur_organisasi_hubunganOrganisasiId_fkey" FOREIGN KEY ("hubunganOrganisasiId") REFERENCES "hubungan_organisasi"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c81f6abb..e1c7d2bb 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1133,7 +1133,7 @@ model Pegawai { telepon String? @db.VarChar(20) alamat String? @db.Text posisiId String @db.VarChar(50) - aktif Boolean @default(true) + isActive Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/prisma/seed.ts b/prisma/seed.ts index cda12440..7cd8a6a3 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -395,6 +395,7 @@ import pegawai from "./data/ekonomi/struktur-organisasi/pegawai.json"; telepon: p.telepon, alamat: p.alamat, posisiId: p.posisiId, + isActive: p.isActive, }, create: { id: p.id, @@ -405,6 +406,7 @@ import pegawai from "./data/ekonomi/struktur-organisasi/pegawai.json"; telepon: p.telepon, alamat: p.alamat, posisiId: p.posisiId, + isActive: p.isActive, }, }); } diff --git a/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts b/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts index 3ae7f99f..1f540f8f 100644 --- a/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts +++ b/src/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { proxy } from "valtio"; import { z } from "zod"; import { toast } from "react-toastify"; @@ -210,12 +211,13 @@ const posisiOrganisasi = proxy({ const templatePegawai = z.object({ namaLengkap: z.string().min(1, "Nama wajib diisi"), gelarAkademik: z.string().optional(), - imageId: z.string().optional(), + imageId: z.string().nullable().optional(), tanggalMasuk: z.string().optional(), // ISO format email: z.string().email("Email tidak valid").optional(), telepon: z.string().optional(), alamat: z.string().optional(), posisiId: z.string().min(1, "Posisi wajib diisi"), + isActive: z.boolean().default(true), }); const pegawaiDefaultForm = { @@ -227,6 +229,7 @@ const templatePegawai = z.object({ telepon: "", alamat: "", posisiId: "", + isActive: true, }; const pegawai = proxy({ @@ -260,24 +263,36 @@ const templatePegawai = z.object({ } }, }, - findMany: { - data: null as Prisma.PegawaiGetPayload<{ include: { posisi: true } }>[] | null, + data: null as (Prisma.PegawaiGetPayload<{ include: { posisi: true, image: true } }> & { isActive: boolean })[] | null, async load() { - const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["pegawai"]["find-many"].get(); - if (res.status === 200) { - pegawai.findMany.data = res.data?.data ?? []; + try { + const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["pegawai"]["find-many"].get(); + if (res.status === 200) { + pegawai.findMany.data = (res.data?.data ?? []).map((item: any) => ({ + ...item, + posisi: item.posisi || { id: '', nama: '' }, // Ensure posisi exists with required fields + isActive: item.isActive ?? true // Default to true if not provided + })); + } else { + console.error('Failed to load pegawai:', res.data?.message); + } + } catch (error) { + console.error('Error loading pegawai:', error); + pegawai.findMany.data = []; } }, }, - findUnique: { - data: null as Prisma.PegawaiGetPayload<{ include: { posisi: true } }> | null, + data: null as (Prisma.PegawaiGetPayload<{ include: { posisi: true, image: true } }> & { isActive: boolean }) | null, async load(id: string) { - const res = await fetch(`/api/ekonomi/strukturorganisasi/pegawai/${id}`); + const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${id}`); if (res.ok) { const json = await res.json(); - pegawai.findUnique.data = json.data ?? null; + pegawai.findUnique.data = json.data ? { + ...json.data, + isActive: json.data.isActive ?? json.data.aktif ?? true // Fallback ke aktif:true jika tidak ada data + } : null; } else { pegawai.findUnique.data = null; } @@ -290,7 +305,7 @@ const templatePegawai = z.object({ if (!id) return toast.warn("ID tidak valid"); try { pegawai.delete.loading = true; - const res = await fetch(`/api/ekonomi/strukturorganisasi/pegawai/del/${id}`, { + const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/del/${id}`, { method: "DELETE", }); const json = await res.json(); @@ -309,65 +324,121 @@ const templatePegawai = z.object({ }, }, - edit: { - id: "", - form: { ...pegawaiDefaultForm }, - loading: false, - async load(id: string) { - const res = await fetch(`/api/organisasi/pegawai/${id}`); - const json = await res.json(); - if (res.ok && json.success) { - pegawai.edit.id = json.data.id; - pegawai.edit.form = { - namaLengkap: json.data.namaLengkap ?? "", - gelarAkademik: json.data.gelarAkademik ?? "", - imageId: json.data.imageId ?? "", - tanggalMasuk: json.data.tanggalMasuk?.slice(0, 10) ?? "", - email: json.data.email ?? "", - telepon: json.data.telepon ?? "", - alamat: json.data.alamat ?? "", - posisiId: json.data.posisiId, - }; - } else { - toast.error("Gagal memuat data"); - } - }, - - async submit() { - const cek = templatePegawai.safeParse(pegawai.edit.form); - if (!cek.success) { - const err = cek.error.issues.map(i => i.message).join("\n"); - toast.error(err); - return; - } - - try { - pegawai.edit.loading = true; - const res = await fetch(`/api/ekonomi/strukturorganisasi/pegawai/${pegawai.edit.id}`, { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(pegawai.edit.form), - }); - const json = await res.json(); - if (res.ok) { - toast.success(json.message ?? "Berhasil update pegawai"); - await pegawai.findMany.load(); - } else { - toast.error(json.message ?? "Gagal update pegawai"); - } - } catch (error) { - console.error("Gagal update:", error); - toast.error("Terjadi kesalahan saat update"); - } finally { - pegawai.edit.loading = false; - } - }, - - reset() { - pegawai.edit.id = ""; - pegawai.edit.form = { ...pegawaiDefaultForm }; - }, - }, + edit: { + id: "", + form: { ...pegawaiDefaultForm }, + loading: false, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + try { + const response = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${id}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const result = await response.json(); + + if (result?.success) { + const data = result.data; + this.id = data.id; + this.form = { + namaLengkap: data.namaLengkap, + gelarAkademik: data.gelarAkademik, + imageId: data.imageId, + tanggalMasuk: data.tanggalMasuk, + email: data.email, + telepon: data.telepon, + alamat: data.alamat, + posisiId: data.posisiId, + isActive: data.isActive, + }; + return data; // Return the loaded data + } else { + throw new Error(result?.message || "Gagal memuat data"); + } + } catch (error) { + console.error("Error loading berita:", error); + toast.error(error instanceof Error ? error.message : "Gagal memuat data"); + return null; + } + }, + + async submit() { + const cek = templatePegawai.safeParse(pegawai.edit.form); + if (!cek.success) { + const err = `[${cek.error.issues + .map((v) => `${v.path.join(".")}`) + .join("\n")}] required`; + toast.error(err); + return false; + } + + try { + pegawai.edit.loading = true; + + // Format tanggalMasuk to ISO string if it exists + const formattedTanggalMasuk = this.form.tanggalMasuk + ? new Date(this.form.tanggalMasuk).toISOString() + : undefined; + + const response = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${this.id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + id: this.id, + namaLengkap: this.form.namaLengkap, + gelarAkademik: this.form.gelarAkademik, + imageId: this.form.imageId || null, + tanggalMasuk: formattedTanggalMasuk, + email: this.form.email, + telepon: this.form.telepon, + alamat: this.form.alamat, + posisiId: this.form.posisiId, + isActive: this.form.isActive, + }), + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.message || `HTTP error! status: ${response.status}`); + } + + const result = await response.json(); + + if (result.success) { + toast.success("Berhasil update pegawai"); + await pegawai.findMany.load(); // refresh list + return true; + } else { + throw new Error(result.message || "Gagal update pegawai"); + } + } catch (error) { + console.error("Error updating pegawai:", error); + toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat update pegawai"); + return false; + } finally { + pegawai.edit.loading = false; + } + }, + + reset() { + pegawai.edit.id = ""; + pegawai.edit.form = { ...pegawaiDefaultForm }; + }, + }, }); @@ -411,16 +482,15 @@ const hubunganOrganisasi = proxy({ } else { return toast.error(res.data?.message || "Gagal menambahkan data"); } - } catch (error) { - console.error("Create Error:", error); + } catch (error) { + console.error("Gagal create:", error); toast.error("Terjadi kesalahan saat menambahkan"); } finally { hubunganOrganisasi.create.loading = false; } }, }, - - findMany: { + findMany: { data: null as Array<{ id: string; atasanId: string; @@ -436,7 +506,7 @@ const hubunganOrganisasi = proxy({ telepon: string | null; alamat: string | null; posisiId: string; - aktif: boolean; + isActive: boolean; createdAt: Date; updatedAt: Date; }; @@ -450,7 +520,7 @@ const hubunganOrganisasi = proxy({ telepon: string | null; alamat: string | null; posisiId: string; - aktif: boolean; + isActive: boolean; createdAt: Date; updatedAt: Date; }; @@ -460,8 +530,18 @@ const hubunganOrganisasi = proxy({ try { const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["hubungan-organisasi"]["find-many"].get(); - if (res.status === 200 && res.data?.success) { - hubunganOrganisasi.findMany.data = res.data.data || []; + if (res.status === 200) { + hubunganOrganisasi.findMany.data = (res.data?.data ?? []).map((item: any) => ({ + ...item, + atasan: item.atasan ? { + ...item.atasan, + isActive: item.atasan.isActive ?? item.atasan.aktif ?? true + } : null, + bawahan: item.bawahan ? { + ...item.bawahan, + isActive: item.bawahan.isActive ?? item.bawahan.aktif ?? true + } : null + })); } else { hubunganOrganisasi.findMany.data = []; } @@ -631,10 +711,10 @@ const hubunganOrganisasi = proxy({ }, }); - const strukturorganisasiState = proxy({ - posisiOrganisasi, - pegawai, - hubunganOrganisasi - }) +const strukturorganisasiState = proxy({ + posisiOrganisasi, + pegawai, + hubunganOrganisasi, +}); export default strukturorganisasiState; diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/edit/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/edit/page.tsx new file mode 100644 index 00000000..2b0f8e55 --- /dev/null +++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/edit/page.tsx @@ -0,0 +1,276 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; +import colors from '@/con/colors'; +import { + Box, + Button, + Group, + Image, + Paper, + Select, + Stack, + Text, + TextInput, + Title +} from '@mantine/core'; +import { Dropzone } from '@mantine/dropzone'; +import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; + +interface PreviewImage { + file?: File; + preview: string; +} + +interface PegawaiFormData { + namaLengkap: string; + gelarAkademik: string; + imageId: string | null; + tanggalMasuk: string; + email: string; + telepon: string; + alamat: string; + posisiId: string; + isActive: boolean; +} + +export default function EditPegawai() { + const router = useRouter(); + const { id } = useParams<{ id: string }>(); + const [previewImage, setPreviewImage] = useState(null); + const stateOrganisasi = useProxy(strukturorganisasiState.pegawai); + const [formData, setFormData] = useState({ + namaLengkap: "", + gelarAkademik: "", + imageId: "", + tanggalMasuk: "", + email: "", + telepon: "", + alamat: "", + posisiId: "", + isActive: true, + }); + + // Format date to YYYY-MM-DD for date input + const formatDateForInput = (dateString: string) => { + if (!dateString) return ''; + const date = new Date(dateString); + return date.toISOString().split('T')[0]; + }; + + useEffect(() => { + strukturorganisasiState.posisiOrganisasi.findMany.load(); + const loadPegawai = async () => { + try { + const data = await stateOrganisasi.edit.load(id); + if (data) { + setFormData({ + namaLengkap: data.namaLengkap || "", + gelarAkademik: data.gelarAkademik || "", + imageId: data.imageId || "", + tanggalMasuk: data.tanggalMasuk || "", + email: data.email || "", + telepon: data.telepon || "", + alamat: data.alamat || "", + posisiId: data.posisiId || "", + isActive: data.isActive ?? true, // pakai nullish coalescing + }); + + if (data.image?.link) { + setPreviewImage(data.image.link); + } else { + setPreviewImage(null); + } + } + } catch (error) { + console.error("Error loading pegawai:", error); + toast.error( + error instanceof Error ? error.message : "Gagal mengambil data pegawai" + ); + } + }; + + loadPegawai(); + }, [id]); + + const handleSubmit = async () => { + try { + if (!formData.namaLengkap.trim()) { + toast.error('Nama lengkap tidak boleh kosong'); + return; + } + + stateOrganisasi.edit.form = { + namaLengkap: formData.namaLengkap.trim(), + gelarAkademik: formData.gelarAkademik.trim(), + imageId: formData.imageId ? formData.imageId.trim() : "", + tanggalMasuk: formData.tanggalMasuk.trim(), + email: formData.email.trim(), + telepon: formData.telepon.trim(), + alamat: formData.alamat.trim(), + posisiId: formData.posisiId.trim(), + isActive: formData.isActive, + }; + + if (id && !stateOrganisasi.edit.id) { + stateOrganisasi.edit.id = id; + } + + const success = await stateOrganisasi.edit.submit(); + + if (success) { + router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai"); + } + } catch (error) { + console.error("Error updating pegawai:", error); + toast.error(error instanceof Error ? error.message : "Gagal memperbarui data pegawai"); + } + }; + + return ( + + + + + + + + Edit Data Pegawai + setFormData({ ...formData, namaLengkap: e.target.value })} + /> + setFormData({ ...formData, gelarAkademik: e.target.value })} + /> + + Gambar + + { + const file = files[0]; // Hanya ambil file pertama + if (file) { + setPreviewImage({ + file, + preview: URL.createObjectURL(file) + }); + } + }} + maxSize={5 * 1024 ** 2} // 5MB + accept={{ + 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] + }} + > + + + + + + + + + + + +
+ + Drag images here or click to select files + + + Attach as many files as you like, each file should not exceed 5mb + +
+
+
+ {previewImage && ( + Preview + )} +
+
+ setFormData({ ...formData, tanggalMasuk: e.target.value })} + /> + (formData.email = e.currentTarget.value)} + /> + (formData.telepon = e.currentTarget.value)} + /> + (formData.alamat = e.currentTarget.value)} + /> + { + if (val !== null) { + setFormData({ ...formData, isActive: val === 'true' }); + } + }} + /> + + + +
+
+
+ ); +} diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/page.tsx new file mode 100644 index 00000000..6fa54e36 --- /dev/null +++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/[id]/page.tsx @@ -0,0 +1,148 @@ +'use client' +import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus'; +import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; +import colors from '@/con/colors'; +import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core'; +import { useShallowEffect } from '@mantine/hooks'; +import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react'; +import { useParams, useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { useProxy } from 'valtio/utils'; + +function DetailPegawai() { + const statePegawai = useProxy(strukturorganisasiState.pegawai) + const [modalHapus, setModalHapus] = useState(false) + const [selectedId, setSelectedId] = useState(null) + const params = useParams() + const router = useRouter(); + + useShallowEffect(() => { + statePegawai.findUnique.load(params?.id as string) + }, []) + + const handleHapus = () => { + if (selectedId) { + statePegawai.delete.byId(selectedId) + setModalHapus(false) + setSelectedId(null) + router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai") + } + } + + if (!statePegawai.findUnique.data) { + return ( + + + + ) + } + + return ( + + + + + + + Detail Pegawai + + + + Nama Lengkap + {statePegawai.findUnique.data?.namaLengkap} + + + Gelar Akademik + {statePegawai.findUnique.data?.gelarAkademik} + + + Image + {statePegawai.findUnique.data?.image?.link ? ( + + ) : ( + Tidak ada gambar + )} + + + Tanggal Masuk + + {statePegawai.findUnique.data?.tanggalMasuk + ? new Date(statePegawai.findUnique.data.tanggalMasuk).toLocaleDateString() + : "-"} + + + + Email + {statePegawai.findUnique.data?.email} + + + Telepon + {statePegawai.findUnique.data?.telepon} + + + Alamat + {statePegawai.findUnique.data?.alamat} + + + Posisi + + {statePegawai.findUnique.data?.posisi ? ( + + {statePegawai.findUnique.data.posisi.nama} + + ) : ( + + Tidak ada posisi + + )} + + + + Aktif + {statePegawai.findUnique.data?.isActive ? "Ya" : "Tidak"} + + + + + + + + + + + + + + {/* Modal Hapus */} + setModalHapus(false)} + onConfirm={handleHapus} + text="Apakah anda yakin ingin menghapus produk ini?" + /> + + ); +} + +export default DetailPegawai; diff --git a/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create/page.tsx b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create/page.tsx new file mode 100644 index 00000000..e99c88e2 --- /dev/null +++ b/src/app/admin/(dashboard)/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create/page.tsx @@ -0,0 +1,200 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi'; +import colors from '@/con/colors'; +import ApiFetch from '@/lib/api-fetch'; +import { Box, Button, Group, Image, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core'; +import { Dropzone } from '@mantine/dropzone'; +import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useProxy } from 'valtio/utils'; + +function CreatePegawai() { + const router = useRouter(); + const [previewImage, setPreviewImage] = useState<{ preview: string; file: File } | null>(null); + const stateOrganisasi = useProxy(strukturorganisasiState) + useEffect(() => { + stateOrganisasi.posisiOrganisasi.findMany.load(); + resetForm(); + }, []); + + const resetForm = () => { + stateOrganisasi.pegawai.create.form = { + namaLengkap: "", + gelarAkademik: "", + imageId: "", + tanggalMasuk: "", + email: "", + telepon: "", + alamat: "", + posisiId: "", + isActive: true, + }; + }; + + const handleSubmit = async () => { + if (!previewImage) { + return toast.warn("Pilih file gambar terlebih dahulu"); + } + + try { + // Upload gambar dulu + const res = await ApiFetch.api.fileStorage.create.post({ + file: previewImage.file, + name: previewImage.file.name, + }); + + const uploaded = res.data?.data; + if (!uploaded?.id) { + return toast.error("Gagal upload gambar"); + } + + // Set status aktif secara otomatis + stateOrganisasi.pegawai.create.form.isActive = true; + + // Simpan ID gambar ke form + stateOrganisasi.pegawai.create.form.imageId = uploaded.id; + + // Submit form + await stateOrganisasi.pegawai.create.submit(); + + + // Reset form dan redirect + resetForm(); + toast.success("Data pegawai berhasil ditambahkan"); + router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai"); + } catch (error) { + console.error("Error creating pegawai:", error); + toast.error("Terjadi kesalahan saat menambahkan pegawai"); + } + }; + + return ( + + + + + + + Create Pegawai + (stateOrganisasi.pegawai.create.form.namaLengkap = e.currentTarget.value)} + /> + (stateOrganisasi.pegawai.create.form.gelarAkademik = e.currentTarget.value)} + /> + + Gambar + + { + const file = files[0]; // Hanya ambil file pertama + if (file) { + setPreviewImage({ + file, + preview: URL.createObjectURL(file) + }); + } + }} + maxSize={5 * 1024 ** 2} // 5MB + accept={{ + 'image/*': ['.jpeg', '.jpg', '.png', '.webp'] + }} + > + + + + + + + + + + + +
+ + Drag images here or click to select files + + + Attach as many files as you like, each file should not exceed 5mb + +
+
+
+ {previewImage && ( + Preview + )} +
+
+ (stateOrganisasi.pegawai.create.form.tanggalMasuk = e.currentTarget.value)} + /> + (stateOrganisasi.pegawai.create.form.email = e.currentTarget.value)} + /> + (stateOrganisasi.pegawai.create.form.telepon = e.currentTarget.value)} + /> + (stateOrganisasi.pegawai.create.form.alamat = e.currentTarget.value)} + /> +