feat(kesehatan): refactor ringkasan kesehatan to auto-derived stats
- Add IbuHamil and Balita models to schema.prisma - Implement IbuHamil and Balita API modules (CRUD) - Implement /stats endpoint for aggregated health KPIs - Refactor ringkasan-kesehatan admin page to dashboard-style UI - Update sidebar with Ibu Hamil and Balita entries - Fix type errors and icon exports in admin UI - Bump version to 0.1.52
This commit is contained in:
47
src/app/api/[[...slugs]]/_lib/kesehatan/balita/create.ts
Normal file
47
src/app/api/[[...slugs]]/_lib/kesehatan/balita/create.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { JenisKelaminBalita, StatusStunting } from "@prisma/client";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormCreate = {
|
||||
nama: string;
|
||||
nik?: string;
|
||||
tanggalLahir: string;
|
||||
jenisKelamin: JenisKelaminBalita;
|
||||
beratBadanKg?: number;
|
||||
tinggiBadanCm?: number;
|
||||
namaOrtu?: string;
|
||||
alamat?: string;
|
||||
noHpOrtu?: string;
|
||||
posyanduId?: string;
|
||||
imunisasiLengkap: boolean;
|
||||
giziBaik: boolean;
|
||||
pemeriksaanRutin: boolean;
|
||||
statusStunting: StatusStunting;
|
||||
catatan?: string;
|
||||
};
|
||||
|
||||
export default async function balitaCreate(context: Context) {
|
||||
const body = context.body as FormCreate;
|
||||
|
||||
const data = await prisma.balita.create({
|
||||
data: {
|
||||
nama: body.nama,
|
||||
nik: body.nik,
|
||||
tanggalLahir: new Date(body.tanggalLahir),
|
||||
jenisKelamin: body.jenisKelamin,
|
||||
beratBadanKg: body.beratBadanKg,
|
||||
tinggiBadanCm: body.tinggiBadanCm,
|
||||
namaOrtu: body.namaOrtu,
|
||||
alamat: body.alamat,
|
||||
noHpOrtu: body.noHpOrtu,
|
||||
posyanduId: body.posyanduId || null,
|
||||
imunisasiLengkap: body.imunisasiLengkap,
|
||||
giziBaik: body.giziBaik,
|
||||
pemeriksaanRutin: body.pemeriksaanRutin,
|
||||
statusStunting: body.statusStunting ?? "NORMAL",
|
||||
catatan: body.catatan,
|
||||
},
|
||||
});
|
||||
|
||||
return { success: true, message: "Balita berhasil ditambahkan", data };
|
||||
}
|
||||
22
src/app/api/[[...slugs]]/_lib/kesehatan/balita/del.ts
Normal file
22
src/app/api/[[...slugs]]/_lib/kesehatan/balita/del.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function balitaDelete(context: Context) {
|
||||
const id = context.params?.id as string;
|
||||
|
||||
if (!id) {
|
||||
return { success: false, message: "ID tidak diberikan" };
|
||||
}
|
||||
|
||||
const existing = await prisma.balita.findUnique({ where: { id } });
|
||||
if (!existing) {
|
||||
return new Response(JSON.stringify({ success: false, message: "Data tidak ditemukan" }), {
|
||||
status: 404,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.balita.update({ where: { id }, data: { isActive: false } });
|
||||
|
||||
return { success: true, message: "Balita berhasil dihapus" };
|
||||
}
|
||||
38
src/app/api/[[...slugs]]/_lib/kesehatan/balita/find-by-id.ts
Normal file
38
src/app/api/[[...slugs]]/_lib/kesehatan/balita/find-by-id.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function balitaFindById(context: Context) {
|
||||
const id = context.params?.id as string;
|
||||
|
||||
if (!id) {
|
||||
return new Response(JSON.stringify({ success: false, message: "ID tidak boleh kosong" }), {
|
||||
status: 400,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await prisma.balita.findUnique({
|
||||
where: { id },
|
||||
include: { posyandu: { select: { id: true, name: true } } },
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
return new Response(JSON.stringify({ success: false, message: "Data tidak ditemukan" }), {
|
||||
status: 404,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify({ success: true, data }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("balitaFindById error:", error);
|
||||
return new Response(JSON.stringify({ success: false, message: "Gagal mengambil data" }), {
|
||||
status: 500,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
}
|
||||
52
src/app/api/[[...slugs]]/_lib/kesehatan/balita/find-many.ts
Normal file
52
src/app/api/[[...slugs]]/_lib/kesehatan/balita/find-many.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function balitaFindMany(context: Context) {
|
||||
const page = Number(context.query.page) || 1;
|
||||
const limit = Number(context.query.limit) || 10;
|
||||
const search = (context.query.search as string) || "";
|
||||
const statusStunting = (context.query.statusStunting as string) || "";
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const where: any = { isActive: true };
|
||||
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ nama: { contains: search, mode: "insensitive" } },
|
||||
{ nik: { contains: search, mode: "insensitive" } },
|
||||
{ namaOrtu: { contains: search, mode: "insensitive" } },
|
||||
{ alamat: { contains: search, mode: "insensitive" } },
|
||||
];
|
||||
}
|
||||
|
||||
if (statusStunting) {
|
||||
where.statusStunting = statusStunting;
|
||||
}
|
||||
|
||||
try {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.balita.findMany({
|
||||
where,
|
||||
include: { posyandu: { select: { id: true, name: true } } },
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: "desc" },
|
||||
}),
|
||||
prisma.balita.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil ambil data balita",
|
||||
data,
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Error balitaFindMany:", e);
|
||||
return { success: false, message: "Gagal mengambil data balita" };
|
||||
}
|
||||
}
|
||||
63
src/app/api/[[...slugs]]/_lib/kesehatan/balita/index.ts
Normal file
63
src/app/api/[[...slugs]]/_lib/kesehatan/balita/index.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import balitaCreate from "./create";
|
||||
import balitaDelete from "./del";
|
||||
import balitaFindById from "./find-by-id";
|
||||
import balitaFindMany from "./find-many";
|
||||
import balitaUpdate from "./updt";
|
||||
|
||||
const Balita = new Elysia({ prefix: "/balita", tags: ["Kesehatan/Balita"] })
|
||||
.post("/create", balitaCreate, {
|
||||
body: t.Object({
|
||||
nama: t.String(),
|
||||
nik: t.Optional(t.String()),
|
||||
tanggalLahir: t.String(),
|
||||
jenisKelamin: t.Union([t.Literal("L"), t.Literal("P")]),
|
||||
beratBadanKg: t.Optional(t.Number()),
|
||||
tinggiBadanCm: t.Optional(t.Number()),
|
||||
namaOrtu: t.Optional(t.String()),
|
||||
alamat: t.Optional(t.String()),
|
||||
noHpOrtu: t.Optional(t.String()),
|
||||
posyanduId: t.Optional(t.String()),
|
||||
imunisasiLengkap: t.Boolean(),
|
||||
giziBaik: t.Boolean(),
|
||||
pemeriksaanRutin: t.Boolean(),
|
||||
statusStunting: t.Union([
|
||||
t.Literal("NORMAL"),
|
||||
t.Literal("ALERT"),
|
||||
t.Literal("STUNTING"),
|
||||
]),
|
||||
catatan: t.Optional(t.String()),
|
||||
}),
|
||||
})
|
||||
.get("/find-many", balitaFindMany)
|
||||
.delete("/del/:id", balitaDelete)
|
||||
.get("/:id", balitaFindById)
|
||||
.put(
|
||||
"/:id",
|
||||
balitaUpdate,
|
||||
{
|
||||
body: t.Object({
|
||||
nama: t.String(),
|
||||
nik: t.Optional(t.String()),
|
||||
tanggalLahir: t.String(),
|
||||
jenisKelamin: t.Union([t.Literal("L"), t.Literal("P")]),
|
||||
beratBadanKg: t.Optional(t.Number()),
|
||||
tinggiBadanCm: t.Optional(t.Number()),
|
||||
namaOrtu: t.Optional(t.String()),
|
||||
alamat: t.Optional(t.String()),
|
||||
noHpOrtu: t.Optional(t.String()),
|
||||
posyanduId: t.Optional(t.String()),
|
||||
imunisasiLengkap: t.Boolean(),
|
||||
giziBaik: t.Boolean(),
|
||||
pemeriksaanRutin: t.Boolean(),
|
||||
statusStunting: t.Union([
|
||||
t.Literal("NORMAL"),
|
||||
t.Literal("ALERT"),
|
||||
t.Literal("STUNTING"),
|
||||
]),
|
||||
catatan: t.Optional(t.String()),
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
export default Balita;
|
||||
75
src/app/api/[[...slugs]]/_lib/kesehatan/balita/updt.ts
Normal file
75
src/app/api/[[...slugs]]/_lib/kesehatan/balita/updt.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { JenisKelaminBalita, StatusStunting } from "@prisma/client";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormUpdate = {
|
||||
nama: string;
|
||||
nik?: string;
|
||||
tanggalLahir: string;
|
||||
jenisKelamin: JenisKelaminBalita;
|
||||
beratBadanKg?: number;
|
||||
tinggiBadanCm?: number;
|
||||
namaOrtu?: string;
|
||||
alamat?: string;
|
||||
noHpOrtu?: string;
|
||||
posyanduId?: string;
|
||||
imunisasiLengkap: boolean;
|
||||
giziBaik: boolean;
|
||||
pemeriksaanRutin: boolean;
|
||||
statusStunting: StatusStunting;
|
||||
catatan?: string;
|
||||
};
|
||||
|
||||
export default async function balitaUpdate(context: Context) {
|
||||
const id = context.params?.id as string;
|
||||
const body = context.body as FormUpdate;
|
||||
|
||||
if (!id) {
|
||||
return new Response(JSON.stringify({ success: false, message: "ID tidak boleh kosong" }), {
|
||||
status: 400,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const existing = await prisma.balita.findUnique({ where: { id } });
|
||||
if (!existing) {
|
||||
return new Response(JSON.stringify({ success: false, message: "Data tidak ditemukan" }), {
|
||||
status: 404,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
const updated = await prisma.balita.update({
|
||||
where: { id },
|
||||
data: {
|
||||
nama: body.nama,
|
||||
nik: body.nik,
|
||||
tanggalLahir: new Date(body.tanggalLahir),
|
||||
jenisKelamin: body.jenisKelamin,
|
||||
beratBadanKg: body.beratBadanKg,
|
||||
tinggiBadanCm: body.tinggiBadanCm,
|
||||
namaOrtu: body.namaOrtu,
|
||||
alamat: body.alamat,
|
||||
noHpOrtu: body.noHpOrtu,
|
||||
posyanduId: body.posyanduId || null,
|
||||
imunisasiLengkap: body.imunisasiLengkap,
|
||||
giziBaik: body.giziBaik,
|
||||
pemeriksaanRutin: body.pemeriksaanRutin,
|
||||
statusStunting: body.statusStunting,
|
||||
catatan: body.catatan,
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify({ success: true, message: "Balita berhasil diperbarui", data: updated }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("balitaUpdate error:", error);
|
||||
return new Response(JSON.stringify({ success: false, message: "Gagal memperbarui data" }), {
|
||||
status: 500,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
}
|
||||
37
src/app/api/[[...slugs]]/_lib/kesehatan/ibu-hamil/create.ts
Normal file
37
src/app/api/[[...slugs]]/_lib/kesehatan/ibu-hamil/create.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { IbuHamilStatus } from "@prisma/client";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormCreate = {
|
||||
nama: string;
|
||||
nik?: string;
|
||||
usiaKehamilan: number;
|
||||
hpht?: string;
|
||||
taksiranLahir?: string;
|
||||
alamat?: string;
|
||||
noHp?: string;
|
||||
catatan?: string;
|
||||
posyanduId?: string;
|
||||
status: IbuHamilStatus;
|
||||
};
|
||||
|
||||
export default async function ibuHamilCreate(context: Context) {
|
||||
const body = context.body as FormCreate;
|
||||
|
||||
const data = await prisma.ibuHamil.create({
|
||||
data: {
|
||||
nama: body.nama,
|
||||
nik: body.nik,
|
||||
usiaKehamilan: body.usiaKehamilan ?? 0,
|
||||
hpht: body.hpht ? new Date(body.hpht) : undefined,
|
||||
taksiranLahir: body.taksiranLahir ? new Date(body.taksiranLahir) : undefined,
|
||||
alamat: body.alamat,
|
||||
noHp: body.noHp,
|
||||
catatan: body.catatan,
|
||||
posyanduId: body.posyanduId || null,
|
||||
status: body.status ?? "AKTIF",
|
||||
},
|
||||
});
|
||||
|
||||
return { success: true, message: "Ibu hamil berhasil ditambahkan", data };
|
||||
}
|
||||
22
src/app/api/[[...slugs]]/_lib/kesehatan/ibu-hamil/del.ts
Normal file
22
src/app/api/[[...slugs]]/_lib/kesehatan/ibu-hamil/del.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function ibuHamilDelete(context: Context) {
|
||||
const id = context.params?.id as string;
|
||||
|
||||
if (!id) {
|
||||
return { success: false, message: "ID tidak diberikan" };
|
||||
}
|
||||
|
||||
const existing = await prisma.ibuHamil.findUnique({ where: { id } });
|
||||
if (!existing) {
|
||||
return new Response(JSON.stringify({ success: false, message: "Data tidak ditemukan" }), {
|
||||
status: 404,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.ibuHamil.update({ where: { id }, data: { isActive: false } });
|
||||
|
||||
return { success: true, message: "Ibu hamil berhasil dihapus" };
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function ibuHamilFindById(context: Context) {
|
||||
const id = context.params?.id as string;
|
||||
|
||||
if (!id) {
|
||||
return new Response(JSON.stringify({ success: false, message: "ID tidak boleh kosong" }), {
|
||||
status: 400,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await prisma.ibuHamil.findUnique({
|
||||
where: { id },
|
||||
include: { posyandu: { select: { id: true, name: true } } },
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
return new Response(JSON.stringify({ success: false, message: "Data tidak ditemukan" }), {
|
||||
status: 404,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
return new Response(JSON.stringify({ success: true, data }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("ibuHamilFindById error:", error);
|
||||
return new Response(JSON.stringify({ success: false, message: "Gagal mengambil data" }), {
|
||||
status: 500,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Context } from "elysia";
|
||||
|
||||
export default async function ibuHamilFindMany(context: Context) {
|
||||
const page = Number(context.query.page) || 1;
|
||||
const limit = Number(context.query.limit) || 10;
|
||||
const search = (context.query.search as string) || "";
|
||||
const status = (context.query.status as string) || "";
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const where: any = { isActive: true };
|
||||
|
||||
if (search) {
|
||||
where.OR = [
|
||||
{ nama: { contains: search, mode: "insensitive" } },
|
||||
{ nik: { contains: search, mode: "insensitive" } },
|
||||
{ alamat: { contains: search, mode: "insensitive" } },
|
||||
];
|
||||
}
|
||||
|
||||
if (status) {
|
||||
where.status = status;
|
||||
}
|
||||
|
||||
try {
|
||||
const [data, total] = await Promise.all([
|
||||
prisma.ibuHamil.findMany({
|
||||
where,
|
||||
include: { posyandu: { select: { id: true, name: true } } },
|
||||
skip,
|
||||
take: limit,
|
||||
orderBy: { createdAt: "desc" },
|
||||
}),
|
||||
prisma.ibuHamil.count({ where }),
|
||||
]);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: "Berhasil ambil data ibu hamil",
|
||||
data,
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Error ibuHamilFindMany:", e);
|
||||
return { success: false, message: "Gagal mengambil data ibu hamil" };
|
||||
}
|
||||
}
|
||||
55
src/app/api/[[...slugs]]/_lib/kesehatan/ibu-hamil/index.ts
Normal file
55
src/app/api/[[...slugs]]/_lib/kesehatan/ibu-hamil/index.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import ibuHamilCreate from "./create";
|
||||
import ibuHamilDelete from "./del";
|
||||
import ibuHamilFindById from "./find-by-id";
|
||||
import ibuHamilFindMany from "./find-many";
|
||||
import ibuHamilUpdate from "./updt";
|
||||
|
||||
const IbuHamil = new Elysia({ prefix: "/ibuhamil", tags: ["Kesehatan/IbuHamil"] })
|
||||
.post("/create", ibuHamilCreate, {
|
||||
body: t.Object({
|
||||
nama: t.String(),
|
||||
nik: t.Optional(t.String()),
|
||||
usiaKehamilan: t.Number({ minimum: 0 }),
|
||||
hpht: t.Optional(t.String()),
|
||||
taksiranLahir: t.Optional(t.String()),
|
||||
alamat: t.Optional(t.String()),
|
||||
noHp: t.Optional(t.String()),
|
||||
catatan: t.Optional(t.String()),
|
||||
posyanduId: t.Optional(t.String()),
|
||||
status: t.Union([
|
||||
t.Literal("AKTIF"),
|
||||
t.Literal("MELAHIRKAN"),
|
||||
t.Literal("KEGUGURAN"),
|
||||
t.Literal("NONAKTIF"),
|
||||
]),
|
||||
}),
|
||||
})
|
||||
.get("/find-many", ibuHamilFindMany)
|
||||
.delete("/del/:id", ibuHamilDelete)
|
||||
.get("/:id", ibuHamilFindById)
|
||||
.put(
|
||||
"/:id",
|
||||
ibuHamilUpdate,
|
||||
{
|
||||
body: t.Object({
|
||||
nama: t.String(),
|
||||
nik: t.Optional(t.String()),
|
||||
usiaKehamilan: t.Number({ minimum: 0 }),
|
||||
hpht: t.Optional(t.String()),
|
||||
taksiranLahir: t.Optional(t.String()),
|
||||
alamat: t.Optional(t.String()),
|
||||
noHp: t.Optional(t.String()),
|
||||
catatan: t.Optional(t.String()),
|
||||
posyanduId: t.Optional(t.String()),
|
||||
status: t.Union([
|
||||
t.Literal("AKTIF"),
|
||||
t.Literal("MELAHIRKAN"),
|
||||
t.Literal("KEGUGURAN"),
|
||||
t.Literal("NONAKTIF"),
|
||||
]),
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
export default IbuHamil;
|
||||
65
src/app/api/[[...slugs]]/_lib/kesehatan/ibu-hamil/updt.ts
Normal file
65
src/app/api/[[...slugs]]/_lib/kesehatan/ibu-hamil/updt.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
import { IbuHamilStatus } from "@prisma/client";
|
||||
import { Context } from "elysia";
|
||||
|
||||
type FormUpdate = {
|
||||
nama: string;
|
||||
nik?: string;
|
||||
usiaKehamilan: number;
|
||||
hpht?: string;
|
||||
taksiranLahir?: string;
|
||||
alamat?: string;
|
||||
noHp?: string;
|
||||
catatan?: string;
|
||||
posyanduId?: string;
|
||||
status: IbuHamilStatus;
|
||||
};
|
||||
|
||||
export default async function ibuHamilUpdate(context: Context) {
|
||||
const id = context.params?.id as string;
|
||||
const body = context.body as FormUpdate;
|
||||
|
||||
if (!id) {
|
||||
return new Response(JSON.stringify({ success: false, message: "ID tidak boleh kosong" }), {
|
||||
status: 400,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const existing = await prisma.ibuHamil.findUnique({ where: { id } });
|
||||
if (!existing) {
|
||||
return new Response(JSON.stringify({ success: false, message: "Data tidak ditemukan" }), {
|
||||
status: 404,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
const updated = await prisma.ibuHamil.update({
|
||||
where: { id },
|
||||
data: {
|
||||
nama: body.nama,
|
||||
nik: body.nik,
|
||||
usiaKehamilan: body.usiaKehamilan ?? 0,
|
||||
hpht: body.hpht ? new Date(body.hpht) : null,
|
||||
taksiranLahir: body.taksiranLahir ? new Date(body.taksiranLahir) : null,
|
||||
alamat: body.alamat,
|
||||
noHp: body.noHp,
|
||||
catatan: body.catatan,
|
||||
posyanduId: body.posyanduId || null,
|
||||
status: body.status,
|
||||
},
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify({ success: true, message: "Ibu hamil berhasil diperbarui", data: updated }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("ibuHamilUpdate error:", error);
|
||||
return new Response(JSON.stringify({ success: false, message: "Gagal memperbarui data" }), {
|
||||
status: 500,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ import DokterTenagaMedis from "./data_kesehatan_warga/fasilitas_kesehatan/dokter
|
||||
import PendaftaranJadwalKegiatan from "./data_kesehatan_warga/jadwal_kegiatan/pendaftaran";
|
||||
import TarifLayanan from "./data_kesehatan_warga/fasilitas_kesehatan/tarif-layanan";
|
||||
import RingkasanKesehatan from "./ringkasan-kesehatan";
|
||||
import IbuHamil from "./ibu-hamil";
|
||||
import Balita from "./balita";
|
||||
|
||||
|
||||
const Kesehatan = new Elysia({
|
||||
@@ -51,4 +53,6 @@ const Kesehatan = new Elysia({
|
||||
.use(TarifLayanan)
|
||||
.use(PendaftaranJadwalKegiatan)
|
||||
.use(RingkasanKesehatan)
|
||||
.use(IbuHamil)
|
||||
.use(Balita)
|
||||
export default Kesehatan;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import Elysia, { t } from "elysia";
|
||||
import ringkasanKesehatanFindUnique from "./findUnique";
|
||||
import ringkasanKesehatanUpdate from "./updt";
|
||||
import ringkasanKesehatanStats from "./stats";
|
||||
|
||||
const RingkasanKesehatan = new Elysia({ prefix: "/ringkasankesehatan", tags: ["Kesehatan/Ringkasan"] })
|
||||
.get("/find", ringkasanKesehatanFindUnique)
|
||||
.get("/stats", ringkasanKesehatanStats)
|
||||
.put("/update", ringkasanKesehatanUpdate, {
|
||||
body: t.Object({
|
||||
ibuHamilAkh: t.Number(),
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
type StatsResult = {
|
||||
ibuHamilAktif: number;
|
||||
balitaTerdaftar: number;
|
||||
alertStunting: number;
|
||||
imunisasiLengkapPct: number;
|
||||
pemeriksaanRutinPct: number;
|
||||
giziBaikPct: number;
|
||||
targetStuntingPct: number;
|
||||
};
|
||||
|
||||
export default async function ringkasanKesehatanStats(): Promise<{ success: boolean; data?: StatsResult; message?: string }> {
|
||||
try {
|
||||
const [
|
||||
ibuHamilAktif,
|
||||
balitaTotal,
|
||||
alertStunting,
|
||||
imunisasiLengkap,
|
||||
pemeriksaanRutin,
|
||||
giziBaik,
|
||||
config,
|
||||
] = await Promise.all([
|
||||
prisma.ibuHamil.count({ where: { status: "AKTIF", isActive: true } }),
|
||||
prisma.balita.count({ where: { isActive: true } }),
|
||||
prisma.balita.count({ where: { isActive: true, statusStunting: { in: ["ALERT", "STUNTING"] } } }),
|
||||
prisma.balita.count({ where: { isActive: true, imunisasiLengkap: true } }),
|
||||
prisma.balita.count({ where: { isActive: true, pemeriksaanRutin: true } }),
|
||||
prisma.balita.count({ where: { isActive: true, giziBaik: true } }),
|
||||
prisma.ringkasanKesehatanDesa.findFirst({ where: { isActive: true }, orderBy: { createdAt: "desc" } }),
|
||||
]);
|
||||
|
||||
const pct = (n: number, total: number) =>
|
||||
total === 0 ? 0 : Math.round((n / total) * 100);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
ibuHamilAktif,
|
||||
balitaTerdaftar: balitaTotal,
|
||||
alertStunting,
|
||||
imunisasiLengkapPct: pct(imunisasiLengkap, balitaTotal),
|
||||
pemeriksaanRutinPct: pct(pemeriksaanRutin, balitaTotal),
|
||||
giziBaikPct: pct(giziBaik, balitaTotal),
|
||||
targetStuntingPct: config?.targetStuntingPct ?? 0,
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("ringkasanKesehatanStats error:", e);
|
||||
return { success: false, message: "Gagal menghitung statistik kesehatan" };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user