Compare commits

...

16 Commits

Author SHA1 Message Date
8a275c2a32 Fix Tampilan Data Kesehatan Warga User 2025-08-22 16:30:35 +08:00
8469ebd2e1 Sinkronisasi UI & API Admin - User Submenu Demografi Pekerjaan, Menu Ekonomi 2025-08-22 01:10:18 +08:00
760ba4b6d2 Sinkronisasi Sinkronisasi UI & API Admin - User Submenu Program Kemiskinan 2025-08-22 00:26:58 +08:00
20d4c90e60 Sinkronisasi UI & API Admin - User Submenu Jumlah Pengangguran - Jumlah Penduduk Miskin 2025-08-21 17:15:06 +08:00
fafbb12a08 Sinkronisasi UI & API Admin - User Submenu Pendapatan Asli Desa 2025-08-21 14:50:23 +08:00
01aa0da5cc Fix admin menu Landing page 2025-08-21 10:16:05 +08:00
b580978f8e Fix eror edit admin lowongan kerja 2025-08-20 17:52:10 +08:00
1c01397c0d Sinkronisasi UI & API Admin - User Submenu lowongan kerja lokal, Menu Ekonomi 2025-08-20 17:17:04 +08:00
90a6605efd Sinkronisasi UI & API Admin - User Submenu Pasar Desa, Menu Ekonomi 2025-08-20 17:01:20 +08:00
c22d865283 Sinkronisasi UI & API Admin - User Submenu Tips Keamanan, Menu Keamanan 2025-08-20 14:35:08 +08:00
49067f0218 Add Detail Semua Polsek, Submenu Polsek Terdekat, Menu Keamanan 2025-08-19 17:40:59 +08:00
d79425d529 Sinkronisasi UI & API Menu Keamanan, Admin - User Submenu Keamanan Lingkungan & Polse Terdekat 2025-08-19 11:12:39 +08:00
4491d23bea Sinkronisasi UI & API Admin - User Submenu Penanganan Darurat, User Submenu Kontak Darurat & User Submenu Info Wabah / Penyakit 2025-08-18 20:56:18 +08:00
1e154ced86 Sinkronisasi UI & API Admin - User Submenu Program Kesehatan 2025-08-18 17:14:33 +08:00
bcc51aec12 Sinkronisasi UI & API Admin - User Submenu Data Kesehatan Warga 2025-08-18 15:01:39 +08:00
8d15563f15 Sinkronisasi UI & API Admin - User Submenu Data Kesehatan Warga
-Dibagian Tanggal Gak Auto Ngambil Tanggal Yang Udah Dipakai
-Dibagian fasilitas kesehatan : data dokter dan tarif rencananya mau pakai select
2025-08-15 14:07:56 +08:00
157 changed files with 8350 additions and 4262 deletions

View File

@@ -223,7 +223,7 @@ model KategoriPrestasiDesa {
model Responden {
id String @id @default(cuid())
name String @unique
tanggal DateTime // misal: 2025-05-01
tanggal String // misal: 2025-05-01
jenisKelamin JenisKelaminResponden @relation(fields: [jenisKelaminId], references: [id])
jenisKelaminId String
rating PilihanRatingResponden @relation(fields: [ratingId], references: [id])
@@ -292,6 +292,9 @@ model PosisiOrganisasiPPID {
pegawai PegawaiPPID[]
strukturOrganisasi StrukturPPID[] // Relasi balik
parentId String?
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
parent PosisiOrganisasiPPID? @relation("Parent", fields: [parentId], references: [id])
children PosisiOrganisasiPPID[] @relation("Parent")
}
@@ -951,13 +954,16 @@ model Kematian {
// ========================================= GRAFIK KEPUASAN ========================================= //
model GrafikKepuasan {
id String @id @default(cuid())
label String
jumlah String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
id String @id @default(cuid())
nama String
tanggal DateTime
jenisKelamin String
alamat String
penyakit String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
// ========================================= ARTIKEL KESEHATAN ========================================= //
@@ -1544,7 +1550,7 @@ model DataDemografiPekerjaan {
model DetailDataPengangguran {
id String @id @default(uuid()) @db.Uuid
month String @db.VarChar(20)
year Int
year DateTime
totalUnemployment Int
educatedUnemployment Int
uneducatedUnemployment Int

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -61,13 +62,39 @@ const lowonganKerjaState = proxy({
findMany: {
data: null as
| Prisma.LowonganPekerjaanGetPayload<{
omit: { isActive: true };
omit: {
isActive: true;
};
}>[]
| null,
async load() {
const res = await ApiFetch.api.ekonomi.lowongankerja["find-many"].get();
if (res.status === 200) {
lowonganKerjaState.findMany.data = res.data?.data ?? [];
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
lowonganKerjaState.findMany.loading = true; // ✅ Akses langsung via nama path
lowonganKerjaState.findMany.page = page;
lowonganKerjaState.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.ekonomi.lowongankerja["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
lowonganKerjaState.findMany.data = res.data.data ?? [];
lowonganKerjaState.findMany.totalPages = res.data.totalPages ?? 1;
} else {
lowonganKerjaState.findMany.data = [];
lowonganKerjaState.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch lowongan kerja paginated:", err);
lowonganKerjaState.findMany.data = [];
lowonganKerjaState.findMany.totalPages = 1;
} finally {
lowonganKerjaState.findMany.loading = false;
}
},
},

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -53,22 +54,47 @@ const pasarDesa = proxy({
},
},
findMany: {
data: null as Array<
Prisma.PasarDesaGetPayload<{
include: {
image: true;
KategoriToPasar: {
include: {
kategori: true;
data: null as
| Prisma.PasarDesaGetPayload<{
include: {
image: true;
KategoriToPasar: {
include: {
kategori: true;
};
};
};
};
}>
> | null,
async load() {
const res = await ApiFetch.api.ekonomi.pasardesa["find-many"].get();
if (res.status === 200) {
pasarDesa.findMany.data = res.data?.data ?? [];
}>[]
| null,
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "", categoryId?: string) => {
pasarDesa.findMany.loading = true;
pasarDesa.findMany.page = page;
pasarDesa.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
if (categoryId) query.categoryId = categoryId;
const res = await ApiFetch.api.ekonomi.pasardesa["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
pasarDesa.findMany.data = res.data.data ?? [];
pasarDesa.findMany.totalPages = res.data.totalPages ?? 1;
} else {
pasarDesa.findMany.data = [];
pasarDesa.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch keamanan lingkungan paginated:", err);
pasarDesa.findMany.data = [];
pasarDesa.findMany.totalPages = 1;
} finally {
pasarDesa.findMany.loading = false;
}
},
},
@@ -272,14 +298,41 @@ const kategoriProduk = proxy({
},
},
findMany: {
data: null as Array<{
id: string;
nama: string;
}> | null,
async load() {
const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many"].get();
if (res.status === 200) {
kategoriProduk.findMany.data = res.data?.data ?? [];
data: null as
| Prisma.KategoriProdukGetPayload<{
omit: {
isActive: true;
};
}>[]
| null,
page: 1,
totalPages: 1,
loading: false,
search2: "",
load: async (page = 1, limit = 10, search2 = "") => {
kategoriProduk.findMany.loading = true; // ✅ Akses langsung via nama path
kategoriProduk.findMany.page = page;
kategoriProduk.findMany.search2 = search2;
try {
const query: any = { page, limit };
if (search2) query.search2 = search2;
const res = await ApiFetch.api.ekonomi.kategoriproduk["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
kategoriProduk.findMany.data = res.data.data ?? [];
kategoriProduk.findMany.totalPages = res.data.totalPages ?? 1;
} else {
kategoriProduk.findMany.data = [];
kategoriProduk.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch kategori produk paginated:", err);
kategoriProduk.findMany.data = [];
kategoriProduk.findMany.totalPages = 1;
} finally {
kategoriProduk.findMany.loading = false;
}
},
},

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -11,8 +12,7 @@ const templateForm = z.object({
statistik: z.object({
tahun: z.string().min(1, "Tahun minimal 1 karakter"),
jumlah: z.string().min(1, "Jumlah minimal 1 karakter"),
})
}),
});
const defaultForm = {
@@ -21,8 +21,8 @@ const defaultForm = {
ikonUrl: "",
statistik: {
tahun: "",
jumlah: ""
}
jumlah: "",
},
};
const programKemiskinanState = proxy({
@@ -64,12 +64,35 @@ const programKemiskinanState = proxy({
};
}>[],
loading: false,
async load() {
const res = await ApiFetch.api.ekonomi.programkemiskinan[
"find-many"
].get();
if (res.status === 200) {
programKemiskinanState.findMany.data = res.data?.data ?? [];
page: 1,
totalPages: 1,
search: "",
load: async (page = 1, limit = 10, search = "") => {
programKemiskinanState.findMany.loading = true; // ✅ Akses langsung via nama path
programKemiskinanState.findMany.page = page;
programKemiskinanState.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.ekonomi.programkemiskinan[
"find-many"
].get({ query });
if (res.status === 200 && res.data?.success) {
programKemiskinanState.findMany.data = res.data.data ?? [];
programKemiskinanState.findMany.totalPages = res.data.totalPages ?? 1;
} else {
programKemiskinanState.findMany.data = [];
programKemiskinanState.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch program kemiskinan paginated:", err);
programKemiskinanState.findMany.data = [];
programKemiskinanState.findMany.totalPages = 1;
} finally {
programKemiskinanState.findMany.loading = false;
}
},
},

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -53,15 +54,39 @@ const keamananLingkunganState = proxy({
findMany: {
data: null as
| Prisma.KeamananLingkunganGetPayload<{
include: { image: true };
include: {
image: true;
};
}>[]
| null,
async load() {
const res = await ApiFetch.api.keamanan.keamananlingkungan[
"find-many"
].get();
if (res.status === 200) {
keamananLingkunganState.findMany.data = res.data?.data ?? [];
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
keamananLingkunganState.findMany.loading = true; // ✅ Akses langsung via nama path
keamananLingkunganState.findMany.page = page;
keamananLingkunganState.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.keamanan.keamananlingkungan["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
keamananLingkunganState.findMany.data = res.data.data ?? [];
keamananLingkunganState.findMany.totalPages = res.data.totalPages ?? 1;
} else {
keamananLingkunganState.findMany.data = [];
keamananLingkunganState.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch keamanan lingkungan paginated:", err);
keamananLingkunganState.findMany.data = [];
keamananLingkunganState.findMany.totalPages = 1;
} finally {
keamananLingkunganState.findMany.loading = false;
}
},
},

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -63,13 +64,41 @@ const polsekTerdekatState = proxy({
findMany: {
data: null as
| Prisma.PolsekTerdekatGetPayload<{
include: { layananPolsek: true };
include: {
layananPolsek: true;
};
}>[]
| null,
async load() {
const res = await ApiFetch.api.keamanan.polsekterdekat["find-many"].get();
if (res.status === 200) {
polsekTerdekatState.findMany.data = res.data?.data ?? [];
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
polsekTerdekatState.findMany.loading = true; // ✅ Akses langsung via nama path
polsekTerdekatState.findMany.page = page;
polsekTerdekatState.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.keamanan.polsekterdekat["find-many"].get(
{ query }
);
if (res.status === 200 && res.data?.success) {
polsekTerdekatState.findMany.data = res.data.data ?? [];
polsekTerdekatState.findMany.totalPages = res.data.totalPages ?? 1;
} else {
polsekTerdekatState.findMany.data = [];
polsekTerdekatState.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch polsek terdekat paginated:", err);
polsekTerdekatState.findMany.data = [];
polsekTerdekatState.findMany.totalPages = 1;
} finally {
polsekTerdekatState.findMany.loading = false;
}
},
},
@@ -237,6 +266,29 @@ const polsekTerdekatState = proxy({
polsekTerdekatState.edit.form = { ...defaultForm };
},
},
findFirst: {
data: null as Prisma.PolsekTerdekatGetPayload<{
include: {
layananPolsek: true;
};
}> | null,
loading: false,
load: async () => { // Changed to arrow function
polsekTerdekatState.findFirst.loading = true;
try {
const res = await ApiFetch.api.keamanan.polsekterdekat["find-first"].get();
if (res.status === 200 && res.data?.success) {
polsekTerdekatState.findFirst.data = res.data.data || null;
} else {
polsekTerdekatState.findFirst.data = null;
}
} catch (err) {
console.error("Gagal fetch polsek terdekat terbaru:", err);
} finally {
polsekTerdekatState.findFirst.loading = false;
}
}
}
});
export default polsekTerdekatState;

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -53,15 +54,39 @@ const tipsKeamananState = proxy({
findMany: {
data: null as
| Prisma.MenuTipsKeamananGetPayload<{
include: { image: true };
include: {
image: true;
};
}>[]
| null,
async load() {
const res = await ApiFetch.api.keamanan.menutipskeamanan[
"find-many"
].get();
if (res.status === 200) {
tipsKeamananState.findMany.data = res.data?.data ?? [];
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
tipsKeamananState.findMany.loading = true; // ✅ Akses langsung via nama path
tipsKeamananState.findMany.page = page;
tipsKeamananState.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.keamanan.menutipskeamanan["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
tipsKeamananState.findMany.data = res.data.data ?? [];
tipsKeamananState.findMany.totalPages = res.data.totalPages ?? 1;
} else {
tipsKeamananState.findMany.data = [];
tipsKeamananState.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch menu tips keamanan paginated:", err);
tipsKeamananState.findMany.data = [];
tipsKeamananState.findMany.totalPages = 1;
} finally {
tipsKeamananState.findMany.loading = false;
}
},
},

View File

@@ -5,6 +5,7 @@ import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
//fasilitas kesehatan aja
// Validasi form
const templateForm = z.object({
name: z.string().min(1, "Nama harus diisi"),
@@ -61,7 +62,7 @@ const defaultForm = {
},
};
const fasilitasKesehatanState = proxy({
const fasilitasKesehatan = proxy({
create: {
form: { ...defaultForm },
loading: false,
@@ -86,7 +87,7 @@ const fasilitasKesehatanState = proxy({
if (res.status === 200) {
toast.success("Berhasil menambahkan fasilitas kesehatan");
this.resetForm();
await fasilitasKesehatanState.findMany.load();
await fasilitasKesehatan.findMany.load();
return res.data;
}
} catch (err: any) {
@@ -102,7 +103,6 @@ const fasilitasKesehatanState = proxy({
this.form = { ...defaultForm };
},
},
findMany: {
data: null as
| Prisma.FasilitasKesehatanGetPayload<{
@@ -156,7 +156,7 @@ const fasilitasKesehatanState = proxy({
const res = await fetch(`/api/kesehatan/fasilitas-kesehatan/${id}`);
if (res.ok) {
const data = await res.json();
fasilitasKesehatanState.findUnique.data = data.data ?? null;
fasilitasKesehatan.findUnique.data = data.data ?? null;
} else {
toast.error("Gagal load data fasilitas kesehatan");
}
@@ -176,8 +176,8 @@ const fasilitasKesehatanState = proxy({
const result = await res.json();
const data = result.data;
fasilitasKesehatanState.edit.id = data.id;
fasilitasKesehatanState.edit.form = {
fasilitasKesehatan.edit.id = data.id;
fasilitasKesehatan.edit.form = {
name: data.name,
informasiUmum: {
fasilitas: data.informasiumum.fasilitas,
@@ -205,7 +205,7 @@ const fasilitasKesehatanState = proxy({
};
},
async submit() {
const cek = templateForm.safeParse(fasilitasKesehatanState.edit.form);
const cek = templateForm.safeParse(fasilitasKesehatan.edit.form);
if (!cek.success) {
const errMsg = cek.error.issues
.map((v) => `${v.path.join(".")}: ${v.message}`)
@@ -215,42 +215,38 @@ const fasilitasKesehatanState = proxy({
}
try {
fasilitasKesehatanState.edit.loading = true;
fasilitasKesehatan.edit.loading = true;
const payload = {
name: fasilitasKesehatanState.edit.form.name,
name: fasilitasKesehatan.edit.form.name,
informasiUmum: {
fasilitas:
fasilitasKesehatanState.edit.form.informasiUmum.fasilitas,
alamat: fasilitasKesehatanState.edit.form.informasiUmum.alamat,
fasilitas: fasilitasKesehatan.edit.form.informasiUmum.fasilitas,
alamat: fasilitasKesehatan.edit.form.informasiUmum.alamat,
jamOperasional:
fasilitasKesehatanState.edit.form.informasiUmum.jamOperasional,
fasilitasKesehatan.edit.form.informasiUmum.jamOperasional,
},
layananUnggulan: {
content: fasilitasKesehatanState.edit.form.layananUnggulan.content,
content: fasilitasKesehatan.edit.form.layananUnggulan.content,
},
dokterdanTenagaMedis: {
name: fasilitasKesehatanState.edit.form.dokterdanTenagaMedis.name,
name: fasilitasKesehatan.edit.form.dokterdanTenagaMedis.name,
specialist:
fasilitasKesehatanState.edit.form.dokterdanTenagaMedis.specialist,
jadwal:
fasilitasKesehatanState.edit.form.dokterdanTenagaMedis.jadwal,
fasilitasKesehatan.edit.form.dokterdanTenagaMedis.specialist,
jadwal: fasilitasKesehatan.edit.form.dokterdanTenagaMedis.jadwal,
},
fasilitasPendukung: {
content:
fasilitasKesehatanState.edit.form.fasilitasPendukung.content,
content: fasilitasKesehatan.edit.form.fasilitasPendukung.content,
},
prosedurPendaftaran: {
content:
fasilitasKesehatanState.edit.form.prosedurPendaftaran.content,
content: fasilitasKesehatan.edit.form.prosedurPendaftaran.content,
},
tarifDanLayanan: {
layanan: fasilitasKesehatanState.edit.form.tarifDanLayanan.layanan,
tarif: fasilitasKesehatanState.edit.form.tarifDanLayanan.tarif,
layanan: fasilitasKesehatan.edit.form.tarifDanLayanan.layanan,
tarif: fasilitasKesehatan.edit.form.tarifDanLayanan.tarif,
},
};
const res = await fetch(
`/api/kesehatan/fasilitas-kesehatan/${fasilitasKesehatanState.edit.id}`,
`/api/kesehatan/fasilitas-kesehatan/${fasilitasKesehatan.edit.id}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
@@ -264,7 +260,7 @@ const fasilitasKesehatanState = proxy({
}
toast.success("Berhasil update fasilitas kesehatan");
await fasilitasKesehatanState.findMany.load();
await fasilitasKesehatan.findMany.load();
return true;
} catch (err) {
toast.error(
@@ -272,37 +268,297 @@ const fasilitasKesehatanState = proxy({
);
return false;
} finally {
fasilitasKesehatanState.edit.loading = false;
fasilitasKesehatan.edit.loading = false;
}
},
resetForm() {
fasilitasKesehatanState.edit.id = "";
fasilitasKesehatanState.edit.form = { ...defaultForm };
fasilitasKesehatan.edit.id = "";
fasilitasKesehatan.edit.form = { ...defaultForm };
},
},
delete: {
loading: false,
async byId(id: string){
async byId(id: string) {
try {
fasilitasKesehatanState.delete.loading = true;
const res = await fetch(`/api/kesehatan/fasilitas-kesehatan/del/${id}`, {
method: "DELETE",
});
fasilitasKesehatan.delete.loading = true;
const res = await fetch(
`/api/kesehatan/fasilitas-kesehatan/del/${id}`,
{
method: "DELETE",
}
);
const result = await res.json();
if (res.ok && result.success) {
toast.success("Fasilitas kesehatan berhasil dihapus");
await fasilitasKesehatanState.findMany.load();
await fasilitasKesehatan.findMany.load();
} else {
toast.error(result.message || "Gagal menghapus");
}
} catch {
toast.error("Terjadi kesalahan saat menghapus");
} finally {
fasilitasKesehatanState.delete.loading = false;
fasilitasKesehatan.delete.loading = false;
}
}
},
},
});
//dokter & tenaga medis
const templateDokterForm = z.object({
name: z.string().min(1, "Nama tidak boleh kosong"),
specialist: z.string().min(1, "Spesialis tidak boleh kosong"),
jadwal: z.string().min(1, "Jadwal tidak boleh kosong"),
});
const defaultDokterForm = {
name: "",
specialist: "",
jadwal: "",
};
const dokter = proxy({
create: {
create: {
form: defaultDokterForm,
loading: false,
async create() {
const cek = templateDokterForm.safeParse(dokter.create.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return null;
}
try {
dokter.create.create.loading = true;
const res = await ApiFetch.api.kesehatan.doktertenagamedis[
"create"
].post(dokter.create.create.form);
if (res.status === 200) {
const id = res.data?.data;
if (id) {
toast.success("Success create");
dokter.create.create.form = { ...defaultDokterForm };
dokter.findMany.load();
return id;
}
}
toast.error("failed create");
return null;
} catch (error) {
console.log((error as Error).message);
return null;
} finally {
dokter.create.create.loading = false;
}
},
},
},
findMany: {
data: null as
| Prisma.DokterdanTenagaMedisGetPayload<{
omit: {
isActive: true;
};
}>[]
| null,
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
dokter.findMany.loading = true; // ✅ Akses langsung via nama path
dokter.findMany.page = page;
dokter.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.kesehatan.doktertenagamedis[
"findMany"
].get({ query });
if (res.status === 200 && res.data?.success) {
dokter.findMany.data = res.data.data ?? [];
dokter.findMany.totalPages = res.data.totalPages ?? 1;
} else {
dokter.findMany.data = [];
dokter.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch dokter tenaga medis paginated:", err);
dokter.findMany.data = [];
dokter.findMany.totalPages = 1;
} finally {
dokter.findMany.loading = false;
}
},
},
findUnique: {
data: null as Prisma.DokterdanTenagaMedisGetPayload<{
omit: { isActive: true };
}> | null,
async load(id: string) {
try {
const res = await fetch(`/api/kesehatan/doktertenagamedis/${id}`);
if (res.ok) {
const data = await res.json();
dokter.findUnique.data = data.data ?? null;
} else {
console.error(
"Failed to fetch dokter dan tenaga medis",
res.statusText
);
dokter.findUnique.data = null;
}
} catch (error) {
console.error("Error fetching dokter dan tenaga medis", error);
dokter.findUnique.data = null;
}
},
},
update: {
id: "",
form: { ...defaultDokterForm },
loading: false,
async load(id: string) {
if (!id) {
toast.warn("ID tidak valid");
return null;
}
try {
const response = await fetch(`/api/kesehatan/doktertenagamedis/${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 = {
name: data.name,
specialist: data.specialist,
jadwal: data.jadwal,
};
return data; // Return the loaded data
} else {
throw new Error(result?.message || "Gagal memuat data");
}
} catch (error) {
console.error("Error loading dokter dan tenaga medis:", error);
toast.error(
error instanceof Error ? error.message : "Gagal memuat data"
);
return null;
}
},
async submit() {
const id = this.id;
if (!id) {
toast.warn("ID tidak valid");
return null;
}
const formData = {
name: this.form.name,
specialist: this.form.specialist,
jadwal: this.form.jadwal,
};
const cek = templateDokterForm.safeParse(formData);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return null;
}
try {
this.loading = true;
const res = await fetch(`/api/kesehatan/doktertenagamedis/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(formData),
});
const result = await res.json();
if (!res.ok || !result?.success) {
throw new Error(result?.message || "Gagal update data");
}
toast.success("Berhasil update data!");
await dokter.findMany.load();
return result.data;
} catch (error) {
console.error("Update error:", error);
toast.error("Gagal update data dokter dan tenaga medis");
throw error;
} finally {
this.loading = false;
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) {
return toast.warn("ID tidak valid");
}
try {
dokter.delete.loading = true;
const response = await fetch(
`/api/kesehatan/doktertenagamedis/del/${id}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
}
);
const result = await response.json();
if (response.ok && result?.success) {
toast.success(
result.message || "Dokter dan tenaga medis berhasil dihapus"
);
await dokter.findMany.load(); // refresh list
} else {
toast.error(
result?.message || "Gagal menghapus dokter dan tenaga medis"
);
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus dokter dan tenaga medis");
} finally {
dokter.delete.loading = false;
}
},
},
});
const fasilitasKesehatanState = proxy({
fasilitasKesehatan,
dokter
});
export default fasilitasKesehatanState;

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -5,20 +6,19 @@ import { proxy } from "valtio";
import { z } from "zod";
const templateGrafikKepuasan = z.object({
label: z.string().min(2, "Label harus diisi"),
jumlah: z.string().min(1, "Jumlah harus diisi"),
nama: z.string().min(2, "Nama harus diisi"),
tanggal: z.string().min(1, "Tanggal harus diisi"),
jenisKelamin: z.string().min(1, "Jenis kelamin harus diisi"),
alamat: z.string().min(1, "Alamat harus diisi"),
penyakit: z.string().min(1, "Penyakit harus diisi"),
});
type GrafikKepuasan = Prisma.GrafikKepuasanGetPayload<{
select: {
label: true;
jumlah: true;
};
}>;
const defaultForm: GrafikKepuasan = {
label: "",
jumlah: ""
const defaultForm = {
nama: "",
tanggal: "",
jenisKelamin: "",
alamat: "",
penyakit: "",
};
const grafikkepuasan = proxy({
@@ -36,16 +36,15 @@ const grafikkepuasan = proxy({
}
try {
grafikkepuasan.create.loading = true;
const res = await ApiFetch.api.kesehatan.grafikkepuasan["create"].post(grafikkepuasan.create.form);
const res = await ApiFetch.api.kesehatan.grafikkepuasan["create"].post(
grafikkepuasan.create.form
);
if (res.status === 200) {
const id = res.data?.data?.id;
const id = res.data?.data;
if (id) {
toast.success("Success create");
grafikkepuasan.create.form = {
label: "",
jumlah: "",
};
grafikkepuasan.create.form = { ...defaultForm };
grafikkepuasan.findMany.load();
return id;
}
@@ -62,21 +61,49 @@ const grafikkepuasan = proxy({
},
findMany: {
data: null as
| Prisma.GrafikKepuasanGetPayload<{ omit: { isActive: true } }>[]
| Prisma.GrafikKepuasanGetPayload<{
omit: {
isActive: true;
};
}>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.grafikkepuasan[
"find-many"
].get();
if (res.status === 200) {
grafikkepuasan.findMany.data = res.data?.data ?? [];
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
grafikkepuasan.findMany.loading = true; // ✅ Akses langsung via nama path
grafikkepuasan.findMany.page = page;
grafikkepuasan.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.kesehatan.grafikkepuasan[
"find-many"
].get({ query });
if (res.status === 200 && res.data?.success) {
grafikkepuasan.findMany.data = res.data.data ?? [];
grafikkepuasan.findMany.totalPages = res.data.totalPages ?? 1;
} else {
grafikkepuasan.findMany.data = [];
grafikkepuasan.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch berita paginated:", err);
grafikkepuasan.findMany.data = [];
grafikkepuasan.findMany.totalPages = 1;
} finally {
grafikkepuasan.findMany.loading = false;
}
},
},
findUnique: {
data: null as Prisma.GrafikKepuasanGetPayload<{
omit: { isActive: true }
}> | null,
omit: { isActive: true };
}> | null,
async load(id: string) {
try {
const res = await fetch(`/api/kesehatan/grafikkepuasan/${id}`);
@@ -95,88 +122,137 @@ const grafikkepuasan = proxy({
},
update: {
id: "",
form: {...defaultForm},
form: { ...defaultForm },
loading: false,
async byId() {
},
async submit() {
const id = this.id;
if (!id) {
toast.warn("ID tidak valid");
return null;
}
const cek = templateGrafikKepuasan.safeParse(grafikkepuasan.update.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/kesehatan/grafikkepuasan/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(this.form),
});
const result = await response.json();
if (!response.ok || !result?.success) {
throw new Error(result?.message || "Gagal update data");
async load(id: string) {
if (!id) {
toast.warn("ID tidak valid");
return null;
}
toast.success("Berhasil update data!");
try {
const response = await fetch(`/api/kesehatan/grafikkepuasan/${id}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
// ✅ Optional: refresh list kalau kamu langsung ke halaman list
await grafikkepuasan.findMany.load();
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return result.data;
} catch (error) {
console.error("Error update data:", error);
toast.error("Gagal update data grafik kepuasan");
} finally {
this.loading = false;
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) {
return toast.warn("ID tidak valid");
}
try {
grafikkepuasan.delete.loading = true;
const result = await response.json();
const response = await fetch(`/api/kesehatan/grafikkepuasan/del/${id}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
const result = await response.json();
if (response.ok && result?.success) {
toast.success(
result.message || "Grafik kepuasan berhasil dihapus"
);
await grafikkepuasan.findMany.load(); // refresh list
} else {
if (result?.success) {
const data = result.data;
this.id = data.id;
this.form = {
nama: data.nama,
tanggal: data.tanggal,
jenisKelamin: data.jenisKelamin,
alamat: data.alamat,
penyakit: data.penyakit,
};
return data; // Return the loaded data
} else {
throw new Error(result?.message || "Gagal memuat data");
}
} catch (error) {
console.error("Error loading grafik kepuasan:", error);
toast.error(
result?.message || "Gagal menghapus grafik kepuasan"
error instanceof Error ? error.message : "Gagal memuat data"
);
return null;
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus grafik kepuasan");
} finally {
grafikkepuasan.delete.loading = false;
}
}
}
},
async submit() {
const id = this.id;
if (!id) {
toast.warn("ID tidak valid");
return null;
}
const formData = {
nama: this.form.nama,
tanggal: this.form.tanggal,
jenisKelamin: this.form.jenisKelamin,
alamat: this.form.alamat,
penyakit: this.form.penyakit,
};
const cek = templateGrafikKepuasan.safeParse(formData);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return null;
}
try {
this.loading = true;
const res = await fetch(`/api/kesehatan/grafikkepuasan/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(formData),
});
const result = await res.json();
if (!res.ok || !result?.success) {
throw new Error(result?.message || "Gagal update data");
}
toast.success("Berhasil update data!");
await grafikkepuasan.findMany.load();
return result.data;
} catch (error) {
console.error("Update error:", error);
toast.error("Gagal update data grafik kepuasan");
throw error;
} finally {
this.loading = false;
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) {
return toast.warn("ID tidak valid");
}
try {
grafikkepuasan.delete.loading = true;
const response = await fetch(
`/api/kesehatan/grafikkepuasan/del/${id}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
}
);
const result = await response.json();
if (response.ok && result?.success) {
toast.success(result.message || "Grafik kepuasan berhasil dihapus");
await grafikkepuasan.findMany.load(); // refresh list
} else {
toast.error(result?.message || "Gagal menghapus grafik kepuasan");
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus grafik kepuasan");
} finally {
grafikkepuasan.delete.loading = false;
}
},
},
});
export default grafikkepuasan;

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -20,17 +21,41 @@ const defaultForm = {
const infoWabahPenyakit = proxy({
findMany: {
data: [] as Prisma.InfoWabahPenyakitGetPayload<{
include: {
image: true;
};
}>[],
async load() {
const res = await ApiFetch.api.kesehatan.infowabahpenyakit[
"find-many"
].get();
if (res.status === 200) {
infoWabahPenyakit.findMany.data = res.data?.data ?? [];
data: null as
| Prisma.InfoWabahPenyakitGetPayload<{
include: {
image: true;
};
}>[]
| null,
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
infoWabahPenyakit.findMany.loading = true; // ✅ Akses langsung via nama path
infoWabahPenyakit.findMany.page = page;
infoWabahPenyakit.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.kesehatan.infowabahpenyakit["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
infoWabahPenyakit.findMany.data = res.data.data ?? [];
infoWabahPenyakit.findMany.totalPages = res.data.totalPages ?? 1;
} else {
infoWabahPenyakit.findMany.data = [];
infoWabahPenyakit.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch info wabah penyakit paginated:", err);
infoWabahPenyakit.findMany.data = [];
infoWabahPenyakit.findMany.totalPages = 1;
} finally {
infoWabahPenyakit.findMany.loading = false;
}
},
},

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -5,204 +6,241 @@ import { proxy } from "valtio";
import { z } from "zod";
const templateForm = z.object({
name: z.string().min(3, "Judul minimal 3 karakter"),
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
imageId: z.string().nonempty(),
})
const defaultForm = {
name: "",
deskripsi: "",
imageId: "",
}
const kontakDarurat = proxy({
findMany: {
data: [] as Prisma.KontakDaruratGetPayload<{
include: {
image: true;
};
}>[],
async load() {
const res = await ApiFetch.api.kesehatan.kontakdarurat[
"find-many"
].get();
if (res.status === 200) {
kontakDarurat.findMany.data = res.data?.data ?? [];
}
},
},
create:{
form: {...defaultForm},
loading: false,
async create() {
const cek = templateForm.safeParse(kontakDarurat.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
kontakDarurat.create.loading = true;
const res = await ApiFetch.api.kesehatan.kontakdarurat[
"create"
].post(kontakDarurat.create.form);
if (res.status === 200) {
kontakDarurat.findMany.load();
return toast.success("Kontak Darurat berhasil disimpan!");
}
return toast.error("Gagal menyimpan kontak darurat");
} catch (error) {
console.log((error as Error).message);
} finally {
kontakDarurat.create.loading = false;
}
},
resetForm() {
kontakDarurat.create.form = {...defaultForm};
}
},
findUnique: {
data: null as Prisma.KontakDaruratGetPayload<{
include: {
image: true;
};
}> | null,
async load(id: string) {
try {
const res = await fetch(`/api/kesehatan/kontakdarurat/${id}`);
if (res.ok) {
const data = await res.json();
kontakDarurat.findUnique.data = data.data ?? null;
} else {
console.error("Failed to fetch data", res.status, res.statusText);
kontakDarurat.findUnique.data = null;
}
} catch (error) {
console.error("Error fetching data:", error);
kontakDarurat.findUnique.data = null;
}
},
},
delete: {
loading: false,
async byId(id: string) {
try {
kontakDarurat.delete.loading = true;
const response = await fetch(`/api/kesehatan/kontakdarurat/del/${id}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
});
const result = await response.json();
if (response.ok && result?.success) {
toast.success(result.message || "Kontak darurat berhasil dihapus");
await kontakDarurat.findMany.load(); // refresh list
} else {
toast.error(result?.message || "Gagal menghapus kontak darurat");
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus kontak darurat");
} finally {
kontakDarurat.delete.loading = false;
}
}
},
edit: {
id: "",
form: { ...defaultForm },
loading: false,
async load(id: string) {
if (!id) {
toast.warn("ID tidak valid");
return null;
}
try {
const response = await fetch(`/api/kesehatan/kontakdarurat/${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 = {
name: data.name,
deskripsi: data.deskripsi,
imageId: data.imageId,
};
return data; // Return the loaded data
} else {
throw new Error(result?.message || "Gagal memuat data");
}
} catch (error) {
console.error("Error fetching kontak darurat:", error);
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
return null;
}
},
async update() {
const cek = templateForm.safeParse(kontakDarurat.edit.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
kontakDarurat.edit.loading = true;
const response = await fetch(`/api/kesehatan/kontakdarurat/${this.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: this.form.name,
deskripsi: this.form.deskripsi,
imageId: this.form.imageId,
}),
});
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(result.message || "Kontak darurat berhasil diupdate");
await kontakDarurat.findMany.load();
return true;
} else {
throw new Error(result.message || "Gagal update kontak darurat");
}
} catch (error) {
console.error("Gagal update:", error);
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat mengupdate kontak darurat");
return false;
} finally {
kontakDarurat.edit.loading = false;
}
},
reset() {
kontakDarurat.edit.id = "";
kontakDarurat.edit.form = { ...defaultForm };
},
},
name: z.string().min(3, "Judul minimal 3 karakter"),
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
imageId: z.string().nonempty(),
});
export default kontakDarurat
const defaultForm = {
name: "",
deskripsi: "",
imageId: "",
};
const kontakDarurat = proxy({
findMany: {
data: null as
| Prisma.KontakDaruratGetPayload<{
include: {
image: true;
};
}>[]
| null,
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
kontakDarurat.findMany.loading = true; // ✅ Akses langsung via nama path
kontakDarurat.findMany.page = page;
kontakDarurat.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.kesehatan.kontakdarurat[
"find-many"
].get({ query });
if (res.status === 200 && res.data?.success) {
kontakDarurat.findMany.data = res.data.data ?? [];
kontakDarurat.findMany.totalPages = res.data.totalPages ?? 1;
} else {
kontakDarurat.findMany.data = [];
kontakDarurat.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch kontak darurat paginated:", err);
kontakDarurat.findMany.data = [];
kontakDarurat.findMany.totalPages = 1;
} finally {
kontakDarurat.findMany.loading = false;
}
},
},
create: {
form: { ...defaultForm },
loading: false,
async create() {
const cek = templateForm.safeParse(kontakDarurat.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
kontakDarurat.create.loading = true;
const res = await ApiFetch.api.kesehatan.kontakdarurat["create"].post(
kontakDarurat.create.form
);
if (res.status === 200) {
kontakDarurat.findMany.load();
return toast.success("Kontak Darurat berhasil disimpan!");
}
return toast.error("Gagal menyimpan kontak darurat");
} catch (error) {
console.log((error as Error).message);
} finally {
kontakDarurat.create.loading = false;
}
},
resetForm() {
kontakDarurat.create.form = { ...defaultForm };
},
},
findUnique: {
data: null as Prisma.KontakDaruratGetPayload<{
include: {
image: true;
};
}> | null,
async load(id: string) {
try {
const res = await fetch(`/api/kesehatan/kontakdarurat/${id}`);
if (res.ok) {
const data = await res.json();
kontakDarurat.findUnique.data = data.data ?? null;
} else {
console.error("Failed to fetch data", res.status, res.statusText);
kontakDarurat.findUnique.data = null;
}
} catch (error) {
console.error("Error fetching data:", error);
kontakDarurat.findUnique.data = null;
}
},
},
delete: {
loading: false,
async byId(id: string) {
try {
kontakDarurat.delete.loading = true;
const response = await fetch(`/api/kesehatan/kontakdarurat/del/${id}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
const result = await response.json();
if (response.ok && result?.success) {
toast.success(result.message || "Kontak darurat berhasil dihapus");
await kontakDarurat.findMany.load(); // refresh list
} else {
toast.error(result?.message || "Gagal menghapus kontak darurat");
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus kontak darurat");
} finally {
kontakDarurat.delete.loading = false;
}
},
},
edit: {
id: "",
form: { ...defaultForm },
loading: false,
async load(id: string) {
if (!id) {
toast.warn("ID tidak valid");
return null;
}
try {
const response = await fetch(`/api/kesehatan/kontakdarurat/${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 = {
name: data.name,
deskripsi: data.deskripsi,
imageId: data.imageId,
};
return data; // Return the loaded data
} else {
throw new Error(result?.message || "Gagal memuat data");
}
} catch (error) {
console.error("Error fetching kontak darurat:", error);
toast.error(
error instanceof Error ? error.message : "Gagal memuat data"
);
return null;
}
},
async update() {
const cek = templateForm.safeParse(kontakDarurat.edit.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
kontakDarurat.edit.loading = true;
const response = await fetch(
`/api/kesehatan/kontakdarurat/${this.id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: this.form.name,
deskripsi: this.form.deskripsi,
imageId: this.form.imageId,
}),
}
);
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(result.message || "Kontak darurat berhasil diupdate");
await kontakDarurat.findMany.load();
return true;
} else {
throw new Error(result.message || "Gagal update kontak darurat");
}
} catch (error) {
console.error("Gagal update:", error);
toast.error(
error instanceof Error
? error.message
: "Terjadi kesalahan saat mengupdate kontak darurat"
);
return false;
} finally {
kontakDarurat.edit.loading = false;
}
},
reset() {
kontakDarurat.edit.id = "";
kontakDarurat.edit.form = { ...defaultForm };
},
},
});
export default kontakDarurat;

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -17,21 +18,45 @@ const defaultForm = {
}
const penangananDarurat = proxy({
findMany: {
data: [] as Prisma.PenangananDaruratGetPayload<{
include: {
image: true;
};
}>[],
async load() {
const res = await ApiFetch.api.kesehatan.penanganandarurat[
"find-many"
].get();
if (res.status === 200) {
penangananDarurat.findMany.data = res.data?.data ?? [];
}
},
findMany: {
data: null as
| Prisma.PenangananDaruratGetPayload<{
include: {
image: true;
};
}>[]
| null,
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
penangananDarurat.findMany.loading = true; // ✅ Akses langsung via nama path
penangananDarurat.findMany.page = page;
penangananDarurat.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.kesehatan.penanganandarurat["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
penangananDarurat.findMany.data = res.data.data ?? [];
penangananDarurat.findMany.totalPages = res.data.totalPages ?? 1;
} else {
penangananDarurat.findMany.data = [];
penangananDarurat.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch berita paginated:", err);
penangananDarurat.findMany.data = [];
penangananDarurat.findMany.totalPages = 1;
} finally {
penangananDarurat.findMany.loading = false;
}
},
},
create:{
form: {...defaultForm},
loading: false,

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -20,17 +21,43 @@ const defaultForm = {
const programKesehatan = proxy({
findMany: {
data: [] as Prisma.ProgramKesehatanGetPayload<{
include: {
image: true;
};
}>[],
async load() {
const res = await ApiFetch.api.kesehatan.programkesehatan[
"find-many"
].get();
if (res.status === 200) {
programKesehatan.findMany.data = res.data?.data ?? [];
data: null as
| Prisma.ProgramKesehatanGetPayload<{
include: {
image: true;
};
}>[]
| null,
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
programKesehatan.findMany.loading = true; // ✅ Akses langsung via nama path
programKesehatan.findMany.page = page;
programKesehatan.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.kesehatan.programkesehatan[
"find-many"
].get({ query });
if (res.status === 200 && res.data?.success) {
programKesehatan.findMany.data = res.data.data ?? [];
programKesehatan.findMany.totalPages = res.data.totalPages ?? 1;
} else {
programKesehatan.findMany.data = [];
programKesehatan.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch berita paginated:", err);
programKesehatan.findMany.data = [];
programKesehatan.findMany.totalPages = 1;
} finally {
programKesehatan.findMany.loading = false;
}
},
},
@@ -97,12 +124,15 @@ const programKesehatan = proxy({
try {
programKesehatan.delete.loading = true;
const response = await fetch(`/api/kesehatan/programkesehatan/del/${id}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
const response = await fetch(
`/api/kesehatan/programkesehatan/del/${id}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
}
);
const result = await response.json();
if (response.ok && result?.success) {
@@ -156,57 +186,70 @@ const programKesehatan = proxy({
}
} catch (error) {
console.error("Error fetching program kesehatan:", error);
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
toast.error(
error instanceof Error ? error.message : "Gagal memuat data"
);
return null;
}
},
async update() {
const cek = templateForm.safeParse(programKesehatan.edit.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
const cek = templateForm.safeParse(programKesehatan.edit.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
programKesehatan.edit.loading = true;
const response = await fetch(`/api/kesehatan/programkesehatan/${this.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: this.form.name,
deskripsiSingkat: this.form.deskripsiSingkat,
deskripsi: this.form.deskripsi,
imageId: this.form.imageId,
}),
});
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(result.message || "Program kesehatan berhasil diupdate");
await programKesehatan.findMany.load();
return true;
} else {
throw new Error(result.message || "Gagal update program kesehatan");
}
} catch (error) {
console.error("Gagal update:", error);
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat mengupdate program kesehatan");
return false;
} finally {
programKesehatan.edit.loading = false;
try {
programKesehatan.edit.loading = true;
const response = await fetch(
`/api/kesehatan/programkesehatan/${this.id}`,
{
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: this.form.name,
deskripsiSingkat: this.form.deskripsiSingkat,
deskripsi: this.form.deskripsi,
imageId: this.form.imageId,
}),
}
);
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(
result.message || "Program kesehatan berhasil diupdate"
);
await programKesehatan.findMany.load();
return true;
} else {
throw new Error(result.message || "Gagal update program kesehatan");
}
} catch (error) {
console.error("Gagal update:", error);
toast.error(
error instanceof Error
? error.message
: "Terjadi kesalahan saat mengupdate program kesehatan"
);
return false;
} finally {
programKesehatan.edit.loading = false;
}
},
reset() {
programKesehatan.edit.id = "";
programKesehatan.edit.form = { ...defaultForm };
programKesehatan.edit.id = "";
programKesehatan.edit.form = { ...defaultForm };
},
},
});

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -163,13 +164,43 @@ const puskesmasState = proxy({
},
findMany: {
data: null as Prisma.PuskesmasGetPayload<{
include: { image: true; jam: true; kontak: true };
}>[] | null,
async load() {
const res = await ApiFetch.api.kesehatan.puskesmas["find-many"].get();
if (res.status === 200) {
puskesmasState.findMany.data = res.data?.data ?? [];
data: null as
| Prisma.PuskesmasGetPayload<{
include: {
image: true;
jam: true;
kontak: true;
};
}>[]
| null,
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
puskesmasState.findMany.loading = true; // ✅ Akses langsung via nama path
puskesmasState.findMany.page = page;
puskesmasState.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.kesehatan.puskesmas["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
puskesmasState.findMany.data = res.data.data ?? [];
puskesmasState.findMany.totalPages = res.data.totalPages ?? 1;
} else {
puskesmasState.findMany.data = [];
puskesmasState.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch berita paginated:", err);
puskesmasState.findMany.data = [];
puskesmasState.findMany.totalPages = 1;
} finally {
puskesmasState.findMany.loading = false;
}
},
},

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -50,18 +51,50 @@ const apbdes = proxy({
},
},
findMany: {
data: null as Array<
Prisma.APBDesGetPayload<{
include: {
image: true;
file: true;
};
}>
> | null,
async load() {
const res = await ApiFetch.api.landingpage.apbdes["find-many"].get();
if (res.status === 200) {
apbdes.findMany.data = res.data?.data ?? [];
data: null as
| Prisma.APBDesGetPayload<{
include: {
image: true;
file: true;
};
}>[]
| null,
page: 1,
totalPages: 1,
total: 0,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
apbdes.findMany.loading = true; // Use the full path to access the property
apbdes.findMany.page = page;
apbdes.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.apbdes[
"findMany"
].get({
query
});
if (res.status === 200 && res.data?.success) {
apbdes.findMany.data = res.data.data || [];
apbdes.findMany.total = res.data.total || 0;
apbdes.findMany.totalPages = res.data.totalPages || 1;
} else {
console.error("Failed to load pegawai:", res.data?.message);
apbdes.findMany.data = [];
apbdes.findMany.total = 0;
apbdes.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading pegawai:", error);
apbdes.findMany.data = [];
apbdes.findMany.total = 0;
apbdes.findMany.totalPages = 1;
} finally {
apbdes.findMany.loading = false;
}
},
},

View File

@@ -60,16 +60,22 @@ const desaAntikorupsi = proxy({
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function
desaAntikorupsi.findMany.loading = true; // Use the full path to access the property
search: "",
load: async (page = 1, limit = 10, search = "") => {
// Change to arrow function
desaAntikorupsi.findMany.loading = true; // Use the full path to access the property
desaAntikorupsi.findMany.page = page;
desaAntikorupsi.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.desaantikorupsi[
"findMany"
].get({
query: { page, limit },
query,
});
if (res.status === 200 && res.data?.success) {
desaAntikorupsi.findMany.data = res.data.data || [];
desaAntikorupsi.findMany.total = res.data.total || 0;
@@ -305,20 +311,25 @@ const kategoriDesaAntiKorupsi = proxy({
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function
kategoriDesaAntiKorupsi.findMany.loading = true; // Use the full path to access the property
search: "",
load: async (page = 1, limit = 10, search = "") => {
// Change to arrow function
kategoriDesaAntiKorupsi.findMany.loading = true; // Use the full path to access the property
kategoriDesaAntiKorupsi.findMany.page = page;
kategoriDesaAntiKorupsi.findMany.search = search;
try {
const res = await ApiFetch.api.landingpage.kategoridak[
"findMany"
].get({
query: { page, limit },
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.kategoridak["findMany"].get({
query,
});
if (res.status === 200 && res.data?.success) {
kategoriDesaAntiKorupsi.findMany.data = res.data.data || [];
kategoriDesaAntiKorupsi.findMany.total = res.data.total || 0;
kategoriDesaAntiKorupsi.findMany.totalPages = res.data.totalPages || 1;
kategoriDesaAntiKorupsi.findMany.totalPages =
res.data.totalPages || 1;
} else {
console.error("Failed to load media sosial:", res.data?.message);
kategoriDesaAntiKorupsi.findMany.data = [];
@@ -363,27 +374,30 @@ const kategoriDesaAntiKorupsi = proxy({
try {
kategoriDesaAntiKorupsi.delete.loading = true;
const response = await fetch(
`/api/landingpage/kategoridak/del/${id}`,
{
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
}
);
const response = await fetch(`/api/landingpage/kategoridak/del/${id}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
const result = await response.json();
if (response.ok && result?.success) {
toast.success(result.message || "Kategori desa anti korupsi berhasil dihapus");
toast.success(
result.message || "Kategori desa anti korupsi berhasil dihapus"
);
await kategoriDesaAntiKorupsi.findMany.load(); // refresh list
} else {
toast.error(result?.message || "Gagal menghapus kategori desa anti korupsi");
toast.error(
result?.message || "Gagal menghapus kategori desa anti korupsi"
);
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus kategori desa anti korupsi");
toast.error(
"Terjadi kesalahan saat menghapus kategori desa anti korupsi"
);
} finally {
kategoriDesaAntiKorupsi.delete.loading = false;
}

View File

@@ -181,7 +181,13 @@ const responden = proxy({
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(this.form),
body: JSON.stringify({
name: this.form.name,
tanggal: this.form.tanggal,
jenisKelaminId: this.form.jenisKelaminId,
ratingId: this.form.ratingId,
kelompokUmurId: this.form.kelompokUmurId,
}),
});
const result = await response.json();
if (!response.ok || !result?.success) {

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -62,12 +63,34 @@ const prestasiDesa = proxy({
};
}>
> | null,
async load() {
const res = await ApiFetch.api.landingpage.prestasidesa[
"find-many"
].get();
if (res.status === 200) {
prestasiDesa.findMany.data = res.data?.data ?? [];
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
prestasiDesa.findMany.loading = true; // ✅ Akses langsung via nama path
prestasiDesa.findMany.page = page;
prestasiDesa.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.prestasidesa["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
prestasiDesa.findMany.data = res.data.data ?? [];
prestasiDesa.findMany.totalPages = res.data.totalPages ?? 1;
} else {
prestasiDesa.findMany.data = [];
prestasiDesa.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch prestasi desa paginated:", err);
prestasiDesa.findMany.data = [];
prestasiDesa.findMany.totalPages = 1;
} finally {
prestasiDesa.findMany.loading = false;
}
},
},
@@ -283,12 +306,34 @@ const kategoriPrestasi = proxy({
id: string;
name: string;
}> | null,
async load() {
const res = await ApiFetch.api.landingpage.kategoriprestasi[
"find-many"
].get();
if (res.status === 200) {
kategoriPrestasi.findMany.data = res.data?.data ?? [];
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
kategoriPrestasi.findMany.loading = true; // ✅ Akses langsung via nama path
kategoriPrestasi.findMany.page = page;
kategoriPrestasi.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.kategoriprestasi["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
kategoriPrestasi.findMany.data = res.data.data ?? [];
kategoriPrestasi.findMany.totalPages = res.data.totalPages ?? 1;
} else {
kategoriPrestasi.findMany.data = [];
kategoriPrestasi.findMany.totalPages = 1;
}
} catch (err) {
console.error("Gagal fetch kategori prestasi paginated:", err);
kategoriPrestasi.findMany.data = [];
kategoriPrestasi.findMany.totalPages = 1;
} finally {
kategoriPrestasi.findMany.loading = false;
}
},
},

View File

@@ -65,14 +65,19 @@ const programInovasi = proxy({
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function
search: "",
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
programInovasi.findMany.loading = true; // Use the full path to access the property
programInovasi.findMany.page = page;
programInovasi.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.programinovasi[
"findMany"
].get({
query: { page, limit },
query
});
if (res.status === 200 && res.data?.success) {
@@ -482,14 +487,19 @@ const mediaSosial = proxy({
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function
search: "",
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
mediaSosial.findMany.loading = true; // Use the full path to access the property
mediaSosial.findMany.page = page;
try {
mediaSosial.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.mediasosial[
"findMany"
].get({
query: { page, limit },
query,
});
if (res.status === 200 && res.data?.success) {

View File

@@ -58,14 +58,19 @@ const sdgsDesa = proxy({
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function
search: "",
load: async (page = 1, limit = 10, search = "") => { // Change to arrow function
sdgsDesa.findMany.loading = true; // Use the full path to access the property
sdgsDesa.findMany.page = page;
sdgsDesa.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.landingpage.sdgsdesa[
"findMany"
].get({
query: { page, limit },
query,
});
if (res.status === 200 && res.data?.success) {

View File

@@ -49,35 +49,38 @@ const daftarInformasiPublik = proxy({
},
},
findMany: {
data: null as any[] | null,
data: null as
| Prisma.DaftarInformasiPublikGetPayload<{
omit: {
isActive: true;
};
}>[]
| null,
page: 1,
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function
daftarInformasiPublik.findMany.loading = true; // Use the full path to access the property
search: "",
load: async (page = 1, limit = 10, search = "") => {
daftarInformasiPublik.findMany.loading = true; // ✅ Akses langsung via nama path
daftarInformasiPublik.findMany.page = page;
try {
const res = await ApiFetch.api.ppid.daftarinformasipublik[
"find-many"
].get({
query: { page, limit },
});
daftarInformasiPublik.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.ppid.daftarinformasipublik["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
daftarInformasiPublik.findMany.data = res.data.data || [];
daftarInformasiPublik.findMany.total = res.data.total || 0;
daftarInformasiPublik.findMany.totalPages = res.data.totalPages || 1;
daftarInformasiPublik.findMany.data = res.data.data ?? [];
daftarInformasiPublik.findMany.totalPages = res.data.totalPages ?? 1;
} else {
console.error("Failed to load daftar informasi publik:", res.data?.message);
daftarInformasiPublik.findMany.data = [];
daftarInformasiPublik.findMany.total = 0;
daftarInformasiPublik.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading daftar informasi publik:", error);
} catch (err) {
console.error("Gagal fetch daftar informasi publik paginated:", err);
daftarInformasiPublik.findMany.data = [];
daftarInformasiPublik.findMany.total = 0;
daftarInformasiPublik.findMany.totalPages = 1;
} finally {
daftarInformasiPublik.findMany.loading = false;

View File

@@ -348,18 +348,34 @@ const posisiOrganisasi = proxy({
deskripsi: string | null;
hierarki: number;
}>,
async load() {
page: 1,
totalPages: 1,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
posisiOrganisasi.findMany.loading = true; // ✅ Akses langsung via nama path
posisiOrganisasi.findMany.page = page;
posisiOrganisasi.findMany.search = search;
try {
const res = await ApiFetch.api.ppid.strukturppid.posisiorganisasi[
"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 ?? [];
const query: any = { page, limit };
if (search) query.search = search;
const res = await ApiFetch.api.ppid.strukturppid.posisiorganisasi["find-many"].get({ query });
if (res.status === 200 && res.data?.success) {
posisiOrganisasi.findMany.data = res.data.data ?? [];
posisiOrganisasi.findMany.totalPages = res.data.totalPages ?? 1;
} else {
posisiOrganisasi.findMany.data = [];
posisiOrganisasi.findMany.totalPages = 1;
}
} catch (error) {
console.error("Find many error:", error);
this.data = [];
} catch (err) {
console.error("Gagal fetch posisi organisasi paginated:", err);
posisiOrganisasi.findMany.data = [];
posisiOrganisasi.findMany.totalPages = 1;
} finally {
posisiOrganisasi.findMany.loading = false;
}
},
},
@@ -438,9 +454,9 @@ const pegawai = proxy({
try {
pegawai.create.loading = true;
const res = await ApiFetch.api.ppid.strukturppid.pegawai[
"create"
].post(pegawai.create.form);
const res = await ApiFetch.api.ppid.strukturppid.pegawai["create"].post(
pegawai.create.form
);
if (res.status === 200) {
toast.success("Pegawai berhasil ditambahkan");
await pegawai.findMany.load();
@@ -457,42 +473,55 @@ const pegawai = proxy({
},
// In struktur-organisasi.ts
findMany: {
data: null as any[] | null,
page: 1,
totalPages: 1,
total: 0,
loading: false,
load: async (page = 1, limit = 10) => { // Change to arrow function
pegawai.findMany.loading = true; // Use the full path to access the property
pegawai.findMany.page = page;
try {
const res = await ApiFetch.api.ppid.strukturppid.pegawai[
"find-many"
].get({
query: { page, limit },
});
findMany: {
data: null as
| Prisma.PegawaiPPIDGetPayload<{
include: {
image: true;
posisi: true;
};
}>[]
| null,
page: 1,
totalPages: 1,
total: 0,
loading: false,
search: "",
load: async (page = 1, limit = 10, search = "") => {
// Change to arrow function
pegawai.findMany.loading = true; // Use the full path to access the property
pegawai.findMany.page = page;
pegawai.findMany.search = search;
try {
const query: any = { page, limit };
if (search) query.search = search;
if (res.status === 200 && res.data?.success) {
pegawai.findMany.data = res.data.data || [];
pegawai.findMany.total = res.data.total || 0;
pegawai.findMany.totalPages = res.data.totalPages || 1;
} else {
console.error("Failed to load pegawai:", res.data?.message);
const res = await ApiFetch.api.ppid.strukturppid.pegawai[
"find-many"
].get({
query,
});
if (res.status === 200 && res.data?.success) {
pegawai.findMany.data = res.data.data || [];
pegawai.findMany.total = res.data.total || 0;
pegawai.findMany.totalPages = res.data.totalPages || 1;
} else {
console.error("Failed to load pegawai:", res.data?.message);
pegawai.findMany.data = [];
pegawai.findMany.total = 0;
pegawai.findMany.totalPages = 1;
}
} catch (error) {
console.error("Error loading pegawai:", error);
pegawai.findMany.data = [];
pegawai.findMany.total = 0;
pegawai.findMany.totalPages = 1;
} finally {
pegawai.findMany.loading = false;
}
} catch (error) {
console.error("Error loading pegawai:", error);
pegawai.findMany.data = [];
pegawai.findMany.total = 0;
pegawai.findMany.totalPages = 1;
} finally {
pegawai.findMany.loading = false;
}
},
},
},
findUnique: {
data: null as
| (Prisma.PegawaiGetPayload<{
@@ -521,12 +550,9 @@ findMany: {
if (!id) return toast.warn("ID tidak valid");
try {
pegawai.delete.loading = true;
const res = await fetch(
`/api/ppid/strukturppid/pegawai/del/${id}`,
{
method: "DELETE",
}
);
const res = await fetch(`/api/ppid/strukturppid/pegawai/del/${id}`, {
method: "DELETE",
});
const json = await res.json();
if (res.ok) {
toast.success(json.message ?? "Berhasil hapus pegawai");
@@ -555,15 +581,12 @@ findMany: {
}
try {
const response = await fetch(
`/api/ppid/strukturppid/pegawai/${id}`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
);
const response = await fetch(`/api/ppid/strukturppid/pegawai/${id}`, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
@@ -677,7 +700,7 @@ findMany: {
const stateStrukturPPID = proxy({
stateStruktur,
posisiOrganisasi,
pegawai
pegawai,
});
export default stateStrukturPPID;

View File

@@ -143,8 +143,8 @@ function ListDemografiPekerjaan({ search }: { search: string }) {
dataKey="pekerjaan"
type="stacked"
series={[
{ name: 'lakiLaki', color: 'red.6', label: 'Laki - Laki' },
{ name: 'perempuan', color: 'orange.6', label: 'Perempuan' },
{ name: 'lakiLaki', color: '#5082EE', label: 'Laki - Laki' },
{ name: 'perempuan', color: '#6EDF9C', label: 'Perempuan' },
]}
/>
</Box>

View File

@@ -35,7 +35,7 @@ function EditJumlahPendudukMiskin() {
// Set the ID before submitting
stateJPM.update.id = id;
await stateJPM.update.submit();
router.push('/admin/ekonomi/jumlah-penduduk-miskin-2024-2025')
router.push('/admin/ekonomi/jumlah-penduduk-miskin')
}
return (
<Box>

View File

@@ -32,7 +32,7 @@ function CreateJumlahPendudukMiskin() {
}
}
resetForm();
router.push("/admin/ekonomi/jumlah-penduduk-miskin-2024-2025");
router.push("/admin/ekonomi/jumlah-penduduk-miskin");
}
return (
<Box>

View File

@@ -91,7 +91,7 @@ function ListJumlahPendudukMiskin({ search }: { search: string }) {
<Paper bg={colors['white-1']} p={'md'}>
<JudulList
title='List Jumlah Penduduk Miskin'
href='/admin/ekonomi/jumlah-penduduk-miskin-2024-2025/create'
href='/admin/ekonomi/jumlah-penduduk-miskin/create'
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
@@ -108,7 +108,7 @@ function ListJumlahPendudukMiskin({ search }: { search: string }) {
<TableTd>{item.year}</TableTd>
<TableTd>{item.totalPoorPopulation}</TableTd>
<TableTd>
<Button color='green' onClick={() => router.push(`/admin/ekonomi/jumlah-penduduk-miskin-2024-2025/${item.id}`)}>
<Button color='green' onClick={() => router.push(`/admin/ekonomi/jumlah-penduduk-miskin/${item.id}`)}>
<IconEdit size={20} />
</Button>
</TableTd>

View File

@@ -8,29 +8,36 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { Cell, Pie, PieChart } from 'recharts';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/judulListTab';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikNganggur from '../../../_state/ekonomi/usia-kerja-nganggur';
function GrafikBerdasarkanPendidikan() {
const [search, setSearch] = useState("")
return (
<Box>
<Stack gap={"xs"}>
<Title order={3}>Grafik Pengangguran Berdasarkan Pendidikan</Title>
<ListGrafikBerdasarkanPendidikan />
<HeaderSearch
title='Detail Data Pengangguran Berdasarkan Pendidikan'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListGrafikBerdasarkanPendidikan search={search}/>
</Stack>
</Box>
);
}
function ListGrafikBerdasarkanPendidikan() {
function ListGrafikBerdasarkanPendidikan({search}: {search: string}) {
const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanPendidikan)
const [donutData, setDonutData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const router = useRouter();
const [search, setSearch] = useState("");
const handleDelete = async () => {
@@ -56,11 +63,11 @@ function ListGrafikBerdasarkanPendidikan() {
const D3 = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.D3 || 0), 0);
const S1 = stategrafik.findMany.data.reduce((acc: number, cur: any) => acc + Number(cur.S1 || 0), 0);
setDonutData([
{ name: 'SD', value: SD, color: colors['blue-button'], key: 'SD' },
{ name: 'SMP', value: SMP, color: '#10A85AFF', key: 'SMP' },
{ name: 'SMA', value: SMA, color: '#C07B13FF', key: 'SMA' },
{ name: 'D3', value: D3, color: '#1094A8FF', key: 'D3' },
{ name: 'S1', value: S1, color: '#A83610FF', key: 'S1' },
{ name: 'SD', value: SD, color: '#4b6Ef5', key: 'SD' },
{ name: 'SMP', value: SMP, color: '#14b885', key: 'SMP' },
{ name: 'SMA', value: SMA, color: '#E6A03B', key: 'SMA' },
{ name: 'D3', value: D3, color: '#DB524D', key: 'D3' },
{ name: 'S1', value: S1, color: '#1018A8FF', key: 'S1' },
]);
}
}, [stategrafik.findMany.data])
@@ -88,13 +95,9 @@ function ListGrafikBerdasarkanPendidikan() {
<Box>
<Stack gap={"xs"}>
<Paper bg={colors['white-1']} p={"md"}>
<JudulListTab
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
<JudulList
title='List Grafik Pengangguran Berdasarkan Pendidikan'
href='/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/create'
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>

View File

@@ -8,29 +8,37 @@ import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { Cell, Pie, PieChart } from 'recharts';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/judulListTab';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikNganggur from '../../../_state/ekonomi/usia-kerja-nganggur';
function GrafikBerdasarkanUsiaKerjaYangMenganggur() {
const [search, setSearch] = useState("")
return (
<Box>
<Stack gap={"xs"}>
<Title order={3}>Grafik Pengangguran Berdasarkan Usia Kerja</Title>
<ListGrafikBerdasarkanUsiaKerjaYangMenganggur />
<HeaderSearch
title='Detail Data Pengangguran'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListGrafikBerdasarkanUsiaKerjaYangMenganggur search={search} />
</Stack>
</Box>
);
}
function ListGrafikBerdasarkanUsiaKerjaYangMenganggur() {
function ListGrafikBerdasarkanUsiaKerjaYangMenganggur({search}: {search: string}) {
const stategrafik = useProxy(grafikNganggur.grafikBerdasarkanUsiaKerjaNganggur)
const [donutData, setDonutData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const router = useRouter();
const [search, setSearch] = useState("");
const handleDelete = async () => {
@@ -85,13 +93,9 @@ function ListGrafikBerdasarkanUsiaKerjaYangMenganggur() {
<Box>
<Stack gap={"xs"}>
<Paper bg={colors['white-1']} p={"md"}>
<JudulListTab
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
<JudulList
title='List Pengangguran Berdasarkan Usia Kerja'
href='/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/create'
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>

View File

@@ -25,7 +25,7 @@ function DetailJumlahPengangguran() {
stateDetail.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
router.push("/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran")
router.push("/admin/ekonomi/jumlah-pengangguran")
}
}
@@ -86,7 +86,7 @@ function DetailJumlahPengangguran() {
color={"red"}>
<IconX size={20} />
</Button>
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran/${stateDetail.findUnique.data?.id}/edit`)} color="green">
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/${stateDetail.findUnique.data?.id}/edit`)} color="green">
<IconEdit size={20} />
</Button>
</Flex>

View File

@@ -90,7 +90,7 @@ function CreateJumlahPengangguran() {
/>
<TextInput
label="Tahun"
type="number"
type="date"
value={stateDetail.create.form.year}
onChange={(e) =>
(stateDetail.create.form.year = Number(e.currentTarget.value))

View File

@@ -8,21 +8,29 @@ import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import { BarChart } from '@mantine/charts';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
import jumlahPengangguranState from '../../_state/ekonomi/jumlah-pengangguran';
import JudulListTab from '../../_com/judulListTab';
function DetailDataPengangguran() {
const [search, setSearch] = useState("")
return (
<Box>
<Stack gap={"xs"}>
<Title order={3}>Detail Data Pengangguran</Title>
<ListDetailDataPengangguran />
<HeaderSearch
title='Detail Data Pengangguran'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListDetailDataPengangguran search={search} />
</Stack>
</Box>
);
}
function ListDetailDataPengangguran() {
function ListDetailDataPengangguran({search}: {search: string}) {
type DetailDataPengangguran = {
id: string;
@@ -37,7 +45,6 @@ function ListDetailDataPengangguran() {
const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready
const stateDetail = useProxy(jumlahPengangguranState.jumlahPengangguran)
const router = useRouter();
const [search, setSearch] = useState("")
useShallowEffect(() => {
setMounted(true)
@@ -78,13 +85,9 @@ function ListDetailDataPengangguran() {
<Box>
<Stack gap={"md"}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulListTab
<JudulList
title='List Detail Data Pengangguran'
href='/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran/create'
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
href='/admin/ekonomi/jumlah-pengangguran/create'
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
@@ -102,7 +105,7 @@ function ListDetailDataPengangguran() {
<TableTd>{item.educatedUnemployment}</TableTd>
<TableTd>{item.uneducatedUnemployment}</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/detail-data-pengangguran/${item.id}`)}>
<Button onClick={() => router.push(`/admin/ekonomi/jumlah-pengangguran/${item.id}`)}>
<IconDeviceImac size={20} />
</Button>
</TableTd>

View File

@@ -55,17 +55,17 @@ function EditLowonganKerja() {
const handleSubmit = async () => {
try {
lowonganKerjaState.update.form = {
...lowonganKerjaState.update.form,
posisi: formData.posisi,
namaPerusahaan: formData.namaPerusahaan,
lokasi: formData.lokasi,
tipePekerjaan: formData.tipePekerjaan,
gaji: formData.gaji,
deskripsi: formData.deskripsi,
kualifikasi: formData.kualifikasi,
}
await lowonganState.update.update()
// Set the ID for the update
lowonganState.update.id = params?.id as string;
// Update the form state
lowonganState.update.form = {
...lowonganState.update.form,
...formData
};
// Call the update function
await lowonganState.update.update();
toast.success("Lowongan kerja berhasil diperbarui!");
router.push("/admin/ekonomi/lowongan-kerja-lokal");
} catch (error) {
@@ -88,7 +88,7 @@ function EditLowonganKerja() {
<TextInput
value={formData.posisi}
onChange={(val) => {
formData.posisi = val.target.value;
setFormData(prev => ({ ...prev, posisi: val.target.value }));
}}
label={<Text fw={"bold"} fz={"sm"}>Posisi</Text>}
placeholder='Masukkan posisi'
@@ -96,7 +96,7 @@ function EditLowonganKerja() {
<TextInput
value={formData.namaPerusahaan}
onChange={(val) => {
formData.namaPerusahaan = val.target.value;
setFormData(prev => ({ ...prev, namaPerusahaan: val.target.value }));
}}
label={<Text fw={"bold"} fz={"sm"}>Nama Perusahaan</Text>}
placeholder='Masukkan nama perusahaan'
@@ -104,7 +104,7 @@ function EditLowonganKerja() {
<TextInput
value={formData.lokasi}
onChange={(val) => {
formData.lokasi = val.target.value;
setFormData(prev => ({ ...prev, lokasi: val.target.value }));
}}
label={<Text fw={"bold"} fz={"sm"}>Lokasi</Text>}
placeholder='Masukkan lokasi'
@@ -112,7 +112,7 @@ function EditLowonganKerja() {
<TextInput
value={formData.tipePekerjaan}
onChange={(val) => {
formData.tipePekerjaan = val.target.value;
setFormData(prev => ({ ...prev, tipePekerjaan: val.target.value }));
}}
label={<Text fw={"bold"} fz={"sm"}>Tipe Pekerjaan</Text>}
placeholder='Masukkan tipe pekerjaan'
@@ -120,7 +120,7 @@ function EditLowonganKerja() {
<TextInput
value={formData.gaji}
onChange={(val) => {
formData.gaji = val.target.value;
setFormData(prev => ({ ...prev, gaji: val.target.value }));
}}
label={<Text fw={"bold"} fz={"sm"}>Gaji selama 1 bulan</Text>}
placeholder='Masukkan gaji'
@@ -130,7 +130,7 @@ function EditLowonganKerja() {
<EditEditor
value={formData.deskripsi}
onChange={(val) => {
formData.deskripsi = val;
setFormData(prev => ({ ...prev, deskripsi: val }));
}}
/>
</Box>
@@ -139,7 +139,7 @@ function EditLowonganKerja() {
<EditEditor
value={formData.kualifikasi}
onChange={(val) => {
formData.kualifikasi = val;
setFormData(prev => ({ ...prev, kualifikasi: val }));
}}
/>
</Box>

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
@@ -30,20 +30,21 @@ function ListLowonganKerjaLokal({ search }: { search: string }) {
const lowonganState = useProxy(lowonganKerjaState)
const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = lowonganState.findMany
useShallowEffect(() => {
lowonganState.findMany.load();
}, [])
load(page, 10, search)
}, [page, search])
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)
);
});
const filteredData = data || []
if (!lowonganState.findMany.data) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
@@ -60,18 +61,24 @@ function ListLowonganKerjaLokal({ search }: { search: string }) {
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Bekerja Sebagai</TableTh>
<TableTh>Nama Usaha</TableTh>
<TableTh>Alamat Usaha</TableTh>
<TableTh>Pekerjaan</TableTh>
<TableTh>Nama Perusahaan</TableTh>
<TableTh>Lokasi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.posisi}</TableTd>
<TableTd>{item.namaPerusahaan}</TableTd>
<TableTd>{item.lokasi}</TableTd>
<TableTd>
<Text fz={"md"}>{item.posisi}</Text>
</TableTd>
<TableTd>
<Text fz={"md"}>{item.namaPerusahaan}</Text>
</TableTd>
<TableTd>
<Text fz={"md"}>{item.lokasi}</Text>
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/ekonomi/lowongan-kerja-lokal/${item.id}`)}>
<IconDeviceImac size={20} />
@@ -82,6 +89,14 @@ function ListLowonganKerjaLokal({ search }: { search: string }) {
</TableTbody>
</Table>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my="md"
/>
</Center>
</Box>
);
}

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconSearch, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -13,31 +13,39 @@ import pasarDesaState from '../../../_state/ekonomi/pasar-desa/pasar-desa';
function PasarDesa() {
const [search, setSearch] = useState("")
const [search2, setSearch2] = useState("")
return (
<Box>
<HeaderSearch
title='Kategori Produk'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
value={search2}
onChange={(e) => setSearch2(e.currentTarget.value)}
/>
<ListPasarDesa search={search} />
<ListPasarDesa search2={search2} />
</Box>
);
}
function ListPasarDesa({ search }: { search: string }) {
function ListPasarDesa({ search2 }: { search2: string }) {
const statePasar = useProxy(pasarDesaState.kategoriProduk)
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
// const params = useParams()
const router = useRouter()
const {
data,
page,
totalPages,
loading,
load,
} = statePasar.findMany
useShallowEffect(() => {
statePasar.findMany.load()
}, [])
load(page, 10, search2)
}, [page, search2])
const handleHapus = () => {
if (selectedId) {
@@ -47,14 +55,9 @@ function ListPasarDesa({ search }: { search: string }) {
}
}
const filteredData = (statePasar.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.nama.toLowerCase().includes(keyword)
);
});
const filteredData = data || []
if (!statePasar.findMany.data) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
@@ -99,6 +102,14 @@ function ListPasarDesa({ search }: { search: string }) {
</TableTbody>
</Table>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useShallowEffect } from '@mantine/hooks';
import { useRouter } from 'next/navigation';
@@ -30,21 +30,21 @@ function ListPasarDesa({ search }: { search: string }) {
const statePasar = useProxy(pasarDesaState.pasarDesa)
const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = statePasar.findMany
useShallowEffect(() => {
statePasar.findMany.load()
}, [])
load(page, 10, search)
}, [page, search])
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)
);
});
const filteredData = data || []
if (!statePasar.findMany.data) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
@@ -86,6 +86,14 @@ function ListPasarDesa({ search }: { search: string }) {
</TableTbody>
</Table>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
</Box>
);
}

View File

@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
@@ -23,7 +23,7 @@ function ProgramKemiskinan() {
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListProgramKemiskinan search={search}/>
<ListProgramKemiskinan search={search} />
</Box>
);
}
@@ -34,14 +34,22 @@ function ListProgramKemiskinan({ search }: { search: string }) {
const [lineChart, setLineChart] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
const {
data,
page,
totalPages,
loading,
load,
} = programState.findMany;
useShallowEffect(() => {
setMounted(true)
programState.findMany.load()
load(page, 10, search)
}, [])
useEffect(() => {
if (programState.findMany.data) {
const chartData = programState.findMany.data
if (data) {
const chartData = data
.filter(item => item.statistik)
.map(item => ({
tahun: item.statistik?.tahun,
@@ -52,18 +60,11 @@ function ListProgramKemiskinan({ search }: { search: string }) {
setLineChart(chartData);
}
}, [programState.findMany.data])
}, [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)
);
});
const filteredData = data || []
if (!programState.findMany.data) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
@@ -112,7 +113,7 @@ function ListProgramKemiskinan({ search }: { search: string }) {
<Box >
<Title pb={10} order={3}>Grafik Berdasarkan Responden</Title>
{mounted && lineChart.length > 0 ? (<Box style={{ width: '100%', height: 'auto', }}>
<Box w={"100%"} style={{overflowX: 'auto'}}>
<Box w={"100%"} style={{ overflowX: 'auto' }}>
<LineChart
width={820}
height={300}
@@ -143,6 +144,14 @@ function ListProgramKemiskinan({ search }: { search: string }) {
</Box>
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
</Box>
</Box>
);

View File

@@ -2,15 +2,16 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import grafikSektorUnggulan from '../../../_state/ekonomi/sektor-unggulan-desa';
import CreateEditor from '../../../_com/createEditor';
function CreateSektorUnggulanDesa() {
const stateGrafik= useProxy(grafikSektorUnggulan);
const stateGrafik = useProxy(grafikSektorUnggulan);
const [chartData, setChartData] = useState<any[]>([]);
const router = useRouter()
@@ -54,15 +55,15 @@ function CreateSektorUnggulanDesa() {
stateGrafik.create.form.name = val.currentTarget.value;
}}
/>
<TextInput
label="Deskripsi Sektor Unggulan"
type="text"
value={stateGrafik.create.form.description}
placeholder="Masukkan deskripsi sektor unggulan"
onChange={(val) => {
stateGrafik.create.form.description = val.currentTarget.value;
}}
/>
<Box>
<Text fw={"bold"} fz={"sm"}>Deskripsi Sektor Ungggulan</Text>
<CreateEditor
value={stateGrafik.create.form.description}
onChange={(val) => {
stateGrafik.create.form.description = val;
}}
/>
</Box>
<TextInput
label="Jumlah"
type="number"

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { Box, Button, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
@@ -30,7 +30,7 @@ function SektorUnggulanDesa() {
function ListSektorUnggulanDesa({ search }: { search: string }) {
const router = useRouter();
const state = useProxy(grafikSektorUnggulan);
const [chartData, setChartData] = useState<{id: string; name: string; description: string | null; value: number | null}[]>([]);
const [chartData, setChartData] = useState<{ id: string; name: string; description: string | null; value: number | null }[]>([]);
const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready
const isTablet = useMediaQuery('(max-width: 1024px)')
const isMobile = useMediaQuery('(max-width: 768px)')
@@ -61,38 +61,41 @@ function ListSektorUnggulanDesa({ search }: { search: string }) {
}, [state.findMany.data]);
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulList
title='List Sektor Unggulan Desa'
href='/admin/ekonomi/sektor-unggulan-desa/create'
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Nama Sektor Unggulan</TableTh>
<TableTh>Deskripsi Sektor Unggulan</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>{item.description}</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/ekonomi/sektor-unggulan-desa/${item.id}`)}>
<IconDeviceImac size={20} />
</Button>
</TableTd>
<Box>
<Stack gap={"xs"}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulList
title='List Sektor Unggulan Desa'
href='/admin/ekonomi/sektor-unggulan-desa/create'
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Nama Sektor Unggulan</TableTh>
<TableTh>Deskripsi Sektor Unggulan</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
))}
</TableTbody>
</Table>
</Paper>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>
<Text truncate={"end"} fz={'sm'} lineClamp={1} dangerouslySetInnerHTML={{ __html: item.description || '' }}></Text>
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/ekonomi/sektor-unggulan-desa/${item.id}`)}>
<IconDeviceImac size={20} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Paper>
{/* Chart */}
{!mounted && !chartData ? (
{/* Chart */}
{!mounted && !chartData ? (
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title pb={10} order={3}>Grafik Hasil Kepuasan Masyarakat</Title>
@@ -115,6 +118,7 @@ function ListSektorUnggulanDesa({ search }: { search: string }) {
</Paper>
</Box>
)}
</Stack>
</Box>
);
}

View File

@@ -54,6 +54,10 @@ function DetailKeamananLingkungan() {
{keamananState.findUnique.data ? (
<Paper key={keamananState.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
<Image w={{ base: 150, md: 490}} src={keamananState.findUnique.data?.image?.link} alt="gambar" />
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Judul Keamanan Lingkungan</Text>
<Text fz={"lg"}>{keamananState.findUnique.data?.name}</Text>
@@ -62,10 +66,6 @@ function DetailKeamananLingkungan() {
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: keamananState.findUnique.data?.deskripsi }} />
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
<Image w={{ base: 150, md: 150, lg: 150 }} src={keamananState.findUnique.data?.image?.link} alt="gambar" />
</Box>
<Flex gap={"xs"} mt={10}>
<Button
onClick={() => {

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
@@ -30,19 +30,21 @@ function ListKeamananLingkungan({ search }: { search: string }) {
const keamananState = useProxy(keamananLingkunganState)
const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = keamananState.findMany;
useShallowEffect(() => {
keamananState.findMany.load()
}, [])
load(page, 10, search)
}, [page, search])
const filteredData = (keamananState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.deskripsi.toLowerCase().includes(keyword)
);
});
const filteredData = data || []
if (!keamananState.findMany.data) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
@@ -67,9 +69,15 @@ function ListKeamananLingkungan({ search }: { search: string }) {
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
<Box w={180}>
<Text fz={"md"} truncate={"end"} lineClamp={1}>{item.name}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={250}>
<Text fz={"md"} truncate={"end"} lineClamp={1} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/keamanan/keamanan-lingkungan-pecalang-patwal/${item.id}`)}>
@@ -81,6 +89,14 @@ function ListKeamananLingkungan({ search }: { search: string }) {
</TableTbody>
</Table>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
</Box>
);
}

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -21,7 +21,7 @@ function PolsekTerdekat() {
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListPolsekTerdekat search={search}/>
<ListPolsekTerdekat search={search} />
</Box>
);
}
@@ -30,20 +30,21 @@ function ListPolsekTerdekat({ search }: { search: string }) {
const polsekState = useProxy(polsekTerdekat)
const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = polsekState.findMany;
useShallowEffect(() => {
polsekState.findMany.load()
}, [])
load(page, 10, search)
}, [page, search])
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)
);
});
const filteredData = data || []
if (!polsekState.findMany.data) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
@@ -64,24 +65,44 @@ function ListPolsekTerdekat({ search }: { search: string }) {
<TableTh>Jarak Polsek</TableTh>
<TableTh>Alamat</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.nama}</TableTd>
<TableTd>{item.jarakKeDesa}</TableTd>
<TableTd>{item.alamat}</TableTd>
<TableTd>
<Box w={180}>
<Text fz='md' truncate={"end"} lineClamp={1}>{item.nama}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={180}>
<Text fz='md' truncate={"end"} lineClamp={1}>{item.jarakKeDesa}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={250}>
<Text fz='md' truncate={"end"} lineClamp={1}>{item.alamat}</Text>
</Box>
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/keamanan/polsek-terdekat/${item.id}`)}>
<IconDeviceImac size={20} />
</Button>
</TableTd>
</TableTr>
))}
<IconDeviceImac size={20} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Table>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my="md"
/>
</Center>
</Box>
);
}

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
@@ -30,19 +30,21 @@ function ListTipsKeamanan({ search }: { search: string }) {
const stateKeamanan = useProxy(tipsKeamananState)
const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = stateKeamanan.findMany
useShallowEffect(() => {
stateKeamanan.findMany.load()
}, [])
load(page, 10, search)
}, [page, search])
const filteredData = (stateKeamanan.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.judul.toLowerCase().includes(keyword) ||
item.deskripsi.toLowerCase().includes(keyword)
);
});
const filteredData = data || []
if (!stateKeamanan.findMany.data) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
@@ -67,9 +69,15 @@ function ListTipsKeamanan({ search }: { search: string }) {
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.judul}</TableTd>
<TableTd>
<Text fz={"xs"} dangerouslySetInnerHTML={{__html: item.deskripsi}} />
<Box w={150}>
<Text fz={"md"} truncate={"end"} lineClamp={1}>{item.judul}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={250}>
<Text fz={"md"} truncate={"end"} lineClamp={1} dangerouslySetInnerHTML={{__html: item.deskripsi}} />
</Box>
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/keamanan/tips-keamanan/${item.id}`)}>
@@ -81,6 +89,14 @@ function ListTipsKeamanan({ search }: { search: string }) {
</TableTbody>
</Table>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my="md"
/>
</Center>
</Box>
);
}

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -22,7 +22,7 @@ function ArtikelKesehatan() {
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListArtikelKesehatan search={search}/>
<ListArtikelKesehatan search={search} />
</Box>
);
}
@@ -46,45 +46,53 @@ function ListArtikelKesehatan({ search }: { search: string }) {
if (!stateArtikelKesehatan.findMany.data) {
return (
<Box py={10}>
<Skeleton h={500}/>
<Skeleton h={500} />
</Box>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Artikel Kesehatan'
href='/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Content</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.title}</TableTd>
<TableTd>{item.content}</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Artikel Kesehatan'
href='/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Content</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Box w={100}>
<Text truncate={'end'} lineClamp={1} fz={'h5'}>{item.title}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={200}>
<Text truncate={'end'} lineClamp={1} fz={'h5'}>{item.content}</Text>
</Box>
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}

View File

@@ -39,7 +39,7 @@ interface FasilitasKesehatanFormBase {
}
function EditFasilitasKesehatan() {
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState);
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.fasilitasKesehatan);
const router = useRouter();
const params = useParams();

View File

@@ -2,7 +2,7 @@
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { Box, Button, Flex, Grid, GridCol, 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';
@@ -12,7 +12,7 @@ import { useProxy } from 'valtio/utils';
function DetailFasilitasKesehatan() {
const params = useParams()
const router = useRouter();
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState)
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.fasilitasKesehatan)
const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState<string | null>(null)
@@ -45,9 +45,23 @@ function DetailFasilitasKesehatan() {
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Text fz={"xl"} fw={"bold"}>Detail Fasilitas Kesehatan</Text>
<Grid>
<GridCol span={12}>
<Text fz={"xl"} fw={"bold"}>Detail Fasilitas Kesehatan</Text>
</GridCol>
{/* <GridCol span={12}>
<Flex gap={"xs"}>
<Button color={colors['blue-button']} onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/${params?.id}/dokter-tenaga-medis`)}>
Tambah Dokter
</Button>
<Button color={colors['blue-button']} onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/${params?.id}/layanan-unggulan/create`)}>
Tambah Layanan
</Button>
</Flex>
</GridCol> */}
</Grid>
{stateFasilitasKesehatan.findUnique.data ? (
<Paper bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
@@ -68,6 +82,14 @@ function DetailFasilitasKesehatan() {
<Text fz={"md"} fw={"bold"}>Layanan Unggulan</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateFasilitasKesehatan.findUnique.data.layananunggulan.content }} />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Fasilitas Pendukung</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateFasilitasKesehatan.findUnique.data.fasilitaspendukung.content }} />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Prosedur Pendaftaran</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateFasilitasKesehatan.findUnique.data.prosedurpendaftaran.content }} />
</Box>
<Box>
<Text fz={"md"} fw={"bold"}>Dokter dan Tenaga Medis</Text>
<Text fz={"md"} fw={"bold"}>Nama Dokter</Text>
@@ -77,14 +99,6 @@ function DetailFasilitasKesehatan() {
<Text fz={"md"} fw={"bold"}>Jadwal</Text>
<Text fz={"md"}>{stateFasilitasKesehatan.findUnique.data.dokterdantenagamedis.jadwal}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Fasilitas Pendukung</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateFasilitasKesehatan.findUnique.data.fasilitaspendukung.content }} />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Prosedur Pendaftaran</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateFasilitasKesehatan.findUnique.data.prosedurpendaftaran.content }} />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Tarif dan Layanan</Text>
<Text fz={"md"} fw={"bold"}>Layanan</Text>
@@ -111,6 +125,7 @@ function DetailFasilitasKesehatan() {
</Paper>
) : null}
</Stack>
</Paper>
{/* Modal Hapus */}

View File

@@ -10,7 +10,7 @@ import { useProxy } from 'valtio/utils';
function CreateFasilitasKesehatan() {
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState)
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.fasilitasKesehatan)
const router = useRouter();

View File

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

View File

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

View File

@@ -0,0 +1,75 @@
'use client'
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
function CreateDokter() {
const params = useParams()
const createState = useProxy(fasilitasKesehatanState.dokter)
const router = useRouter();
const resetForm = () => {
createState.create.create.form = {
name: "",
specialist: "",
jadwal: "",
};
};
const handleSubmit = async () => {
await createState.create.create.create();
resetForm();
router.push(`/admin/kesehatan/fasilitas-kesehatan/${params?.id}/dokter-tenaga-medis`)
};
return (
<Box component="form" onSubmit={handleSubmit}>
<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 Dokter</Title>
<TextInput
label={<Text fz="sm" fw="bold">Nama Dokter</Text>}
placeholder="masukkan nama dokter"
value={createState.create.create.form.name}
onChange={(e) => {
createState.create.create.form.name = e.target.value;
}}
/>
<Text fz="md" fw="bold">Specialist</Text>
<TextInput
label={<Text fz="sm" fw="bold">Specialist</Text>}
placeholder="masukkan specialist"
value={createState.create.create.form.specialist}
onChange={(e) => {
createState.create.create.form.specialist = e.target.value;
}}
/>
<Box>
<Text fz="md" fw="bold">Jadwal</Text>
<CreateEditor
value={createState.create.create.form.jadwal}
onChange={(htmlContent) => {
createState.create.create.form.jadwal = htmlContent;
}}
/>
</Box>
<Button onClick={handleSubmit} bg={colors['blue-button']}>
Simpan
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreateDokter;

View File

@@ -0,0 +1,112 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '@/app/admin/(dashboard)/_com/header';
import JudulList from '@/app/admin/(dashboard)/_com/judulList';
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import { useState } from 'react';
function DokterTenagaMedis() {
const [search, setSearch] = useState("");
const router = useRouter();
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<HeaderSearch
title='Dokter dan Tenaga Medis'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListDokterTenagaMedis search={search} />
</Box>
);
}
function ListDokterTenagaMedis({ search }: { search: string }) {
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.dokter)
const router = useRouter();
const {
data,
loading,
load,
page,
totalPages
} = stateFasilitasKesehatan.findMany
useShallowEffect(() => {
load(page, 10, search)
}, [page, search])
const filteredData = data || []
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
</Box>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Fasilitas Kesehatan'
href={`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis/create`}
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Fasilitas Kesehatan</TableTh>
<TableTh>Alamat</TableTh>
<TableTh>Jam Operasional</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>{item.specialist}</TableTd>
<TableTd>
<Text dangerouslySetInnerHTML={{ __html: item.jadwal }} />
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
</Box>
)
}
export default DokterTenagaMedis;

View File

@@ -4,17 +4,39 @@ import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableT
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
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';
import fasilitasKesehatanState from '../../../_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
function FasilitasKesehatan() {
const [search, setSearch] = useState("");
// const router = useRouter();
return (
<Box>
{/* <Grid>
<GridCol span={12}>
<HeaderSearch
title='Fasilitas Kesehatan'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
</GridCol>
<GridCol span={12}>
<Flex gap={"xs"}>
<Button color={colors['blue-button']} onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis`)}>
<IconList size={20} /> List Dokter
</Button>
<Button color={colors['blue-button']} onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/tarif-layanan`)}>
<IconList size={20} /> List Layanan
</Button>
</Flex>
</GridCol>
</Grid> */}
<HeaderSearch
title='Fasilitas Kesehatan'
placeholder='pencarian'
@@ -22,13 +44,13 @@ function FasilitasKesehatan() {
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListFasilitasKesehatan search={search}/>
<ListFasilitasKesehatan search={search} />
</Box>
);
}
function ListFasilitasKesehatan({ search }: { search: string }) {
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState)
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.fasilitasKesehatan)
const router = useRouter();
useShallowEffect(() => {
@@ -47,47 +69,47 @@ function ListFasilitasKesehatan({ search }: { search: string }) {
if (!stateFasilitasKesehatan.findMany.data) {
return (
<Box py={10}>
<Skeleton h={500}/>
<Skeleton h={500} />
</Box>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Fasilitas Kesehatan'
href='/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Fasilitas Kesehatan</TableTh>
<TableTh>Alamat</TableTh>
<TableTh>Jam Operasional</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>{item.informasiumum.alamat}</TableTd>
<TableTd>{item.informasiumum.jamOperasional}</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Fasilitas Kesehatan'
href='/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Fasilitas Kesehatan</TableTh>
<TableTh>Dokter</TableTh>
<TableTh>Layanan</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>{item.dokterdantenagamedis.name}</TableTd>
<TableTd>{item.tarifdanlayanan.layanan}</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}

View File

@@ -0,0 +1,112 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '@/app/admin/(dashboard)/_com/header';
import JudulList from '@/app/admin/(dashboard)/_com/judulList';
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import { useState } from 'react';
function TarifLayanan() {
const [search, setSearch] = useState("");
const router = useRouter();
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<HeaderSearch
title='Dokter dan Tenaga Medis'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListTarifLayanan search={search} />
</Box>
);
}
function ListTarifLayanan({ search }: { search: string }) {
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState.dokter)
const router = useRouter();
const {
data,
loading,
load,
page,
totalPages
} = stateFasilitasKesehatan.findMany
useShallowEffect(() => {
load(page, 10, search)
}, [page, search])
const filteredData = data || []
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
</Box>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Fasilitas Kesehatan'
href={`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/dokter-tenaga-medis/create`}
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Fasilitas Kesehatan</TableTh>
<TableTh>Alamat</TableTh>
<TableTh>Jam Operasional</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>{item.specialist}</TableTd>
<TableTd>
<Text dangerouslySetInnerHTML={{ __html: item.jadwal }} />
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
</Box>
)
}
export default TarifLayanan;

View File

@@ -0,0 +1,116 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import grafikkepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
import colors from '@/con/colors';
import { Box, Button, 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 EditGrafikHasilKepuasan() {
const editState = useProxy(grafikkepuasan)
const router = useRouter();
const params = useParams();
const [formData, setFormData] = useState({
nama: editState.update.form.nama || '',
tanggal: editState.update.form.tanggal || '',
jenisKelamin: editState.update.form.jenisKelamin || '',
alamat: editState.update.form.alamat || '',
penyakit: editState.update.form.penyakit || '',
});
useEffect(() => {
const loadKelahiran = async () => {
const id = params?.id as string;
if (!id) return;
try {
const data = await editState.update.load(id); // akses langsung, bukan dari proxy
if (data) {
setFormData({
nama: data.nama || '',
tanggal: data.tanggal || '',
jenisKelamin: data.jenisKelamin || '',
alamat: data.alamat || '',
penyakit: data.penyakit || '',
});
}
} catch (error) {
console.error("Error loading grafik hasil kepuasan:", error);
toast.error("Gagal memuat data grafik hasil kepuasan");
}
};
loadKelahiran();
}, [params?.id]);
const handleSubmit = async () => {
try {
editState.update.form = {
...editState.update.form,
nama: formData.nama,
tanggal: formData.tanggal,
jenisKelamin: formData.jenisKelamin,
alamat: formData.alamat,
penyakit: formData.penyakit,
};
await editState.update.submit();
toast.success('grafik hasil kepuasan berhasil diperbarui!');
router.push('/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan');
} catch (error) {
console.error('Error updating grafik hasil kepuasan:', error);
toast.error('Terjadi kesalahan saat memperbarui grafik hasil kepuasan');
}
};
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors["blue-button"]} size={30} />
</Button>
</Box>
<Paper bg={"white"} p={"md"} w={{ base: "100%", md: "50%" }}>
<Stack gap={"xs"}>
<Title order={3}>Edit grafik hasil kepuasan</Title>
<TextInput
value={formData.nama}
onChange={(e) => setFormData({ ...formData, nama: e.target.value })}
label={<Text fz={"sm"} fw={"bold"}>Nama</Text>}
placeholder="masukkan nama"
/>
<TextInput
type='date'
value={formData.tanggal}
onChange={(e) => setFormData({ ...formData, tanggal: e.target.value })}
label={<Text fz={"sm"} fw={"bold"}>Tanggal</Text>}
placeholder="masukkan tanggal"
/>
<TextInput
value={formData.jenisKelamin}
onChange={(e) => setFormData({ ...formData, jenisKelamin: e.target.value })}
label={<Text fz={"sm"} fw={"bold"}>Jenis Kelamin</Text>}
placeholder="masukkan jenis kelamin"
/>
<TextInput
value={formData.alamat}
onChange={(e) => setFormData({ ...formData, alamat: e.target.value })}
label={<Text fz={"sm"} fw={"bold"}>Alamat</Text>}
placeholder="masukkan alamat"
/>
<TextInput
value={formData.penyakit}
onChange={(e) => setFormData({ ...formData, penyakit: e.target.value })}
label={<Text fz={"sm"} fw={"bold"}>Penyakit</Text>}
placeholder="masukkan penyakit"
/>
<Button onClick={handleSubmit}>Simpan</Button>
</Stack>
</Paper>
</Box>
);
}
export default EditGrafikHasilKepuasan;

View File

@@ -1,80 +1,125 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import grafikkepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { useProxy } from 'valtio/utils';
function EditGrafikHasilKepuasan() {
import { Box, Button, Flex, 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 { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
import grafikkepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
import colors from '@/con/colors';
function DetailGrafikHasilKepuasan() {
const state = useProxy(grafikkepuasan)
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const params = useParams()
const router = useRouter()
const params = useParams() as { id: string }
const stateGrafikKepuasan = useProxy(grafikkepuasan)
const id = params.id
useShallowEffect(() => {
state.findUnique.load(params?.id as string)
}, [])
// Load data saat komponen mount
useEffect(() => {
if (id) {
stateGrafikKepuasan.findUnique.load(id).then(() => {
const data = stateGrafikKepuasan.findUnique.data
if (data) {
stateGrafikKepuasan.update.form = {
label: data.label || '',
jumlah: data.jumlah || '',
}
}
})
const handleHapus = () => {
if (selectedId) {
state.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
router.push("/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan")
}
}, [id])
const handleSubmit = async () => {
// Set the ID before submitting
stateGrafikKepuasan.update.id = id;
await stateGrafikKepuasan.update.submit();
router.push('/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan')
}
return (
if (!state.findUnique.data) {
return (
<Stack py={10}>
<Skeleton h={40} />
</Stack>
)
}
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack size={20} />
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
<Paper bg={colors['white-1']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
<Stack>
<Title order={3}>Edit Grafik Hasil Kepuasan</Title>
<TextInput
label="Label"
placeholder="masukkan label"
value={stateGrafikKepuasan.update.form.label}
onChange={(val) => {
stateGrafikKepuasan.update.form.label = val.currentTarget.value;
}}
/>
<TextInput
label="Jumlah"
type="number"
placeholder="masukkan jumlah"
value={stateGrafikKepuasan.update.form.jumlah}
onChange={(val) => {
stateGrafikKepuasan.update.form.jumlah = val.currentTarget.value;
}}
/>
<Button
mt={10}
bg={colors['blue-button']}
onClick={handleSubmit}
>
Simpan
</Button>
<Text fz={"xl"} fw={"bold"}>Detail Data Grafik Hasil Kepuasan</Text>
{state.findUnique.data ? (
<Paper key={state.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fw={"bold"} fz={"lg"}>Nama</Text>
<Text fz={"lg"}>{state.findUnique.data?.nama}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Tanggal</Text>
<Text fz={"lg"}>
{new Date(state.findUnique.data?.tanggal).toLocaleDateString('id-ID', {
day: '2-digit',
month: 'long',
year: 'numeric'
})}
</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Jenis Kelamin</Text>
<Text fz={"lg"} >{state.findUnique.data?.jenisKelamin}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Alamat</Text>
<Text fz={"lg"} >{state.findUnique.data?.alamat}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Penyakit</Text>
<Text fz={"lg"} >{state.findUnique.data?.penyakit}</Text>
</Box>
<Flex gap={"xs"} mt={10}>
<Button
onClick={() => {
if (state.findUnique.data) {
setSelectedId(state.findUnique.data.id);
setModalHapus(true);
}
}}
disabled={state.delete.loading || !state.findUnique.data}
color={"red"}
>
<IconX size={20} />
</Button>
<Button
onClick={() => {
if (state.findUnique.data) {
router.push(`/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/${state.findUnique.data.id}/edit`);
}
}}
disabled={!state.findUnique.data}
color={"green"}
>
<IconEdit size={20} />
</Button>
</Flex>
</Stack>
</Paper>
) : null}
</Stack>
</Paper>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text='Apakah anda yakin ingin menghapus data ini?'
/>
</Box>
)
);
}
export default EditGrafikHasilKepuasan;
export default DetailGrafikHasilKepuasan;

View File

@@ -16,20 +16,16 @@ function CreateGrafikHasilKepuasanMasyarakat() {
const resetForm = () => {
stateGrafikKepuasan.create.form = {
label: "",
jumlah: "",
nama: "",
tanggal: "",
jenisKelamin: "",
alamat: "",
penyakit: "",
}
}
const handleSubmit = async () => {
const id = await stateGrafikKepuasan.create.create();
if (id) {
const idStr = String(id);
await stateGrafikKepuasan.findUnique.load(idStr);
if (stateGrafikKepuasan.findUnique.data) {
setChartData([stateGrafikKepuasan.findUnique.data]);
}
}
await stateGrafikKepuasan.create.create();
resetForm();
router.push("/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan");
}
@@ -45,21 +41,48 @@ function CreateGrafikHasilKepuasanMasyarakat() {
<Title order={4}>Tambah Grafik Hasil Kepuasan Masyarakat</Title>
<Stack gap={"xs"}>
<TextInput
label="Label"
label="Nama"
type="text"
value={stateGrafikKepuasan.create.form.label}
placeholder="Masukkan label"
value={stateGrafikKepuasan.create.form.nama}
placeholder="Masukkan nama"
onChange={(val) => {
stateGrafikKepuasan.create.form.label = val.currentTarget.value;
stateGrafikKepuasan.create.form.nama = val.currentTarget.value;
}}
/>
<TextInput
label="Jumlah"
type="number"
value={stateGrafikKepuasan.create.form.jumlah}
placeholder="Masukkan jumlah"
label="Tanggal"
type="date"
value={stateGrafikKepuasan.create.form.tanggal}
placeholder="Masukkan tanggal"
onChange={(val) => {
stateGrafikKepuasan.create.form.jumlah = val.currentTarget.value;
stateGrafikKepuasan.create.form.tanggal = val.currentTarget.value;
}}
/>
<TextInput
label="Jenis Kelamin"
type="text"
value={stateGrafikKepuasan.create.form.jenisKelamin}
placeholder="Masukkan jenis kelamin"
onChange={(val) => {
stateGrafikKepuasan.create.form.jenisKelamin = val.currentTarget.value;
}}
/>
<TextInput
label="Alamat"
type="text"
value={stateGrafikKepuasan.create.form.alamat}
placeholder="Masukkan alamat"
onChange={(val) => {
stateGrafikKepuasan.create.form.alamat = val.currentTarget.value;
}}
/>
<TextInput
label="Penyakit"
type="text"
value={stateGrafikKepuasan.create.form.penyakit}
placeholder="Masukkan penyakit"
onChange={(val) => {
stateGrafikKepuasan.create.form.penyakit = val.currentTarget.value;
}}
/>
<Group>

View File

@@ -1,16 +1,16 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/judulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikkepuasan from '../../../_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import grafikkepuasan from '../../../_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
function GrafikHasilKepuasanMasyarakat() {
const [search, setSearch] = useState("");
@@ -30,53 +30,79 @@ function GrafikHasilKepuasanMasyarakat() {
function ListGrafikHasilKepuasanMasyarakat({ search }: { search: string }) {
type PDKMGrafik = {
id: string;
label: string;
jumlah: number;
nama: string;
tanggal: string | Date; // Allow both string and Date types
jenisKelamin: string;
alamat: string;
penyakit: string;
createdAt?: Date; // Add optional fields that might come from the API
updatedAt?: Date;
deletedAt?: Date | null;
}
const stateGrafikKepuasan = useProxy(grafikkepuasan);
const [chartData, setChartData] = useState<PDKMGrafik[]>([]);
const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready
const isTablet = useMediaQuery('(max-width: 1024px)')
const isMobile = useMediaQuery('(max-width: 768px)')
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const router = useRouter();
const handleDelete = () => {
if (selectedId) {
stateGrafikKepuasan.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
stateGrafikKepuasan.findMany.load()
}
}
const {
data,
page,
totalPages,
loading,
load
} = stateGrafikKepuasan.findMany;
useShallowEffect(() => {
setMounted(true)
stateGrafikKepuasan.findMany.load()
}, [])
load(page, 10, search)
}, [page, search])
useEffect(() => {
setMounted(true);
if (stateGrafikKepuasan.findMany.data) {
setChartData(stateGrafikKepuasan.findMany.data.map((item) => ({
if (data) {
setChartData(data.map((item) => ({
id: item.id,
label: item.label,
jumlah: Number(item.jumlah),
nama: item.nama,
tanggal: item.tanggal instanceof Date ? item.tanggal.toISOString() : item.tanggal,
jenisKelamin: item.jenisKelamin,
alamat: item.alamat,
penyakit: item.penyakit,
})));
}
}, [stateGrafikKepuasan.findMany.data]);
}, [data]);
const filteredData = (stateGrafikKepuasan.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.label.toLowerCase().includes(keyword) ||
item.jumlah.toString().toLowerCase().includes(keyword)
);
});
// Add this function to process the data
const processDiseaseData = (data: PDKMGrafik[]) => {
const diseaseCount: Record<string, number> = {};
if (!stateGrafikKepuasan.findMany.data) {
data.forEach(item => {
const penyakit = item.penyakit.trim();
if (penyakit) {
diseaseCount[penyakit] = (diseaseCount[penyakit] || 0) + 1;
}
});
return Object.entries(diseaseCount).map(([name, count]) => ({
name,
count
}));
};
// Add this state to store the processed chart data
const [diseaseChartData, setDiseaseChartData] = useState<{ name: string, count: number }[]>([]);
// Update the chart data when data changes
useEffect(() => {
if (data && data.length > 0) {
setDiseaseChartData(processDiseaseData(data));
}
}, [data]);
const filteredData = data || [];
if (loading || !data) {
return (
<Stack>
<Skeleton h={500} />
@@ -89,40 +115,36 @@ function ListGrafikHasilKepuasanMasyarakat({ search }: { search: string }) {
<Stack gap={"xs"}>
{/* Form Input */}
<Paper bg={colors['white-1']} p={'md'}>
<JudulListTab
<JudulList
title='List Grafik Hasil Kepuasan Masyarakat'
href='/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/create'
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Label</TableTh>
<TableTh>Jumlah</TableTh>
<TableTh>Edit</TableTh>
<TableTh>Delete</TableTh>
<TableTh>Nama</TableTh>
<TableTh>Tanggal</TableTh>
<TableTh>Jenis Kelamin</TableTh>
<TableTh>Penyakit</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.label}</TableTd>
<TableTd>{item.jumlah}</TableTd>
<TableTd>{item.nama}</TableTd>
<TableTd>
{new Date(item.tanggal).toLocaleDateString('id-ID', {
day: '2-digit',
month: 'long',
year: 'numeric'
})}
</TableTd>
<TableTd>{item.jenisKelamin}</TableTd>
<TableTd>{item.penyakit}</TableTd>
<TableTd>
<Button color='green' onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/${item.id}`)}>
<IconEdit size={20} />
</Button>
</TableTd>
<TableTd>
<Button
color='red'
disabled={stateGrafikKepuasan.delete.loading}
onClick={() => {
setSelectedId(item.id)
setModalHapus(true)
}}>
<IconTrash size={20} />
<IconDeviceImacCog size={20} />
</Button>
</TableTd>
</TableTr>
@@ -130,6 +152,15 @@ function ListGrafikHasilKepuasanMasyarakat({ search }: { search: string }) {
</TableTbody>
</Table>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
{/* Chart */}
@@ -141,30 +172,29 @@ function ListGrafikHasilKepuasanMasyarakat({ search }: { search: string }) {
</Paper>
</Box>
) : (
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Box style={{ width: '100%', minWidth: 300, height: 420, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title pb={10} order={4}>Grafik Hasil Kepuasan Masyarakat</Title>
{mounted && chartData.length > 0 && (
<BarChart width={isMobile ? 450 : isTablet ? 500 : 550} height={350} data={chartData} >
<XAxis dataKey="label" />
{mounted && diseaseChartData.length > 0 && (
<BarChart width={isMobile ? 450 : isTablet ? 500 : 550} height={350} data={diseaseChartData} >
<XAxis
dataKey="name"
tick={{ fontSize: 12 }}
interval={0}
angle={-45}
textAnchor="end"
height={70}
/>
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="jumlah" fill={colors['blue-button']} name="Jumlah" />
<Bar dataKey="count" fill={colors['blue-button']} name="Jumlah Kasus" />
</BarChart>
)}
</Paper>
</Box>
)}
</Stack>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleDelete}
text='Apakah anda yakin ingin menghapus grafik hasil kepuasan masyarakat ini?'
/>
</Box>
);
}

View File

@@ -5,7 +5,7 @@ import persentasekelahiran from '@/app/admin/(dashboard)/_state/kesehatan/data_k
import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack, IconEdit, IconSearch } from '@tabler/icons-react';
import { IconArrowBack, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
@@ -47,7 +47,7 @@ function ListKelahiran({ search }: { search: string }) {
useShallowEffect(() => {
load(page, 10, search)
}, [search])
}, [page, search])
const filteredData = data || []
@@ -93,7 +93,7 @@ function ListKelahiran({ search }: { search: string }) {
<TableTd>{item.alamat}</TableTd>
<TableTd>
<Button color='green' onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/kelahiran/${item.id}`)}>
<IconEdit size={20} />
<IconDeviceImacCog size={20} />
</Button>
</TableTd>
</TableTr>

View File

@@ -2,7 +2,7 @@
'use client'
import persentasekelahiran from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
import colors from '@/con/colors';
import { ActionIcon, Box, Button, Center, Flex, Paper, Skeleton, Stack, Table, Text, Title } from '@mantine/core';
import { ActionIcon, Box, Center, Flex, Paper, Select, Skeleton, Stack, Table, Text, Title } from '@mantine/core';
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
import { IconBabyCarriage, IconGrave2 } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -157,20 +157,18 @@ function GrafikPersentaseKelahiranKematian() {
) : (
<>
{/* Year Selector */}
<Box mb="md">
<Text mb="xs" fw={500}>Pilih Tahun:</Text>
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
{chartData.map(({ tahun }) => (
<Button
key={tahun}
variant={selectedYear === tahun ? 'filled' : 'outline'}
onClick={() => setSelectedYear(tahun)}
size="xs"
>
{tahun}
</Button>
))}
</div>
<Box mb="md" style={{ maxWidth: '200px' }}>
<Select
label="Pilih Tahun"
placeholder="Pilih Tahun"
data={chartData.map((item) => ({
value: item.tahun,
label: item.tahun
}))}
value={selectedYear}
onChange={(value) => setSelectedYear(value || '')}
size="xs"
/>
</Box>
{/* Main Chart */}

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import JudulList from '../../_com/judulList';
import HeaderSearch from '../../_com/header';
@@ -21,7 +21,7 @@ function InfoWabahPenyakit() {
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListInfoWabahPenyakit search={search}/>
<ListInfoWabahPenyakit search={search} />
</Box>
);
}
@@ -30,19 +30,21 @@ function ListInfoWabahPenyakit({ search }: { search: string }) {
const infoWabahPenyakitState = useProxy(infoWabahPenyakit)
const router = useRouter()
const {
data,
page,
totalPages,
loading,
load,
} = infoWabahPenyakitState.findMany;
useShallowEffect(() => {
infoWabahPenyakitState.findMany.load()
}, [])
load(page, 10, search)
}, [page, search])
const filteredData = (infoWabahPenyakitState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.deskripsiSingkat.toLowerCase().includes(keyword)
);
});
const filteredData = data || []
if (!infoWabahPenyakitState.findMany.data) {
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
@@ -51,49 +53,60 @@ function ListInfoWabahPenyakit({ search }: { search: string }) {
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Info Wabah Penyakit'
href='/admin/kesehatan/info-wabah-penyakit/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Deskripsi Singkat</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>{item.name}</Text>
</Box>
</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"}>{item.deskripsiSingkat}</Text>
</TableTd>
<TableTd>
<Image w={100} src={item.image?.link} alt="image" />
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/info-wabah-penyakit/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Info Wabah Penyakit'
href='/admin/kesehatan/info-wabah-penyakit/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Deskripsi Singkat</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>{item.name}</Text>
</Box>
</TableTd>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>{item.deskripsiSingkat}</Text>
</Box>
</TableTd>
<TableTd>
<Image w={100} src={item.image?.link} alt="image" />
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/info-wabah-penyakit/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
</Box>
)
}

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import JudulList from '../../_com/judulList';
import HeaderSearch from '../../_com/header';
@@ -21,7 +21,7 @@ function KontakDarurat() {
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListKontakDarurat search={search}/>
<ListKontakDarurat search={search} />
</Box>
);
}
@@ -30,19 +30,21 @@ function ListKontakDarurat({ search }: { search: string }) {
const kontakDaruratState = useProxy(kontakDarurat)
const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = kontakDaruratState.findMany;
useShallowEffect(() => {
kontakDaruratState.findMany.load()
}, [])
load(page, 10, search)
}, [page, search])
const filteredData = (kontakDaruratState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.deskripsi.toLowerCase().includes(keyword)
);
});
const filteredData = data || []
if (!kontakDaruratState.findMany.data) {
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
@@ -77,7 +79,9 @@ function ListKontakDarurat({ search }: { search: string }) {
</Box>
</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
<Box w={100}>
<Text truncate="end" lineClamp={1} fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
</TableTd>
<TableTd>
<Image w={100} src={item.image?.link} alt="image" />
@@ -94,6 +98,15 @@ function ListKontakDarurat({ search }: { search: string }) {
</Box>
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
</Box>
)
}

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import JudulList from '../../_com/judulList';
import HeaderSearch from '../../_com/header';
@@ -21,7 +21,7 @@ function PenangananDarurat() {
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListPenangananDarurat search={search}/>
<ListPenangananDarurat search={search} />
</Box>
);
}
@@ -30,19 +30,21 @@ function ListPenangananDarurat({ search }: { search: string }) {
const penangananDaruratState = useProxy(penangananDarurat)
const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = penangananDaruratState.findMany;
useShallowEffect(() => {
penangananDaruratState.findMany.load()
}, [])
load(page, 10, search)
}, [page, search])
const filteredData = (penangananDaruratState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.deskripsi.toLowerCase().includes(keyword)
);
});
const filteredData = data || []
if (!penangananDaruratState.findMany.data) {
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
@@ -52,48 +54,59 @@ function ListPenangananDarurat({ search }: { search: string }) {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Penanganan Darurat'
href='/admin/kesehatan/penanganan-darurat/create'
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Penanganan Darurat'
href='/admin/kesehatan/penanganan-darurat/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>{item.name}</Text>
</Box></TableTd>
<TableTd>
<Box w={100}>
<Text lineClamp={1} truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
</TableTd>
<TableTd>
<Image w={100} src={item.image?.link} alt="image" />
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/penanganan-darurat/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>{item.name}</Text>
</Box></TableTd>
<TableTd>
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</TableTd>
<TableTd>
<Image w={100} src={item.image?.link} alt="image" />
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/penanganan-darurat/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
</Center>
</Box>
)
}

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import JudulList from '../../_com/judulList';
import HeaderSearch from '../../_com/header';
@@ -21,7 +21,7 @@ function ProgramKesehatan() {
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListProgramKesehatan search={search}/>
<ListProgramKesehatan search={search} />
</Box>
);
}
@@ -30,19 +30,21 @@ function ListProgramKesehatan({ search }: { search: string }) {
const programKesehatanState = useProxy(programKesehatan)
const router = useRouter()
const {
data,
page,
totalPages,
loading,
load,
} = programKesehatanState.findMany;
useShallowEffect(() => {
programKesehatanState.findMany.load()
}, [])
load(page, 10, search)
}, [page, search])
const filteredData = (programKesehatanState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.deskripsiSingkat.toLowerCase().includes(keyword)
);
});
const filteredData = data || []
if (!programKesehatanState.findMany.data) {
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500} />
@@ -77,7 +79,9 @@ function ListProgramKesehatan({ search }: { search: string }) {
</Box>
</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsiSingkat }} />
<Box w={100}>
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsiSingkat }} />
</Box>
</TableTd>
<TableTd>
<Image w={100} src={item.image?.link} alt="image" />
@@ -94,6 +98,15 @@ function ListProgramKesehatan({ search }: { search: string }) {
</Box>
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
</Box>
)
}

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { Box, Button, Center, Image, Pagination, 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';
@@ -21,7 +21,7 @@ function Puskesmas() {
value={search}
onChange={(e) => setSearch(e.currentTarget.value)}
/>
<ListPuskesmas search={search}/>
<ListPuskesmas search={search} />
</Box>
);
}
@@ -30,64 +30,75 @@ function ListPuskesmas({ search }: { search: string }) {
const statePuskesmas = useProxy(puskesmasState)
const router = useRouter();
const {
data,
page,
totalPages,
loading,
load,
} = statePuskesmas.findMany;
useShallowEffect(() => {
statePuskesmas.findMany.load()
}, [])
load(page, 10, search)
}, [page, search])
const filteredData = (statePuskesmas.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.alamat.toLowerCase().includes(keyword)
);
});
const filteredData = data || []
if (!statePuskesmas.findMany.data) {
if (loading || !data) {
return (
<Box py={10}>
<Skeleton h={500}/>
<Skeleton h={500} />
</Box>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Puskesmas'
href='/admin/kesehatan/puskesmas/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Nama Puskesmas</TableTh>
<TableTh>Alamat</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>{item.alamat}</TableTd>
<TableTd>
<Image w={100} src={item.image.link} alt="image" />
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/puskesmas/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Puskesmas'
href='/admin/kesehatan/puskesmas/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Nama Puskesmas</TableTh>
<TableTh>Alamat</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
</TableThead>
<TableTbody>
{filteredData.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>{item.alamat}</TableTd>
<TableTd>
<Image w={100} src={item.image.link} alt="image" />
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/puskesmas/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)} // ini penting!
total={totalPages}
mt="md"
mb="md"
/>
</Center>
</Box>
)
}

View File

@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { ActionIcon, Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { ActionIcon, Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconFile, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
@@ -30,19 +30,22 @@ function APBDes() {
function ListAPBDes({ search }: { search: string }) {
const listState = useProxy(apbdes)
const router = useRouter();
useEffect(() => {
listState.findMany.load()
}, [])
const filteredData = (listState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.jumlah.toLowerCase().includes(keyword)
)
});
const {
data,
page,
totalPages,
loading,
load,
} = listState.findMany
if (!listState.findMany.data) {
useShallowEffect(() => {
load(page, 10, search)
}, [page, search])
const filteredData = data || []
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
@@ -88,7 +91,7 @@ function ListAPBDes({ search }: { search: string }) {
rel="noopener noreferrer"
variant='transparent'
>
<IconFile size={25} color={colors['blue-button']}/>
<IconFile size={25} color={colors['blue-button']} />
</ActionIcon>
) : (
<Text>Tidak ada dokumen tersedia</Text>
@@ -106,6 +109,14 @@ function ListAPBDes({ search }: { search: string }) {
</Box>
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
</Box>
)
}

View File

@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconSearch, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
@@ -50,19 +50,11 @@ function ListKategoriKegiatan({ search }: { search: string }) {
}
}
useEffect(() => {
load(page, 10)
}, [page])
useShallowEffect(() => {
load(page, 10, search)
}, [page, search])
const filteredData = useMemo(() => {
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword)
);
})
}, [data, search]);
const filteredData = data || []
// Handle loading state
if (loading || !data) {

View File

@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
@@ -38,22 +38,11 @@ function ListDesaAntiKorupsi({ search }: { search: string }) {
load,
} = listState.findMany;
useEffect(() => {
load(page, 10);
}, [page]);
useShallowEffect(() => {
load(page, 10, search);
}, [page, search]);
const filteredData = useMemo(() => {
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword) ||
item.deskripsi?.toLowerCase().includes(keyword) ||
item.kategori?.name?.toLowerCase().includes(keyword)
);
})
.sort((a, b) => b.createdAt - a.createdAt);
}, [data, search]);
const filteredData = data || []
// Handle loading state
if (loading || !data) {

View File

@@ -1,6 +1,6 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconSearch, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
@@ -42,18 +42,21 @@ function ListKategoriPrestasi({ search }: { search: string }) {
}
}
const {
data,
page,
totalPages,
loading,
load,
} = stateKategori.findMany
useShallowEffect(() => {
stateKategori.findMany.load()
}, [])
load(page, 10, search)
}, [page, search])
const filteredData = (stateKategori.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword)
);
});
const filteredData = data || []
if (!stateKategori.findMany.data) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
@@ -100,6 +103,14 @@ function ListKategoriPrestasi({ search }: { search: string }) {
</Table>
</Box>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}

View File

@@ -1,7 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
@@ -30,20 +30,22 @@ function ListPrestasiDesa() {
function ListPrestasi({ search }: { search: string }) {
const listState = useProxy(prestasiState.prestasiDesa)
const router = useRouter();
const{
data,
page,
totalPages,
loading,
load,
} = listState.findMany
useEffect(() => {
listState.findMany.load()
}, [])
load(page, 10, search)
}, [page, search])
const filteredData = (listState.findMany.data || []).filter(item => {
const keyword = search.toLowerCase();
return (
item.name.toLowerCase().includes(keyword) ||
item.deskripsi.toLowerCase().includes(keyword) ||
item.kategori?.name?.toLowerCase().includes(keyword)
);
});
const filteredData = data || []
if (!listState.findMany.data) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
@@ -95,6 +97,14 @@ function ListPrestasi({ search }: { search: string }) {
</Box>
</Stack>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
</Box>
)
}

View File

@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Center, Image, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
@@ -38,20 +38,11 @@ function ListMediaSosial({ search }: { search: string }) {
load,
} = stateMediaSosial.findMany;
useEffect(() => {
load(page, 10)
}, [page])
useShallowEffect(() => {
load(page, 10, search)
}, [page, search])
const filteredData = useMemo(() => {
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword) ||
item.iconUrl?.toLowerCase().includes(keyword)
);
})
}, [data, search]);
const filteredData = data || []
// Handle loading state
if (loading || !data) {

View File

@@ -55,7 +55,10 @@ function DetailProgramInovasi() {
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
<Text fz={"lg"}>{stateProgramInovasi.findUnique.data?.description}</Text>
<Text
fz={"lg"}
>{stateProgramInovasi.findUnique.data?.description}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Link</Text>
@@ -63,6 +66,12 @@ function DetailProgramInovasi() {
href={stateProgramInovasi.findUnique.data?.link || "#"}
target="_blank"
rel="noopener noreferrer"
style={{
wordWrap: 'break-word',
whiteSpace: 'pre-wrap',
overflowWrap: 'break-word',
width: '100%'
}}
>
{stateProgramInovasi.findUnique.data?.link || "Tidak ada link"}
</a>

View File

@@ -1,10 +1,10 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
@@ -38,21 +38,11 @@ function ListProgramInovasi({ search }: { search: string }) {
load,
} = stateProgramInovasi.findMany;
useEffect(() => {
load(page, 10);
}, [page]);
useShallowEffect(() => {
load(page, 10, search);
}, [page, search]);
const filteredData = useMemo(() => {
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword) ||
item.description?.toLowerCase().includes(keyword) ||
item.link?.toLowerCase().includes(keyword)
);
})
}, [data, search]);
const filteredData = data || []
if (loading || !data) {
return (

View File

@@ -1,14 +1,14 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../_com/header';
import sdgsDesa from '../../_state/landing-page/sdgs-desa';
import JudulList from '../../_com/judulList';
import sdgsDesa from '../../_state/landing-page/sdgs-desa';
function SdgsDesa() {
@@ -39,20 +39,11 @@ function ListSdgsDesa({ search }: { search: string }) {
load,
} = listState.findMany;
useEffect(() => {
load(page, 10)
}, [])
useShallowEffect(() => {
load(page, 10, search)
}, [page, search])
const filteredData = useMemo(() => {
if (!data) return [];
return data.filter(item => {
const keyword = search.toLowerCase();
return (
item.name?.toLowerCase().includes(keyword) ||
item.jumlah?.toLowerCase().includes(keyword)
);
})
}, [data, search]);
const filteredData = data || []
// Handle loading state
if (loading || !data) {

View File

@@ -106,8 +106,16 @@ function ListDaftarInformasi({ search }: { search: string }) {
{filteredData.map((item, index) => (
<TableTr key={item.id}>
<TableTd style={{ textAlign: 'center' }}>{index + 1}</TableTd>
<TableTd style={{ wordWrap: 'break-word' }}>{item.jenisInformasi}</TableTd>
<TableTd style={{ wordWrap: 'break-word' }} dangerouslySetInnerHTML={{ __html: item.deskripsi }}></TableTd>
<TableTd style={{ wordWrap: 'break-word' }}>
<Box w={200}>
<Text fz={"md"} truncate={"end"} lineClamp={1}>{item.jenisInformasi}</Text>
</Box>
</TableTd>
<TableTd style={{ wordWrap: 'break-word' }}>
<Box w={200}>
<Text fz={"md"} truncate={"end"} lineClamp={1} dangerouslySetInnerHTML={{ __html: item.deskripsi }}></Text>
</Box>
</TableTd>
<TableTd style={{ textAlign: 'center' }}>
<Button bg={"green"} onClick={() => router.push(`/admin/ppid/daftar-informasi-publik-desa-darmasaba/${item.id}`)}>
<IconDeviceImacCog size={25} />

View File

@@ -41,15 +41,11 @@ function EditResponden() {
try {
const data = await state.update.load(id);
if (data) {
const formattedDate = data.tanggal && !isNaN(new Date(data.tanggal).getTime())
? new Date(data.tanggal).toISOString().split('T')[0]
: '';
// ⬇️ FIX PENTING: tambahkan ini
state.update.id = id;
state.update.form = {
name: data.name,
tanggal: formattedDate,
tanggal: data.tanggal,
jenisKelaminId: data.jenisKelaminId,
ratingId: data.ratingId,
kelompokUmurId: data.kelompokUmurId,
@@ -76,6 +72,7 @@ function EditResponden() {
const handleSubmit = async () => {
state.update.id = id;
state.update.form = { ...formData }; // <-- sinkronisasi manual
await state.update.submit();
router.push('/admin/ppid/ikm-desa-darmasaba/responden')
}
@@ -103,12 +100,15 @@ function EditResponden() {
}}
/>
<TextInput
label="Tanggal"
type="date"
value={formData.tanggal}
placeholder='Pilih tanggal'
value={formData.tanggal ? new Date(formData.tanggal).toISOString().split('T')[0] : ''}
onChange={(e) => {
const selectedDate = e.currentTarget.value;
setFormData({
...formData,
tanggal: e.currentTarget.value, // ✅ sudah format YYYY-MM-DD
tanggal: selectedDate,
});
}}
/>

View File

@@ -1,13 +1,12 @@
'use client';
import colors from '@/con/colors';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Button, Center, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImac, 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 JudulList from '../../../_com/judulList';
import indeksKepuasanState from '../../../_state/landing-page/indeks-kepuasan';
function Responden() {
@@ -60,10 +59,7 @@ function ListResponden({ search }: ListRespondenProps) {
<Box py={10}>
<Paper p="md">
<Stack>
<JudulList
title='List Data Berdasarkan Jenis Kelamin Responden'
href='/admin/ppid/ikm-desa-darmasaba/responden/create'
/>
<Title>Responden</Title>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
@@ -86,10 +82,7 @@ function ListResponden({ search }: ListRespondenProps) {
<Box>
<Stack gap="xs">
<Paper bg={colors['white-1']} p="md" h={{ base: 730, md: 650 }}>
<JudulList
title='List Data Responden'
href='/admin/ppid/ikm-desa-darmasaba/responden/create'
/>
<Title mb={10} order={3}>List Responden</Title>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>

View File

@@ -4,7 +4,7 @@ import colors from '@/con/colors';
import { Badge, Box, Button, Center, Group, Pagination, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, ThemeIcon } from '@mantine/core';
import { IconCheck, IconDeviceImacCog, IconSearch, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useMemo, useState } from 'react';
import { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
@@ -39,22 +39,10 @@ function ListPegawaiPPID({ search }: { search: string }) {
} = stateOrganisasi.findMany;
useEffect(() => {
load(page, 10);
}, [page]);
load(page, 10, search);
}, [page, search]);
const filteredData = useMemo(() => {
if (!data) return [];
return 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)
);
})
.sort((a, b) => a.posisi?.hierarki - b.posisi?.hierarki);
}, [data, search]);
const filteredData = data || []
// Handle loading state
if (loading || !data) {

View File

@@ -1,7 +1,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { Box, Button, Center, Pagination, 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 { useEffect, useState } from 'react';
@@ -33,9 +33,17 @@ function ListPosisiOrganisasiPPID({ search }: { search: string }) {
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const {
data,
page,
totalPages,
loading,
load,
} = stateOrganisasi.findMany;
useEffect(() => {
stateOrganisasi.findMany.load()
}, [])
load(page, 10, search);
}, [page, search]);
const handleHapus = async () => {
if (selectedId) {
@@ -45,17 +53,9 @@ function ListPosisiOrganisasiPPID({ search }: { search: string }) {
}
}
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)
);
})
.sort((a, b) => a.hierarki - b.hierarki);
const filteredData = data || []
if (!stateOrganisasi.findMany.data) {
if (loading || !data) {
return (
<Stack py={10}>
<Skeleton h={500} />
@@ -120,6 +120,14 @@ function ListPosisiOrganisasiPPID({ search }: { search: string }) {
</Table>
</Box>
</Paper>
<Center>
<Pagination
value={page}
onChange={(newPage) => load(newPage)}
total={totalPages}
my={"md"}
/>
</Center>
{/* Modal Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}

View File

@@ -1,21 +1,55 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function lowonganKerjaFindMany() {
try {
const data = await prisma.lowonganPekerjaan.findMany({
where: { isActive: true },
});
async function lowonganKerjaFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit;
return {
success: true,
message: "Success fetch lowongan kerja",
data,
};
} catch (e) {
console.error("Find many error:", e);
return {
success: false,
message: "Failed fetch lowongan kerja",
};
}
}
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ posisi: { contains: search, mode: 'insensitive' } },
{ namaPerusahaan: { contains: search, mode: 'insensitive' } },
{ lokasi: { contains: search, mode: 'insensitive' } },
];
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.lowonganPekerjaan.findMany({
where,
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.lowonganPekerjaan.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil lowongan kerja dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data lowongan kerja",
};
}
}
export default lowonganKerjaFindMany;

View File

@@ -1,26 +1,84 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function pasarDesaFindMany() {
const data = await prisma.pasarDesa.findMany({
where: {
isActive: true, // Opsional filter
},
orderBy: {
createdAt: "desc",
},
include: {
image: true,
KategoriToPasar: {
async function pasarDesaFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const categoryId = context.query.categoryId as string | undefined;
const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan filter kategori (jika ada)
if (categoryId) {
where.KategoriToPasar = {
some: {
kategoriId: categoryId
}
};
}
// Tambahkan pencarian (jika ada)
if (search) {
where.AND = where.AND || [];
where.AND.push({
OR: [
{ nama: { contains: search, mode: 'insensitive' } },
{ alamatUsaha: { contains: search, mode: 'insensitive' } },
{
KategoriToPasar: {
some: {
kategori: {
nama: { contains: search, mode: 'insensitive' }
}
}
}
}
]
});
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.pasarDesa.findMany({
where,
include: {
kategori: true,
image: true,
KategoriToPasar: {
include: {
kategori: true
}
}
},
},
},
});
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.pasarDesa.count({ where }),
]);
return {
success: true,
message: "Berhasil mengambil semua data pasar desa",
data,
};
return {
success: true,
message: "Berhasil ambil pasar desa dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data pasar desa",
};
}
}
export default pasarDesaFindMany;

View File

@@ -1,15 +1,60 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function kategoriProdukFindMany() {
const data = await prisma.kategoriProduk.findMany();
return {
success: true,
data: data.map((item: any) => {
return {
id: item.id,
nama: item.nama,
}
}),
async function kategoriProdukFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search2 = (context.query.search as string) || '';
const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search2) {
where.OR = [
{ nama: { contains: search2, mode: 'insensitive' } },
{KategoriToPasar : {
some: {
kategori: {
nama: { contains: search2, mode: 'insensitive' }
}
}
}}
];
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.kategoriProduk.findMany({
where,
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.kategoriProduk.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil kategori produk dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
}
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data kategori produk",
};
}
}
export default kategoriProdukFindMany;

View File

@@ -1,18 +1,53 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function programKemiskinanFindMany() {
const data = await prisma.programKemiskinan.findMany({
include: {
statistik: true, // ikut sertakan relasinya
},
orderBy: {
createdAt: "desc",
},
});
return {
success: true,
message: "Success get all program layanan",
data,
};
export default async function programKemiskinanFindMany(context: Context) {
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ nama: { contains: search, mode: 'insensitive' } },
{ deskripsi: { contains: search, mode: 'insensitive' } },
];
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.programKemiskinan.findMany({
where,
include: {
statistik: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.programKemiskinan.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil program kemiskinan dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data program kemiskinan",
};
}
}

View File

@@ -1,24 +1,57 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function keamananLingkunganFindMany() {
try {
const data = await prisma.keamananLingkungan.findMany({
where: { isActive: true },
include: {
image: true,
},
});
async function keamananLingkunganFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit;
return {
success: true,
message: "Success fetch keamanan lingkungan",
data,
};
} catch (e) {
console.error("Find many error:", e);
return {
success: false,
message: "Failed fetch keamanan lingkungan",
};
}
}
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
{ deskripsi: { contains: search, mode: 'insensitive' } },
];
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.keamananLingkungan.findMany({
where,
include: {
image: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.keamananLingkungan.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil keamanan lingkungan dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data keamanan lingkungan",
};
}
}
export default keamananLingkunganFindMany;

View File

@@ -0,0 +1,31 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// find-first.ts
import prisma from "@/lib/prisma";
async function polsekTerdekatFindFirst() {
const where: any = { isActive: true };
try {
const data = await prisma.polsekTerdekat.findFirst({
where,
include: {
layananPolsek: true,
},
orderBy: { createdAt: 'desc' },
});
return {
success: true,
message: "Berhasil ambil polsek terdekat terbaru",
data,
};
} catch (e) {
console.error("Error di findFirst:", e);
return {
success: false,
message: "Gagal ambil polsek terdekat terbaru",
};
}
}
export default polsekTerdekatFindFirst;

View File

@@ -1,24 +1,57 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function polsekTerdekatFindMany() {
try {
const data = await prisma.polsekTerdekat.findMany({
where: { isActive: true },
include: {
layananPolsek: true,
},
});
async function polsekTerdekatFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit;
return {
success: true,
message: "Success fetch polsek terdekat",
data,
};
} catch (e) {
console.error("Find many error:", e);
return {
success: false,
message: "Failed fetch polsek terdekat",
};
}
}
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ nama: { contains: search, mode: 'insensitive' } },
{ alamat: { contains: search, mode: 'insensitive' } },
];
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.polsekTerdekat.findMany({
where,
include: {
layananPolsek: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.polsekTerdekat.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil polsek terdekat dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data polsek terdekat",
};
}
}
export default polsekTerdekatFindMany;

View File

@@ -4,6 +4,7 @@ import polsekTerdekatDelete from "./del";
import polsekTerdekatFindMany from "./findMany";
import polsekTerdekatFindUnique from "./findUnique";
import polsekTerdekatUpdate from "./updt";
import polsekTerdekatFindFirst from "./findFirst";
const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/Polsek Terdekat"] })
@@ -12,6 +13,7 @@ const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/
const response = await polsekTerdekatFindUnique(new Request(context.request));
return response;
})
.get("/find-first", polsekTerdekatFindFirst)
.post("/create", polsekTerdekatCreate, {
body: t.Object({
nama: t.String(),

View File

@@ -1,24 +1,57 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function menuTipsKeamananFindMany() {
try {
const data = await prisma.menuTipsKeamanan.findMany({
where: { isActive: true },
include: {
image: true,
},
});
async function tipsKeamananFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit;
return {
success: true,
message: "Success fetch menu tips keamanan",
data,
};
} catch (e) {
console.error("Find many error:", e);
return {
success: false,
message: "Failed fetch menu tips keamanan",
};
}
}
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ judul: { contains: search, mode: 'insensitive' } },
{ deskripsi: { contains: search, mode: 'insensitive' } },
];
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.menuTipsKeamanan.findMany({
where,
include: {
image: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.menuTipsKeamanan.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil menu tips keamanan dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data tips keamanan",
};
}
}
export default tipsKeamananFindMany;

View File

@@ -0,0 +1,31 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormCreate = {
name: string;
specialist: string;
jadwal: string;
};
export default async function dokterTenagaMedisCreate(context: Context) {
const body = context.body as FormCreate;
const created = await prisma.dokterdanTenagaMedis.create({
data: {
name: body.name,
specialist: body.specialist,
jadwal: body.jadwal,
},
select: {
name: true,
specialist: true,
jadwal: true,
}
});
return {
success: true,
message: "Success create dokter tenaga medis",
data: created,
};
}

View File

@@ -0,0 +1,37 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function dokterTenagaMedisDelete(context: Context) {
const id = context.params?.id;
if (!id) {
return {
success: false,
message: "ID tidak ditemukan",
};
}
const existing = await prisma.dokterdanTenagaMedis.findUnique({
where: {
id: id,
},
});
if (!existing) {
return {
success: false,
message: "Data tidak ditemukan",
};
}
const deleted = await prisma.dokterdanTenagaMedis.delete({
where: { id },
});
return {
success: true,
message: "Data berhasil dihapus",
data: deleted,
};
}

View File

@@ -0,0 +1,55 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
async function dokterTenagaMedisFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
{ specialist: { contains: search, mode: 'insensitive' } },
{ jadwal: { contains: search, mode: 'insensitive' } },
];
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.dokterdanTenagaMedis.findMany({
where,
skip,
take: limit,
orderBy: { name: 'asc' },
}),
prisma.dokterdanTenagaMedis.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil data dokter tenaga medis dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data dokter tenaga medis",
};
}
}
export default dokterTenagaMedisFindMany;

View File

@@ -0,0 +1,47 @@
import prisma from "@/lib/prisma";
export default async function dokterTenagaMedisFindUnique(request: Request) {
const url = new URL(request.url);
const pathSegments = url.pathname.split('/');
const id = pathSegments[pathSegments.length - 1];
if (!id) {
return Response.json({
success: false,
message: 'ID tidak boleh kosong',
}, {status: 400})
}
try {
if (typeof id !== 'string') {
return Response.json({
success: false,
message: "ID tidak valid",
}, { status: 400 });
}
const data = await prisma.dokterdanTenagaMedis.findUnique({
where: { id },
});
if (!data) {
return Response.json({
success: false,
message: "Data tidak ditemukan",
}, { status: 404 });
}
return Response.json({
success: true,
message: "Berhasil mengambil data berdasarkan ID",
data,
}, { status: 200 });
} catch (error) {
console.error("Error fetching data:", error);
return Response.json({
success: false,
message: "Terjadi kesalahan saat mengambil data",
}, { status: 500 });
}
}

View File

@@ -0,0 +1,32 @@
import Elysia, { t } from "elysia";
import dokterTenagaMedisCreate from "./create";
import dokterTenagaMedisFindMany from "./findMany";
import dokterTenagaMedisFindUnique from "./findUnique";
import dokterTenagaMedisUpdate from "./updt";
import dokterTenagaMedisDelete from "./del";
const DokterTenagaMedis = new Elysia({
prefix: "/doktertenagamedis",
tags: ["Data Kesehatan/Fasilitas Kesehatan/Dokter Tenaga Medis"]
})
.get("/:id", async (context) => {
const response = await dokterTenagaMedisFindUnique(new Request(context.request));
return response;
})
.get("/findMany", dokterTenagaMedisFindMany)
.post("/create", dokterTenagaMedisCreate, {
body: t.Object({
name: t.String(),
specialist: t.String(),
jadwal: t.String(),
}),
})
.put("/:id", dokterTenagaMedisUpdate, {
body: t.Object({
name: t.String(),
specialist: t.String(),
jadwal: t.String(),
}),
})
.delete("/del/:id", dokterTenagaMedisDelete)
export default DokterTenagaMedis

View File

@@ -0,0 +1,32 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormUpdate = {
name: string;
specialist: string;
jadwal: string;
}
export default async function dokterTenagaMedisUpdate(context: Context) {
const body = (await context.body) as FormUpdate;
const id = context.params.id as string;
try {
const result = await prisma.dokterdanTenagaMedis.update({
where: { id },
data: {
name: body.name,
specialist: body.specialist,
jadwal: body.jadwal,
},
});
return {
success: true,
message: "Berhasil mengupdate data dokter tenaga medis",
data: result,
};
} catch (error) {
console.error("Error updating data dokter tenaga medis:", error);
throw new Error("Gagal mengupdate data dokter tenaga medis: " + (error as Error).message);
}
}

View File

@@ -1,25 +1,31 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.GrafikKepuasanGetPayload<{
select: {
label: true;
jumlah: true
};
}>;
type FormCreate = {
nama: string;
tanggal: string;
jenisKelamin: string;
alamat: string;
penyakit: string;
};
export default async function grafikKepuasanCreate(context: Context) {
const body = context.body as FormCreate;
const created = await prisma.grafikKepuasan.create({
data: {
label: body.label,
jumlah: body.jumlah,
nama: body.nama,
tanggal: new Date(body.tanggal),
jenisKelamin: body.jenisKelamin,
alamat: body.alamat,
penyakit: body.penyakit,
},
select: {
id: true,
label: true,
jumlah: true,
nama: true,
tanggal: true,
jenisKelamin: true,
alamat: true,
penyakit: true,
}
});
return {

View File

@@ -1,8 +1,56 @@
import prisma from "@/lib/prisma"
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
async function grafikKepuasanFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ nama: { contains: search, mode: 'insensitive' } },
{ alamat: { contains: search, mode: 'insensitive' } },
{ jenisKelamin: { contains: search, mode: 'insensitive' } },
{ penyakit: { contains: search, mode: 'insensitive' } },
];
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.grafikKepuasan.findMany({
where,
skip,
take: limit,
orderBy: { nama: 'asc' },
}),
prisma.grafikKepuasan.count({ where }),
]);
export default async function grafikKepuasanFindMany() {
const res = await prisma.grafikKepuasan.findMany()
return {
data: res
}
}
success: true,
message: "Berhasil ambil data grafik kepuasan dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data grafik kepuasan",
};
}
}
export default grafikKepuasanFindMany;

View File

@@ -16,22 +16,21 @@ const GrafikKepuasan = new Elysia({
.get("/find-many", grafikKepuasanFindMany)
.post("/create", grafikKepuasanCreate, {
body: t.Object({
label: t.String(),
jumlah: t.String(),
nama: t.String(),
tanggal: t.String(),
jenisKelamin: t.String(),
alamat: t.String(),
penyakit: t.String(),
}),
})
.put("/:id", grafikKepuasanUpdate, {
params: t.Object({
id: t.String(),
}),
body: t.Object({
label: t.String(),
jumlah: t.String(),
}),
})
.delete("/del/:id", grafikKepuasanDelete, {
params: t.Object({
id: t.String(),
nama: t.String(),
tanggal: t.String(),
jenisKelamin: t.String(),
alamat: t.String(),
penyakit: t.String(),
}),
})
.delete("/del/:id", grafikKepuasanDelete)
export default GrafikKepuasan

View File

@@ -1,45 +1,36 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FormUpdate = {
nama: string;
tanggal: string;
jenisKelamin: string;
alamat: string;
penyakit: string;
}
export default async function grafikKepuasanUpdate(context: Context) {
const id = context.params?.id;
if (!id) {
const body = (await context.body) as FormUpdate;
const id = context.params.id as string;
try {
const result = await prisma.grafikKepuasan.update({
where: { id },
data: {
nama: body.nama,
tanggal: new Date(body.tanggal),
jenisKelamin: body.jenisKelamin,
alamat: body.alamat,
penyakit: body.penyakit,
},
});
return {
success: false,
message: "ID tidak ditemukan"
}
}
const {label, jumlah} = context.body as {
label: string;
jumlah: string;
}
const existing = await prisma.grafikKepuasan.findUnique({
where: {
id: id,
},
})
if (!existing) {
return {
success: false,
message: "Data tidak ditemukan",
}
}
const updated = await prisma.grafikKepuasan.update({
where: { id },
data: {
label,
jumlah,
},
})
return {
success: true,
message: "Data berhasil diupdate",
data: updated,
success: true,
message: "Berhasil mengupdate data grafik kepuasan",
data: result,
};
} catch (error) {
console.error("Error updating data grafik kepuasan:", error);
throw new Error("Gagal mengupdate data grafik kepuasan: " + (error as Error).message);
}
}

View File

@@ -18,6 +18,7 @@ import JadwalKegiatan from "./data_kesehatan_warga/jadwal_kegiatan";
import ArtikelKesehatan from "./data_kesehatan_warga/artikel_kesehatan";
import Kelahiran from "./data_kesehatan_warga/persentase_kelahiran_kematian/kelahiran";
import Kematian from "./data_kesehatan_warga/persentase_kelahiran_kematian/kematian";
import DokterTenagaMedis from "./data_kesehatan_warga/fasilitas_kesehatan/dokter-tenaga-medis";
const Kesehatan = new Elysia({
@@ -43,4 +44,5 @@ const Kesehatan = new Elysia({
.use(ArtikelKesehatan)
.use(Kelahiran)
.use(Kematian)
.use(DokterTenagaMedis)
export default Kesehatan;

View File

@@ -1,25 +1,57 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function infoWabahPenyakitFindMany() {
try {
const data = await prisma.infoWabahPenyakit.findMany({
where: {
isActive: true,
},
include: {
image: true,
}
})
return {
success: true,
message: "Success fetch info wabah penyakit",
data,
}
} catch (error) {
console.error("Find many error:", error);
return {
success: false,
message: "Failed fetch info wabah penyakit",
}
}
}
async function infoWabahPenyakitFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
{ deskripsiLengkap: { contains: search, mode: 'insensitive' } },
];
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.infoWabahPenyakit.findMany({
where,
include: {
image: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.infoWabahPenyakit.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil info wabah penyakit dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data info wabah penyakit",
};
}
}
export default infoWabahPenyakitFindMany;

View File

@@ -1,25 +1,57 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function kontakDaruratFindMany() {
try {
const data = await prisma.kontakDarurat.findMany({
where: {
isActive: true,
},
include: {
image: true,
}
})
return {
success: true,
message: "Success fetch kontak darurat",
data,
}
} catch (error) {
console.error("Find many error:", error);
return {
success: false,
message: "Failed fetch kontak darurat",
}
}
}
async function kontakDaruratFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
{ deskripsi: { contains: search, mode: 'insensitive' } },
];
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.kontakDarurat.findMany({
where,
include: {
image: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.kontakDarurat.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil kontak darurat dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data kontak darurat",
};
}
}
export default kontakDaruratFindMany;

View File

@@ -1,25 +1,57 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// /api/berita/findManyPaginated.ts
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function penangananDaruratFindMany() {
try {
const data = await prisma.penangananDarurat.findMany({
where: {
isActive: true,
},
include: {
image: true,
}
})
return {
success: true,
message: "Success fetch penanganan darurat",
data,
}
} catch (error) {
console.error("Find many error:", error);
return {
success: false,
message: "Failed fetch penanganan darurat",
}
}
}
async function penangananDaruratFindMany(context: Context) {
// Ambil parameter dari query
const page = Number(context.query.page) || 1;
const limit = Number(context.query.limit) || 10;
const search = (context.query.search as string) || '';
const skip = (page - 1) * limit;
// Buat where clause
const where: any = { isActive: true };
// Tambahkan pencarian (jika ada)
if (search) {
where.OR = [
{ name: { contains: search, mode: 'insensitive' } },
{ deskripsi: { contains: search, mode: 'insensitive' } },
];
}
try {
// Ambil data dan total count secara paralel
const [data, total] = await Promise.all([
prisma.penangananDarurat.findMany({
where,
include: {
image: true,
},
skip,
take: limit,
orderBy: { createdAt: 'desc' },
}),
prisma.penangananDarurat.count({ where }),
]);
return {
success: true,
message: "Berhasil ambil penanganan darurat dengan pagination",
data,
page,
limit,
total,
totalPages: Math.ceil(total / limit),
};
} catch (e) {
console.error("Error di findMany paginated:", e);
return {
success: false,
message: "Gagal mengambil data penanganan darurat",
};
}
}
export default penangananDaruratFindMany;

Some files were not shown because too many files have changed in this diff Show More