Compare commits

...

3 Commits

Author SHA1 Message Date
795c79dd5f Tambahan Admin Di Menu PPID 2025-05-06 13:57:33 +08:00
a8556aacb7 Fix Eror Admin Persentase & Grafik 2025-05-02 16:08:30 +08:00
ea7de13d28 Admin Dashboard Bagian Data Kesehatan 2025-04-30 16:31:18 +08:00
75 changed files with 3185 additions and 274 deletions

BIN
bun.lockb

Binary file not shown.

View File

@@ -1,6 +1,6 @@
{
"name": "desa-darmasaba",
"version": "0.1.1",
"version": "0.1.2",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
@@ -62,7 +62,7 @@
"react-simple-toasts": "^6.1.0",
"react-toastify": "^11.0.5",
"readdirp": "^4.1.1",
"recharts": "2",
"recharts": "^2.15.3",
"swr": "^2.3.2",
"valtio": "^2.1.3",
"zod": "^3.24.3"

View File

@@ -46,6 +46,67 @@ model AppMenuChild {
AppMenu AppMenu? @relation(fields: [appMenuId], references: [id])
appMenuId String?
}
//========================================= MENU PPID ========================================= //
// ========================================= DAFTAR INFORMASI PUBLIK ========================================= //
model DaftarInformasiPublik {
id String @id @default(cuid())
nomor Int @default(autoincrement())
jenisInformasi String
deskripsi String
tanggal String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
//========================================= MENU PPID ========================================= //
// ========================================= IKM ========================================= //
model IndeksKepuasanMasyarakat {
id Int @id @default(autoincrement())
label String
kepuasan String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model GrafikBerdasarkanJenisKelamin {
id String @id @default(cuid())
perempuan String
laki String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model GrafikBerdasarkanResponden {
id String @id @default(cuid())
sangatbaik String
baik String
kurangbaik String
tidakbaik String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model GrafikBerdasarkanUmur {
id String @id @default(cuid())
remaja String
dewasa String
orangtua String
lansia String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
// ========================================= MENU DESA ========================================= //
// ========================================= BERITA ========================================= //
model Berita {
@@ -106,6 +167,7 @@ model Images {
updatedAt DateTime @updatedAt
GalleryFoto GalleryFoto[]
}
// ========================================= VIDEOS ========================================= //
model Videos {
id String @id @default(cuid())
@@ -117,7 +179,6 @@ model Videos {
GalleryVideo GalleryVideo[]
}
// ========================================= GALLERY ========================================= //
model GalleryFoto {
id String @id @default(cuid())
@@ -132,14 +193,14 @@ model GalleryFoto {
}
model GalleryVideo {
id String @id @default(cuid())
name String
video String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
videosId String? @unique
id String @id @default(cuid())
name String
video String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
videosId String? @unique
videosGalleryVideo Videos? @relation(fields: [videosId], references: [id])
}
@@ -147,6 +208,159 @@ model GalleryVideo {
// ========================================= DATA KESEHATAN WARGA ========================================= //
// ========================================= FASILITAS KESEHATAN ========================================= //
model FasilitasKesehatan {
id String @id @default(cuid())
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
InformasiUmum InformasiUmum[]
LayananUnggulan LayananUnggulan[]
DokterdanTenagaMedis DokterdanTenagaMedis[]
FasilitasPendukung FasilitasPendukung[]
ProsedurPendaftaran ProsedurPendaftaran[]
TarifDanLayanan TarifDanLayanan[]
}
model InformasiUmum {
id String @id @default(cuid())
fasilitas String
alamat String
jamOperasional String
FasilitasKesehatan FasilitasKesehatan[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model LayananUnggulan {
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
FasilitasKesehatan FasilitasKesehatan[]
}
model DokterdanTenagaMedis {
id String @id @default(cuid())
name String
specialist String
jadwal String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
FasilitasKesehatan FasilitasKesehatan[]
}
model FasilitasPendukung {
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
FasilitasKesehatan FasilitasKesehatan[]
}
model ProsedurPendaftaran {
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
FasilitasKesehatan FasilitasKesehatan[]
}
model TarifDanLayanan {
id String @id @default(cuid())
layanan String
tarif String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
FasilitasKesehatan FasilitasKesehatan[]
}
// ========================================= JADWAL KEGIATAN ========================================= //
model JadwalKegiatan {
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model InformasiJadwalKegiatan {
id String @id @default(cuid())
name String
tanggal String
waktu String
lokasi String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model DeskripsiJadwalKegiatan {
id String @id @default(cuid())
deskripsi String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model LayananJadwalKegiatan {
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model SyaratKetentuanJadwalKegiatan {
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model DokumenJadwalKegiatan {
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model PendaftaranJadwalKegiatan {
id String @id @default(cuid())
name String
tanggal String
namaOrangtua String
nomor String
alamat String
catatan String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
// ========================================= PERSENTASE KELAHIRAN & KEMATIAN ========================================= //
model DataKematian_Kelahiran {
id Int @id @default(autoincrement())
tahun String
@@ -155,156 +369,87 @@ model DataKematian_Kelahiran {
kelahiranKasar String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model FasilitasKesehatan {
id String @id @default(cuid())
name String
// ========================================= GRAFIK KEPUASAN ========================================= //
model GrafikKepuasan {
id Int @id @default(autoincrement())
label String
jumlah String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
InformasiUmum InformasiUmum[]
LayananUnggulan LayananUnggulan[]
DokterdanTenagaMedis DokterdanTenagaMedis[]
FasilitasPendukung FasilitasPendukung[]
ProsedurPendaftaran ProsedurPendaftaran[]
TarifDanLayanan TarifDanLayanan[]
isActive Boolean @default(true)
}
model InformasiUmum{
id String @id @default(cuid())
fasilitas String
alamat String
jamOperasional String
FasilitasKesehatan FasilitasKesehatan[]
// ========================================= ARTIKEL KESEHATAN ========================================= //
model ArtikelKesehatan {
id Int @id @default(autoincrement())
title String
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
isActive Boolean @default(true)
}
model LayananUnggulan{
id String @id @default(cuid())
content String
model Introduction {
id Int @id @default(autoincrement())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
FasilitasKesehatan FasilitasKesehatan[]
isActive Boolean @default(true)
}
model DokterdanTenagaMedis{
id String @id @default(cuid())
name String
specialist String
jadwal String
model Symptom {
id Int @id @default(autoincrement())
title String
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
FasilitasKesehatan FasilitasKesehatan[]
isActive Boolean @default(true)
}
model FasilitasPendukung{
id String @id @default(cuid())
content String
model Prevention {
id Int @id @default(autoincrement())
title String
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
FasilitasKesehatan FasilitasKesehatan[]
isActive Boolean @default(true)
}
model ProsedurPendaftaran{
id String @id @default(cuid())
content String
model FirstAid {
id Int @id @default(autoincrement())
title String
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
FasilitasKesehatan FasilitasKesehatan[]
isActive Boolean @default(true)
}
model TarifDanLayanan{
id String @id @default(cuid())
layanan String
tarif String
model MythVsFact {
id Int @id @default(autoincrement())
title String
mitos String
fakta String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
FasilitasKesehatan FasilitasKesehatan[]
isActive Boolean @default(true)
}
// ========================================= JADWAL KEGIATAN ========================================= //
model JadwalKegiatan{
id String @id @default(cuid())
content String
model DoctorSign {
id Int @id @default(autoincrement())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model InformasiJadwalKegiatan{
id String @id @default(cuid())
name String
tanggal String
waktu String
lokasi String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model DeskripsiJadwalKegiatan{
id String @id @default(cuid())
deskripsi String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model LayananJadwalKegiatan{
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model SyaratKetentuanJadwalKegiatan{
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model DokumenJadwalKegiatan{
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model PendaftaranJadwalKegiatan{
id String @id @default(cuid())
name String
tanggal DateTime
namaOrangtua String
nomor String
alamat String
catatan String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
isActive Boolean @default(true)
}

View File

@@ -0,0 +1,339 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
/* Introduction */
const templateIntroduction = z.object({
content: z.string().min(3, "Content minimal 3 karakter"),
})
type Introduction = Prisma.IntroductionGetPayload<{
select: {
content: true;
};
}>;
const introduction = proxy({
create: {
form: {} as Introduction,
loading: false,
async create() {
const cek = templateIntroduction.safeParse(introduction.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
introduction.create.loading = true;
const res = await ApiFetch.api.kesehatan.introduction["create"].post(introduction.create.form);
if (res.status === 200) {
introduction.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
introduction.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.IntroductionGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.introduction["find-many"].get();
if (res.status === 200) {
introduction.findMany.data = res.data?.data ?? [];
}
}
}
});
/* ======================================================================= */
/* symptom */
const templateSymptom = z.object({
title: z.string().min(3, "Title minimal 3 karakter"),
content: z.string().min(3, "Content minimal 3 karakter"),
})
type Symptom = Prisma.SymptomGetPayload<{
select: {
title: true;
content: true;
};
}>;
const symptom = proxy({
create: {
form: {} as Symptom,
loading: false,
async create() {
const cek = templateSymptom.safeParse(symptom.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
symptom.create.loading = true;
const res = await ApiFetch.api.kesehatan.symptom["create"].post(symptom.create.form);
if (res.status === 200) {
symptom.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
symptom.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.SymptomGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.symptom["find-many"].get();
if (res.status === 200) {
symptom.findMany.data = res.data?.data ?? [];
}
},
},
});
/* ======================================================================= */
/* Prevention */
const templatePrevention = z.object({
title: z.string().min(3, "Title minimal 3 karakter"),
content: z.string().min(3, "Content minimal 3 karakter"),
})
type Prevention = Prisma.PreventionGetPayload<{
select: {
title: true;
content: true;
};
}>;
const prevention = proxy({
create: {
form: {} as Prevention,
loading: false,
async create() {
const cek = templatePrevention.safeParse(prevention.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
prevention.create.loading = true;
const res = await ApiFetch.api.kesehatan.prevention["create"].post(prevention.create.form);
if (res.status === 200) {
prevention.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
prevention.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.PreventionGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.prevention["find-many"].get();
if (res.status === 200) {
prevention.findMany.data = res.data?.data ?? [];
}
},
},
});
/* ======================================================================= */
/* First Aid */
const templateFirstAid = z.object({
title: z.string().min(3, "Title minimal 3 karakter"),
content: z.string().min(3, "Content minimal 3 karakter"),
})
type FirstAid = Prisma.FirstAidGetPayload<{
select: {
title: true;
content: true;
};
}>;
const firstAid = proxy({
create: {
form: {} as FirstAid,
loading: false,
async create() {
const cek = templateFirstAid.safeParse(firstAid.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
firstAid.create.loading = true;
const res = await ApiFetch.api.kesehatan.firstaid["create"].post(firstAid.create.form);
if (res.status === 200) {
firstAid.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
firstAid.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.FirstAidGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.firstaid["find-many"].get();
if (res.status === 200) {
firstAid.findMany.data = res.data?.data ?? [];
}
},
},
})
/* ======================================================================= */
/* Myth vs Fact */
const templateMythFact = z.object({
title: z.string().min(3, "Title minimal 3 karakter"),
mitos: z.string().min(3, "Mitos minimal 3 karakter"),
fakta: z.string().min(3, "Fakta minimal 3 karakter"),
})
type MythFact = Prisma.MythVsFactGetPayload<{
select: {
title: true;
mitos: true;
fakta: true;
};
}>;
const mythFact = proxy({
create: {
form: {} as MythFact,
loading: false,
async create() {
const cek = templateMythFact.safeParse(mythFact.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
mythFact.create.loading = true;
const res = await ApiFetch.api.kesehatan.mythvsfact["create"].post(mythFact.create.form);
if (res.status === 200) {
mythFact.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
mythFact.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.MythVsFactGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.mythvsfact["find-many"].get();
if (res.status === 200) {
mythFact.findMany.data = res.data?.data ?? [];
}
},
},
})
/* ======================================================================= */
/* Doctor Sign */
const templateDoctorSign = z.object({
content: z.string().min(3, "Content minimal 3 karakter"),
})
type DoctorSign = Prisma.DoctorSignGetPayload<{
select: {
content: true
}
}>
const doctorSign = proxy({
create: {
form: {} as DoctorSign,
loading: false,
async create() {
const cek = templateDoctorSign.safeParse(doctorSign.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
doctorSign.create.loading = true;
const res = await ApiFetch.api.kesehatan.doctor_sign["create"].post(doctorSign.create.form);
if (res.status === 200) {
doctorSign.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
doctorSign.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.DoctorSignGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.doctor_sign["find-many"].get();
if (res.status === 200) {
doctorSign.findMany.data = res.data?.data ?? [];
}
},
},
})
/* ======================================================================= */
const stateArtikelKesehatan = proxy({
introduction,
symptom,
prevention,
firstAid,
mythFact,
doctorSign
})
export default stateArtikelKesehatan

View File

@@ -0,0 +1,76 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateGrafikKepuasan = z.object({
label: z.string().min(2, "Label harus diisi"),
jumlah: z.string().min(2, "Jumlah harus diisi"),
});
type GrafikKepuasan = Prisma.GrafikKepuasanGetPayload<{
select: {
label: true;
jumlah: true;
};
}>;
const defaultForm: GrafikKepuasan = {
label: "",
jumlah: ""
};
const grafikkepuasan = proxy({
create: {
form: defaultForm,
loading: false,
async create() {
const cek = templateGrafikKepuasan.safeParse(grafikkepuasan.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
grafikkepuasan.create.loading = true;
const res = await ApiFetch.api.kesehatan.grafikkepuasan["create"].post(
grafikkepuasan.create.form
);
if (res.status === 200) {
grafikkepuasan.create.form = {
label: "",
jumlah: ""
};
grafikkepuasan.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
grafikkepuasan.create.loading = false;
}
},
},
findMany: {
data: null as
| 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 ?? [];
}
},
},
});
const stategrafikKepuasan = proxy({
grafikkepuasan,
});
export default stategrafikKepuasan;

View File

@@ -0,0 +1,84 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templatePersentase = z.object({
tahun: z.string().min(4, "Tahun harus diisi"),
kematianKasar: z.string().min(2, "Kematian kasar harus diisi"),
kelahiranKasar: z.string().min(2, "Kelahiran kasar harus diisi"),
kematianBayi: z.string().min(2, "Kematian bayi harus diisi"),
});
type Persentase = Prisma.DataKematian_KelahiranGetPayload<{
select: {
tahun: true;
kematianKasar: true;
kelahiranKasar: true;
kematianBayi: true;
};
}>;
const defaultForm: Persentase = {
tahun: "",
kematianKasar: "",
kelahiranKasar: "",
kematianBayi: "",
};
const persentasekelahiran = proxy({
create: {
form: defaultForm,
loading: false,
async create() {
const cek = templatePersentase.safeParse(persentasekelahiran.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
persentasekelahiran.create.loading = true;
const res = await ApiFetch.api.kesehatan.persentasekelahiran[
"create"
].post(persentasekelahiran.create.form);
if (res.status === 200) {
persentasekelahiran.create.form = {
tahun: "",
kematianKasar: "",
kelahiranKasar: "",
kematianBayi: "",
};
persentasekelahiran.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
persentasekelahiran.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.DataKematian_KelahiranGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.persentasekelahiran[
"find-many"
].get();
if (res.status === 200) {
persentasekelahiran.findMany.data = res.data?.data ?? [];
}
},
},
});
const statePersentase = proxy({
persentasekelahiran,
});
export default statePersentase;

View File

@@ -0,0 +1,65 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateDaftarInformasi = z.object({
jenisInformasi: z.string().min(3, "Jenis Informasi minimal 3 karakter"),
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
tanggal: z.string().min(3, "Tanggal minimal 3 karakter"),
})
type DaftarInformasi = Prisma.DaftarInformasiPublikGetPayload<{
select: {
jenisInformasi: true;
deskripsi: true;
tanggal: true;
};
}>;
const daftarInformasi = proxy({
create: {
form: {} as DaftarInformasi,
loading: false,
async create() {
const cek = templateDaftarInformasi.safeParse(daftarInformasi.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
daftarInformasi.create.loading = true;
const res = await ApiFetch.api.ppid.daftarinformasipublik["create"].post(daftarInformasi.create.form);
if (res.status === 200) {
daftarInformasi.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
daftarInformasi.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.DaftarInformasiPublikGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.ppid.daftarinformasipublik["find-many"].get();
if (res.status === 200) {
daftarInformasi.findMany.data = res.data?.data ?? [];
}
}
}
});
const stateDaftarInformasiPublik = proxy({
daftarInformasi
})
export default stateDaftarInformasiPublik;

View File

@@ -0,0 +1,77 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateGrafikJenisKelamin = z.object({
laki: z.string().min(2, "Data laki-laki harus diisi"),
perempuan: z.string().min(2, "Data perempuan harus diisi"),
});
type GrafikJenisKelamin = Prisma.GrafikBerdasarkanJenisKelaminGetPayload<{
select: {
laki: true;
perempuan: true;
};
}>;
const defaultForm: GrafikJenisKelamin = {
laki: "",
perempuan: "",
};
const grafikBerdasarkanJenisKelamin = proxy({
create: {
form: defaultForm,
loading: false,
async create() {
const cek = templateGrafikJenisKelamin.safeParse(
grafikBerdasarkanJenisKelamin.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
grafikBerdasarkanJenisKelamin.create.loading = true;
const res = await ApiFetch.api.ppid.grafikberdasarkanjeniskelamin[
"create"
].post(grafikBerdasarkanJenisKelamin.create.form);
if (res.status === 200) {
grafikBerdasarkanJenisKelamin.create.form = defaultForm;
grafikBerdasarkanJenisKelamin.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
grafikBerdasarkanJenisKelamin.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.GrafikBerdasarkanJenisKelaminGetPayload<{
omit: { isActive: true };
}>[]
| null,
loading: false,
async load() {
const res = await ApiFetch.api.ppid.grafikberdasarkanjeniskelamin[
"find-many"
].get();
if (res.status === 200) {
grafikBerdasarkanJenisKelamin.findMany.data = res.data?.data ?? [];
}
},
},
});
const stateGrafikBerdasarkanJenisKelamin = proxy({
grafikBerdasarkanJenisKelamin,
});
export default stateGrafikBerdasarkanJenisKelamin;

View File

@@ -0,0 +1,84 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateGrafikResponden = z.object({
sangatbaik: z.string().min(1, "Data sangat baik harus diisi"),
baik: z.string().min(1, "Data baik harus diisi"),
kurangbaik: z.string().min(1, "Data kurang baik harus diisi"),
tidakbaik: z.string().min(1, "Data tidak baik harus diisi"),
});
type GrafikResponden = Prisma.GrafikBerdasarkanRespondenGetPayload<{
select: {
sangatbaik: true;
baik: true;
kurangbaik: true;
tidakbaik: true;
};
}>;
const defaultForm: GrafikResponden = {
sangatbaik: "",
baik: "",
kurangbaik: "",
tidakbaik: "",
};
const grafikBerdasarkanResponden = proxy({
create: {
form: defaultForm,
loading: false,
async create() {
const cek = templateGrafikResponden.safeParse(
grafikBerdasarkanResponden.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
grafikBerdasarkanResponden.create.loading = true;
const res = await ApiFetch.api.ppid.grafikberdasarkanresponden[
"create"
].post(grafikBerdasarkanResponden.create.form);
if (res.status === 200) {
grafikBerdasarkanResponden.create.form = defaultForm;
grafikBerdasarkanResponden.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
grafikBerdasarkanResponden.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.GrafikBerdasarkanRespondenGetPayload<{
omit: { isActive: true };
}>[]
| null,
loading: false,
async load() {
const res = await ApiFetch.api.ppid.grafikberdasarkanresponden[
"find-many"
].get();
if (res.status === 200) {
grafikBerdasarkanResponden.findMany.data = res.data?.data ?? [];
}
},
},
});
const stateGrafikResponden = proxy({
grafikBerdasarkanResponden,
});
export default stateGrafikResponden;

View File

@@ -0,0 +1,84 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateGrafikUmur = z.object({
remaja: z.string().min(2, "Data remaja harus diisi"),
dewasa: z.string().min(2, "Data dewasa harus diisi"),
orangtua: z.string().min(2, "Data orangtua harus diisi"),
lansia: z.string().min(2, "Data lansia harus diisi"),
});
type GrafikUmur = Prisma.GrafikBerdasarkanUmurGetPayload<{
select: {
remaja: true;
dewasa: true;
orangtua: true;
lansia: true;
};
}>;
const defaultForm: GrafikUmur = {
remaja: "",
dewasa: "",
orangtua: "",
lansia: "",
};
const grafikBerdasarkanUmur = proxy({
create: {
form: defaultForm,
loading: false,
async create() {
const cek = templateGrafikUmur.safeParse(
grafikBerdasarkanUmur.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
grafikBerdasarkanUmur.create.loading = true;
const res = await ApiFetch.api.ppid.grafikberdasarkanumur[
"create"
].post(grafikBerdasarkanUmur.create.form);
if (res.status === 200) {
grafikBerdasarkanUmur.create.form = defaultForm;
grafikBerdasarkanUmur.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
grafikBerdasarkanUmur.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.GrafikBerdasarkanUmurGetPayload<{
omit: { isActive: true };
}>[]
| null,
loading: false,
async load() {
const res = await ApiFetch.api.ppid.grafikberdasarkanumur[
"find-many"
].get();
if (res.status === 200) {
grafikBerdasarkanUmur.findMany.data = res.data?.data ?? [];
}
},
},
})
const stateGrafikBerdasarkanUmur = proxy({
grafikBerdasarkanUmur,
})
export default stateGrafikBerdasarkanUmur;

View File

@@ -0,0 +1,76 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateGrafikHasilKepuasanMasyarakat = z.object({
label: z.string().min(2, "Label harus diisi"),
kepuasan: z.string().min(2, "Kepuasan harus diisi"),
});
type GrafikHasilKepuasanMasyarakat = Prisma.IndeksKepuasanMasyarakatGetPayload<{
select: {
label: true;
kepuasan: true;
};
}>;
const defaultForm: GrafikHasilKepuasanMasyarakat = {
label: "",
kepuasan: "",
};
const grafikHasilKepuasanMasyarakat = proxy({
create: {
form: defaultForm,
loading: false,
async create() {
const cek = templateGrafikHasilKepuasanMasyarakat.safeParse(
grafikHasilKepuasanMasyarakat.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
grafikHasilKepuasanMasyarakat.create.loading = true;
const res = await ApiFetch.api.ppid.grafikhasilkepuasamanmasyarakat["create"].post(
grafikHasilKepuasanMasyarakat.create.form
);
if (res.status === 200) {
grafikHasilKepuasanMasyarakat.create.form = {
label: "",
kepuasan: ""
};
grafikHasilKepuasanMasyarakat.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
grafikHasilKepuasanMasyarakat.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.IndeksKepuasanMasyarakatGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.ppid.grafikhasilkepuasamanmasyarakat["find-many"].get();
if (res.status === 200) {
grafikHasilKepuasanMasyarakat.findMany.data = res.data?.data ?? [];
}
}
}
});
const stateGrafikHasilKepuasanMasyarakat = proxy({
grafikHasilKepuasanMasyarakat,
});
export default stateGrafikHasilKepuasanMasyarakat;

View File

@@ -24,8 +24,14 @@ export function BeritaEditor({ onSubmit }: { onSubmit: (val: string) => void })
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
content,
immediatelyRender: false
});
if (!editor) {
return null;
}
return (
<Stack>
<RichTextEditor editor={editor}>

View File

@@ -0,0 +1,23 @@
'use client'
import { Box, Text } from '@mantine/core';
import React from 'react';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import { useProxy } from 'valtio/utils';
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
function DoctorSignUI() {
const doctorSign = useProxy(stateArtikelKesehatan.doctorSign)
return (
<Box>
<Text fw={"bold"}>Kapan Harus ke Dokter</Text>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
doctorSign.create.form.content = val
}}
/>
</Box>
);
}
export default DoctorSignUI;

View File

@@ -0,0 +1,29 @@
'use client'
import { Box, Text, TextInput } from '@mantine/core';
import React from 'react';
import { useProxy } from 'valtio/utils';
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
function FirstAidUI() {
const firstAidState = useProxy(stateArtikelKesehatan.firstAid)
return (
<Box>
<TextInput
label={<Text fw={"bold"}>Title Pertolongan Pertama</Text>}
placeholder="Masukkan title"
onChange={(val) => {
firstAidState.create.form.title = val.target.value
}}
/>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
firstAidState.create.form.content = val
}}
/>
</Box>
);
}
export default FirstAidUI;

View File

@@ -0,0 +1,23 @@
'use client'
import { Box, Text } from '@mantine/core';
import React from 'react';
import { useProxy } from 'valtio/utils';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
function IntoductionUI() {
const introduction = useProxy(stateArtikelKesehatan.introduction)
return (
<Box py={10}>
<Text fw={"bold"}>Pendahuluan</Text>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
introduction.create.form.content = val;
}}
/>
</Box>
);
}
export default IntoductionUI;

View File

@@ -0,0 +1,35 @@
'use client'
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import { Box, Text, TextInput } from '@mantine/core';
import { useProxy } from 'valtio/utils';
function MythFactUI() {
const mythFact = useProxy(stateArtikelKesehatan.mythFact)
return (
<Box py={10}>
<TextInput
label={<Text fw={"bold"}>Title Pertolongan Pertama Penyakit</Text>}
placeholder="Masukkan title"
onChange={(val) => {
mythFact.create.form.title = val.target.value
}}
/>
<TextInput
label={<Text fw={"bold"}>Mitos</Text>}
placeholder="Masukkan mitos"
onChange={(val) => {
mythFact.create.form.mitos = val.target.value
}}
/>
<TextInput
label={<Text fw={"bold"}>Fakta</Text>}
placeholder="Masukkan fakta"
onChange={(val) => {
mythFact.create.form.fakta = val.target.value
}}
/>
</Box>
);
}
export default MythFactUI;

View File

@@ -1,26 +1,149 @@
import { Box, SimpleGrid, Stack, TextInput, Title } from '@mantine/core';
import React from 'react';
'use client'
import { Box, Button, Center, SimpleGrid, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import IntoductionUI from './introduction/page';
import SymptomUI from './symptom/page';
import PreventionUI from './prevention/page';
import MythFactUI from './mythVsfact/page';
import DoctorSignUI from './doctor_sign/page';
import { useProxy } from 'valtio/utils';
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import FirstAidUI from './first_aid/page';
import { useShallowEffect } from '@mantine/hooks';
import colors from '@/con/colors';
function ArtikelKesehatan() {
const state = useProxy(stateArtikelKesehatan)
const submitAllForms = () => {
if (state.introduction.create.form.content) {
state.introduction.create.create()
}
if (state.symptom.create.form.title && state.symptom.create.form.content) {
state.symptom.create.create()
}
if (state.prevention.create.form.title && state.prevention.create.form.content) {
state.prevention.create.create()
}
if (state.firstAid.create.form.title && state.firstAid.create.form.content) {
state.firstAid.create.create()
}
if (state.mythFact.create.form.title && state.mythFact.create.form.mitos && state.mythFact.create.form.fakta) {
state.mythFact.create.create()
}
if (state.doctorSign.create.form.content) {
state.doctorSign.create.create()
}
}
return (
<Stack py={10}>
<SimpleGrid cols={{
base: 1, md: 2
}}>
<Box>
<Box >
<Title order={3}>Artikel Kesehatan</Title>
<TextInput
label="Jadwal"
placeholder='masukkan artikel kesehatan'
/>
<IntoductionUI />
<SymptomUI />
<PreventionUI />
<FirstAidUI />
<MythFactUI />
<DoctorSignUI />
<Button mt={10} onClick={submitAllForms}>Submit</Button>
</Box>
<Box>
<Title order={3}>List Artikel Kesehatan</Title>
<AllList />
</Box>
</SimpleGrid>
</Stack>
);
}
function AllList() {
const listState = useProxy(stateArtikelKesehatan)
useShallowEffect(() => {
listState.introduction.findMany.load();
listState.symptom.findMany.load();
listState.prevention.findMany.load();
listState.firstAid.findMany.load();
listState.mythFact.findMany.load();
listState.doctorSign.findMany.load();
}, [])
if (!listState.introduction.findMany.data
|| !listState.symptom.findMany.data
|| !listState.prevention.findMany.data
|| !listState.firstAid.findMany.data
|| !listState.mythFact.findMany.data
|| !listState.doctorSign.findMany.data
) return <Stack>
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
return <Stack>
<Title order={4}>Intoduction</Title>
{listState.introduction.findMany.data?.map((item) => (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{ __html: item.content }}></Text>
</Box>
))}
{/* Symptom */}
{listState.symptom.findMany.data?.map((item) => (
<Box key={item.id}>
<Title order={4}>{item.title}</Title>
<Text dangerouslySetInnerHTML={{ __html: item.content }}></Text>
</Box>
))}
{/* Prevention */}
{listState.prevention.findMany.data?.map((item) => (
<Box key={item.id}>
<Title order={4}>{item.title}</Title>
<Text dangerouslySetInnerHTML={{ __html: item.content }}></Text>
</Box>
))}
{/* First Aid */}
{listState.firstAid.findMany.data?.map((item) => (
<Box key={item.id}>
<Title order={4}>{item.title}</Title>
<Text dangerouslySetInnerHTML={{ __html: item.content }}></Text>
</Box>
))}
{/* Myth Fact */}
{listState.mythFact.findMany.data?.map((item) => (
<Box key={item.id}>
<Title order={4}>{item.title}</Title>
<Table
striped
highlightOnHover
withTableBorder
withColumnBorders
bg={colors['white-1']}
>
<TableThead >
<TableTr >
<TableTh >
<Center>Mitos</Center>
</TableTh>
<TableTh >
<Center>Fakta</Center>
</TableTh>
</TableTr>
</TableThead>
<TableTbody >
<TableTr>
<TableTd ta="center">{item.mitos}</TableTd>
<TableTd ta="center">{item.fakta}</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
))}
{/* Doctor Sign */}
<Title order={4}>Kapan Harus Ke Dokter?</Title>
{listState.doctorSign.findMany.data?.map((item) => (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{ __html: item.content }}/>
</Box>
))}
</Stack>
}
export default ArtikelKesehatan;

View File

@@ -0,0 +1,30 @@
'use client'
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import { Box, Text, TextInput } from '@mantine/core';
import React from 'react';
import { useProxy } from 'valtio/utils';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
function PreventionUI() {
const preventionState = useProxy(stateArtikelKesehatan.prevention)
return (
<Box py={10}>
<TextInput
mb={10}
label={<Text fw={"bold"}>Title Pencegahan Penyakit</Text>}
placeholder="Masukkan title"
onChange={(val) => {
preventionState.create.form.title = val.target.value
}}
/>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
preventionState.create.form.content = val
}}
/>
</Box>
);
}
export default PreventionUI;

View File

@@ -0,0 +1,29 @@
'use client'
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import { Box, Text, TextInput } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
function SymptomUI() {
const symptomState = useProxy(stateArtikelKesehatan.symptom)
return (
<Box py={10}>
<TextInput
mb={10}
label={<Text fw={"bold"}>Title Gejala Penyakit</Text>}
placeholder='masukkan title'
onChange={(val) => {
symptomState.create.form.title = val.target.value
}}
/>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
symptomState.create.form.content = val
}}
/>
</Box>
);
}
export default SymptomUI;

View File

@@ -1,10 +1,88 @@
import { Stack, Title } from '@mantine/core';
import React from 'react';
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import stategrafikKepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
import colors from '@/con/colors';
import { Box, Button, Group, Stack, TextInput, Title } from '@mantine/core';
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
import { useEffect, useState } from 'react';
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
import { useProxy } from 'valtio/utils';
function GrafikHasilKepuasan() {
const grafikkepuasan = useProxy(stategrafikKepuasan.grafikkepuasan)
const [mounted, setMounted] = useState(false);
const isTablet = useMediaQuery('(max-width: 1024px)')
const isMobile = useMediaQuery('(max-width: 768px)')
const [chartData, setChartData] = useState<any[]>([])
useShallowEffect(() => {
const fetchData = async () => {
await grafikkepuasan.findMany.load();
if (grafikkepuasan.findMany.data && grafikkepuasan.findMany.data.length > 0) {
setChartData(grafikkepuasan.findMany.data);
}
};
fetchData();
}, []);
useEffect(() => {
setMounted(true); // setelah komponen siap di client
}, []);
if (!mounted) {
return null; // Jangan render apa-apa dulu sebelum mounted
}
return (
<Stack py={10}>
<Stack gap={"xs"}>
<Title order={3}>Grafik Hasil Kepuasan</Title>
<Box>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Label"
placeholder='Masukkan label yang diinginkan'
value={grafikkepuasan.create.form.label}
onChange={(val) => {
grafikkepuasan.create.form.label = val.currentTarget.value
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Jumlah Penderita"
type='number'
placeholder='Masukkan jumlah penderita'
value={grafikkepuasan.create.form.jumlah}
onChange={(val) => {
grafikkepuasan.create.form.jumlah = val.currentTarget.value
}}
/>
</Box>
<Group>
<Button mt={10}
onClick={async () => {
await grafikkepuasan.create.create();
await grafikkepuasan.findMany.load();
if (grafikkepuasan.findMany.data) {
setChartData(grafikkepuasan.findMany.data);
}
}}
>Submit</Button>
</Group>
<Box h={400} w={{ base: "100%", md: "80%" }}>
<Title py={15} order={3}>Data Kelahiran & Kematian</Title>
<BarChart
width={isMobile ? 450 : isTablet ? 600 : 900}
height={380}
data={chartData}
>
<XAxis dataKey="label" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="jumlah" fill={colors['blue-button']} name="Jumlah" />
</BarChart>
</Box>
</Stack>
);
}

View File

@@ -61,7 +61,7 @@ function JadwalKegiatan() {
<SyaratDanKetentuan />
<DokumenYangDiperlukan />
<Pendaftaran />
<Button onClick={submitAllForms}>
<Button mt={10} onClick={submitAllForms}>
Submit
</Button>
</Box>
@@ -84,13 +84,15 @@ function AllList() {
allList.layanantersedia.findMany.load()
allList.syaratketentuan.findMany.load()
allList.dokumenjadwalkegiatan.findMany.load()
})
allList.pendaftaranjadwal.findMany.load()
}, [])
const isLoading = !allList.informasiKegiatan.findMany.data ||
!allList.deskripsiKegiatan.findMany.data ||
!allList.layanantersedia.findMany.data ||
!allList.syaratketentuan.findMany.data ||
!allList.dokumenjadwalkegiatan.findMany.data
!allList.dokumenjadwalkegiatan.findMany.data ||
!allList.pendaftaranjadwal.findMany.data
if (isLoading) {
return (

View File

@@ -1,21 +1,10 @@
'use client'
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import { ActionIcon, Box, Text, Textarea, TextInput } from '@mantine/core';
import { DateInput } from '@mantine/dates';
import { IconCalendar } from '@tabler/icons-react';
import { useState } from 'react';
import { Box, Text, Textarea, TextInput } from '@mantine/core';
import { useProxy } from 'valtio/utils';
function Pendaftaran() {
const pendaftaran = useProxy(stateJadwalKegiatan.pendaftaranjadwal)
const [dateInputOpened, setDateInputOpened] = useState(false);
const pickerControl = (
<ActionIcon onClick={() => setDateInputOpened(true)} variant="subtle" color="gray">
<IconCalendar size={18} />
</ActionIcon>
);
const formatDate = (date: Date | null): string => { if (!date) return ""; return date.toISOString().split('T')[0]; }
return (
<Box>
@@ -27,16 +16,11 @@ function Pendaftaran() {
pendaftaran.create.form.name = val.target.value
}}
/>
<DateInput
clearable defaultValue={new Date()}
styles={{label: {fontSize: '14px'}}}
label='Tanggal Lahir'
placeholder='dd/mm/yyyy'
value={pendaftaran.create.form.tanggal ? new Date(pendaftaran.create.form.tanggal) : null}
popoverProps={{opened: dateInputOpened, onChange: setDateInputOpened}}
rightSection={pickerControl}
<TextInput
label='Tanggal'
placeholder='Masukkan tanggal'
onChange={(val) => {
pendaftaran.create.form.tanggal = formatDate(val);
pendaftaran.create.form.tanggal = val.target.value
}}
/>
<TextInput

View File

@@ -1,10 +1,107 @@
import { Stack, Title } from '@mantine/core';
import React from 'react';
'use client'
/* eslint-disable @typescript-eslint/no-explicit-any */
import statePersentase from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
import { Box, Button, Stack, TextInput, Title } from '@mantine/core';
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
import { useEffect, useState } from 'react';
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
import { useProxy } from 'valtio/utils';
function PersentaseDataKelahiranKematian() {
const persentase = useProxy(statePersentase.persentasekelahiran);
const [chartData, setChartData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready
const isTablet = useMediaQuery('(max-width: 1024px)')
const isMobile = useMediaQuery('(max-width: 768px)')
useEffect(() => {
setMounted(true);
}, []);
useShallowEffect(() => {
const fetchData = async () => {
await persentase.findMany.load();
if (persentase.findMany.data && persentase.findMany.data.length > 0) {
setChartData(persentase.findMany.data);
}
};
fetchData();
}, []);
return (
<Stack py={10}>
<Title order={3}>Persentase Data Kelahiran & Kematian</Title>
{/* Form Input */}
<Box>
<Title order={3}>Persentase Data Kelahiran & Kematian</Title>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Tahun"
type="number"
value={persentase.create.form.tahun}
placeholder="Masukkan tahun"
onChange={(val) => {
persentase.create.form.tahun = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Kematian Kasar"
type="number"
value={persentase.create.form.kematianKasar}
placeholder="Masukkan kematian kasar"
onChange={(val) => {
persentase.create.form.kematianKasar = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Kematian Bayi"
type="number"
value={persentase.create.form.kematianBayi}
placeholder="Masukkan kematian bayi"
onChange={(val) => {
persentase.create.form.kematianBayi = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Kelahiran Kasar"
type="number"
value={persentase.create.form.kelahiranKasar}
placeholder="Masukkan kelahiran kasar"
onChange={(val) => {
persentase.create.form.kelahiranKasar = val.currentTarget.value;
}}
/>
<Button
mt={10}
onClick={async () => {
await persentase.create.create();
await persentase.findMany.load();
if (persentase.findMany.data) {
setChartData(persentase.findMany.data);
}
}}
>
Submit
</Button>
</Box>
{/* Chart */}
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Title pb={10} order={3}>Data Kelahiran & Kematian</Title>
{mounted && chartData.length > 0 && (
<BarChart width={isMobile ? 450 : isTablet ? 600 : 900} height={380} data={chartData} >
<XAxis dataKey="tahun" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="kematianKasar" fill="#f03e3e" name="Kematian Kasar" />
<Bar dataKey="kematianBayi" fill="#ff922b" name="Kematian Bayi" />
<Bar dataKey="kelahiranKasar" fill="#4dabf7" name="Kelahiran Kasar" />
</BarChart>
)}
</Box>
</Stack>
);
}

View File

@@ -1,10 +1,10 @@
import colors from '@/con/colors';
import { Stack, Tabs, TabsList, TabsPanel, TabsTab } from '@mantine/core';
import ArtikelKesehatan from './_ui/artikel_kesehatan/page';
import FasilitasKesehatan from './_ui/fasilitas_kesehatan/page';
import GrafikHasilKepuasan from './_ui/grafik_hasil_kepuasan/page';
import JadwalKegiatan from './_ui/jadwal_kegiatan/page';
import PersentaseDataKelahiranKematian from './_ui/persentase_data_kelahiran_kematian/page';
import GrafikHasilKepuasan from './_ui/grafik_hasil_kepuasan/page';
import colors from '@/con/colors';
function Page() {

View File

@@ -0,0 +1,94 @@
'use client'
import { Button, Stack } from '@mantine/core';
import { Link, RichTextEditor } from '@mantine/tiptap';
import Highlight from '@tiptap/extension-highlight';
import SubScript from '@tiptap/extension-subscript';
import Superscript from '@tiptap/extension-superscript';
import TextAlign from '@tiptap/extension-text-align';
import Underline from '@tiptap/extension-underline';
import { useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
const content =
'<h2 style="text-align: center;">Welcome to Mantine rich text editor</h2><p><code>RichTextEditor</code> component focuses on usability and is designed to be as simple as possible to bring a familiar editing experience to regular users. <code>RichTextEditor</code> is based on <a href="https://tiptap.dev/" rel="noopener noreferrer" target="_blank">Tiptap.dev</a> and supports all of its features:</p><ul><li>General text formatting: <strong>bold</strong>, <em>italic</em>, <u>underline</u>, <s>strike-through</s> </li><li>Headings (h1-h6)</li><li>Sub and super scripts (<sup>&lt;sup /&gt;</sup> and <sub>&lt;sub /&gt;</sub> tags)</li><li>Ordered and bullet lists</li><li>Text align&nbsp;</li><li>And all <a href="https://tiptap.dev/extensions" target="_blank" rel="noopener noreferrer">other extensions</a></li></ul>';
export function PPIDEditor({ onSubmit, onChange, showSubmit = true }: {
onSubmit?: (val: string) => void,
onChange: (val: string) => void,
showSubmit?: boolean }) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Superscript,
SubScript,
Highlight,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
immediatelyRender: false,
content,
onUpdate : ({editor}) => {
onChange(editor.getHTML())
}
});
return (
<Stack>
<RichTextEditor editor={editor}>
<RichTextEditor.Toolbar sticky stickyOffset={60}>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold />
<RichTextEditor.Italic />
<RichTextEditor.Underline />
<RichTextEditor.Strikethrough />
<RichTextEditor.ClearFormatting />
<RichTextEditor.Highlight />
<RichTextEditor.Code />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.H1 />
<RichTextEditor.H2 />
<RichTextEditor.H3 />
<RichTextEditor.H4 />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Blockquote />
<RichTextEditor.Hr />
<RichTextEditor.BulletList />
<RichTextEditor.OrderedList />
<RichTextEditor.Subscript />
<RichTextEditor.Superscript />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Link />
<RichTextEditor.Unlink />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.AlignLeft />
<RichTextEditor.AlignCenter />
<RichTextEditor.AlignJustify />
<RichTextEditor.AlignRight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Undo />
<RichTextEditor.Redo />
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
<RichTextEditor.Content />
</RichTextEditor>
{showSubmit && (
<Button onClick={() => {
if (!editor) return
onSubmit?.(editor?.getHTML())
}}>Submit</Button>
)}
</Stack>
);
}

View File

@@ -1,11 +1,109 @@
'use client'
import { Box, Button, Group, SimpleGrid, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, TextInput, Title } from '@mantine/core';
import React from 'react';
import { useProxy } from 'valtio/utils';
import stateDaftarInformasiPublik from '../../_state/ppid/daftar_informasi_publik/daftarInformasiPublik';
import { PPIDEditor } from '../_com/ppid_Editor';
import colors from '@/con/colors';
import { useShallowEffect } from '@mantine/hooks';
function Page() {
const daftarInformasi = useProxy(stateDaftarInformasiPublik.daftarInformasi)
const submit = () => {
if (daftarInformasi.create.form.jenisInformasi &&
daftarInformasi.create.form.deskripsi &&
daftarInformasi.create.form.tanggal) {
daftarInformasi.create.create()
}
}
return (
<div>
daftar-informasi-publik-desa-darmasaba
</div>
<Box>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Stack gap={"xs"}>
<Title fw={"bold"} order={3}>Daftar Informasi Publik Desa Darmasaba</Title>
<TextInput
label="Jenis Informasi"
placeholder="masukkan jenis informasi"
onChange={(val) => {
daftarInformasi.create.form.jenisInformasi = val.target.value
}}
/>
<PPIDEditor
showSubmit={false}
onChange={(val) => {
daftarInformasi.create.form.deskripsi = val
}}
/>
<TextInput
label="Tanggal Publikasi"
placeholder="masukkan tanggal publikasi"
onChange={(val) => {
daftarInformasi.create.form.tanggal = val.target.value
}}
/>
<Group>
<Button
bg={colors['blue-button']}
onClick={submit}
>Submit</Button>
</Group>
</Stack>
</Box>
<Box>
<ListDaftarInformasi />
</Box>
</SimpleGrid>
</Box>
);
}
function ListDaftarInformasi() {
const listData = useProxy(stateDaftarInformasiPublik.daftarInformasi)
useShallowEffect(() => {
listData.findMany.load()
}, [])
if (!listData.findMany.data) return <Stack>
{Array.from({length: 10}).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
return <Stack gap={"xs"}>
<Title fw={"bold"} order={3}>List Daftar Informasi Publik Desa Darmasaba</Title>
<Table
suppressHydrationWarning
striped
highlightOnHover
withTableBorder
withColumnBorders
bg={colors['white-1']}
>
<TableThead>
<TableTr>
<TableTh>
No
</TableTh>
<TableTh>
Jenis Informasi
</TableTh>
<TableTh>
Deskripsi
</TableTh>
<TableTh>
Tanggal Publikasi
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{listData.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.nomor}</TableTd>
<TableTd>{item.jenisInformasi}</TableTd>
<TableTd dangerouslySetInnerHTML={{ __html: item.deskripsi }}></TableTd>
<TableTd>{item.tanggal}</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Stack>
}
export default Page;

View File

@@ -0,0 +1,138 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import stateGrafikBerdasarkanJenisKelamin from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanJenisKelamin';
import colors from '@/con/colors';
import { Box, Button, Center, Flex, Stack, Text, TextInput, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useEffect, useState } from 'react';
import { Cell, Pie, PieChart } from 'recharts';
import { useProxy } from 'valtio/utils';
function GrafikBerdasarkanJenisKelamin() {
const grafikBerdasarkanJenisKelamin = useProxy(stateGrafikBerdasarkanJenisKelamin.grafikBerdasarkanJenisKelamin)
const [donutData, setDonutData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, [])
const updateChartData = (data: any) => {
if (data && data.length > 0) {
const totalLaki = data.reduce((acc: number, cur: any) => acc + Number(cur.laki || 0), 0);
const totalPerempuan = data.reduce((acc: number, cur: any) => acc + Number(cur.perempuan || 0), 0);
setDonutData([
{ name: 'Laki-laki', value: totalLaki, color: colors['blue-button'], key: 'laki-laki' },
{ name: 'Perempuan', value: totalPerempuan, color: '#FF6384', key: 'perempuan' }
]);
}
};
useShallowEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
await grafikBerdasarkanJenisKelamin.findMany.load();
if (grafikBerdasarkanJenisKelamin.findMany.data) {
updateChartData(grafikBerdasarkanJenisKelamin.findMany.data);
}
};
const handleSubmit = async () => {
try {
// Simpan data baru
await grafikBerdasarkanJenisKelamin.create.create();
// Muat ulang data
await grafikBerdasarkanJenisKelamin.findMany.load();
// Update chart dengan data baru
if (grafikBerdasarkanJenisKelamin.findMany.data) {
updateChartData(grafikBerdasarkanJenisKelamin.findMany.data);
}
// Reset form setelah submit
grafikBerdasarkanJenisKelamin.create.form.laki = '';
grafikBerdasarkanJenisKelamin.create.form.perempuan = '';
} catch (error) {
console.error("Error submitting data:", error);
}
};
return (
<Box>
<Box py={15}>
<Title order={3}>Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik</Title>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Laki-laki"
placeholder="masukkan jumlah laki-laki"
value={grafikBerdasarkanJenisKelamin.create.form.laki}
onChange={(val) => {
grafikBerdasarkanJenisKelamin.create.form.laki = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Perempuan"
type="number"
placeholder="masukkan jumlah perempuan"
value={grafikBerdasarkanJenisKelamin.create.form.perempuan}
onChange={(val) => {
grafikBerdasarkanJenisKelamin.create.form.perempuan = val.currentTarget.value;
}}
/>
<Button
mt={10}
bg={colors['blue-button']}
onClick={handleSubmit}
>
Submit
</Button>
</Box>
{/* Chart */}
<Box>
<Stack>
<Title pb={10} order={3}>Grafik Berdasarkan Jenis Kelamin Responden</Title>
{mounted && donutData.length > 0 && (
<Box style={{ width: '100%', height: 'auto', minHeight: 900 }}>
<Center>
<PieChart
width={1000} height={400}
data={donutData}
>
<Pie
dataKey="value"
nameKey="name"
data={donutData}
innerRadius={120}
outerRadius={160}
label
>
{donutData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</Center>
<Flex gap={"md"} align={"center"}>
<Box bg={'#FF6384'} w={20} h={20} />
<Text>Perempuan: {donutData.find((entry) => entry.name === 'Perempuan')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={colors['blue-button']} w={20} h={20} />
<Text>Laki-laki: {donutData.find((entry) => entry.name === 'Laki-laki')?.value}</Text>
</Flex>
</Box>
)}
</Stack>
</Box>
</Box>
);
}
export default GrafikBerdasarkanJenisKelamin;

View File

@@ -0,0 +1,166 @@
'use client'
/* eslint-disable @typescript-eslint/no-explicit-any */
import stateGrafikResponden from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanResponden';
import colors from '@/con/colors';
import { Box, Button, Center, Flex, Stack, Text, TextInput, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import React, { useEffect, useState } from 'react';
import { PieChart, Pie, Cell } from 'recharts';
import { useProxy } from 'valtio/utils';
function GrafikBerdasarkanResponden() {
const grafikBerdasarkanResponden = useProxy(stateGrafikResponden.grafikBerdasarkanResponden)
const [donutData, setDonutData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, [])
const updateChartData = (data: any) => {
if (data && data.length > 0) {
const totalSangatBaik = data.reduce((acc: number, cur: any) => acc + Number(cur.sangatbaik || 0), 0);
const totalBaik = data.reduce((acc: number, cur: any) => acc + Number(cur.baik || 0), 0);
const totalKurangBaik = data.reduce((acc: number, cur: any) => acc + Number(cur.kurangbaik || 0), 0);
const totalTidakBaik = data.reduce((acc: number, cur: any) => acc + Number(cur.tidakbaik || 0), 0);
setDonutData([
{ name: 'sangatbaik', value: totalSangatBaik, color: colors['blue-button'], key: 'sangatbaik' },
{ name: 'baik', value: totalBaik, color: '#10A85AFF', key: 'baik' },
{ name: 'kurangbaik', value: totalKurangBaik, color: '#B3AA12FF', key: 'kurangbaik' },
{ name: 'tidakbaik', value: totalTidakBaik, color: '#B21313FF', key: 'tidakbaik' }
]);
}
};
useShallowEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
await grafikBerdasarkanResponden.findMany.load();
if (grafikBerdasarkanResponden.findMany.data) {
updateChartData(grafikBerdasarkanResponden.findMany.data);
}
};
const handleSubmit = async () => {
try {
// Simpan data baru
await grafikBerdasarkanResponden.create.create();
// Muat ulang data
await grafikBerdasarkanResponden.findMany.load();
// Update chart dengan data baru
if (grafikBerdasarkanResponden.findMany.data) {
updateChartData(grafikBerdasarkanResponden.findMany.data);
}
// Reset form setelah submit
grafikBerdasarkanResponden.create.form.sangatbaik = '';
grafikBerdasarkanResponden.create.form.baik = '';
grafikBerdasarkanResponden.create.form.kurangbaik = '';
grafikBerdasarkanResponden.create.form.tidakbaik = '';
} catch (error) {
console.error("Error submitting data:", error);
}
};
return (
<Box>
<Box py={15}>
<Title order={3}>Grafik Berdasarkan Responden</Title>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Sangat Baik"
value={grafikBerdasarkanResponden.create.form.sangatbaik}
placeholder="masukkan jumlah respon sangat baik"
onChange={(val) => {
grafikBerdasarkanResponden.create.form.sangatbaik = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Baik"
value={grafikBerdasarkanResponden.create.form.baik}
placeholder="masukkan jumlah respon baik"
onChange={(val) => {
grafikBerdasarkanResponden.create.form.baik = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Kurang Baik"
value={grafikBerdasarkanResponden.create.form.kurangbaik}
placeholder="masukkan jumlah respon kurang baik"
onChange={(val) => {
grafikBerdasarkanResponden.create.form.kurangbaik = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Tidak Baik"
value={grafikBerdasarkanResponden.create.form.tidakbaik}
placeholder="masukkan jumlah respon tidak baik"
onChange={(val) => {
grafikBerdasarkanResponden.create.form.tidakbaik = val.currentTarget.value;
}}
/>
<Button
mt={10}
bg={colors['blue-button']}
onClick={handleSubmit}
>
Submit
</Button>
</Box>
{/* Chart */}
<Box>
<Stack>
<Title pb={10} order={3}>Grafik Berdasarkan Responden</Title>
{mounted && donutData.length > 0 && (
<Box style={{ width: '100%', height: 'auto', minHeight: 900 }}>
<Center>
<PieChart
width={1000} height={400}
data={donutData}
>
<Pie
dataKey="value"
nameKey="name"
data={donutData}
innerRadius={120}
outerRadius={160}
label
>
{donutData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</Center>
<Flex gap={"md"} align={"center"}>
<Box bg={colors['blue-button']} w={20} h={20} />
<Text>Sangat Baik: {donutData.find((entry) => entry.name === 'sangatbaik')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={'#10A85AFF'} w={20} h={20} />
<Text>Baik: {donutData.find((entry) => entry.name === 'baik')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={'#B3AA12FF'} w={20} h={20} />
<Text>Kurang Baik: {donutData.find((entry) => entry.name === 'kurangbaik')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={'#B21313FF'} w={20} h={20} />
<Text>Tidak Baik: {donutData.find((entry) => entry.name === 'tidakbaik')?.value}</Text>
</Flex>
</Box>
)}
</Stack>
</Box>
</Box>
);
}
export default GrafikBerdasarkanResponden;

View File

@@ -0,0 +1,156 @@
'use client'
/* eslint-disable @typescript-eslint/no-explicit-any */
import colors from '@/con/colors';
import { Box, Button, Center, Flex, Stack, Text, TextInput, Title } from '@mantine/core';
import React, { useEffect, useState } from 'react';
import { useProxy } from 'valtio/utils';
import stateGrafikBerdasarkanUmur from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikBerdasarkanUmur';
import { useShallowEffect } from '@mantine/hooks';
import { PieChart, Pie, Cell } from 'recharts';
function GrafikBerdasarakanUmur() {
const grafikBerdasarkanUmur = useProxy(stateGrafikBerdasarkanUmur.grafikBerdasarkanUmur)
const [donutData, setDonutData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
const updateChartData = (data: any) => {
if (data && data.length > 0) {
const totalRemaja = data.reduce((acc: number, cur: any) => acc + Number(cur.remaja || 0), 0);
const totalDewasa = data.reduce((acc: number, cur: any) => acc + Number(cur.dewasa || 0), 0);
const totalOrangtua = data.reduce((acc: number, cur: any) => acc + Number(cur.orangtua || 0), 0);
const totalLansia = data.reduce((acc: number, cur: any) => acc + Number(cur.lansia || 0), 0);
setDonutData([
{ name: 'Remaja', value: totalRemaja, color: colors['blue-button'], key: 'remaja' },
{ name: 'Dewasa', value: totalDewasa, color: '#D32711FF', key: 'dewasa' },
{ name: 'Orangtua', value: totalOrangtua, color: '#B46B04FF', key: 'orangtua' },
{ name: 'Lansia', value: totalLansia, color: '#038617FF', key: 'lansia' }
]);
}
};
useShallowEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
await grafikBerdasarkanUmur.findMany.load();
if (grafikBerdasarkanUmur.findMany.data) {
updateChartData(grafikBerdasarkanUmur.findMany.data);
}
}
const handleSubmit = async () => {
try {
await grafikBerdasarkanUmur.create.create();
await grafikBerdasarkanUmur.findMany.load();
if (grafikBerdasarkanUmur.findMany.data) {
updateChartData(grafikBerdasarkanUmur.findMany.data);
}
} catch (error) {
console.error("Error submitting data:", error);
}
}
return (
<Box>
<Box py={15}>
<Title order={3}>Grafik Berdasarkan Umur Responden</Title>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Remaja"
placeholder="masukkan jumlah responden remaja"
value={grafikBerdasarkanUmur.create.form.remaja}
onChange={(val) => {
grafikBerdasarkanUmur.create.form.remaja = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Dewasa"
placeholder="masukkan jumlah responden dewasa"
value={grafikBerdasarkanUmur.create.form.dewasa}
onChange={(val) => {
grafikBerdasarkanUmur.create.form.dewasa = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Orangtua"
placeholder="masukkan jumlah responden orangtua"
value={grafikBerdasarkanUmur.create.form.orangtua}
onChange={(val) => {
grafikBerdasarkanUmur.create.form.orangtua = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Lansia"
placeholder="masukkan jumlah responden lansia"
value={grafikBerdasarkanUmur.create.form.lansia}
onChange={(val) => {
grafikBerdasarkanUmur.create.form.lansia = val.currentTarget.value;
}}
/>
<Button
mt={10}
bg={colors['blue-button']}
onClick={handleSubmit}
>
Submit
</Button>
</Box>
{/* Chart */}
<Box>
<Stack>
<Title pb={10} order={3}>Grafik Berdasarkan Umur Responden</Title>
{mounted && donutData.length > 0 && (
<Box style={{ width: '100%', height: 'auto', minHeight: 900 }}>
<Center>
<PieChart
width={1000} height={400}
data={donutData}
>
<Pie
dataKey="value"
nameKey="name"
data={donutData}
innerRadius={120}
outerRadius={160}
label
>
{donutData.map((entry, index) => (
<Cell key={`cell-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</Center>
<Flex gap={"md"} align={"center"}>
<Box bg={colors['blue-button']} w={20} h={20} />
<Text>17 - 25 tahun: {donutData.find((entry) => entry.name === 'remaja')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={'#D32711FF'} w={20} h={20} />
<Text>26 - 45 tahun: {donutData.find((entry) => entry.name === 'dewasa')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={'#B46B04FF'} w={20} h={20} />
<Text>46 - 60 tahun: {donutData.find((entry) => entry.name === 'orangtua')?.value}</Text>
</Flex>
<Flex gap={"md"} align={"center"}>
<Box bg={'#038617FF'} w={20} h={20} />
<Text>di atas 60 tahun: {donutData.find((entry) => entry.name === 'lansia')?.value}</Text>
</Flex>
</Box>
)}
</Stack>
</Box>
</Box>
);
}
export default GrafikBerdasarakanUmur;

View File

@@ -0,0 +1,88 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import stateGrafikHasilKepuasanMasyarakat from '@/app/admin/(dashboard)/_state/ppid/indeks_kepuasan_masyarakat/grafikHasilKepuasan';
import colors from '@/con/colors';
import { Box, Button, TextInput, Title } from '@mantine/core';
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
import React, { useEffect, useState } from 'react';
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
import { useProxy } from 'valtio/utils';
function GrafikHasilKepuasan() {
const grafikHasilKepuasan = useProxy(stateGrafikHasilKepuasanMasyarakat.grafikHasilKepuasanMasyarakat)
const [chartData, setChartData] = useState<any[]>([]);
const [mounted, setMounted] = useState(false);
const isTablet = useMediaQuery('(max-width: 1024px)')
const isMobile = useMediaQuery('(max-width: 768px)')
useEffect(() => {
setMounted(true);
}, [])
useShallowEffect(() => {
const fetchData = async () => {
await grafikHasilKepuasan.findMany.load();
if (grafikHasilKepuasan.findMany.data && grafikHasilKepuasan.findMany.data.length > 0) {
setChartData(grafikHasilKepuasan.findMany.data);
}
};
fetchData();
}, []);
return (
<Box>
<Box py={15}>
<Title order={3}>Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik</Title>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Label"
placeholder="masukkan label"
value={grafikHasilKepuasan.create.form.label}
onChange={(val) => {
grafikHasilKepuasan.create.form.label = val.currentTarget.value;
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Jumlah Kepuasan"
type="number"
placeholder="masukkan jumlah kepuasan"
value={grafikHasilKepuasan.create.form.kepuasan}
onChange={(val) => {
grafikHasilKepuasan.create.form.kepuasan = val.currentTarget.value;
}}
/>
<Button
mt={10}
bg={colors['blue-button']}
onClick={async () => {
await grafikHasilKepuasan.create.create();
await grafikHasilKepuasan.findMany.load();
if (grafikHasilKepuasan.findMany.data) {
setChartData(grafikHasilKepuasan.findMany.data);
}
}}
>
Submit
</Button>
</Box>
{/* Chart */}
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Title pb={10} order={3}>Data Kepuasan Masyarakat</Title>
{mounted && chartData.length > 0 && (
<BarChart width={isMobile ? 450 : isTablet ? 600 : 900} height={380} data={chartData} >
<XAxis dataKey="label" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="kepuasan" fill={colors['blue-button']} name="Kepuasan" />
</BarChart>
)}
</Box>
</Box>
);
}
export default GrafikHasilKepuasan;

View File

@@ -1,10 +1,44 @@
import { Stack, Tabs, TabsList, TabsPanel, TabsTab } from '@mantine/core';
import React from 'react';
import colors from '@/con/colors';
import GrafikHasilKepuasan from './_ui/grafik_hasil_kepuasan_masyarakat/page';
import GrafikBerdasarkanJenisKelamin from './_ui/grafik_berdasarkan_jenis_kelamin_responden/page';
import GrafikBerdasarkanResponden from './_ui/grafik_berdasarkan_responden/page';
import GrafikBerdasarakanUmur from './_ui/grafik_berdasarkan_umur/page';
function Page() {
return (
<div>
ikm-desa-darmasaba
</div>
<Stack>
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik"}>
<TabsList>
<TabsTab value="Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik">
Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik
</TabsTab>
<TabsTab value="Grafik Berdasarkan Jenis Kelamin Responden">
Grafik Berdasarkan Jenis Kelamin Responden
</TabsTab>
<TabsTab value="Grafik Berdasarkan Pilihan Responden">
Grafik Berdasarkan Pilihan Responden
</TabsTab>
<TabsTab value="Grafik Berdasarkan Umur Responden">
Grafik Berdasarkan Umur Responden
</TabsTab>
</TabsList>
<TabsPanel value="Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik">
<GrafikHasilKepuasan/>
</TabsPanel>
<TabsPanel value="Grafik Berdasarkan Jenis Kelamin Responden">
<GrafikBerdasarkanJenisKelamin/>
</TabsPanel>
<TabsPanel value="Grafik Berdasarkan Pilihan Responden">
<GrafikBerdasarkanResponden/>
</TabsPanel>
<TabsPanel value="Grafik Berdasarkan Umur Responden">
<GrafikBerdasarakanUmur/>
</TabsPanel>
</Tabs>
</Stack>
);
}

View File

@@ -0,0 +1,26 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.DoctorSignGetPayload<{
select: {
content: true;
}
}>
export default async function doctorSignCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.doctorSign.create({
data: {
content: body.content,
},
});
return {
success: true,
message: "Success create doctor sign",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,10 @@
import prisma from "@/lib/prisma";
export default async function doctorSignFindMany() {
const data = await prisma.doctorSign.findMany();
return {
success: true,
message: "Success get doctor sign",
data,
};
}

View File

@@ -0,0 +1,15 @@
import Elysia, { t } from "elysia"
import doctorSignCreate from "./create"
import doctorSignFindMany from "./find-many"
const DoctorSign = new Elysia({
prefix: "/doctor_sign",
tags: ["Data Kesehatan/Artikel Kesehatan/Doctor Sign"],
})
.get("/find-many", doctorSignFindMany)
.post("/create", doctorSignCreate, {
body: t.Object({
content: t.String(),
}),
})
export default DoctorSign

View File

@@ -0,0 +1,27 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.FirstAidGetPayload<{
select: {
title: true;
content: true;
}
}>
export default async function firstAidCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.firstAid.create({
data: {
title: body.title,
content: body.content,
},
});
return {
success: true,
message: "Success create first aid",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma"
export default async function firstAidFindMany() {
const res = await prisma.firstAid.findMany()
return {
data: res
}
}

View File

@@ -0,0 +1,16 @@
import Elysia, { t } from "elysia"
import firstAidCreate from "./create"
import firstAidFindMany from "./find-many"
const FirstAid = new Elysia({
prefix: "/firstaid",
tags: ["Data Kesehatan/Artikel Kesehatan/First Aid"]
})
.get("/find-many", firstAidFindMany)
.post("/create", firstAidCreate, {
body: t.Object({
title: t.String(),
content: t.String(),
}),
})
export default FirstAid

View File

@@ -0,0 +1,26 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.IntroductionGetPayload<{
select: {
content: true;
};
}>;
export default async function introductionCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.introduction.create({
data: {
content: body.content,
},
});
return {
success: true,
message: "Success create introduction",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma";
export default async function introductionFindMany() {
const res = await prisma.introduction.findMany();
return {
data: res,
};
}

View File

@@ -0,0 +1,16 @@
import Elysia, { t } from "elysia";
import introductionCreate from "./create";
import introductionFindMany from "./find-many";
const Introduction = new Elysia({
prefix: "/introduction",
tags: ["Data Kesehatan/Artikel Kesehatan/Introduction"]
})
.get("/find-many", introductionFindMany)
.post("/create", introductionCreate, {
body: t.Object({
content: t.String(),
}),
})
export default Introduction;

View File

@@ -0,0 +1,30 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
import { Prisma } from "@prisma/client";
type FormCreate = Prisma.MythVsFactGetPayload<{
select: {
title: true;
mitos: true;
fakta: true;
}
}>
export default async function mythVsFactCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.mythVsFact.create({
data: {
title: body.title,
mitos: body.mitos,
fakta: body.fakta,
},
});
return {
success: true,
message: "Success create myth vs fact",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,10 @@
import prisma from "@/lib/prisma";
export default async function mythVsFactFindMany() {
const mythVsFacts = await prisma.mythVsFact.findMany();
return {
success: true,
message: "Success get myth vs fact",
data: mythVsFacts,
};
}

View File

@@ -0,0 +1,17 @@
import Elysia, { t } from "elysia";
import mythVsFactCreate from "./create";
import mythVsFactFindMany from "./find-many";
const MythVsFact = new Elysia({
prefix: "/mythvsfact",
tags: ["Data Kesehatan/Artikel Kesehatan/Myth vs Fact"]
})
.get("/find-many", mythVsFactFindMany)
.post("/create", mythVsFactCreate, {
body: t.Object({
title: t.String(),
mitos: t.String(),
fakta: t.String(),
}),
})
export default MythVsFact

View File

@@ -0,0 +1,28 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.PreventionGetPayload<{
select: {
title: true;
content: true;
}
}>
export default async function preventionCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.prevention.create({
data: {
title: body.title,
content: body.content,
},
});
return {
success: true,
message: "Success create prevention",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma"
export default async function preventionFindMany() {
const res = await prisma.prevention.findMany()
return {
data: res
}
}

View File

@@ -0,0 +1,16 @@
import Elysia, { t } from "elysia"
import preventionCreate from "./create"
import preventionFindMany from "./find-many"
const Prevention = new Elysia({
prefix: "/prevention",
tags: ["Data Kesehatan/Artikel Kesehatan/Prevention"]
})
.get("/find-many", preventionFindMany)
.post("/create", preventionCreate, {
body: t.Object({
title: t.String(),
content: t.String(),
}),
})
export default Prevention

View File

@@ -0,0 +1,27 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.SymptomGetPayload<{
select: {
title: true;
content: true;
}
}>
export default async function symptomCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.symptom.create({
data: {
title: body.title,
content: body.content,
},
});
return {
success: true,
message: "Success create symptom",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma"
export default async function symptomFindMany() {
const res = await prisma.symptom.findMany()
return {
data: res
}
}

View File

@@ -0,0 +1,16 @@
import Elysia, { t } from "elysia";
import symptomCreate from "./create";
import symptomFindMany from "./find-many";
const Syptom = new Elysia({
prefix: "/symptom",
tags: ["Data Kesehatan/Artikel Kesehatan/Syptom"]
})
.get("/find-many", symptomFindMany)
.post("/create", symptomCreate, {
body: t.Object({
title: t.String(),
content: t.String(),
}),
})
export default Syptom

View File

@@ -0,0 +1,27 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.GrafikKepuasanGetPayload<{
select: {
label: true;
jumlah: true
};
}>;
export default async function grafikKepuasanCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.grafikKepuasan.create({
data: {
label: body.label,
jumlah: body.jumlah,
},
});
return {
success: true,
message: "Success create grafik kepuasan",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma"
export default async function grafikKepuasanFindMany() {
const res = await prisma.grafikKepuasan.findMany()
return {
data: res
}
}

View File

@@ -0,0 +1,16 @@
import Elysia, { t } from "elysia";
import grafikKepuasanCreate from "./create";
import grafikKepuasanFindMany from "./find-many";
const GrafikKepuasan = new Elysia({
prefix: "/grafikkepuasan",
tags: ["Data Kesehatan/Grafik Kepuasan"]
})
.get("/find-many", grafikKepuasanFindMany)
.post("/create", grafikKepuasanCreate, {
body: t.Object({
label: t.String(),
jumlah: t.String(),
}),
})
export default GrafikKepuasan

View File

@@ -0,0 +1,32 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.DataKematian_KelahiranGetPayload<{
select: {
tahun: true;
kematianKasar: true;
kematianBayi: true;
kelahiranKasar: true;
};
}>;
export default async function persentaseKelahiranKematianCreate(context: Context) {
const body = context.body as FormCreate
await prisma.dataKematian_Kelahiran.create({
data: {
tahun: body.tahun,
kematianKasar: body.kematianKasar,
kematianBayi: body.kematianBayi,
kelahiranKasar: body.kelahiranKasar,
}
})
return{
success: true,
message: "Success create persentase kelahiran kematian",
data: {
...body
}
}
}

View File

@@ -0,0 +1,9 @@
import prisma from "@/lib/prisma";
export default async function persentaseKelahiranKematianFindMany() {
const res = await prisma.dataKematian_Kelahiran.findMany();
return {
data: res
}
}

View File

@@ -0,0 +1,19 @@
import Elysia, { t } from "elysia";
import persentaseKelahiranKematianCreate from "./create";
import persentaseKelahiranKematianFindMany from "./find-many";
const PersentaseKelahiranKematian = new Elysia({
prefix: "/persentasekelahiran",
tags: ["Data Kesehatan/Persentase Kelahiran Kematian"],
})
.get("/find-many", persentaseKelahiranKematianFindMany)
.post("/create", persentaseKelahiranKematianCreate, {
body: t.Object({
tahun: t.String(),
kematianKasar: t.String(),
kematianBayi: t.String(),
kelahiranKasar: t.String(),
}),
})
export default PersentaseKelahiranKematian;

View File

@@ -11,6 +11,15 @@ import LayananTersedia from "./data_kesehatan_warga/jadwal_kegiatan/layanan_yang
import SyaratKetentuan from "./data_kesehatan_warga/jadwal_kegiatan/syarat_dan_ketentuan";
import DokumenDiperlukan from "./data_kesehatan_warga/jadwal_kegiatan/dokumen_yang_diperlukan";
import PendaftaranJadwal from "./data_kesehatan_warga/jadwal_kegiatan/pendaftaran";
import PersentaseKelahiranKematian from "./data_kesehatan_warga/persentase_kelahiran_kematian";
import GrafikKepuasan from "./data_kesehatan_warga/grafik_kepuasan";
import Introduction from "./data_kesehatan_warga/artikel_kesehatan/introduction";
import Syptom from "./data_kesehatan_warga/artikel_kesehatan/syptom";
import Prevention from "./data_kesehatan_warga/artikel_kesehatan/prevention";
import FirstAid from "./data_kesehatan_warga/artikel_kesehatan/first_aid";
import MythVsFact from "./data_kesehatan_warga/artikel_kesehatan/myth_vs_fact";
import DoctorSign from "./data_kesehatan_warga/artikel_kesehatan/doctor_sign";
const Kesehatan = new Elysia({
prefix: "/api/kesehatan",
@@ -28,4 +37,12 @@ const Kesehatan = new Elysia({
.use(SyaratKetentuan)
.use(DokumenDiperlukan)
.use(PendaftaranJadwal)
.use(PersentaseKelahiranKematian)
.use(GrafikKepuasan)
.use(Introduction)
.use(Syptom)
.use(Prevention)
.use(FirstAid)
.use(MythVsFact)
.use(DoctorSign)
export default Kesehatan;

View File

@@ -0,0 +1,29 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.DaftarInformasiPublikGetPayload<{
select: {
jenisInformasi: true;
deskripsi: true;
tanggal: true;
}
}>
export default async function daftarInformasiPublikCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.daftarInformasiPublik.create({
data: {
jenisInformasi: body.jenisInformasi,
deskripsi: body.deskripsi,
tanggal: body.tanggal,
},
})
return {
success: true,
message: "Success create daftar informasi publik",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,9 @@
import prisma from "@/lib/prisma";
export default async function daftarInformasiPublikFindMany() {
const res = await prisma.daftarInformasiPublik.findMany();
return {
data: res
}
}

View File

@@ -0,0 +1,18 @@
import Elysia, { t } from "elysia";
import daftarInformasiPublikCreate from "./create";
import daftarInformasiPublikFindMany from "./find-many";
const DaftarInformasiPublik = new Elysia({
prefix: "/daftarinformasipublik",
tags: ["PPID/Daftar Informasi Publik"]
})
.get("/find-many", daftarInformasiPublikFindMany)
.post("/create", daftarInformasiPublikCreate, {
body: t.Object({
jenisInformasi: t.String(),
deskripsi: t.String(),
tanggal: t.String(),
}),
})
export default DaftarInformasiPublik

View File

@@ -0,0 +1,27 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.GrafikBerdasarkanJenisKelaminGetPayload<{
select: {
perempuan: true;
laki: true;
}
}>
export default async function grafikBerdasarkanJenisKelaminCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.grafikBerdasarkanJenisKelamin.create({
data: {
perempuan: body.perempuan,
laki: body.laki,
},
});
return {
success: true,
message: "Success create grafik berdasarkan jenis kelamin",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma";
export default async function grafikBerdasarkanJenisKelaminFindMany() {
const res = await prisma.grafikBerdasarkanJenisKelamin.findMany();
return {
data: res
}
}

View File

@@ -0,0 +1,17 @@
import Elysia, { t } from "elysia";
import grafikBerdasarkanJenisKelaminCreate from "./create";
import grafikBerdasarkanJenisKelaminFindMany from "./find-many";
const GrafikBerdasarkanJenisKelamin = new Elysia({
prefix: "/grafikberdasarkanjeniskelamin",
tags: ["PPID/IKM/grafikberdasarkanjeniskelamin"],
})
.get("/find-many", grafikBerdasarkanJenisKelaminFindMany)
.post("/create", grafikBerdasarkanJenisKelaminCreate, {
body: t.Object({
perempuan: t.String(),
laki: t.String(),
}),
});
export default GrafikBerdasarkanJenisKelamin;

View File

@@ -0,0 +1,31 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.GrafikBerdasarkanUmurGetPayload<{
select: {
remaja: true;
dewasa: true;
orangtua: true;
lansia: true;
}
}>
export async function grafikBerdasarkanUmurCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.grafikBerdasarkanUmur.create({
data: {
remaja: body.remaja,
dewasa: body.dewasa,
orangtua: body.orangtua,
lansia: body.lansia,
},
});
return {
success: true,
message: "Success create grafik berdasarkan umur",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma";
export async function grafikBerdasarkanUmurFindMany(){
const res = await prisma.grafikBerdasarkanUmur.findMany();
return {
data: res
}
}

View File

@@ -0,0 +1,19 @@
import Elysia, { t } from "elysia";
import { grafikBerdasarkanUmurFindMany } from "./find-many";
import { grafikBerdasarkanUmurCreate } from "./create";
const GrafikBerdasarkanUmur = new Elysia({
prefix: "/grafikberdasarkanumur",
tags: ["PPID/IKM/grafikberdasarkanumur"]
})
.get("/find-many", grafikBerdasarkanUmurFindMany)
.post("/create", grafikBerdasarkanUmurCreate, {
body: t.Object({
remaja: t.String(),
dewasa: t.String(),
orangtua: t.String(),
lansia: t.String(),
}),
});
export default GrafikBerdasarkanUmur;

View File

@@ -0,0 +1,27 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.IndeksKepuasanMasyarakatGetPayload<{
select: {
label: true;
kepuasan: true;
};
}>;
export default async function grafikHasilKepuasanMasyarakatCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.indeksKepuasanMasyarakat.create({
data: {
label: body.label,
kepuasan: body.kepuasan,
},
});
return {
success: true,
message: "Success create grafik hasil kepuasan masyarakat",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma";
export default async function grafikHasilKepuasanMasyarakatFindMany() {
const res = await prisma.indeksKepuasanMasyarakat.findMany();
return {
data: res,
};
}

View File

@@ -0,0 +1,17 @@
import Elysia, { t } from "elysia";
import grafikHasilKepuasanMasyarakatCreate from "./create";
import grafikHasilKepuasanMasyarakatFindMany from "./find-many";
const GrafikHasilKepuasanMasyarakat = new Elysia({
prefix: "/grafikhasilkepuasamanmasyarakat",
tags: ["PPID/IKM/grafikhasilkepuasanmasyarakat"],
})
.get("/find-many", grafikHasilKepuasanMasyarakatFindMany)
.post("/create", grafikHasilKepuasanMasyarakatCreate, {
body: t.Object({
label: t.String(),
kepuasan: t.String(),
}),
});
export default GrafikHasilKepuasanMasyarakat;

View File

@@ -0,0 +1,31 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.GrafikBerdasarkanRespondenGetPayload<{
select: {
sangatbaik: true;
baik: true;
kurangbaik: true;
tidakbaik: true
};
}>;
export default async function grafikRespondenCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.grafikBerdasarkanResponden.create({
data: {
sangatbaik: body.sangatbaik,
baik: body.baik,
kurangbaik: body.kurangbaik,
tidakbaik: body.tidakbaik,
},
});
return {
success: true,
message: "Success create grafik berdasarkan responden",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,8 @@
import prisma from "@/lib/prisma";
export default async function grafikRespondenFindMany(){
const res = await prisma.grafikBerdasarkanResponden.findMany();
return{
data: res
}
}

View File

@@ -0,0 +1,20 @@
import Elysia, { t } from "elysia";
import grafikRespondenCreate from "./create";
import grafikRespondenFindMany from "./find-many";
const GrafikBerdasarkanResponden = new Elysia({
prefix: "/grafikberdasarkanresponden",
tags: ["PPID/IKM/grafikberdasarkanresponden"]
})
.get("/find-many", grafikRespondenFindMany)
.post("/create", grafikRespondenCreate, {
body: t.Object({
sangatbaik: t.String(),
baik: t.String(),
kurangbaik: t.String(),
tidakbaik: t.String(),
}),
})
export default GrafikBerdasarkanResponden

View File

@@ -0,0 +1,19 @@
import Elysia from "elysia";
import DaftarInformasiPublik from "./daftar_informasi_publik";
import GrafikHasilKepuasanMasyarakat from "./ikm/grafik_hasil_kepuasan_masyarakat";
import GrafikBerdasarkanJenisKelamin from "./ikm/grafik_berdasarkan_jenis_kelamin";
import GrafikBerdasarkanResponden from "./ikm/grafik_responden";
import GrafikBerdasarkanUmur from "./ikm/grafik_berdasarkan_umur";
const PPID = new Elysia({ prefix: "/api/ppid", tags: ["PPID"] })
.use(DaftarInformasiPublik)
.use(GrafikHasilKepuasanMasyarakat)
.use(GrafikBerdasarkanJenisKelamin)
.use(GrafikBerdasarkanResponden)
.use(GrafikBerdasarkanUmur)
export default PPID

View File

@@ -14,6 +14,8 @@ import uplImg from "./_lib/upl-img";
import { uplImgSingle } from "./_lib/upl-img-single";
import Desa from "./_lib/desa";
import Kesehatan from "./_lib/kesehatan";
import PPID from "./_lib/ppid";
const ROOT = process.cwd();
if (!process.env.WIBU_UPLOAD_DIR)
@@ -45,11 +47,26 @@ async function layanan() {
const data = await prisma.layanan.findMany();
return { data };
}
const Utils = new Elysia({
prefix: "/api/utils",
tags: ["Utils"],
}).get("/version", async () => {
const packageJson = await fs.readFile(
path.join(ROOT, "package.json"),
"utf-8"
);
const version = JSON.parse(packageJson).version;
return { version };
});
const ApiServer = new Elysia()
.use(swagger({ path: "/api/docs" }))
.use(cors(corsConfig))
.use(PPID)
.use(Kesehatan)
.use(Desa)
.use(Utils)
.onError(({ code }) => {
if (code === "NOT_FOUND") {
return {

View File

@@ -8,7 +8,7 @@ const data = [
title: "Olahraga dan Kepemudaan",
deskripsi: "Tim Bola Voli Putri Dharma Temaja meraih juara 3 dalam Turnamen Bola Voli Mangupura Cup 2024 kategori Putri Se-Bali ",
image: "/api/img/prestasi-voli.jpeg",
link: "/darmasaba/prestasi/tim-bola-voli-putri-dharma-temaja-meraih-juara-3-dalam-turnamen-bola-voli-mangupura-cup-2024-kategori-putri-se-bali"
link: "/darmasaba/prestasi-desa/tim-bola-voli-putri-dharma-temaja-meraih-juara-3-dalam-turnamen-bola-voli-mangupura-cup-2024-kategori-putri-se-bali"
},
{
id: 2,

View File

@@ -1,105 +0,0 @@
'use client'
import { Box, Container, Flex, Grid, SimpleGrid, Skeleton, Stack, Text, Title } from '@mantine/core';
import { useSearchParams } from 'next/navigation';
import React from 'react';
const tx = `
Untuk menambahkan fitur berbagi nomor WhatsApp di kode yang Anda miliki, saya akan menjelaskan beberapa pendekatan yang bisa digunakan. Biasanya ini dilakukan dengan membuat link yang ketika diklik akan membuka aplikasi WhatsApp dengan nomor tujuan yang sudah diatur.
Berikut adalah cara mengimplementasikannya pada kode React Anda:
`
function Page() {
return (
<Stack>
<Grid>
<Grid.Col span={{
base: 12,
sm: 6,
md: 4,
xl: 10
}}>
<Box h={"200"} bg={"blue"}>1</Box>
</Grid.Col>
<Grid.Col span={{
base: 12,
sm: 6,
md: 8,
xl: 2
}}>
<Box h={"200"} bg={"red"}>1</Box>
</Grid.Col>
</Grid>
<SimpleGrid cols={{
base: 1,
sm: 2,
md: 4,
xl: 20
}}>
{Array.from({ length: 10 }).map((_, i) => (
<Box key={i} h={"60"} bg={"blue"}>1</Box>
))}
</SimpleGrid>
<Flex >
<Box w={400} h={"200"} bg={"blue"}>1</Box>
<Box w={400} bg={"red"}>
<Text fz={"42"} lineClamp={1} >{tx}</Text>
<Text bg={"blue"} style={{
fontSize: "2rem"
}} lineClamp={1} >{tx}</Text>
<Title order={1}>apa kabar</Title>
</Box>
</Flex>
<Page2/>
</Stack>
);
}
export default Page;
const Halaman = [Halaman1, Halaman2, Halaman3]
function Page2() {
const page = useSearchParams().get("p");
if (!page) return <Container >
<Stack>
<Text>halo 1</Text>
{Array.from({ length: 4 }).map((v, k) => <Skeleton h={100} key={k} />)}
</Stack>
</Container>
return (
<Container w={"100%"}>
<Stack>
<Text>halo 2</Text>
{Halaman[Number(page)-1]()}
</Stack>
</Container>
);
}
function Halaman1() {
return <Stack bg={"blue"}>
ini halaman 1
</Stack>
}
function Halaman2() {
return <Stack bg={"red"}>
ini halaman 2
</Stack>
}
function Halaman3() {
return <Stack bg={"grape"}>
ini halaman 3
</Stack>
}