Compare commits
6 Commits
nico/4-jul
...
nico/7-jul
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e95d5fbb4 | |||
| 2725c2c064 | |||
| be189df37c | |||
| c0b941395d | |||
| a2e25a3e3a | |||
| d86824a943 |
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"id": "650e8400-e29b-41d4-a716-446655440001",
|
||||
"atasanId": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"bawahanId": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"tipe": "Langsung Melapor"
|
||||
}
|
||||
]
|
||||
24
prisma/data/ekonomi/struktur-organisasi/pegawai.json
Normal file
24
prisma/data/ekonomi/struktur-organisasi/pegawai.json
Normal file
@@ -0,0 +1,24 @@
|
||||
[
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"namaLengkap": "Budi Santoso",
|
||||
"gelarAkademik": "S.IP",
|
||||
"tanggalMasuk": "2020-01-01T00:00:00.000Z",
|
||||
"email": "budi@desa.id",
|
||||
"telepon": "081234567891",
|
||||
"alamat": "Jl. Raya Desa No. 1",
|
||||
"posisiId": "kepala_desa",
|
||||
"isActive": true
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440002",
|
||||
"namaLengkap": "Ani Lestari",
|
||||
"gelarAkademik": "S.Pd",
|
||||
"tanggalMasuk": "2020-02-01T00:00:00.000Z",
|
||||
"email": "ani@desa.id",
|
||||
"telepon": "081234567892",
|
||||
"alamat": "Jl. Raya Desa No. 2",
|
||||
"posisiId": "sekretaris_desa",
|
||||
"isActive": true
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,27 @@
|
||||
[
|
||||
{
|
||||
"id": "kepala_desa",
|
||||
"nama": "Kepala Desa",
|
||||
"deskripsi": "Kepala Desa",
|
||||
"hierarki": 1
|
||||
},
|
||||
{
|
||||
"id": "sekretaris_desa",
|
||||
"nama": "Sekretaris Desa",
|
||||
"deskripsi": "Sekretaris Desa",
|
||||
"hierarki": 2
|
||||
},
|
||||
{
|
||||
"id": "bendahara_desa",
|
||||
"nama": "Bendahara Desa",
|
||||
"deskripsi": "Bendahara Desa",
|
||||
"hierarki": 3
|
||||
},
|
||||
{
|
||||
"id": "staff_umum",
|
||||
"nama": "Staff Umum",
|
||||
"deskripsi": "Staff Umum",
|
||||
"hierarki": 4
|
||||
}
|
||||
]
|
||||
|
||||
11
prisma/migrations/20250704091225_4_jul_2025/migration.sql
Normal file
11
prisma/migrations/20250704091225_4_jul_2025/migration.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `kategoriProdukId` to the `PasarDesa` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "PasarDesa" ADD COLUMN "kategoriProdukId" TEXT NOT NULL;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PasarDesa" ADD CONSTRAINT "PasarDesa_kategoriProdukId_fkey" FOREIGN KEY ("kategoriProdukId") REFERENCES "KategoriProduk"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -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;
|
||||
@@ -88,6 +88,8 @@ model FileStorage {
|
||||
KontakDaruratKeamanan KontakDaruratKeamanan[]
|
||||
|
||||
KontakItem KontakItem[]
|
||||
|
||||
Pegawai Pegawai[]
|
||||
}
|
||||
|
||||
//========================================= MENU PPID ========================================= //
|
||||
@@ -1050,20 +1052,20 @@ model MenuTipsKeamanan {
|
||||
// ========================================= MENU EKONOMI ========================================= //
|
||||
// ========================================= PASAR DESA ========================================= //
|
||||
model PasarDesa {
|
||||
id String @id @default(uuid())
|
||||
nama String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
harga Int
|
||||
rating Float
|
||||
alamatUsaha String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
kategoriProduk KategoriProduk @relation(fields: [kategoriProdukId], references: [id])
|
||||
id String @id @default(uuid())
|
||||
nama String
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
harga Int
|
||||
rating Float
|
||||
alamatUsaha String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
kategoriProduk KategoriProduk @relation(fields: [kategoriProdukId], references: [id])
|
||||
kategoriProdukId String
|
||||
KategoriToPasar KategoriToPasar[]
|
||||
KategoriToPasar KategoriToPasar[]
|
||||
}
|
||||
|
||||
model KategoriProduk {
|
||||
@@ -1074,7 +1076,7 @@ model KategoriProduk {
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
KategoriToPasar KategoriToPasar[]
|
||||
PasarDesa PasarDesa[]
|
||||
PasarDesa PasarDesa[]
|
||||
}
|
||||
|
||||
model KategoriToPasar {
|
||||
@@ -1091,21 +1093,93 @@ model KategoriToPasar {
|
||||
|
||||
// ========================================= LOWONGAN KERJA LOKAL ========================================= //
|
||||
model LowonganPekerjaan {
|
||||
id String @id @default(uuid()) // ID unik untuk setiap lowongan
|
||||
posisi String // Contoh: "Kasir"
|
||||
namaPerusahaan String // Contoh: "Toko Sumber Rejeki"
|
||||
lokasi String // Contoh: "Desa Munggu , Badung"
|
||||
tipePekerjaan String // Contoh: "Full Time", "Part Time", "Contract"
|
||||
gaji String // Contoh: "Rp. 2.500.000 / bulan". Menggunakan String karena formatnya bisa bervariasi
|
||||
deskripsi String // Opsional: Detail deskripsi pekerjaan (tidak terlihat di UI ini, tapi umum ada)
|
||||
kualifikasi String // Opsional: Kualifikasi yang dibutuhkan (tidak terlihat di UI ini, tapi umum ada)
|
||||
tanggalPosting DateTime @default(now()) // Tanggal lowongan diposting
|
||||
isActive Boolean @default(true) // Menandakan apakah lowongan masih aktif
|
||||
id String @id @default(uuid())
|
||||
posisi String
|
||||
namaPerusahaan String
|
||||
lokasi String
|
||||
tipePekerjaan String
|
||||
gaji String
|
||||
deskripsi String
|
||||
kualifikasi String
|
||||
tanggalPosting DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
}
|
||||
|
||||
// ========================================= STRUKTUR ORGANISASI ========================================= //
|
||||
|
||||
model PosisiOrganisasi {
|
||||
id String @id @default(uuid()) @db.VarChar(50)
|
||||
nama String @db.VarChar(100)
|
||||
deskripsi String? @db.Text
|
||||
hierarki Int
|
||||
|
||||
pegawai Pegawai[]
|
||||
strukturOrganisasi StrukturOrganisasi[] // Relasi balik
|
||||
|
||||
@@map("posisi_organisasi")
|
||||
}
|
||||
|
||||
model Pegawai {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
namaLengkap String @db.VarChar(255)
|
||||
gelarAkademik String? @db.VarChar(100)
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
tanggalMasuk DateTime? @db.Date
|
||||
email String? @unique @db.VarChar(255)
|
||||
telepon String? @db.VarChar(20)
|
||||
alamat String? @db.Text
|
||||
posisiId String @db.VarChar(50)
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
posisi PosisiOrganisasi @relation(fields: [posisiId], references: [id])
|
||||
|
||||
sebagaiAtasan HubunganOrganisasi[] @relation("AtasanToBawahan")
|
||||
sebagaiBawahan HubunganOrganisasi[] @relation("BawahanToAtasan")
|
||||
|
||||
strukturOrganisasi StrukturOrganisasi[] // Relasi balik
|
||||
|
||||
@@map("pegawai")
|
||||
}
|
||||
|
||||
model HubunganOrganisasi {
|
||||
id String @id @default(uuid()) @db.Uuid
|
||||
atasanId String @db.Uuid
|
||||
bawahanId String @db.Uuid
|
||||
tipe String? @db.VarChar(50)
|
||||
|
||||
atasan Pegawai @relation("AtasanToBawahan", fields: [atasanId], references: [id])
|
||||
bawahan Pegawai @relation("BawahanToAtasan", fields: [bawahanId], references: [id])
|
||||
|
||||
strukturOrganisasi StrukturOrganisasi[] // Relasi balik
|
||||
|
||||
@@unique([atasanId, bawahanId])
|
||||
@@map("hubungan_organisasi")
|
||||
}
|
||||
|
||||
model StrukturOrganisasi {
|
||||
id String @id @default(uuid())
|
||||
posisiOrganisasiId String @db.VarChar(50)
|
||||
pegawaiId String @db.Uuid
|
||||
hubunganOrganisasiId String @db.Uuid
|
||||
|
||||
posisiOrganisasi PosisiOrganisasi @relation(fields: [posisiOrganisasiId], references: [id])
|
||||
pegawai Pegawai @relation(fields: [pegawaiId], references: [id])
|
||||
hubunganOrganisasi HubunganOrganisasi @relation(fields: [hubunganOrganisasiId], references: [id])
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime?
|
||||
isActive Boolean @default(true)
|
||||
|
||||
@@map("struktur_organisasi")
|
||||
}
|
||||
|
||||
// ========================================= PROGRAM KEMISKINAN ========================================= //
|
||||
model ProgramKemiskinan {
|
||||
id String @id @default(uuid())
|
||||
@@ -1113,8 +1187,7 @@ model ProgramKemiskinan {
|
||||
deskripsi String
|
||||
ikonUrl String?
|
||||
isActive Boolean @default(true)
|
||||
// Tambahkan relasi one-to-one ke StatistikKemiskinan
|
||||
statistikId String? @unique // Foreign key ke StatistikKemiskinan, unique untuk one-to-one
|
||||
statistikId String? @unique
|
||||
statistik StatistikKemiskinan? @relation(fields: [statistikId], references: [id])
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -1124,7 +1197,6 @@ model StatistikKemiskinan {
|
||||
id String @id @default(uuid())
|
||||
tahun Int @unique
|
||||
jumlah Int
|
||||
// Tidak perlu foreign key di sini jika relasi di ProgramLayanan
|
||||
programKemiskinan ProgramKemiskinan?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@ -18,6 +18,9 @@ import lambangDesa from "./data/desa/profile/lambang_desa.json";
|
||||
import maskotDesa from "./data/desa/profile/maskot_desa.json";
|
||||
import profilPerbekel from "./data/desa/profile/profil_perbekel.json";
|
||||
import kategoriProduk from "./data/ekonomi/pasar-desa/kategori-produk.json";
|
||||
import hubunganOrganisasi from "./data/ekonomi/struktur-organisasi/hubungan-organisasi.json";
|
||||
import posisiOrganisasi from "./data/ekonomi/struktur-organisasi/posisi-organisasi.json";
|
||||
import pegawai from "./data/ekonomi/struktur-organisasi/pegawai.json";
|
||||
|
||||
(async () => {
|
||||
for (const l of layanan) {
|
||||
@@ -357,6 +360,77 @@ import kategoriProduk from "./data/ekonomi/pasar-desa/kategori-produk.json";
|
||||
});
|
||||
}
|
||||
console.log("kategori produk success ...");
|
||||
|
||||
|
||||
for (const p of posisiOrganisasi) {
|
||||
await prisma.posisiOrganisasi.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
nama: p.nama,
|
||||
deskripsi: p.deskripsi,
|
||||
hierarki: p.hierarki,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
nama: p.nama,
|
||||
deskripsi: p.deskripsi,
|
||||
hierarki: p.hierarki,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("posisi organisasi success ...");
|
||||
|
||||
for (const p of pegawai) {
|
||||
await prisma.pegawai.upsert({
|
||||
where: {
|
||||
id: p.id,
|
||||
},
|
||||
update: {
|
||||
namaLengkap: p.namaLengkap,
|
||||
gelarAkademik: p.gelarAkademik,
|
||||
tanggalMasuk: new Date(p.tanggalMasuk),
|
||||
email: p.email,
|
||||
telepon: p.telepon,
|
||||
alamat: p.alamat,
|
||||
posisiId: p.posisiId,
|
||||
isActive: p.isActive,
|
||||
},
|
||||
create: {
|
||||
id: p.id,
|
||||
namaLengkap: p.namaLengkap,
|
||||
gelarAkademik: p.gelarAkademik,
|
||||
tanggalMasuk: new Date(p.tanggalMasuk),
|
||||
email: p.email,
|
||||
telepon: p.telepon,
|
||||
alamat: p.alamat,
|
||||
posisiId: p.posisiId,
|
||||
isActive: p.isActive,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("pegawai success ...");
|
||||
|
||||
for (const p of hubunganOrganisasi) {
|
||||
await prisma.hubunganOrganisasi.upsert({
|
||||
where: {
|
||||
atasanId_bawahanId: {
|
||||
atasanId: p.atasanId,
|
||||
bawahanId: p.bawahanId,
|
||||
},
|
||||
},
|
||||
update: {
|
||||
tipe: p.tipe,
|
||||
},
|
||||
create: {
|
||||
atasanId: p.atasanId,
|
||||
bawahanId: p.bawahanId,
|
||||
tipe: p.tipe,
|
||||
},
|
||||
});
|
||||
}
|
||||
console.log("hubungan organisasi success ...");
|
||||
})()
|
||||
.then(() => prisma.$disconnect())
|
||||
.catch((e) => {
|
||||
|
||||
@@ -1,22 +1,37 @@
|
||||
import React from 'react';
|
||||
import { Grid, GridCol, Paper, TextInput, Title } from '@mantine/core';
|
||||
import { IconSearch } from '@tabler/icons-react'; // Sesuaikan jika kamu pakai icon lain
|
||||
import { IconSearch } from '@tabler/icons-react';
|
||||
import colors from '@/con/colors';
|
||||
|
||||
type HeaderSearchProps = {
|
||||
title: string;
|
||||
placeholder?: string;
|
||||
searchIcon?: React.ReactNode;
|
||||
value?: string;
|
||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
};
|
||||
|
||||
const HeaderSearch = ({ title = "", placeholder = "pencarian", searchIcon = <IconSearch size={20} /> }: { title: string, placeholder?: string, searchIcon?: React.ReactNode }) => {
|
||||
const HeaderSearch = ({
|
||||
title = "",
|
||||
placeholder = "pencarian",
|
||||
searchIcon = <IconSearch size={20} />,
|
||||
value,
|
||||
onChange,
|
||||
}: HeaderSearchProps) => {
|
||||
return (
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 9 }}>
|
||||
<Title order={3}>{title}</Title>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 3 }}>
|
||||
<Paper radius={"lg"} bg={colors['white-1']}>
|
||||
<Paper radius="lg" bg={colors['white-1']}>
|
||||
<TextInput
|
||||
radius="lg"
|
||||
placeholder={placeholder}
|
||||
leftSection={searchIcon}
|
||||
w="100%"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</Paper>
|
||||
</GridCol>
|
||||
@@ -24,4 +39,4 @@ const HeaderSearch = ({ title = "", placeholder = "pencarian", searchIcon = <Ico
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderSearch;
|
||||
export default HeaderSearch;
|
||||
|
||||
@@ -0,0 +1,720 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
import { toast } from "react-toastify";
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
|
||||
const templatePosisiOrganisasi = z.object({
|
||||
nama: z.string().min(1, "Nama harus diisi"),
|
||||
deskripsi: z.string().optional(),
|
||||
hierarki: z.number().int().positive("Hierarki harus angka positif"),
|
||||
});
|
||||
|
||||
const posisiOrganisasiDefaultForm = {
|
||||
nama: "",
|
||||
deskripsi: "",
|
||||
hierarki: 0,
|
||||
};
|
||||
|
||||
const posisiOrganisasi = proxy({
|
||||
create: {
|
||||
form: { ...posisiOrganisasiDefaultForm },
|
||||
loading: false,
|
||||
async submit() {
|
||||
const cek = templatePosisiOrganisasi.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = cek.error.issues.map((v) => v.message).join("\n");
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["posisi-organisasi"]["create"].post(
|
||||
this.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
toast.success("Berhasil menambahkan posisi organisasi");
|
||||
posisiOrganisasi.findMany.load();
|
||||
this.reset();
|
||||
} else {
|
||||
toast.error(res.data?.message || "Gagal menambahkan posisi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Create error:", error);
|
||||
toast.error("Terjadi kesalahan saat menambahkan posisi");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
this.form = { ...posisiOrganisasiDefaultForm };
|
||||
},
|
||||
},
|
||||
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...posisiOrganisasiDefaultForm },
|
||||
loading: false,
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(`/api/ekonomi/struktur-organisasi/posisi-organisasi/${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 = {
|
||||
nama: data.nama,
|
||||
deskripsi: data.deskripsi,
|
||||
hierarki: data.hierarki,
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading posisi organisasi:", error);
|
||||
toast.error(
|
||||
error instanceof Error ? error.message : "Gagal memuat data"
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templatePosisiOrganisasi.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
const response = await fetch(
|
||||
`/api/ekonomi/struktur-organisasi/posisi-organisasi/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
nama: this.form.nama,
|
||||
deskripsi: this.form.deskripsi,
|
||||
hierarki: this.form.hierarki,
|
||||
}),
|
||||
}
|
||||
);
|
||||
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 posisi organisasi");
|
||||
await posisiOrganisasi.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(
|
||||
result.message || "Gagal mengupdate posisi organisasi"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating posisi organisasi:", error);
|
||||
toast.error(
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Gagal mengupdate posisi organisasi"
|
||||
);
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
this.id = "";
|
||||
this.form = { ...posisiOrganisasiDefaultForm };
|
||||
},
|
||||
},
|
||||
|
||||
findMany: {
|
||||
data: [] as Array<{
|
||||
id: string;
|
||||
nama: string;
|
||||
deskripsi: string | null;
|
||||
hierarki: number;
|
||||
}>,
|
||||
async load() {
|
||||
try {
|
||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["posisi-organisasi"]["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
// The API now returns the id field, so we can use it directly
|
||||
this.data = res.data?.data ?? [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Find many error:", error);
|
||||
this.data = [];
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
posisiOrganisasi.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/ekonomi/struktur-organisasi/posisi-organisasi/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Posisi organisasi berhasil dihapus");
|
||||
await posisiOrganisasi.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus posisi organisasi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus posisi organisasi");
|
||||
} finally {
|
||||
posisiOrganisasi.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const templatePegawai = z.object({
|
||||
namaLengkap: z.string().min(1, "Nama wajib diisi"),
|
||||
gelarAkademik: 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 = {
|
||||
namaLengkap: "",
|
||||
gelarAkademik: "",
|
||||
imageId: "",
|
||||
tanggalMasuk: "",
|
||||
email: "",
|
||||
telepon: "",
|
||||
alamat: "",
|
||||
posisiId: "",
|
||||
isActive: true,
|
||||
};
|
||||
|
||||
const pegawai = proxy({
|
||||
create: {
|
||||
form: { ...pegawaiDefaultForm },
|
||||
loading: false,
|
||||
async submit() {
|
||||
const cek = templatePegawai.safeParse(pegawai.create.form);
|
||||
if (!cek.success) {
|
||||
const err = cek.error.issues.map(i => i.message).join("\n");
|
||||
toast.error(err);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
pegawai.create.loading = true;
|
||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["pegawai"]["create"].post(
|
||||
pegawai.create.form
|
||||
);
|
||||
if (res.status === 200) {
|
||||
toast.success("Pegawai berhasil ditambahkan");
|
||||
await pegawai.findMany.load();
|
||||
} else {
|
||||
toast.error(res.data?.message ?? "Gagal tambah pegawai");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal create:", error);
|
||||
toast.error("Terjadi kesalahan saat menambahkan pegawai");
|
||||
} finally {
|
||||
pegawai.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as (Prisma.PegawaiGetPayload<{ include: { posisi: true, image: true } }> & { isActive: boolean })[] | null,
|
||||
async load() {
|
||||
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, image: true } }> & { isActive: boolean }) | null,
|
||||
async load(id: string) {
|
||||
const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/${id}`);
|
||||
if (res.ok) {
|
||||
const json = await res.json();
|
||||
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;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
try {
|
||||
pegawai.delete.loading = true;
|
||||
const res = await fetch(`/api/ekonomi/struktur-organisasi/pegawai/del/${id}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
const json = await res.json();
|
||||
if (res.ok) {
|
||||
toast.success(json.message ?? "Berhasil hapus pegawai");
|
||||
await pegawai.findMany.load();
|
||||
} else {
|
||||
toast.error(json.message ?? "Gagal hapus pegawai");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus");
|
||||
} finally {
|
||||
pegawai.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
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 };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
// Schema Zod untuk form validasi
|
||||
const templateHubunganOrganisasiForm = z.object({
|
||||
atasanId: z.string().min(1, "Atasan wajib dipilih"),
|
||||
bawahanId: z.string().min(1, "Bawahan wajib dipilih"),
|
||||
tipe: z.string().optional(),
|
||||
});
|
||||
|
||||
// Default form state
|
||||
const defaultHubunganOrganisasiForm = {
|
||||
atasanId: "",
|
||||
bawahanId: "",
|
||||
tipe: "",
|
||||
};
|
||||
|
||||
// ====================== STATE ===========================
|
||||
const hubunganOrganisasi = proxy({
|
||||
create: {
|
||||
form: { ...defaultHubunganOrganisasiForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateHubunganOrganisasiForm.safeParse(
|
||||
hubunganOrganisasi.create.form
|
||||
);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}: ${v.message}`)
|
||||
.join("\n")}]`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
hubunganOrganisasi.create.loading = true;
|
||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["hubungan-organisasi"]["create"].post(hubunganOrganisasi.create.form);
|
||||
|
||||
if (res.status === 200 && res.data?.success) {
|
||||
hubunganOrganisasi.findMany.load();
|
||||
return toast.success("Berhasil menambahkan hubungan organisasi");
|
||||
} else {
|
||||
return toast.error(res.data?.message || "Gagal menambahkan data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal create:", error);
|
||||
toast.error("Terjadi kesalahan saat menambahkan");
|
||||
} finally {
|
||||
hubunganOrganisasi.create.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
findMany: {
|
||||
data: null as Array<{
|
||||
id: string;
|
||||
atasanId: string;
|
||||
bawahanId: string;
|
||||
tipe?: string | null;
|
||||
atasan: {
|
||||
id: string;
|
||||
namaLengkap: string;
|
||||
gelarAkademik: string | null;
|
||||
imageId: string | null;
|
||||
tanggalMasuk: Date | null;
|
||||
email: string | null;
|
||||
telepon: string | null;
|
||||
alamat: string | null;
|
||||
posisiId: string;
|
||||
isActive: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
bawahan: {
|
||||
id: string;
|
||||
namaLengkap: string;
|
||||
gelarAkademik: string | null;
|
||||
imageId: string | null;
|
||||
tanggalMasuk: Date | null;
|
||||
email: string | null;
|
||||
telepon: string | null;
|
||||
alamat: string | null;
|
||||
posisiId: string;
|
||||
isActive: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
}> | null,
|
||||
|
||||
async load() {
|
||||
try {
|
||||
const res = await ApiFetch.api.ekonomi["struktur-organisasi"]["hubungan-organisasi"]["find-many"].get();
|
||||
|
||||
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 = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Fetch list error:", error);
|
||||
toast.error("Gagal memuat data hubungan organisasi");
|
||||
hubunganOrganisasi.findMany.data = [];
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
findUnique: {
|
||||
data: null as {
|
||||
id: string;
|
||||
atasanId: string;
|
||||
bawahanId: string;
|
||||
tipe?: string | null;
|
||||
atasan?: {
|
||||
id: string;
|
||||
namaLengkap: string;
|
||||
gelarAkademik: string | null;
|
||||
imageId: string;
|
||||
tanggalMasuk: Date | null;
|
||||
email: string | null;
|
||||
telepon: string | null;
|
||||
alamat: string | null;
|
||||
posisiId: string;
|
||||
aktif: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
bawahan?: {
|
||||
id: string;
|
||||
namaLengkap: string;
|
||||
gelarAkademik: string | null;
|
||||
imageId: string;
|
||||
tanggalMasuk: Date | null;
|
||||
email: string | null;
|
||||
telepon: string | null;
|
||||
alamat: string | null;
|
||||
posisiId: string;
|
||||
aktif: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
} | null,
|
||||
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/ekonomi/struktur-organisasi/hubungan-organisasi/${id}`);
|
||||
const result = await res.json();
|
||||
|
||||
if (res.ok && result?.success) {
|
||||
hubunganOrganisasi.findUnique.data = result.data;
|
||||
} else {
|
||||
hubunganOrganisasi.findUnique.data = null;
|
||||
toast.error(result?.message || "Gagal mengambil data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Find unique error:", error);
|
||||
hubunganOrganisasi.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultHubunganOrganisasiForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/ekonomi/struktur-organisasi/hubungan-organisasi/${id}`);
|
||||
const result = await res.json();
|
||||
|
||||
if (res.ok && result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
atasanId: data.atasanId,
|
||||
bawahanId: data.bawahanId,
|
||||
tipe: data.tipe || "",
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateHubunganOrganisasiForm.safeParse(this.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}: ${v.message}`)
|
||||
.join("\n")}]`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
this.loading = true;
|
||||
const res = await fetch(
|
||||
`/api/ekonomi/struktur-organisasi/hubungan-organisasi/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
}
|
||||
);
|
||||
|
||||
const result = await res.json();
|
||||
if (res.ok && result.success) {
|
||||
await hubunganOrganisasi.findMany.load();
|
||||
toast.success("Berhasil mengupdate hubungan organisasi");
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal mengupdate");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Update error:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal update");
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
hubunganOrganisasi.edit.id = "";
|
||||
hubunganOrganisasi.edit.form = { ...defaultHubunganOrganisasiForm };
|
||||
},
|
||||
},
|
||||
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
hubunganOrganisasi.delete.loading = true;
|
||||
const res = await fetch(`/api/ekonomi/struktur-organisasi/hubungan-organisasi/del/${id}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
const result = await res.json();
|
||||
if (res.ok && result?.success) {
|
||||
toast.success("Hubungan organisasi berhasil dihapus");
|
||||
hubunganOrganisasi.findMany.load();
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus hubungan organisasi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Delete error:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus");
|
||||
} finally {
|
||||
hubunganOrganisasi.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const strukturorganisasiState = proxy({
|
||||
posisiOrganisasi,
|
||||
pegawai,
|
||||
hubunganOrganisasi,
|
||||
});
|
||||
|
||||
export default strukturorganisasiState;
|
||||
@@ -1,43 +1,47 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Grid, GridCol, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Box, Button, Grid, GridCol, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import stateDashboardBerita from '../../_state/desa/berita';
|
||||
|
||||
|
||||
function Page() {
|
||||
function Berita() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 9 }}>
|
||||
<Title order={3}>Berita</Title>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 3 }}>
|
||||
<Paper radius={"lg"} bg={colors['white-1']}>
|
||||
<TextInput
|
||||
radius={"lg"}
|
||||
placeholder='pencarian'
|
||||
leftSection={<IconSearch size={20} />}
|
||||
/>
|
||||
</Paper>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<BeritaList />
|
||||
<HeaderSearch
|
||||
title='Berita'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListBerita search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function BeritaList() {
|
||||
function ListBerita({ search }: { search: string }) {
|
||||
const beritaState = useProxy(stateDashboardBerita)
|
||||
const router = useRouter()
|
||||
|
||||
useShallowEffect(() => {
|
||||
beritaState.berita.findMany.load()
|
||||
}, [])
|
||||
|
||||
const router = useRouter()
|
||||
const filteredData = (beritaState.berita.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.judul.toLowerCase().includes(keyword) ||
|
||||
item.kategoriBerita?.name.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
if (!beritaState.berita.findMany.data) {
|
||||
return (
|
||||
@@ -62,7 +66,7 @@ function BeritaList() {
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Box style={{ overflowX: "auto" }}>
|
||||
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
||||
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh w={250}>Judul</TableTh>
|
||||
@@ -73,7 +77,7 @@ function BeritaList() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody >
|
||||
{beritaState.berita.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd >
|
||||
<Box w={100}>
|
||||
@@ -101,4 +105,4 @@ function BeritaList() {
|
||||
)
|
||||
}
|
||||
|
||||
export default Page;
|
||||
export default Berita;
|
||||
|
||||
@@ -7,8 +7,26 @@ import JudulListTab from '../../../_com/jusulListTab';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import stateGallery from '../../../_state/desa/gallery';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import { useState } from 'react';
|
||||
|
||||
function Foto() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Posisi Organisasi'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListFoto search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListFoto({ search }: { search: string }) {
|
||||
const fotoState = useProxy(stateGallery.foto)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -16,6 +34,14 @@ function Foto() {
|
||||
fotoState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (fotoState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!fotoState.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -43,7 +69,7 @@ function Foto() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{fotoState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>{new Date(item.createdAt).toDateString()}</TableTd>
|
||||
|
||||
@@ -7,8 +7,26 @@ import JudulListTab from '../../../_com/jusulListTab';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import stateGallery from '../../../_state/desa/gallery';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import { useState } from 'react';
|
||||
|
||||
function Video() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Posisi Organisasi'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListVideo search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListVideo({ search }: { search: string }) {
|
||||
const videoState = useProxy(stateGallery.video)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -16,6 +34,14 @@ function Video() {
|
||||
videoState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (videoState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!videoState.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -43,7 +69,7 @@ function Video() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{videoState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>{new Date(item.createdAt).toDateString()}</TableTd>
|
||||
|
||||
@@ -7,8 +7,26 @@ import { useProxy } from 'valtio/utils';
|
||||
import stateLayananDesa from '../../../_state/desa/layananDesa';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
|
||||
function SuratKeterangan() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Posisi Organisasi'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListSuratKeterangan search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListSuratKeterangan({ search }: { search: string }) {
|
||||
const suratKeteranganState = useProxy(stateLayananDesa.suratKeterangan)
|
||||
const router = useRouter()
|
||||
|
||||
@@ -16,6 +34,14 @@ function SuratKeterangan() {
|
||||
suratKeteranganState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (suratKeteranganState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!suratKeteranganState.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
@@ -43,7 +69,7 @@ function SuratKeterangan() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{suratKeteranganState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>
|
||||
|
||||
@@ -7,8 +7,26 @@ import { useProxy } from 'valtio/utils';
|
||||
import stateLayananDesa from '../../../_state/desa/layananDesa';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
|
||||
function PelayananTelunjukSakti() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Posisi Organisasi'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPelayananTelunjukSakti search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPelayananTelunjukSakti({ search }: { search: string }) {
|
||||
const telunjukSaktiState = useProxy(stateLayananDesa.pelayananTelunjukSaktiDesa)
|
||||
const router = useRouter()
|
||||
|
||||
@@ -16,6 +34,14 @@ function PelayananTelunjukSakti() {
|
||||
telunjukSaktiState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (telunjukSaktiState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!telunjukSaktiState.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
@@ -42,7 +68,7 @@ function PelayananTelunjukSakti() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{telunjukSaktiState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd><Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} /></TableTd>
|
||||
|
||||
@@ -7,14 +7,40 @@ import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import JudulListTab from '../../_com/jusulListTab';
|
||||
import { useState } from 'react';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
|
||||
function Penghargaan() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Posisi Organisasi'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPenghargaan search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPenghargaan({ search }: { search: string }) {
|
||||
const state = useProxy(penghargaanState)
|
||||
const router = useRouter()
|
||||
useShallowEffect(() => {
|
||||
state.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (state.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!state.findMany.data) {
|
||||
return(
|
||||
<Stack py={10}>
|
||||
@@ -41,7 +67,7 @@ function Penghargaan() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{state.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>
|
||||
|
||||
@@ -11,21 +11,25 @@ import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus';
|
||||
import { useState } from 'react';
|
||||
|
||||
|
||||
function Page() {
|
||||
function Pengumuman() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Pengumuman'
|
||||
title='Posisi Organisasi'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<PengumumanList />
|
||||
<ListPengumuman search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function PengumumanList() {
|
||||
function ListPengumuman({ search }: { search: string }) {
|
||||
const pengumumanState = useProxy(stateDesaPengumuman)
|
||||
const router = useRouter()
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
|
||||
@@ -33,7 +37,6 @@ function PengumumanList() {
|
||||
pengumumanState.pengumuman.findMany.load()
|
||||
}, [])
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
@@ -43,6 +46,14 @@ function PengumumanList() {
|
||||
}
|
||||
}
|
||||
|
||||
const filteredData = (pengumumanState.pengumuman.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.judul.toLowerCase().includes(keyword) ||
|
||||
item.CategoryPengumuman?.name.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!pengumumanState.pengumuman.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
@@ -76,7 +87,7 @@ function PengumumanList() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody >
|
||||
{pengumumanState.pengumuman.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd >
|
||||
<Box w={100}>
|
||||
@@ -108,4 +119,4 @@ function PengumumanList() {
|
||||
)
|
||||
}
|
||||
|
||||
export default Page;
|
||||
export default Pengumuman;
|
||||
|
||||
@@ -9,29 +9,40 @@ import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import JudulList from '../../_com/judulList';
|
||||
import potensiDesaState from '../../_state/desa/potensi';
|
||||
import { useState } from 'react';
|
||||
|
||||
|
||||
function Potensi() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Potensi Desa'
|
||||
title='Posisi Organisasi'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPotensi />
|
||||
<ListPotensi search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPotensi() {
|
||||
function ListPotensi({ search }: { search: string }) {
|
||||
const potensiState = useProxy(potensiDesaState)
|
||||
const router = useRouter()
|
||||
|
||||
useShallowEffect(() => {
|
||||
potensiState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const filteredData = (potensiState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.kategori.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!potensiState.findMany.data) {
|
||||
return (
|
||||
@@ -60,7 +71,7 @@ function ListPotensi() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{potensiState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useRouter } from 'next/navigation';
|
||||
import lowonganKerjaState from '../../_state/ekonomi/lowongan-kerja';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
|
||||
function LowonganKerjaLokal() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Lowongan Kerja Lokal'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListLowonganKerjaLokal/>
|
||||
<ListLowonganKerjaLokal search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListLowonganKerjaLokal() {
|
||||
function ListLowonganKerjaLokal({ search }: { search: string }) {
|
||||
const lowonganState = useProxy(lowonganKerjaState)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -30,6 +34,15 @@ function ListLowonganKerjaLokal() {
|
||||
lowonganState.findMany.load();
|
||||
}, [])
|
||||
|
||||
const filteredData = (lowonganState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.posisi.toLowerCase().includes(keyword) ||
|
||||
item.namaPerusahaan.toLowerCase().includes(keyword) ||
|
||||
item.lokasi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!lowonganState.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
@@ -54,7 +67,7 @@ function ListLowonganKerjaLokal() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{lowonganState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.posisi}</TableTd>
|
||||
<TableTd>{item.namaPerusahaan}</TableTd>
|
||||
|
||||
@@ -11,20 +11,24 @@ import JudulList from '../../../_com/judulList';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import pasarDesaState from '../../../_state/ekonomi/pasar-desa/pasar-desa';
|
||||
|
||||
|
||||
function PasarDesa() {
|
||||
const [search, setSearch] = useState("")
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Kategori Produk'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPasarDesa />
|
||||
<ListPasarDesa search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPasarDesa() {
|
||||
function ListPasarDesa({ search }: { search: string }) {
|
||||
const statePasar = useProxy(pasarDesaState.kategoriProduk)
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
@@ -43,6 +47,13 @@ function ListPasarDesa() {
|
||||
}
|
||||
}
|
||||
|
||||
const filteredData = (statePasar.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.nama.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!statePasar.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
@@ -67,11 +78,11 @@ function ListPasarDesa() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{statePasar.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.nama}</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/ekonomi/pasar-desa/kategori-produk/${item.id}`)}>
|
||||
<Button color="green" onClick={() => router.push(`/admin/ekonomi/pasar-desa/kategori-produk/${item.id}`)}>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
import pasarDesaState from '../../../_state/ekonomi/pasar-desa/pasar-desa';
|
||||
import { useState } from 'react';
|
||||
|
||||
function PasarDesa() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Produk Pasar Desa'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPasarDesa />
|
||||
<ListPasarDesa search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPasarDesa() {
|
||||
function ListPasarDesa({ search }: { search: string }) {
|
||||
const statePasar = useProxy(pasarDesaState.pasarDesa)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -30,6 +34,16 @@ function ListPasarDesa() {
|
||||
statePasar.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (statePasar.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.nama.toLowerCase().includes(keyword) ||
|
||||
item.harga.toString().toLowerCase().includes(keyword) ||
|
||||
item.rating.toString().toLowerCase().includes(keyword) ||
|
||||
item.alamatUsaha.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!statePasar.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
@@ -56,7 +70,7 @@ function ListPasarDesa() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{statePasar.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.nama}</TableTd>
|
||||
<TableTd>Rp.{item.harga}</TableTd>
|
||||
|
||||
@@ -13,19 +13,22 @@ import { useEffect, useState } from 'react';
|
||||
import { CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis } from 'recharts';
|
||||
|
||||
function ProgramKemiskinan() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Program Kemiskinan'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListProgramKemiskinan />
|
||||
<ListProgramKemiskinan search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListProgramKemiskinan() {
|
||||
function ListProgramKemiskinan({ search }: { search: string }) {
|
||||
const programState = useProxy(programKemiskinanState)
|
||||
const router = useRouter();
|
||||
const [lineChart, setLineChart] = useState<any[]>([]);
|
||||
@@ -51,6 +54,15 @@ function ListProgramKemiskinan() {
|
||||
}
|
||||
}, [programState.findMany.data])
|
||||
|
||||
const filteredData = (programState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.nama.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword) ||
|
||||
item.statistik?.tahun.toString().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!programState.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
@@ -75,7 +87,7 @@ function ListProgramKemiskinan() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{programState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.nama}</TableTd>
|
||||
<TableTd>
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
function LayoutTabs({ children }: { children: React.ReactNode }) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const tabs = [
|
||||
{
|
||||
label: "Posisi Organisasi",
|
||||
value: "posisiorganisasi",
|
||||
href: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi"
|
||||
},
|
||||
{
|
||||
label: "Pegawai",
|
||||
value: "pegawai",
|
||||
href: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai"
|
||||
},
|
||||
{
|
||||
label: "Hubungan Organisasi",
|
||||
value: "hubunganorganisasi",
|
||||
href: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi"
|
||||
},
|
||||
];
|
||||
const curentTab = tabs.find(tab => tab.href === pathname)
|
||||
const [activeTab, setActiveTab] = useState<string | null>(curentTab?.value || tabs[0].value);
|
||||
|
||||
const handleTabChange = (value: string | null) => {
|
||||
const tab = tabs.find(t => t.value === value)
|
||||
if (tab) {
|
||||
router.push(tab.href)
|
||||
}
|
||||
setActiveTab(value)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const match = tabs.find(tab => tab.href === pathname)
|
||||
if (match) {
|
||||
setActiveTab(match.value)
|
||||
}
|
||||
}, [pathname])
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={3}>Struktur Organisasi & SK Pengurus BUMDesa</Title>
|
||||
<Tabs color={colors['blue-button']} variant='pills' value={activeTab} onChange={handleTabChange}>
|
||||
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
||||
{tabs.map((e, i) => (
|
||||
<TabsTab key={i} value={e.value}>{e.label}</TabsTab>
|
||||
))}
|
||||
</TabsList>
|
||||
{tabs.map((e, i) => (
|
||||
<TabsPanel key={i} value={e.value}>
|
||||
{/* Konten dummy, bisa diganti tergantung routing */}
|
||||
<></>
|
||||
</TabsPanel>
|
||||
))}
|
||||
</Tabs>
|
||||
{children}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default LayoutTabs;
|
||||
@@ -0,0 +1,94 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { Box, Button, Paper, Select, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
|
||||
|
||||
export default function EditHubunganOrganisasi() {
|
||||
const router = useRouter();
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const state = useProxy(strukturorganisasiState.hubunganOrganisasi);
|
||||
const pegawaiList = strukturorganisasiState.pegawai.findMany.data;
|
||||
|
||||
const [form, setForm] = useState({
|
||||
atasanId: '',
|
||||
bawahanId: '',
|
||||
tipe: '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
strukturorganisasiState.pegawai.findMany.load();
|
||||
|
||||
if (id) {
|
||||
state.edit.load(id).then(data => {
|
||||
if (data) {
|
||||
setForm({
|
||||
atasanId: data.atasanId,
|
||||
bawahanId: data.bawahanId,
|
||||
tipe: data.tipe || '',
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!form.atasanId || !form.bawahanId) {
|
||||
toast.warn("Atasan dan bawahan harus diisi");
|
||||
return;
|
||||
}
|
||||
|
||||
state.edit.id = id;
|
||||
state.edit.form = form;
|
||||
|
||||
const result = await state.edit.update();
|
||||
|
||||
if (result) {
|
||||
toast.success("Data berhasil diperbarui");
|
||||
router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Paper p="md" w={{ base: '100%', md: '50%' }}>
|
||||
<Stack>
|
||||
<Title order={3}>Edit Hubungan Organisasi</Title>
|
||||
<Select
|
||||
label="Atasan"
|
||||
placeholder="Pilih atasan"
|
||||
searchable
|
||||
data={pegawaiList?.map(p => ({
|
||||
value: p.id,
|
||||
label: p.namaLengkap,
|
||||
})) || []}
|
||||
value={form.atasanId}
|
||||
onChange={(val) => setForm({ ...form, atasanId: val || '' })}
|
||||
/>
|
||||
<Select
|
||||
label="Bawahan"
|
||||
placeholder="Pilih bawahan"
|
||||
searchable
|
||||
data={pegawaiList?.map(p => ({
|
||||
value: p.id,
|
||||
label: p.namaLengkap,
|
||||
})) || []}
|
||||
value={form.bawahanId}
|
||||
onChange={(val) => setForm({ ...form, bawahanId: val || '' })}
|
||||
/>
|
||||
<TextInput
|
||||
label="Tipe"
|
||||
placeholder="Contoh: langsung_melapor"
|
||||
value={form.tipe}
|
||||
onChange={(e) => setForm({ ...form, tipe: e.currentTarget.value })}
|
||||
/>
|
||||
<Button onClick={handleSubmit} color="blue">Simpan</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Box, Button, Paper, Select, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
|
||||
|
||||
export default function CreateHubunganOrganisasi() {
|
||||
const router = useRouter();
|
||||
const state = useProxy(strukturorganisasiState.hubunganOrganisasi);
|
||||
const pegawaiList = strukturorganisasiState.pegawai.findMany.data;
|
||||
|
||||
const [form, setForm] = useState({
|
||||
atasanId: '',
|
||||
bawahanId: '',
|
||||
tipe: '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
strukturorganisasiState.pegawai.findMany.load();
|
||||
}, []);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!form.atasanId || !form.bawahanId) {
|
||||
toast.warn("Atasan dan bawahan harus diisi");
|
||||
return;
|
||||
}
|
||||
|
||||
state.create.form = form;
|
||||
const result = await state.create.create();
|
||||
|
||||
if (result) {
|
||||
toast.success("Hubungan Organisasi berhasil ditambahkan");
|
||||
router.push('/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Paper p="md" w={{ base: '100%', md: '50%' }}>
|
||||
<Stack>
|
||||
<Title order={3}>Create Hubungan Organisasi</Title>
|
||||
<Select
|
||||
label="Atasan"
|
||||
placeholder="Pilih atasan"
|
||||
searchable
|
||||
data={pegawaiList?.map(p => ({
|
||||
value: p.id,
|
||||
label: p.namaLengkap,
|
||||
})) || []}
|
||||
value={form.atasanId}
|
||||
onChange={(val) => setForm({ ...form, atasanId: val || '' })}
|
||||
/>
|
||||
<Select
|
||||
label="Bawahan"
|
||||
placeholder="Pilih bawahan"
|
||||
searchable
|
||||
data={pegawaiList?.map(p => ({
|
||||
value: p.id,
|
||||
label: p.namaLengkap,
|
||||
})) || []}
|
||||
value={form.bawahanId}
|
||||
onChange={(val) => setForm({ ...form, bawahanId: val || '' })}
|
||||
/>
|
||||
<TextInput
|
||||
label="Tipe"
|
||||
placeholder="Contoh: langsung_melapor"
|
||||
value={form.tipe}
|
||||
onChange={(e) => setForm({ ...form, tipe: e.currentTarget.value })}
|
||||
/>
|
||||
<Button onClick={handleSubmit} color="blue">Simpan</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi';
|
||||
import { useState } from 'react';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
|
||||
function HubunganOrganisasi() {
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Hubungan Organisasi'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListHubunganOrganisasi search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function ListHubunganOrganisasi({ search }: { search: string }) {
|
||||
const stateOrganisasi = useProxy(strukturorganisasiState.hubunganOrganisasi);
|
||||
const [modalHapus, setModalHapus] = useState(false);
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null);
|
||||
const router = useRouter();
|
||||
|
||||
useShallowEffect(() => {
|
||||
stateOrganisasi.findMany.load();
|
||||
}, []);
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
stateOrganisasi.delete.byId(selectedId);
|
||||
setModalHapus(false);
|
||||
setSelectedId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const filteredData = (stateOrganisasi.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.atasan?.namaLengkap?.toLowerCase().includes(keyword) ||
|
||||
item.bawahan?.namaLengkap?.toLowerCase().includes(keyword) ||
|
||||
item.tipe?.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!stateOrganisasi.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulList
|
||||
title='List Hubungan Organisasi'
|
||||
href='/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/create'
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Atasan</TableTh>
|
||||
<TableTh>Bawahan</TableTh>
|
||||
<TableTh>Tipe</TableTh>
|
||||
<TableTh>Edit</TableTh>
|
||||
<TableTh>Hapus</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData
|
||||
.sort((a, b) =>
|
||||
a.atasan?.namaLengkap.localeCompare(b.atasan?.namaLengkap)
|
||||
)
|
||||
.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.atasan?.namaLengkap}</TableTd>
|
||||
<TableTd>{item.bawahan?.namaLengkap}</TableTd>
|
||||
<TableTd>{item.tipe}</TableTd>
|
||||
<TableTd>
|
||||
<Button
|
||||
bg="green"
|
||||
onClick={() => router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/hubungan-organisasi/${item.id}`)}
|
||||
>
|
||||
<IconEdit size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button
|
||||
color="red"
|
||||
onClick={() => {
|
||||
setSelectedId(item.id);
|
||||
setModalHapus(true);
|
||||
}}
|
||||
>
|
||||
<IconTrash size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text='Apakah anda yakin ingin menghapus hubungan organisasi ini?'
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default HubunganOrganisasi;
|
||||
@@ -0,0 +1,12 @@
|
||||
'use client'
|
||||
|
||||
import LayoutTabs from "./_lib/layoutTabs"
|
||||
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<LayoutTabs>
|
||||
{children}
|
||||
</LayoutTabs>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
function Page() {
|
||||
return (
|
||||
<div>
|
||||
struktur-organisasi-dan-sk-pengurus-bumdesa
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,279 @@
|
||||
/* 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<PreviewImage | string | null>(null);
|
||||
const stateOrganisasi = useProxy(strukturorganisasiState.pegawai);
|
||||
const [formData, setFormData] = useState<PegawaiFormData>({
|
||||
namaLengkap: "",
|
||||
gelarAkademik: "",
|
||||
imageId: "",
|
||||
tanggalMasuk: "",
|
||||
email: "",
|
||||
telepon: "",
|
||||
alamat: "",
|
||||
posisiId: "",
|
||||
isActive: true,
|
||||
});
|
||||
|
||||
const statusOptions = [
|
||||
{ value: true, label: 'Aktif' },
|
||||
{ value: false, label: 'Tidak Aktif' },
|
||||
];
|
||||
|
||||
// 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 (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={4}>Edit Data Pegawai</Title>
|
||||
<TextInput
|
||||
label="Nama Lengkap"
|
||||
placeholder="Masukkan nama lengkap"
|
||||
value={formData.namaLengkap}
|
||||
onChange={(e) => setFormData({ ...formData, namaLengkap: e.target.value })}
|
||||
/>
|
||||
<TextInput
|
||||
label="Gelar Akademik"
|
||||
placeholder="Contoh: S.Kom"
|
||||
value={formData.gelarAkademik}
|
||||
onChange={(e) => setFormData({ ...formData, gelarAkademik: e.target.value })}
|
||||
/>
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Gambar</Text>
|
||||
<Box >
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
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']
|
||||
}}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
|
||||
</Dropzone.Reject>
|
||||
<Dropzone.Idle>
|
||||
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
|
||||
</Dropzone.Idle>
|
||||
|
||||
<div>
|
||||
<Text size="xl" inline>
|
||||
Drag images here or click to select files
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" inline mt={7}>
|
||||
Attach as many files as you like, each file should not exceed 5mb
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
{previewImage && (
|
||||
<Image
|
||||
src={typeof previewImage === 'string' ? previewImage : previewImage?.preview}
|
||||
alt="Preview"
|
||||
width={280}
|
||||
height={180}
|
||||
fit="cover"
|
||||
radius="sm"
|
||||
mt="md"
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<TextInput
|
||||
label="Tanggal Masuk"
|
||||
type="date"
|
||||
placeholder="Contoh: 2022-01-01"
|
||||
value={formatDateForInput(formData.tanggalMasuk)}
|
||||
onChange={(e) => setFormData({ ...formData, tanggalMasuk: e.target.value })}
|
||||
/>
|
||||
<TextInput
|
||||
label="Email"
|
||||
placeholder="Contoh: email@example.com"
|
||||
value={formData.email}
|
||||
onChange={(e) => (formData.email = e.currentTarget.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Telepon"
|
||||
placeholder="Contoh: 08123456789"
|
||||
value={formData.telepon}
|
||||
onChange={(e) => (formData.telepon = e.currentTarget.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Alamat"
|
||||
placeholder="Contoh: Jl. Contoh No. 1"
|
||||
value={formData.alamat}
|
||||
onChange={(e) => (formData.alamat = e.currentTarget.value)}
|
||||
/>
|
||||
<Select
|
||||
label="Posisi"
|
||||
placeholder="Pilih posisi"
|
||||
data={
|
||||
strukturorganisasiState.posisiOrganisasi.findMany.data?.map((p) => ({
|
||||
value: p.id, // harus string
|
||||
label: p.nama,
|
||||
})) || []
|
||||
}
|
||||
value={formData.posisiId}
|
||||
onChange={(value) => {
|
||||
if (value !== null) {
|
||||
setFormData({ ...formData, posisiId: value }); // value harus string
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
label="Status Pegawai"
|
||||
data={statusOptions.map((s) => ({
|
||||
value: String(s.value),
|
||||
label: s.label,
|
||||
}))}
|
||||
value={String(formData.isActive)}
|
||||
onChange={(val) => {
|
||||
setFormData({ ...formData, isActive: val === 'true' });
|
||||
}}
|
||||
/>
|
||||
|
||||
<Group>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
color="blue"
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box >
|
||||
);
|
||||
}
|
||||
@@ -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<string | null>(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 (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Text fz={"xl"} fw={"bold"}>Detail Pegawai</Text>
|
||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Nama Lengkap</Text>
|
||||
<Text fz={"lg"}>{statePegawai.findUnique.data?.namaLengkap}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Gelar Akademik</Text>
|
||||
<Text fz={"lg"}>{statePegawai.findUnique.data?.gelarAkademik}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Image</Text>
|
||||
{statePegawai.findUnique.data?.image?.link ? (
|
||||
<Image src={statePegawai.findUnique.data?.image?.link} alt='' />
|
||||
) : (
|
||||
<Text fz={"md"} c="dimmed">Tidak ada gambar</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Tanggal Masuk</Text>
|
||||
<Text fz={"lg"}>
|
||||
{statePegawai.findUnique.data?.tanggalMasuk
|
||||
? new Date(statePegawai.findUnique.data.tanggalMasuk).toLocaleDateString()
|
||||
: "-"}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Email</Text>
|
||||
<Text fz={"lg"}>{statePegawai.findUnique.data?.email}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Telepon</Text>
|
||||
<Text fz={"lg"}>{statePegawai.findUnique.data?.telepon}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Alamat</Text>
|
||||
<Text fz={"lg"}>{statePegawai.findUnique.data?.alamat}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Posisi</Text>
|
||||
<Stack gap={4}>
|
||||
{statePegawai.findUnique.data?.posisi ? (
|
||||
<Text fz={"lg"}>
|
||||
{statePegawai.findUnique.data.posisi.nama}
|
||||
</Text>
|
||||
) : (
|
||||
<Text fz={"lg"} c="dimmed">
|
||||
Tidak ada posisi
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Aktif</Text>
|
||||
<Text fz={"lg"}>{statePegawai.findUnique.data?.isActive ? "Ya" : "Tidak"}</Text>
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Flex gap={"xs"}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (statePegawai.findUnique.data) {
|
||||
setSelectedId(statePegawai.findUnique.data.id);
|
||||
setModalHapus(true);
|
||||
}
|
||||
}}
|
||||
disabled={!statePegawai.findUnique.data}
|
||||
color="red">
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (statePegawai.findUnique.data) {
|
||||
router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/${statePegawai.findUnique.data.id}/edit`);
|
||||
}
|
||||
}}
|
||||
disabled={!statePegawai.findUnique.data}
|
||||
color="green">
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text="Apakah anda yakin ingin menghapus produk ini?"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailPegawai;
|
||||
@@ -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 (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Create Pegawai</Title>
|
||||
<TextInput
|
||||
label="Nama Lengkap"
|
||||
placeholder="Masukkan nama lengkap"
|
||||
value={stateOrganisasi.pegawai.create.form.namaLengkap}
|
||||
onChange={(e) => (stateOrganisasi.pegawai.create.form.namaLengkap = e.currentTarget.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Gelar Akademik"
|
||||
placeholder="Contoh: S.Kom"
|
||||
value={stateOrganisasi.pegawai.create.form.gelarAkademik}
|
||||
onChange={(e) => (stateOrganisasi.pegawai.create.form.gelarAkademik = e.currentTarget.value)}
|
||||
/>
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Gambar</Text>
|
||||
<Box >
|
||||
<Dropzone
|
||||
onDrop={(files) => {
|
||||
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']
|
||||
}}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
|
||||
</Dropzone.Reject>
|
||||
<Dropzone.Idle>
|
||||
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
|
||||
</Dropzone.Idle>
|
||||
|
||||
<div>
|
||||
<Text size="xl" inline>
|
||||
Drag images here or click to select files
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" inline mt={7}>
|
||||
Attach as many files as you like, each file should not exceed 5mb
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
{previewImage && (
|
||||
<Image
|
||||
src={previewImage.preview}
|
||||
alt="Preview"
|
||||
width={280}
|
||||
height={180}
|
||||
fit="cover"
|
||||
radius="sm"
|
||||
mt="md"
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
<TextInput
|
||||
label="Tanggal Masuk"
|
||||
type="date"
|
||||
placeholder="Contoh: 2022-01-01"
|
||||
value={stateOrganisasi.pegawai.create.form.tanggalMasuk}
|
||||
onChange={(e) => (stateOrganisasi.pegawai.create.form.tanggalMasuk = e.currentTarget.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Email"
|
||||
placeholder="Contoh: email@example.com"
|
||||
value={stateOrganisasi.pegawai.create.form.email}
|
||||
onChange={(e) => (stateOrganisasi.pegawai.create.form.email = e.currentTarget.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Telepon"
|
||||
placeholder="Contoh: 08123456789"
|
||||
value={stateOrganisasi.pegawai.create.form.telepon}
|
||||
onChange={(e) => (stateOrganisasi.pegawai.create.form.telepon = e.currentTarget.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Alamat"
|
||||
placeholder="Contoh: Jl. Contoh No. 1"
|
||||
value={stateOrganisasi.pegawai.create.form.alamat}
|
||||
onChange={(e) => (stateOrganisasi.pegawai.create.form.alamat = e.currentTarget.value)}
|
||||
/>
|
||||
<Select
|
||||
label="Posisi"
|
||||
placeholder="Pilih posisi"
|
||||
data={stateOrganisasi.posisiOrganisasi.findMany.data?.map(p => ({
|
||||
value: p.id,
|
||||
label: p.nama
|
||||
})) || []}
|
||||
value={stateOrganisasi.pegawai.create.form.posisiId}
|
||||
onChange={(value) => {
|
||||
if (value) stateOrganisasi.pegawai.create.form.posisiId = value;
|
||||
}}
|
||||
searchable
|
||||
/>
|
||||
|
||||
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
color="blue"
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreatePegawai;
|
||||
@@ -0,0 +1,151 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Badge, Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi';
|
||||
import { useState } from 'react';
|
||||
|
||||
function Pegawai() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Pegawai'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPegawai search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPegawai({ search }: { search: string }) {
|
||||
const stateOrganisasi = useProxy(strukturorganisasiState.pegawai);
|
||||
const router = useRouter();
|
||||
|
||||
useShallowEffect(() => {
|
||||
const loadData = async () => {
|
||||
console.log('1. Starting to load pegawai data...');
|
||||
try {
|
||||
// Clear existing data to ensure we see the loading state
|
||||
stateOrganisasi.findMany.data = [];
|
||||
|
||||
// Load new data
|
||||
await stateOrganisasi.findMany.load();
|
||||
|
||||
// Log the raw response and state
|
||||
console.log('2. Raw API response:', stateOrganisasi.findMany.data);
|
||||
|
||||
// Type guard to ensure data is an array
|
||||
const data = stateOrganisasi.findMany.data || [];
|
||||
console.log(`3. Loaded ${data.length} pegawai records`);
|
||||
if (data.length > 0) {
|
||||
console.log('4. First record sample:', data[0]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading pegawai data:', error);
|
||||
stateOrganisasi.findMany.data = [];
|
||||
}
|
||||
};
|
||||
|
||||
loadData();
|
||||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
console.log('Cleanup: Unmounting component');
|
||||
};
|
||||
}, []);
|
||||
|
||||
const filteredData = (stateOrganisasi.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.namaLengkap?.toLowerCase().includes(keyword) ||
|
||||
item.gelarAkademik?.toLowerCase().includes(keyword) ||
|
||||
item.telepon?.toLowerCase().includes(keyword) ||
|
||||
item.posisi?.nama?.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
// Handle loading state
|
||||
if (stateOrganisasi.findMany.data === null) {
|
||||
console.log('Showing loading state');
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton height={300} />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
// Check if data is an empty array
|
||||
const data = stateOrganisasi.findMany.data || [];
|
||||
if (data.length === 0) {
|
||||
console.log('No data available to display');
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper p="md" ta="center">
|
||||
<p>Tidak ada data pegawai yang tersedia</p>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulList
|
||||
title='List Pegawai'
|
||||
href='/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/create'
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama</TableTh>
|
||||
<TableTh>Gelar Akademik</TableTh>
|
||||
<TableTh>Telepon</TableTh>
|
||||
<TableTh>Posisi</TableTh>
|
||||
<TableTh>Aktif</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{(() => {
|
||||
console.log('Rendering table with items:', stateOrganisasi.findMany.data);
|
||||
return null;
|
||||
})()}
|
||||
{([...filteredData]
|
||||
.sort((a, b) => {
|
||||
if (a.isActive === b.isActive) {
|
||||
return a.namaLengkap.localeCompare(b.namaLengkap); // kalau status sama, urut nama
|
||||
}
|
||||
return Number(b.isActive) - Number(a.isActive); // aktif duluan
|
||||
}) // Aktif di atas
|
||||
).map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.namaLengkap}</TableTd>
|
||||
<TableTd>{item.gelarAkademik}</TableTd>
|
||||
<TableTd>{item.telepon}</TableTd>
|
||||
<TableTd>{item.posisi?.nama}</TableTd>
|
||||
<TableTd>
|
||||
<Badge color={item.isActive ? "green" : "red"}>{item.isActive ? "Aktif" : "Tidak Aktif"}</Badge>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button bg={"green"} onClick={() => router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/pegawai/${item.id}`)}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Pegawai;
|
||||
@@ -0,0 +1,117 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } 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';
|
||||
|
||||
function EditPosisiOrganisasi() {
|
||||
const router = useRouter();
|
||||
const params = useParams();
|
||||
const id = params?.id as string;
|
||||
const stateOrganisasi = useProxy(strukturorganisasiState.posisiOrganisasi);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
nama: "",
|
||||
deskripsi: "",
|
||||
hierarki: 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const loadPosisiOrganisasi = async () => {
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await stateOrganisasi.edit.load(id);
|
||||
|
||||
if (data) {
|
||||
// pastikan id-nya masuk ke state edit
|
||||
stateOrganisasi.edit.id = id;
|
||||
setFormData({
|
||||
nama: data.nama || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
hierarki: data.hierarki || 0,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading posisi organisasi:", error);
|
||||
toast.error("Gagal memuat data posisi organisasi");
|
||||
}
|
||||
};
|
||||
|
||||
loadPosisiOrganisasi();
|
||||
}, [id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
if (!formData.nama.trim()) {
|
||||
toast.error('Nama posisi organisasi tidak boleh kosong');
|
||||
return;
|
||||
}
|
||||
|
||||
stateOrganisasi.edit.form = {
|
||||
nama: formData.nama.trim(),
|
||||
deskripsi: formData.deskripsi.trim(),
|
||||
hierarki: formData.hierarki,
|
||||
};
|
||||
|
||||
// Safety check tambahan: pastikan ID tidak kosong
|
||||
if (!stateOrganisasi.edit.id) {
|
||||
stateOrganisasi.edit.id = id; // fallback
|
||||
}
|
||||
|
||||
const success = await stateOrganisasi.edit.update();
|
||||
|
||||
if (success) {
|
||||
router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error updating posisi organisasi:", error);
|
||||
// toast akan ditampilkan dari fungsi update
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={4}>Edit Posisi Organisasi</Title>
|
||||
<TextInput
|
||||
value={formData.nama}
|
||||
onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Nama Posisi Organisasi</Text>}
|
||||
placeholder='Masukkan nama posisi organisasi'
|
||||
/>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData({ ...formData, deskripsi: htmlContent });
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
value={formData.hierarki}
|
||||
onChange={(e) => setFormData({ ...formData, hierarki: parseInt(e.target.value) })}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Hierarki</Text>}
|
||||
placeholder='Masukkan hierarki'
|
||||
/>
|
||||
<Group>
|
||||
<Button bg={colors['blue-button']} onClick={handleSubmit}>Submit</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditPosisiOrganisasi;
|
||||
@@ -0,0 +1,79 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
|
||||
import strukturorganisasiState from '@/app/admin/(dashboard)/_state/ekonomi/struktur-organisasi/struktur-organisasi';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function CreatePosisiOrganisasi() {
|
||||
const router = useRouter();
|
||||
const stateOrganisasi = useProxy(strukturorganisasiState.posisiOrganisasi)
|
||||
useEffect(() => {
|
||||
stateOrganisasi.findMany.load();
|
||||
}, []);
|
||||
|
||||
const resetForm = () => {
|
||||
stateOrganisasi.create.form = {
|
||||
nama: "",
|
||||
deskripsi: "",
|
||||
hierarki: 0, // Initialize as 0 to allow any number input
|
||||
};
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
await stateOrganisasi.create.submit();
|
||||
resetForm();
|
||||
router.push("/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi")
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Create Posisi Organisasi</Title>
|
||||
<TextInput
|
||||
label="Nama Posisi"
|
||||
placeholder="Contoh: Kepala Desa"
|
||||
value={stateOrganisasi.create.form.nama}
|
||||
onChange={(e) => (stateOrganisasi.create.form.nama = e.currentTarget.value)}
|
||||
/>
|
||||
<CreateEditor
|
||||
value={stateOrganisasi.create.form.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
stateOrganisasi.create.form.deskripsi = htmlContent;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Hierarki"
|
||||
type="number"
|
||||
placeholder="Contoh: 1"
|
||||
value={stateOrganisasi.create.form.hierarki}
|
||||
onChange={(e) => {
|
||||
const value = parseInt(e.currentTarget.value, 10);
|
||||
if (!isNaN(value)) {
|
||||
stateOrganisasi.create.form.hierarki = value;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
color="blue"
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreatePosisiOrganisasi;
|
||||
@@ -0,0 +1,131 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import strukturorganisasiState from '../../../_state/ekonomi/struktur-organisasi/struktur-organisasi';
|
||||
import { useState } from 'react';
|
||||
|
||||
function PosisiOrganisasi() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Posisi Organisasi'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPosisiOrganisasi search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPosisiOrganisasi({ search }: { search: string }) {
|
||||
const stateOrganisasi = useProxy(strukturorganisasiState.posisiOrganisasi)
|
||||
const router = useRouter();
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
|
||||
useShallowEffect(() => {
|
||||
stateOrganisasi.findMany.load()
|
||||
}, [])
|
||||
|
||||
const handleHapus = async () => {
|
||||
if (selectedId) {
|
||||
await stateOrganisasi.delete.byId(selectedId);
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
}
|
||||
}
|
||||
|
||||
const filteredData = (stateOrganisasi.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.nama?.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi?.toLowerCase().includes(keyword) ||
|
||||
item.hierarki?.toString().toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!stateOrganisasi.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulList
|
||||
title='List Posisi Organisasi'
|
||||
href='/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/create'
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Nama Posisi</TableTh>
|
||||
<TableTh>Deskripsi</TableTh>
|
||||
<TableTh>Hierarki</TableTh>
|
||||
<TableTh>Edit</TableTh>
|
||||
<TableTh>Hapus</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.nama}</TableTd>
|
||||
<TableTd>
|
||||
<Text truncate dangerouslySetInnerHTML={{ __html: item.deskripsi ?? "" }} />
|
||||
</TableTd>
|
||||
<TableTd>{item.hierarki}</TableTd>
|
||||
<TableTd>
|
||||
<Button color="green"
|
||||
onClick={() => {
|
||||
if (item) {
|
||||
router.push(`/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi/${item.id}`);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button color="red"
|
||||
onClick={() => {
|
||||
if (item) {
|
||||
setSelectedId(item.id);
|
||||
setModalHapus(true);
|
||||
}
|
||||
}}
|
||||
disabled={!item}
|
||||
|
||||
>
|
||||
<IconTrash size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
{/* Modal Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text="Apakah anda yakin ingin menghapus posisi organisasi ini?"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default PosisiOrganisasi;
|
||||
@@ -8,21 +8,25 @@ import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import keamananLingkunganState from '../../_state/keamanan/keamanan-lingkungan';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
|
||||
function KeamananLingkungan() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Keamanan Lingkungan'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListKeamananLingkungan />
|
||||
<ListKeamananLingkungan search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListKeamananLingkungan() {
|
||||
function ListKeamananLingkungan({ search }: { search: string }) {
|
||||
const keamananState = useProxy(keamananLingkunganState)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -30,6 +34,14 @@ function ListKeamananLingkungan() {
|
||||
keamananState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (keamananState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!keamananState.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
@@ -53,7 +65,7 @@ function ListKeamananLingkungan() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{keamananState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import kontakDaruratKeamananState from '../../_state/keamanan/kontak-darurat-keamanan';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
|
||||
function KontakDaurat() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Kontak Darurat'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListKontakDaurat/>
|
||||
<ListKontakDaurat search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListKontakDaurat() {
|
||||
function ListKontakDaurat({ search }: { search: string }) {
|
||||
const kontakState = useProxy(kontakDaruratKeamananState)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -30,6 +34,15 @@ function ListKontakDaurat() {
|
||||
kontakState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (kontakState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.nama.toLowerCase().includes(keyword) ||
|
||||
item.kontakItems[0].nama.toLowerCase().includes(keyword) ||
|
||||
item.kontakItems[0].nomorTelepon.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!kontakState.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
@@ -54,7 +67,7 @@ function ListKontakDaurat() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{kontakState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.nama}</TableTd>
|
||||
<TableTd>{item.kontakItems[0].nama}</TableTd>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import laporanPublikState from '../../_state/keamanan/laporan-publik';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
|
||||
function LaporanPublik() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Laporan Publik'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListLaporanPublik/>
|
||||
<ListLaporanPublik search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListLaporanPublik() {
|
||||
function ListLaporanPublik({ search }: { search: string }) {
|
||||
const stateLaporan = useProxy(laporanPublikState)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -30,6 +34,15 @@ function ListLaporanPublik() {
|
||||
stateLaporan.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (stateLaporan.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.judul.toLowerCase().includes(keyword) ||
|
||||
item.status.toLowerCase().includes(keyword) ||
|
||||
item.kronologi?.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!stateLaporan.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
@@ -55,7 +68,7 @@ function ListLaporanPublik() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{stateLaporan.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.judul}</TableTd>
|
||||
<TableTd>{new Date(item.tanggalWaktu).toLocaleDateString('id-ID')}</TableTd>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import pencegahanKriminalitasState from '../../_state/keamanan/pencegahan-kriminalitas';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
|
||||
function PencegahanKriminalitas() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Pencegahan Kriminalitas'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPencegahanKriminalitas/>
|
||||
<ListPencegahanKriminalitas search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPencegahanKriminalitas() {
|
||||
function ListPencegahanKriminalitas({ search }: { search: string }) {
|
||||
const kriminalitasState = useProxy(pencegahanKriminalitasState)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -30,6 +34,15 @@ function ListPencegahanKriminalitas() {
|
||||
kriminalitasState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (kriminalitasState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.programKeamanan.nama.toLowerCase().includes(keyword) ||
|
||||
item.programKeamanan.slug.toLowerCase().includes(keyword) ||
|
||||
item.programKeamanan.deskripsi?.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!kriminalitasState.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -55,7 +68,7 @@ function ListPencegahanKriminalitas() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{kriminalitasState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.programKeamanan.nama}</TableTd>
|
||||
<TableTd>{item.programKeamanan.slug}</TableTd>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import JudulList from '../../_com/judulList';
|
||||
import polsekTerdekat from '../../_state/keamanan/polsek-terdekat';
|
||||
import { useState } from 'react';
|
||||
|
||||
function PolsekTerdekat() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Polsek Terdekat'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPolsekTerdekat/>
|
||||
<ListPolsekTerdekat search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPolsekTerdekat() {
|
||||
function ListPolsekTerdekat({ search }: { search: string }) {
|
||||
const polsekState = useProxy(polsekTerdekat)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -30,6 +34,15 @@ function ListPolsekTerdekat() {
|
||||
polsekState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (polsekState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.nama.toLowerCase().includes(keyword) ||
|
||||
item.jarakKeDesa.toLowerCase().includes(keyword) ||
|
||||
item.alamat.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!polsekState.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
@@ -54,7 +67,7 @@ function ListPolsekTerdekat() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{polsekState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.nama}</TableTd>
|
||||
<TableTd>{item.jarakKeDesa}</TableTd>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useRouter } from 'next/navigation';
|
||||
import tipsKeamananState from '../../_state/keamanan/tips-keamanan';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
|
||||
function TipsKeamanan() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Tips Keamanan'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListTipsKeamanan/>
|
||||
<ListTipsKeamanan search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListTipsKeamanan() {
|
||||
function ListTipsKeamanan({ search }: { search: string }) {
|
||||
const stateKeamanan = useProxy(tipsKeamananState)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -30,6 +34,14 @@ function ListTipsKeamanan() {
|
||||
stateKeamanan.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (stateKeamanan.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.judul.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!stateKeamanan.findMany.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
@@ -53,7 +65,7 @@ function ListTipsKeamanan() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{stateKeamanan.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.judul}</TableTd>
|
||||
<TableTd>
|
||||
|
||||
@@ -8,22 +8,26 @@ import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
import artikelKesehatanState from '../../../_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
|
||||
import { useState } from 'react';
|
||||
|
||||
|
||||
function ArtikelKesehatan() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Artikel Kesehatan'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListArtikelKesehatan/>
|
||||
<ListArtikelKesehatan search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListArtikelKesehatan() {
|
||||
function ListArtikelKesehatan({ search }: { search: string }) {
|
||||
const stateArtikelKesehatan = useProxy(artikelKesehatanState)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -31,6 +35,14 @@ function ListArtikelKesehatan() {
|
||||
stateArtikelKesehatan.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (stateArtikelKesehatan.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.title.toLowerCase().includes(keyword) ||
|
||||
item.content.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!stateArtikelKesehatan.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -56,7 +68,7 @@ function ListArtikelKesehatan() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{stateArtikelKesehatan.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.title}</TableTd>
|
||||
<TableTd>{item.content}</TableTd>
|
||||
|
||||
@@ -8,22 +8,26 @@ import { useProxy } from 'valtio/utils';
|
||||
import fasilitasKesehatanState from '../../../_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
import { useState } from 'react';
|
||||
|
||||
|
||||
function FasilitasKesehatan() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Fasilitas Kesehatan'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListFasilitasKesehatan/>
|
||||
<ListFasilitasKesehatan search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListFasilitasKesehatan() {
|
||||
function ListFasilitasKesehatan({ search }: { search: string }) {
|
||||
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -31,6 +35,15 @@ function ListFasilitasKesehatan() {
|
||||
stateFasilitasKesehatan.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (stateFasilitasKesehatan.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.informasiumum.alamat.toLowerCase().includes(keyword) ||
|
||||
item.informasiumum.jamOperasional.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!stateFasilitasKesehatan.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -57,7 +70,7 @@ function ListFasilitasKesehatan() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{stateFasilitasKesehatan.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>{item.informasiumum.alamat}</TableTd>
|
||||
|
||||
@@ -10,8 +10,24 @@ import { useProxy } from 'valtio/utils';
|
||||
import JudulListTab from '../../../_com/jusulListTab';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import grafikkepuasan from '../../../_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
|
||||
function GrafikHasilKepuasanMasyarakat() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Grafik Hasil Kepuasan Masyarakat'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListGrafikHasilKepuasanMasyarakat search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
function ListGrafikHasilKepuasanMasyarakat({ search }: { search: string }) {
|
||||
type PDKMGrafik = {
|
||||
id: string;
|
||||
label: string;
|
||||
@@ -52,6 +68,14 @@ function GrafikHasilKepuasanMasyarakat() {
|
||||
}
|
||||
}, [stateGrafikKepuasan.findMany.data]);
|
||||
|
||||
const filteredData = (stateGrafikKepuasan.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.label.toLowerCase().includes(keyword) ||
|
||||
item.jumlah.toString().toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!stateGrafikKepuasan.findMany.data) {
|
||||
return (
|
||||
<Stack>
|
||||
@@ -81,7 +105,7 @@ function GrafikHasilKepuasanMasyarakat() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{stateGrafikKepuasan.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.label}</TableTd>
|
||||
<TableTd>{item.jumlah}</TableTd>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
import JudulList from '../../../_com/judulList';
|
||||
import jadwalKegiatanState from '../../../_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
|
||||
import { useState } from 'react';
|
||||
|
||||
function JadwalKegiatan() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Jadwal Kegiatan'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListJadwalKegiatan/>
|
||||
<ListJadwalKegiatan search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListJadwalKegiatan() {
|
||||
function ListJadwalKegiatan({ search }: { search: string }) {
|
||||
const stateJadwalKegiatan = useProxy(jadwalKegiatanState)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -30,6 +34,16 @@ function ListJadwalKegiatan() {
|
||||
stateJadwalKegiatan.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (stateJadwalKegiatan.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.informasijadwalkegiatan.name.toLowerCase().includes(keyword) ||
|
||||
item.informasijadwalkegiatan.tanggal.toLowerCase().includes(keyword) ||
|
||||
item.informasijadwalkegiatan.waktu.toLowerCase().includes(keyword) ||
|
||||
item.informasijadwalkegiatan.lokasi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!stateJadwalKegiatan.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -57,7 +71,7 @@ function ListJadwalKegiatan() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{stateJadwalKegiatan.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.informasijadwalkegiatan.name}</TableTd>
|
||||
<TableTd>{item.informasijadwalkegiatan.tanggal}</TableTd>
|
||||
|
||||
@@ -10,8 +10,24 @@ import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import JudulListTab from '../../../_com/jusulListTab';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
|
||||
function PersentaseDataKelahiranKematian() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Persentase Data Kelahiran & Kematian'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPersentaseDataKelahiranKematian search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
function ListPersentaseDataKelahiranKematian({ search }: { search: string }) {
|
||||
type PDKMGrafik = {
|
||||
id: string;
|
||||
tahun: string;
|
||||
@@ -56,6 +72,16 @@ function PersentaseDataKelahiranKematian() {
|
||||
}
|
||||
}, [statePersentase.findMany.data]);
|
||||
|
||||
const filteredData = (statePersentase.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.tahun.toLowerCase().includes(keyword) ||
|
||||
item.kematianKasar.toString().toLowerCase().includes(keyword) ||
|
||||
item.kematianBayi.toString().toLowerCase().includes(keyword) ||
|
||||
item.kelahiranKasar.toString().toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!statePersentase.findMany.data) {
|
||||
return (
|
||||
<Stack>
|
||||
@@ -87,7 +113,7 @@ function PersentaseDataKelahiranKematian() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{statePersentase.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.tahun}</TableTd>
|
||||
<TableTd>{item.kematianKasar}</TableTd>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import infoWabahPenyakit from '../../_state/kesehatan/info-wabah-penyakit/infoWabahPenyakit';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
|
||||
function InfoWabahPenyakit() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Info Wabah Penyakit'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListInfoWabahPenyakit/>
|
||||
<ListInfoWabahPenyakit search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListInfoWabahPenyakit() {
|
||||
function ListInfoWabahPenyakit({ search }: { search: string }) {
|
||||
const infoWabahPenyakitState = useProxy(infoWabahPenyakit)
|
||||
const router = useRouter()
|
||||
|
||||
@@ -30,6 +34,14 @@ function ListInfoWabahPenyakit() {
|
||||
infoWabahPenyakitState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (infoWabahPenyakitState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsiSingkat.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!infoWabahPenyakitState.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -56,7 +68,7 @@ function ListInfoWabahPenyakit() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{infoWabahPenyakitState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import kontakDarurat from '../../_state/kesehatan/kontak-darurat/kontakDarurat';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
|
||||
function KontakDarurat() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Kontak Darurat'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListKontakDarurat />
|
||||
<ListKontakDarurat search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListKontakDarurat() {
|
||||
function ListKontakDarurat({ search }: { search: string }) {
|
||||
const kontakDaruratState = useProxy(kontakDarurat)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -30,6 +34,14 @@ function ListKontakDarurat() {
|
||||
kontakDaruratState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (kontakDaruratState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!kontakDaruratState.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -57,7 +69,7 @@ function ListKontakDarurat() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{kontakDaruratState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
|
||||
@@ -8,22 +8,25 @@ import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import penangananDarurat from '../../_state/kesehatan/penanganan-darurat/penangananDarurat';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
function PenangananDarurat() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='PenangananDarurat'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPenangananDarurat/>
|
||||
<ListPenangananDarurat search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPenangananDarurat() {
|
||||
function ListPenangananDarurat({ search }: { search: string }) {
|
||||
const penangananDaruratState = useProxy(penangananDarurat)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -31,6 +34,14 @@ function ListPenangananDarurat() {
|
||||
penangananDaruratState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (penangananDaruratState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!penangananDaruratState.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -58,7 +69,7 @@ function ListPenangananDarurat() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{penangananDaruratState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import posyandustate from '../../_state/kesehatan/posyandu/posyandu';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
|
||||
function Posyandu() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Posyandu'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPosyandu />
|
||||
<ListPosyandu search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPosyandu() {
|
||||
function ListPosyandu({ search }: { search: string }) {
|
||||
const statePosyandu = useProxy(posyandustate)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -30,6 +34,14 @@ function ListPosyandu() {
|
||||
statePosyandu.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (statePosyandu.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.nomor.toString().toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!statePosyandu.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -56,7 +68,7 @@ function ListPosyandu() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{statePosyandu.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>{item.nomor}</TableTd>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useRouter } from 'next/navigation';
|
||||
import programKesehatan from '../../_state/kesehatan/program-kesehatan/programKesehatan';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useState } from 'react';
|
||||
|
||||
function ProgramKesehatan() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Program Kesehatan'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListProgramKesehatan />
|
||||
<ListProgramKesehatan search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListProgramKesehatan() {
|
||||
function ListProgramKesehatan({ search }: { search: string }) {
|
||||
const programKesehatanState = useProxy(programKesehatan)
|
||||
const router = useRouter()
|
||||
|
||||
@@ -30,6 +34,14 @@ function ListProgramKesehatan() {
|
||||
programKesehatanState.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (programKesehatanState.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.deskripsiSingkat.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!programKesehatanState.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -57,14 +69,16 @@ function ListProgramKesehatan() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{programKesehatanState.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
<Text truncate="end" fz={"sm"}>{item.name}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>{item.deskripsiSingkat}</TableTd>
|
||||
<TableTd>
|
||||
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsiSingkat }} />
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Image w={100} src={item.image?.link} alt="image" />
|
||||
</TableTd>
|
||||
|
||||
@@ -8,21 +8,25 @@ import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import JudulList from '../../_com/judulList';
|
||||
import puskesmasState from '../../_state/kesehatan/puskesmas/puskesmas';
|
||||
import { useState } from 'react';
|
||||
|
||||
function Puskesmas() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Puskesmas'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListPuskesmas/>
|
||||
<ListPuskesmas search={search}/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListPuskesmas() {
|
||||
function ListPuskesmas({ search }: { search: string }) {
|
||||
const statePuskesmas = useProxy(puskesmasState)
|
||||
const router = useRouter();
|
||||
|
||||
@@ -30,6 +34,14 @@ function ListPuskesmas() {
|
||||
statePuskesmas.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (statePuskesmas.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.name.toLowerCase().includes(keyword) ||
|
||||
item.alamat.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!statePuskesmas.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
@@ -56,7 +68,7 @@ function ListPuskesmas() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{statePuskesmas.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.name}</TableTd>
|
||||
<TableTd>{item.alamat}</TableTd>
|
||||
|
||||
@@ -1,42 +1,46 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import daftarInformasiPublik from '../../_state/ppid/daftar_informasi_publik/daftarInformasiPublik';
|
||||
|
||||
function Page() {
|
||||
function DaftarInformasiPublik() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 9 }}>
|
||||
<Title order={3}>Daftar Informasi Publik Desa Darmasaba</Title>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 3 }}>
|
||||
<Paper radius={"lg"} bg={colors['white-1']}>
|
||||
<TextInput
|
||||
radius={"lg"}
|
||||
placeholder='pencarian'
|
||||
leftSection={<IconSearch size={20} />}
|
||||
/>
|
||||
</Paper>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<ListDaftarInformasi />
|
||||
<HeaderSearch
|
||||
title='Posisi Organisasi'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListDaftarInformasi search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListDaftarInformasi() {
|
||||
function ListDaftarInformasi({ search }: { search: string }) {
|
||||
const listData = useProxy(daftarInformasiPublik)
|
||||
const router = useRouter()
|
||||
|
||||
useShallowEffect(() => {
|
||||
listData.findMany.load()
|
||||
}, [])
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const filteredData = (listData.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.jenisInformasi.toLowerCase().includes(keyword) ||
|
||||
item.deskripsi.toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!listData.findMany.data) {
|
||||
return (
|
||||
@@ -77,7 +81,7 @@ function ListDaftarInformasi() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{listData.findMany.data?.map((item, index) => (
|
||||
{filteredData.map((item, index) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{index + 1}</TableTd>
|
||||
<TableTd>{item.jenisInformasi}</TableTd>
|
||||
@@ -98,4 +102,4 @@ function ListDaftarInformasi() {
|
||||
)
|
||||
}
|
||||
|
||||
export default Page;
|
||||
export default DaftarInformasiPublik;
|
||||
|
||||
@@ -11,8 +11,25 @@ import { Cell, Pie, PieChart } from 'recharts';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import JudulListTab from '../../../_com/jusulListTab';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
|
||||
function GrafikBerdasarkanJenisKelamin() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Grafik Berdasarkan Jenis Kelamin Responden'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListGrafikBerdasarkanJenisKelamin search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListGrafikBerdasarkanJenisKelamin({ search }: { search: string }) {
|
||||
const stategrafikBerdasarkanJenisKelamin = useProxy(grafikBerdasarkanJenisKelamin)
|
||||
const [donutData, setDonutData] = useState<any[]>([]);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
@@ -46,6 +63,14 @@ function GrafikBerdasarkanJenisKelamin() {
|
||||
}
|
||||
}, [stategrafikBerdasarkanJenisKelamin.findMany.data])
|
||||
|
||||
const filteredData = (stategrafikBerdasarkanJenisKelamin.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.laki.toString().toLowerCase().includes(keyword) ||
|
||||
item.perempuan.toString().toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!stategrafikBerdasarkanJenisKelamin.findMany.data) {
|
||||
return (
|
||||
<Box>
|
||||
@@ -74,14 +99,14 @@ function GrafikBerdasarkanJenisKelamin() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{stategrafikBerdasarkanJenisKelamin.findMany.data.length === 0 ? (
|
||||
{filteredData.length === 0 ? (
|
||||
<TableTr>
|
||||
<TableTd colSpan={6}>
|
||||
<Text ta='center' c='dimmed'>Belum ada data grafik responden</Text>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
) : (
|
||||
stategrafikBerdasarkanJenisKelamin.findMany.data.map((item) => (
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.laki}</TableTd>
|
||||
<TableTd>{item.perempuan}</TableTd>
|
||||
|
||||
@@ -11,9 +11,25 @@ import { useSnapshot } from 'valtio';
|
||||
import JudulListTab from '../../../_com/jusulListTab';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import grafikBerdasarkanResponden from '../../../_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanResponden';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
|
||||
function GrafikBerdasarkanResponden() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Grafik Berdasarkan Pilihan Responden'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListGrafikBerdasarkanResponden search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListGrafikBerdasarkanResponden({ search }: { search: string }) {
|
||||
const stategrafikBerdasarkanResponden = useSnapshot(grafikBerdasarkanResponden)
|
||||
const [donutData, setDonutData] = useState<any[]>([]);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
@@ -38,6 +54,16 @@ function GrafikBerdasarkanResponden() {
|
||||
stategrafikBerdasarkanResponden.findMany.load()
|
||||
}, [])
|
||||
|
||||
const filteredData = (stategrafikBerdasarkanResponden.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.sangatbaik.toString().toLowerCase().includes(keyword) ||
|
||||
item.baik.toString().toLowerCase().includes(keyword) ||
|
||||
item.kurangbaik.toString().toLowerCase().includes(keyword) ||
|
||||
item.tidakbaik.toString().toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (stategrafikBerdasarkanResponden.findMany.data) {
|
||||
const totalSangatBaik = stategrafikBerdasarkanResponden.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.sangatbaik || 0), 0);
|
||||
@@ -83,14 +109,14 @@ function GrafikBerdasarkanResponden() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{stategrafikBerdasarkanResponden.findMany.data.length === 0 ? (
|
||||
{filteredData.length === 0 ? (
|
||||
<TableTr>
|
||||
<TableTd colSpan={6}>
|
||||
<Text ta='center' c='dimmed'>Belum ada data grafik responden</Text>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
) : (
|
||||
stategrafikBerdasarkanResponden.findMany.data.map((item) => (
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.sangatbaik}</TableTd>
|
||||
<TableTd>{item.baik}</TableTd>
|
||||
|
||||
@@ -11,8 +11,25 @@ import { Cell, Pie, PieChart } from 'recharts';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import JudulListTab from '../../../_com/jusulListTab';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
|
||||
function GrafikBerdasarakanUmur() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Grafik Berdasarkan Umur Responden'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListGrafikBerdasarakanUmur search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListGrafikBerdasarakanUmur({ search }: { search: string }) {
|
||||
const stategrafikBerdasarkanUmur = useProxy(grafikBerdasarkanUmur)
|
||||
const [donutData, setDonutData] = useState<any[]>([]);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
@@ -50,6 +67,16 @@ function GrafikBerdasarakanUmur() {
|
||||
}
|
||||
}, [stategrafikBerdasarkanUmur.findMany.data])
|
||||
|
||||
const filteredData = (stategrafikBerdasarkanUmur.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.remaja.toString().toLowerCase().includes(keyword) ||
|
||||
item.dewasa.toString().toLowerCase().includes(keyword) ||
|
||||
item.orangtua.toString().toLowerCase().includes(keyword) ||
|
||||
item.lansia.toString().toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
if (!stategrafikBerdasarkanUmur.findMany.data) {
|
||||
return (
|
||||
<Box>
|
||||
@@ -57,7 +84,7 @@ function GrafikBerdasarakanUmur() {
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
@@ -80,14 +107,14 @@ function GrafikBerdasarakanUmur() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{stategrafikBerdasarkanUmur.findMany.data.length === 0 ? (
|
||||
{filteredData.length === 0 ? (
|
||||
<TableTr>
|
||||
<TableTd colSpan={6}>
|
||||
<Text ta='center' c='dimmed'>Belum ada data grafik responden</Text>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
) : (
|
||||
stategrafikBerdasarkanUmur.findMany.data.map((item) => (
|
||||
filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.remaja}</TableTd>
|
||||
<TableTd>{item.dewasa}</TableTd>
|
||||
|
||||
@@ -10,9 +10,26 @@ import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
|
||||
import { useSnapshot } from 'valtio';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import grafikHasilKepuasanMasyarakat from '../../../_state/ppid/indeks_kepuasan_masyarakat/grafikHasilKepuasan';
|
||||
import HeaderSearch from '../../../_com/header';
|
||||
|
||||
|
||||
function GrafikHasilKepuasanMasyarakat() {
|
||||
const [search, setSearch] = useState("");
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='Grafik Hasil Kepuasan Masyarakat'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.currentTarget.value)}
|
||||
/>
|
||||
<ListGrafikHasilKepuasanMasyarakat search={search} />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListGrafikHasilKepuasanMasyarakat({ search }: { search: string }) {
|
||||
type IKMGrafik = {
|
||||
id: string;
|
||||
label: string;
|
||||
@@ -58,6 +75,14 @@ function GrafikHasilKepuasanMasyarakat() {
|
||||
}
|
||||
}, [stateGrafikHasilKepuasan.findMany.data]);
|
||||
|
||||
const filteredData = (stateGrafikHasilKepuasan.findMany.data || []).filter(item => {
|
||||
const keyword = search.toLowerCase();
|
||||
return (
|
||||
item.label.toLowerCase().includes(keyword) ||
|
||||
item.kepuasan.toString().toLowerCase().includes(keyword)
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
|
||||
if (!stateGrafikHasilKepuasan.findMany.data) {
|
||||
@@ -87,7 +112,7 @@ function GrafikHasilKepuasanMasyarakat() {
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{stateGrafikHasilKepuasan.findMany.data?.map((item) => (
|
||||
{filteredData.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.label}</TableTd>
|
||||
<TableTd>{item.kepuasan}</TableTd>
|
||||
|
||||
@@ -225,7 +225,7 @@ export const navBar = [
|
||||
{
|
||||
id: "Ekonomi_3",
|
||||
name: "Struktur Organisasi dan SK Pengurus BUMDesa",
|
||||
path: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa"
|
||||
path: "/admin/ekonomi/struktur-organisasi-dan-sk-pengurus-bumdesa/posisi-organisasi"
|
||||
},
|
||||
{
|
||||
id: "Ekonomi_4",
|
||||
|
||||
@@ -3,6 +3,7 @@ import PasarDesa from "./pasar-desa";
|
||||
import LowonganKerja from "./lowongan-kerja";
|
||||
import ProgramKemiskinan from "./program-kemiskinan";
|
||||
import KategoriProduk from "./pasar-desa/kategori-produk";
|
||||
import StrukturOrganisasi from "./struktur-organisasi";
|
||||
|
||||
const Ekonomi = new Elysia({
|
||||
prefix: "/api/ekonomi",
|
||||
@@ -12,5 +13,6 @@ const Ekonomi = new Elysia({
|
||||
.use(KategoriProduk)
|
||||
.use(LowonganKerja)
|
||||
.use(ProgramKemiskinan)
|
||||
.use(StrukturOrganisasi)
|
||||
|
||||
export default Ekonomi
|
||||
@@ -0,0 +1,51 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormCreateHubunganOrganisasi = {
|
||||
atasanId: string;
|
||||
bawahanId: string;
|
||||
tipe?: string;
|
||||
};
|
||||
|
||||
export default async function hubunganOrganisasiCreate(context: Context) {
|
||||
const body = await context.body as FormCreateHubunganOrganisasi;
|
||||
|
||||
// Validasi minimal
|
||||
if (!body || !body.atasanId || !body.bawahanId) {
|
||||
return {
|
||||
success: false,
|
||||
message: "atasanId dan bawahanId wajib diisi",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await prisma.hubunganOrganisasi.create({
|
||||
data: {
|
||||
atasanId: body.atasanId,
|
||||
bawahanId: body.bawahanId,
|
||||
tipe: body.tipe,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil membuat hubungan organisasi",
|
||||
data,
|
||||
};
|
||||
} catch (error: any) {
|
||||
if (error.code === "P2002") {
|
||||
return {
|
||||
success: false,
|
||||
message: "Hubungan antara atasan dan bawahan sudah ada",
|
||||
};
|
||||
}
|
||||
|
||||
console.error("Error create hubungan organisasi:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal membuat hubungan organisasi",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function hubunganOrganisasiDelete(context: Context) {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
if (!id) {
|
||||
return {
|
||||
success: false,
|
||||
message: "ID wajib diisi",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const deleted = await prisma.hubunganOrganisasi.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Hubungan organisasi berhasil dihapus",
|
||||
data: deleted,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error("Error delete hubungan organisasi:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal menghapus hubungan organisasi",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export default async function hubunganOrganisasiFindMany() {
|
||||
try {
|
||||
const data = await prisma.hubunganOrganisasi.findMany({
|
||||
include: {
|
||||
atasan: true,
|
||||
bawahan: true,
|
||||
},
|
||||
orderBy: {
|
||||
atasanId: "asc",
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error("Error findMany hubungan organisasi:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengambil data hubungan organisasi",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function hubunganOrganisasiFindUnique(context: Context) {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
if (!id) {
|
||||
return {
|
||||
success: false,
|
||||
message: "ID hubungan organisasi wajib diisi",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await prisma.hubunganOrganisasi.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
atasan: true,
|
||||
bawahan: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Data hubungan organisasi tidak ditemukan",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error("Error findUnique hubungan organisasi:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengambil data",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import hubunganOrganisasiFindMany from "./findMany";
|
||||
import hubunganOrganisasiFindUnique from "./findUnique";
|
||||
import hubunganOrganisasiCreate from "./create";
|
||||
import hubunganOrganisasiUpdate from "./updt";
|
||||
import hubunganOrganisasiDelete from "./del";
|
||||
|
||||
|
||||
const HubunganOrganisasi = new Elysia({
|
||||
prefix: "/hubungan-organisasi",
|
||||
tags: ["Ekonomi/Struktur Organisasi/Hubungan Organisasi"],
|
||||
})
|
||||
|
||||
// 🔍 GET /find-many
|
||||
.get("/find-many", hubunganOrganisasiFindMany)
|
||||
|
||||
// 🔍 GET /:id
|
||||
.get("/:id", async (context) => {
|
||||
return await hubunganOrganisasiFindUnique(context);
|
||||
})
|
||||
|
||||
// ➕ POST /create
|
||||
.post("/create", hubunganOrganisasiCreate, {
|
||||
body: t.Object({
|
||||
atasanId: t.String(),
|
||||
bawahanId: t.String(),
|
||||
tipe: t.Optional(t.String()),
|
||||
}),
|
||||
})
|
||||
|
||||
// ✏️ PUT /:id
|
||||
.put( "/:id",
|
||||
async (context) => {
|
||||
const response = await hubunganOrganisasiUpdate(context);
|
||||
return response;
|
||||
}, {
|
||||
body: t.Object({
|
||||
atasanId: t.Optional(t.String()),
|
||||
bawahanId: t.Optional(t.String()),
|
||||
tipe: t.Optional(t.String()),
|
||||
}),
|
||||
})
|
||||
|
||||
// ❌ DELETE /del/:id
|
||||
.delete("/del/:id", hubunganOrganisasiDelete);
|
||||
|
||||
export default HubunganOrganisasi;
|
||||
@@ -0,0 +1,52 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
type FormUpdateHubungan = {
|
||||
atasanId?: string;
|
||||
bawahanId?: string;
|
||||
tipe?: string;
|
||||
};
|
||||
|
||||
export default async function hubunganOrganisasiUpdate(context: Context) {
|
||||
const body = await context.body as Omit<FormUpdateHubungan, 'id'>;
|
||||
const id = context.params?.id;
|
||||
|
||||
if (!id) {
|
||||
return {
|
||||
success: false,
|
||||
message: "ID wajib ada di URL",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const updated = await prisma.hubunganOrganisasi.update({
|
||||
where: { id },
|
||||
data: {
|
||||
atasanId: body.atasanId,
|
||||
bawahanId: body.bawahanId,
|
||||
tipe: body.tipe,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Hubungan organisasi berhasil diupdate",
|
||||
data: updated,
|
||||
};
|
||||
} catch (error: any) {
|
||||
if (error.code === "P2002") {
|
||||
return {
|
||||
success: false,
|
||||
message: "Relasi atasan-bawahan sudah ada",
|
||||
};
|
||||
}
|
||||
|
||||
console.error("Error update hubungan organisasi:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal update data hubungan organisasi",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import Elysia from "elysia";
|
||||
import PosisiOrganisasi from "./posisi-organisasi";
|
||||
import Pegawai from "./pegawai";
|
||||
import HubunganOrganisasi from "./hubungan-organisasi";
|
||||
|
||||
const StrukturOrganisasi = new Elysia({
|
||||
prefix: "/struktur-organisasi",
|
||||
tags: ["Ekonomi/Struktur Organisasi"],
|
||||
})
|
||||
.use(PosisiOrganisasi)
|
||||
.use(Pegawai)
|
||||
.use(HubunganOrganisasi)
|
||||
|
||||
export default StrukturOrganisasi;
|
||||
@@ -0,0 +1,79 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormCreatePegawai = {
|
||||
namaLengkap: string;
|
||||
gelarAkademik?: string;
|
||||
imageId: string;
|
||||
tanggalMasuk?: string; // Kirim dari frontend dalam format ISO (ex: '2025-07-04')
|
||||
email?: string;
|
||||
telepon?: string;
|
||||
alamat?: string;
|
||||
posisiId: string;
|
||||
isActive?: boolean;
|
||||
};
|
||||
|
||||
export default async function pegawaiCreate(context: Context) {
|
||||
const body = await context.body as FormCreatePegawai;
|
||||
|
||||
if (!body || typeof body !== 'object') {
|
||||
return {
|
||||
success: false,
|
||||
message: "Data tidak valid",
|
||||
};
|
||||
}
|
||||
|
||||
if (!body.namaLengkap || !body.posisiId || !body.imageId) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Nama lengkap, posisi, dan gambar wajib diisi",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const pegawai = await prisma.pegawai.create({
|
||||
data: {
|
||||
namaLengkap: body.namaLengkap,
|
||||
gelarAkademik: body.gelarAkademik,
|
||||
imageId: body.imageId,
|
||||
tanggalMasuk: body.tanggalMasuk ? new Date(body.tanggalMasuk) : undefined,
|
||||
email: body.email,
|
||||
telepon: body.telepon,
|
||||
alamat: body.alamat,
|
||||
posisiId: body.posisiId,
|
||||
isActive: body.isActive ?? true, // default true kalau tidak dikirim
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil menambahkan pegawai",
|
||||
data: pegawai,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error("Error creating pegawai:", error);
|
||||
|
||||
if (error.code === "P2002") {
|
||||
// Cek field mana yang duplicate
|
||||
const target = error.meta?.target?.[0];
|
||||
let message = "Data sudah ada";
|
||||
|
||||
if (target === 'email') message = "Email sudah digunakan";
|
||||
else if (target === 'telepon') message = "Nomor telepon sudah digunakan";
|
||||
|
||||
return {
|
||||
success: false,
|
||||
message,
|
||||
};
|
||||
}
|
||||
|
||||
console.error("Gagal menambahkan pegawai:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Terjadi kesalahan saat membuat pegawai",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function pegawaiDelete(context: Context) {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
if (!id) {
|
||||
return {
|
||||
success: false,
|
||||
message: "ID pegawai tidak ditemukan",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const deleted = await prisma.pegawai.update({
|
||||
where: { id },
|
||||
data: {
|
||||
isActive: false, // soft delete
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Pegawai berhasil di-nonaktifkan",
|
||||
data: deleted,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error("Error delete pegawai:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal menghapus pegawai",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export default async function pegawaiFindMany() {
|
||||
try {
|
||||
const pegawaiList = await prisma.pegawai.findMany({
|
||||
orderBy: { createdAt: "desc" },
|
||||
include: {
|
||||
posisi: true,
|
||||
image: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: pegawaiList,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error("Error findMany pegawai:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengambil data pegawai",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function pegawaiFindUnique(context: Context) {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
if (!id) {
|
||||
return {
|
||||
success: false,
|
||||
message: "ID pegawai diperlukan",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const pegawai = await prisma.pegawai.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
posisi: true,
|
||||
image: true
|
||||
},
|
||||
});
|
||||
|
||||
if (!pegawai) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Pegawai tidak ditemukan",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: pegawai,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error("Error findUnique pegawai:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengambil data pegawai",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import pegawaiFindMany from "./findMany";
|
||||
import pegawaiFindUnique from "./findUnique";
|
||||
import pegawaiCreate from "./create";
|
||||
import pegawaiDelete from "./del";
|
||||
import pegawaiUpdate from "./updt";
|
||||
|
||||
const Pegawai = new Elysia({
|
||||
prefix: "/pegawai",
|
||||
tags: ["Ekonomi/Struktur Organisasi/Pegawai"],
|
||||
})
|
||||
|
||||
// ✅ Find all
|
||||
.get("/find-many", pegawaiFindMany)
|
||||
|
||||
// ✅ Find by ID
|
||||
.get("/:id", async (context) => {
|
||||
const response = await pegawaiFindUnique(context);
|
||||
return response;
|
||||
})
|
||||
|
||||
// ✅ Create
|
||||
.post("/create", pegawaiCreate, {
|
||||
body: t.Object({
|
||||
namaLengkap: t.String(),
|
||||
gelarAkademik: t.Optional(t.String()),
|
||||
imageId: t.String(),
|
||||
tanggalMasuk: t.Optional(t.String()), // ISO string (YYYY-MM-DD)
|
||||
email: t.Optional(t.String()),
|
||||
telepon: t.Optional(t.String()),
|
||||
alamat: t.Optional(t.String()),
|
||||
posisiId: t.String(),
|
||||
}),
|
||||
})
|
||||
|
||||
// ✅ Update
|
||||
.put(
|
||||
"/:id",
|
||||
async (context) => {
|
||||
const response = await pegawaiUpdate(context);
|
||||
return response;
|
||||
},
|
||||
{
|
||||
body: t.Object({
|
||||
id: t.String(),
|
||||
namaLengkap: t.Optional(t.String()),
|
||||
gelarAkademik: t.Optional(t.String()),
|
||||
imageId: t.String(),
|
||||
tanggalMasuk: t.Optional(t.String()),
|
||||
email: t.Optional(t.String()),
|
||||
telepon: t.Optional(t.String()),
|
||||
alamat: t.Optional(t.String()),
|
||||
posisiId: t.Optional(t.String()),
|
||||
aktif: t.Optional(t.Boolean()),
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
// ✅ Delete
|
||||
.delete("/del/:id", pegawaiDelete);
|
||||
|
||||
export default Pegawai;
|
||||
@@ -0,0 +1,67 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormUpdatePegawai = {
|
||||
id: string;
|
||||
namaLengkap?: string;
|
||||
gelarAkademik?: string;
|
||||
imageId: string;
|
||||
tanggalMasuk?: string;
|
||||
email?: string;
|
||||
telepon?: string;
|
||||
alamat?: string;
|
||||
posisiId?: string;
|
||||
isActive?: boolean;
|
||||
};
|
||||
|
||||
export default async function pegawaiUpdate(context: Context) {
|
||||
const body = await context.body as FormUpdatePegawai;
|
||||
|
||||
if (!body?.id) {
|
||||
return {
|
||||
success: false,
|
||||
message: "ID pegawai wajib diisi",
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof body.isActive !== 'boolean') {
|
||||
body.isActive = true; // fallback
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const updated = await prisma.pegawai.update({
|
||||
where: { id: body.id },
|
||||
data: {
|
||||
namaLengkap: body.namaLengkap,
|
||||
gelarAkademik: body.gelarAkademik,
|
||||
imageId: body.imageId,
|
||||
tanggalMasuk: body.tanggalMasuk ? new Date(body.tanggalMasuk) : undefined,
|
||||
email: body.email,
|
||||
telepon: body.telepon,
|
||||
alamat: body.alamat,
|
||||
posisiId: body.posisiId,
|
||||
isActive: body.isActive ?? true, // default true kalau tidak dikirim
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
include: {
|
||||
image: true,
|
||||
posisi: true,
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Pegawai berhasil diperbarui",
|
||||
data: updated,
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error("Error update pegawai:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal memperbarui data pegawai",
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormCreate = {
|
||||
nama: string;
|
||||
deskripsi: string;
|
||||
hierarki: number;
|
||||
}
|
||||
|
||||
export default async function posisiOrganisasiCreate(context: Context) {
|
||||
const body = context.body as FormCreate;
|
||||
|
||||
if(!body) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Body is required",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const posisiOrganisasi = await prisma.posisiOrganisasi.create({
|
||||
data: {
|
||||
nama: body.nama,
|
||||
deskripsi: body.deskripsi,
|
||||
hierarki: body.hierarki,
|
||||
},
|
||||
});
|
||||
return {
|
||||
success: true,
|
||||
message: "Success create posisi organisasi",
|
||||
data: posisiOrganisasi
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error creating PosisiOrganisasi:", error);
|
||||
throw new Error("Failed to create PosisiOrganisasi: " + (error as Error).message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function posisiOrganisasiDelete(context: Context) {
|
||||
const { id } = context.params as { id: string };
|
||||
|
||||
if (!id) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "ID wajib diisi",
|
||||
}),
|
||||
{ status: 400, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if the position exists first
|
||||
const existing = await prisma.posisiOrganisasi.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Posisi organisasi tidak ditemukan",
|
||||
}),
|
||||
{ status: 404, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
// Check if there are any pegawai associated with this position
|
||||
const pegawaiCount = await prisma.pegawai.count({
|
||||
where: { posisiId: id },
|
||||
});
|
||||
|
||||
if (pegawaiCount > 0) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Tidak dapat menghapus posisi yang masih memiliki pegawai",
|
||||
}),
|
||||
{ status: 400, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
// Check if this position is used in any hubungan organisasi
|
||||
const hubunganCount = await prisma.hubunganOrganisasi.count({
|
||||
where: {
|
||||
OR: [
|
||||
{ atasanId: id },
|
||||
{ bawahanId: id },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
if (hubunganCount > 0) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Tidak dapat menghapus posisi yang masih terdaftar dalam struktur organisasi",
|
||||
}),
|
||||
{ status: 400, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
// If all checks pass, delete the position
|
||||
const deleted = await prisma.posisiOrganisasi.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
message: "Posisi organisasi berhasil dihapus",
|
||||
data: deleted,
|
||||
}),
|
||||
{ status: 200, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error delete posisi organisasi:", error);
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
success: false,
|
||||
message: "Terjadi kesalahan saat menghapus posisi organisasi",
|
||||
}),
|
||||
{ status: 500, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export default async function posisiOrganisasiFindMany() {
|
||||
const data = await prisma.posisiOrganisasi.findMany();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil mengambil semua data posisi organisasi",
|
||||
data: data.map((item: any) => ({
|
||||
id: item.id,
|
||||
nama: item.nama,
|
||||
deskripsi: item.deskripsi,
|
||||
hierarki: item.hierarki,
|
||||
})),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function posisiOrganisasiFindUnique(context: Context) {
|
||||
const url = new URL(context.request.url);
|
||||
const pathSegments = url.pathname.split('/');
|
||||
const id = pathSegments[pathSegments.length - 1];
|
||||
|
||||
if (!id) {
|
||||
return {
|
||||
success: false,
|
||||
message: "ID is required",
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (typeof id !== 'string') {
|
||||
return {
|
||||
success: false,
|
||||
message: "ID is required",
|
||||
}
|
||||
}
|
||||
|
||||
const data = await prisma.posisiOrganisasi.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
return {
|
||||
success: false,
|
||||
message: "Posisi organisasi tidak ditemukan",
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success find posisi organisasi",
|
||||
data,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Find by ID error:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal mengambil posisi organisasi: " + (error instanceof Error ? error.message : 'Unknown error'),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import posisiOrganisasiFindMany from "./findMany";
|
||||
import posisiOrganisasiFindUnique from "./findUnique";
|
||||
import posisiOrganisasiCreate from "./create";
|
||||
import posisiOrganisasiUpdate from "./updt";
|
||||
import posisiOrganisasiDelete from "./del";
|
||||
|
||||
const PosisiOrganisasi = new Elysia({
|
||||
prefix: "/posisi-organisasi",
|
||||
tags: ["Ekonomi/Struktur Organisasi/Posisi Organisasi"],
|
||||
})
|
||||
|
||||
.get("/find-many", posisiOrganisasiFindMany)
|
||||
.get("/:id", async (context) => {
|
||||
const response = await posisiOrganisasiFindUnique(context);
|
||||
return response;
|
||||
})
|
||||
.post("/create", posisiOrganisasiCreate, {
|
||||
body: t.Object({
|
||||
nama: t.String(),
|
||||
deskripsi: t.String(),
|
||||
hierarki: t.Number(),
|
||||
}),
|
||||
})
|
||||
.put("/:id", async (context) => {
|
||||
const response = await posisiOrganisasiUpdate(context);
|
||||
return response;
|
||||
}, {
|
||||
body: t.Object({
|
||||
nama: t.String(),
|
||||
deskripsi: t.String(),
|
||||
hierarki: t.Number(),
|
||||
}),
|
||||
})
|
||||
.delete("/del/:id", posisiOrganisasiDelete);
|
||||
|
||||
export default PosisiOrganisasi;
|
||||
@@ -0,0 +1,49 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormUpdate = {
|
||||
id: string;
|
||||
nama: string;
|
||||
deskripsi: string;
|
||||
hierarki: number;
|
||||
};
|
||||
|
||||
export default async function posisiOrganisasiUpdate(context: Context) {
|
||||
const body = context.body as FormUpdate;
|
||||
const id = context.params?.id as string;
|
||||
|
||||
if (!id) {
|
||||
return {
|
||||
success: false,
|
||||
message: "ID is required",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
await prisma.posisiOrganisasi.update({
|
||||
where: { id },
|
||||
data: {
|
||||
nama: body.nama,
|
||||
deskripsi: body.deskripsi,
|
||||
hierarki: body.hierarki,
|
||||
},
|
||||
});
|
||||
|
||||
const updated = await prisma.posisiOrganisasi.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Success update posisi organisasi",
|
||||
data: updated,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Update error:", error);
|
||||
return {
|
||||
success: false,
|
||||
message: "Gagal update posisi organisasi",
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user