Compare commits

..

7 Commits

128 changed files with 6263 additions and 3542 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "desa-darmasaba",
"version": "0.1.2",
"version": "0.1.3",
"private": true,
"scripts": {
"dev": "next dev --turbopack",

View File

@@ -0,0 +1,139 @@
/*
Warnings:
- The primary key for the `DataKematian_Kelahiran` table will be changed. If it partially fails, the table could be left without primary key constraint.
*/
-- AlterTable
ALTER TABLE "DataKematian_Kelahiran" DROP CONSTRAINT "DataKematian_Kelahiran_pkey",
ALTER COLUMN "id" DROP DEFAULT,
ALTER COLUMN "id" SET DATA TYPE TEXT,
ADD CONSTRAINT "DataKematian_Kelahiran_pkey" PRIMARY KEY ("id");
DROP SEQUENCE "DataKematian_Kelahiran_id_seq";
-- AlterTable
ALTER TABLE "ProfilePPID" ADD COLUMN "imageUrl" TEXT;
-- CreateTable
CREATE TABLE "Puskesmas" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"alamat" TEXT NOT NULL,
"jamId" TEXT NOT NULL,
"imageId" TEXT NOT NULL,
"kontakId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "Puskesmas_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "JamOperasional" (
"id" TEXT NOT NULL,
"workDays" TEXT NOT NULL,
"weekDays" TEXT NOT NULL,
"holiday" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "JamOperasional_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "KontakPuskesmas" (
"id" TEXT NOT NULL,
"kontakPuskesmas" TEXT NOT NULL,
"email" TEXT NOT NULL,
"facebook" TEXT NOT NULL,
"kontakUGD" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "KontakPuskesmas_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ProgramKesehatan" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"deskripsiSingkat" TEXT NOT NULL,
"deskripsi" TEXT NOT NULL,
"imageId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "ProgramKesehatan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "PenangananDarurat" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"deskripsi" TEXT NOT NULL,
"imageId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "PenangananDarurat_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "KontakDarurat" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"deskripsi" TEXT NOT NULL,
"imageId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "KontakDarurat_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InfoWabahPenyakit" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"deskripsiSingkat" TEXT NOT NULL,
"deskripsiLengkap" TEXT NOT NULL,
"imageId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "InfoWabahPenyakit_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "Puskesmas" ADD CONSTRAINT "Puskesmas_jamId_fkey" FOREIGN KEY ("jamId") REFERENCES "JamOperasional"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Puskesmas" ADD CONSTRAINT "Puskesmas_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Puskesmas" ADD CONSTRAINT "Puskesmas_kontakId_fkey" FOREIGN KEY ("kontakId") REFERENCES "KontakPuskesmas"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProgramKesehatan" ADD CONSTRAINT "ProgramKesehatan_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PenangananDarurat" ADD CONSTRAINT "PenangananDarurat_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "KontakDarurat" ADD CONSTRAINT "KontakDarurat_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "InfoWabahPenyakit" ADD CONSTRAINT "InfoWabahPenyakit_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -0,0 +1,12 @@
/*
Warnings:
- The primary key for the `GrafikKepuasan` table will be changed. If it partially fails, the table could be left without primary key constraint.
*/
-- AlterTable
ALTER TABLE "GrafikKepuasan" DROP CONSTRAINT "GrafikKepuasan_pkey",
ALTER COLUMN "id" DROP DEFAULT,
ALTER COLUMN "id" SET DATA TYPE TEXT,
ADD CONSTRAINT "GrafikKepuasan_pkey" PRIMARY KEY ("id");
DROP SEQUENCE "GrafikKepuasan_id_seq";

View File

@@ -0,0 +1,96 @@
/*
Warnings:
- You are about to drop the `_DokterdanTenagaMedisToFasilitasKesehatan` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `_FasilitasKesehatanToFasilitasPendukung` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `_FasilitasKesehatanToInformasiUmum` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `_FasilitasKesehatanToLayananUnggulan` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `_FasilitasKesehatanToProsedurPendaftaran` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `_FasilitasKesehatanToTarifDanLayanan` table. If the table is not empty, all the data it contains will be lost.
- Added the required column `dokterdanTenagaMedisId` to the `FasilitasKesehatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `fasilitasPendukungId` to the `FasilitasKesehatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `informasiUmumId` to the `FasilitasKesehatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `layananUnggulanId` to the `FasilitasKesehatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `prosedurPendaftaranId` to the `FasilitasKesehatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `tarifDanLayananId` to the `FasilitasKesehatan` table without a default value. This is not possible if the table is not empty.
*/
-- DropForeignKey
ALTER TABLE "_DokterdanTenagaMedisToFasilitasKesehatan" DROP CONSTRAINT "_DokterdanTenagaMedisToFasilitasKesehatan_A_fkey";
-- DropForeignKey
ALTER TABLE "_DokterdanTenagaMedisToFasilitasKesehatan" DROP CONSTRAINT "_DokterdanTenagaMedisToFasilitasKesehatan_B_fkey";
-- DropForeignKey
ALTER TABLE "_FasilitasKesehatanToFasilitasPendukung" DROP CONSTRAINT "_FasilitasKesehatanToFasilitasPendukung_A_fkey";
-- DropForeignKey
ALTER TABLE "_FasilitasKesehatanToFasilitasPendukung" DROP CONSTRAINT "_FasilitasKesehatanToFasilitasPendukung_B_fkey";
-- DropForeignKey
ALTER TABLE "_FasilitasKesehatanToInformasiUmum" DROP CONSTRAINT "_FasilitasKesehatanToInformasiUmum_A_fkey";
-- DropForeignKey
ALTER TABLE "_FasilitasKesehatanToInformasiUmum" DROP CONSTRAINT "_FasilitasKesehatanToInformasiUmum_B_fkey";
-- DropForeignKey
ALTER TABLE "_FasilitasKesehatanToLayananUnggulan" DROP CONSTRAINT "_FasilitasKesehatanToLayananUnggulan_A_fkey";
-- DropForeignKey
ALTER TABLE "_FasilitasKesehatanToLayananUnggulan" DROP CONSTRAINT "_FasilitasKesehatanToLayananUnggulan_B_fkey";
-- DropForeignKey
ALTER TABLE "_FasilitasKesehatanToProsedurPendaftaran" DROP CONSTRAINT "_FasilitasKesehatanToProsedurPendaftaran_A_fkey";
-- DropForeignKey
ALTER TABLE "_FasilitasKesehatanToProsedurPendaftaran" DROP CONSTRAINT "_FasilitasKesehatanToProsedurPendaftaran_B_fkey";
-- DropForeignKey
ALTER TABLE "_FasilitasKesehatanToTarifDanLayanan" DROP CONSTRAINT "_FasilitasKesehatanToTarifDanLayanan_A_fkey";
-- DropForeignKey
ALTER TABLE "_FasilitasKesehatanToTarifDanLayanan" DROP CONSTRAINT "_FasilitasKesehatanToTarifDanLayanan_B_fkey";
-- AlterTable
ALTER TABLE "FasilitasKesehatan" ADD COLUMN "dokterdanTenagaMedisId" TEXT NOT NULL,
ADD COLUMN "fasilitasPendukungId" TEXT NOT NULL,
ADD COLUMN "informasiUmumId" TEXT NOT NULL,
ADD COLUMN "layananUnggulanId" TEXT NOT NULL,
ADD COLUMN "prosedurPendaftaranId" TEXT NOT NULL,
ADD COLUMN "tarifDanLayananId" TEXT NOT NULL;
-- DropTable
DROP TABLE "_DokterdanTenagaMedisToFasilitasKesehatan";
-- DropTable
DROP TABLE "_FasilitasKesehatanToFasilitasPendukung";
-- DropTable
DROP TABLE "_FasilitasKesehatanToInformasiUmum";
-- DropTable
DROP TABLE "_FasilitasKesehatanToLayananUnggulan";
-- DropTable
DROP TABLE "_FasilitasKesehatanToProsedurPendaftaran";
-- DropTable
DROP TABLE "_FasilitasKesehatanToTarifDanLayanan";
-- AddForeignKey
ALTER TABLE "FasilitasKesehatan" ADD CONSTRAINT "FasilitasKesehatan_informasiUmumId_fkey" FOREIGN KEY ("informasiUmumId") REFERENCES "InformasiUmum"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "FasilitasKesehatan" ADD CONSTRAINT "FasilitasKesehatan_layananUnggulanId_fkey" FOREIGN KEY ("layananUnggulanId") REFERENCES "LayananUnggulan"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "FasilitasKesehatan" ADD CONSTRAINT "FasilitasKesehatan_dokterdanTenagaMedisId_fkey" FOREIGN KEY ("dokterdanTenagaMedisId") REFERENCES "DokterdanTenagaMedis"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "FasilitasKesehatan" ADD CONSTRAINT "FasilitasKesehatan_fasilitasPendukungId_fkey" FOREIGN KEY ("fasilitasPendukungId") REFERENCES "FasilitasPendukung"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "FasilitasKesehatan" ADD CONSTRAINT "FasilitasKesehatan_prosedurPendaftaranId_fkey" FOREIGN KEY ("prosedurPendaftaranId") REFERENCES "ProsedurPendaftaran"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "FasilitasKesehatan" ADD CONSTRAINT "FasilitasKesehatan_tarifDanLayananId_fkey" FOREIGN KEY ("tarifDanLayananId") REFERENCES "TarifDanLayanan"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -0,0 +1,32 @@
/*
Warnings:
- The primary key for the `DataKematian_Kelahiran` table will be changed. If it partially fails, the table could be left without primary key constraint.
- The `id` column on the `DataKematian_Kelahiran` table would be dropped and recreated. This will lead to data loss if there is data in the column.
- The primary key for the `GrafikKepuasan` table will be changed. If it partially fails, the table could be left without primary key constraint.
- The `id` column on the `GrafikKepuasan` table would be dropped and recreated. This will lead to data loss if there is data in the column.
- A unique constraint covering the columns `[uuid]` on the table `DataKematian_Kelahiran` will be added. If there are existing duplicate values, this will fail.
- A unique constraint covering the columns `[uuid]` on the table `GrafikKepuasan` will be added. If there are existing duplicate values, this will fail.
- The required column `uuid` was added to the `DataKematian_Kelahiran` table with a prisma-level default value. This is not possible if the table is not empty. Please add this column as optional, then populate it before making it required.
- The required column `uuid` was added to the `GrafikKepuasan` table with a prisma-level default value. This is not possible if the table is not empty. Please add this column as optional, then populate it before making it required.
*/
-- AlterTable
ALTER TABLE "DataKematian_Kelahiran" DROP CONSTRAINT "DataKematian_Kelahiran_pkey",
ADD COLUMN "uuid" TEXT NOT NULL,
DROP COLUMN "id",
ADD COLUMN "id" SERIAL NOT NULL,
ADD CONSTRAINT "DataKematian_Kelahiran_pkey" PRIMARY KEY ("id");
-- AlterTable
ALTER TABLE "GrafikKepuasan" DROP CONSTRAINT "GrafikKepuasan_pkey",
ADD COLUMN "uuid" TEXT NOT NULL,
DROP COLUMN "id",
ADD COLUMN "id" SERIAL NOT NULL,
ADD CONSTRAINT "GrafikKepuasan_pkey" PRIMARY KEY ("id");
-- CreateIndex
CREATE UNIQUE INDEX "DataKematian_Kelahiran_uuid_key" ON "DataKematian_Kelahiran"("uuid");
-- CreateIndex
CREATE UNIQUE INDEX "GrafikKepuasan_uuid_key" ON "GrafikKepuasan"("uuid");

View File

@@ -0,0 +1,92 @@
/*
Warnings:
- The primary key for the `ArtikelKesehatan` table will be changed. If it partially fails, the table could be left without primary key constraint.
- The primary key for the `DoctorSign` table will be changed. If it partially fails, the table could be left without primary key constraint.
- The primary key for the `FirstAid` table will be changed. If it partially fails, the table could be left without primary key constraint.
- The primary key for the `Introduction` table will be changed. If it partially fails, the table could be left without primary key constraint.
- The primary key for the `MythVsFact` table will be changed. If it partially fails, the table could be left without primary key constraint.
- The primary key for the `Prevention` table will be changed. If it partially fails, the table could be left without primary key constraint.
- The primary key for the `Symptom` table will be changed. If it partially fails, the table could be left without primary key constraint.
- Added the required column `deskripsiJadwalKegiatanId` to the `JadwalKegiatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `dokumenJadwalKegiatanId` to the `JadwalKegiatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `informasiJadwalKegiatanId` to the `JadwalKegiatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `layananJadwalKegiatanId` to the `JadwalKegiatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `pendaftaranJadwalKegiatanId` to the `JadwalKegiatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `syaratKetentuanJadwalKegiatanId` to the `JadwalKegiatan` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "ArtikelKesehatan" DROP CONSTRAINT "ArtikelKesehatan_pkey",
ALTER COLUMN "id" DROP DEFAULT,
ALTER COLUMN "id" SET DATA TYPE TEXT,
ADD CONSTRAINT "ArtikelKesehatan_pkey" PRIMARY KEY ("id");
DROP SEQUENCE "ArtikelKesehatan_id_seq";
-- AlterTable
ALTER TABLE "DoctorSign" DROP CONSTRAINT "DoctorSign_pkey",
ALTER COLUMN "id" DROP DEFAULT,
ALTER COLUMN "id" SET DATA TYPE TEXT,
ADD CONSTRAINT "DoctorSign_pkey" PRIMARY KEY ("id");
DROP SEQUENCE "DoctorSign_id_seq";
-- AlterTable
ALTER TABLE "FirstAid" DROP CONSTRAINT "FirstAid_pkey",
ALTER COLUMN "id" DROP DEFAULT,
ALTER COLUMN "id" SET DATA TYPE TEXT,
ADD CONSTRAINT "FirstAid_pkey" PRIMARY KEY ("id");
DROP SEQUENCE "FirstAid_id_seq";
-- AlterTable
ALTER TABLE "Introduction" DROP CONSTRAINT "Introduction_pkey",
ALTER COLUMN "id" DROP DEFAULT,
ALTER COLUMN "id" SET DATA TYPE TEXT,
ADD CONSTRAINT "Introduction_pkey" PRIMARY KEY ("id");
DROP SEQUENCE "Introduction_id_seq";
-- AlterTable
ALTER TABLE "JadwalKegiatan" ADD COLUMN "deskripsiJadwalKegiatanId" TEXT NOT NULL,
ADD COLUMN "dokumenJadwalKegiatanId" TEXT NOT NULL,
ADD COLUMN "informasiJadwalKegiatanId" TEXT NOT NULL,
ADD COLUMN "layananJadwalKegiatanId" TEXT NOT NULL,
ADD COLUMN "pendaftaranJadwalKegiatanId" TEXT NOT NULL,
ADD COLUMN "syaratKetentuanJadwalKegiatanId" TEXT NOT NULL;
-- AlterTable
ALTER TABLE "MythVsFact" DROP CONSTRAINT "MythVsFact_pkey",
ALTER COLUMN "id" DROP DEFAULT,
ALTER COLUMN "id" SET DATA TYPE TEXT,
ADD CONSTRAINT "MythVsFact_pkey" PRIMARY KEY ("id");
DROP SEQUENCE "MythVsFact_id_seq";
-- AlterTable
ALTER TABLE "Prevention" DROP CONSTRAINT "Prevention_pkey",
ALTER COLUMN "id" DROP DEFAULT,
ALTER COLUMN "id" SET DATA TYPE TEXT,
ADD CONSTRAINT "Prevention_pkey" PRIMARY KEY ("id");
DROP SEQUENCE "Prevention_id_seq";
-- AlterTable
ALTER TABLE "Symptom" DROP CONSTRAINT "Symptom_pkey",
ALTER COLUMN "id" DROP DEFAULT,
ALTER COLUMN "id" SET DATA TYPE TEXT,
ADD CONSTRAINT "Symptom_pkey" PRIMARY KEY ("id");
DROP SEQUENCE "Symptom_id_seq";
-- AddForeignKey
ALTER TABLE "JadwalKegiatan" ADD CONSTRAINT "JadwalKegiatan_informasiJadwalKegiatanId_fkey" FOREIGN KEY ("informasiJadwalKegiatanId") REFERENCES "InformasiJadwalKegiatan"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "JadwalKegiatan" ADD CONSTRAINT "JadwalKegiatan_deskripsiJadwalKegiatanId_fkey" FOREIGN KEY ("deskripsiJadwalKegiatanId") REFERENCES "DeskripsiJadwalKegiatan"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "JadwalKegiatan" ADD CONSTRAINT "JadwalKegiatan_layananJadwalKegiatanId_fkey" FOREIGN KEY ("layananJadwalKegiatanId") REFERENCES "LayananJadwalKegiatan"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "JadwalKegiatan" ADD CONSTRAINT "JadwalKegiatan_syaratKetentuanJadwalKegiatanId_fkey" FOREIGN KEY ("syaratKetentuanJadwalKegiatanId") REFERENCES "SyaratKetentuanJadwalKegiatan"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "JadwalKegiatan" ADD CONSTRAINT "JadwalKegiatan_dokumenJadwalKegiatanId_fkey" FOREIGN KEY ("dokumenJadwalKegiatanId") REFERENCES "DokumenJadwalKegiatan"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "JadwalKegiatan" ADD CONSTRAINT "JadwalKegiatan_pendaftaranJadwalKegiatanId_fkey" FOREIGN KEY ("pendaftaranJadwalKegiatanId") REFERENCES "PendaftaranJadwalKegiatan"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -0,0 +1,68 @@
/*
Warnings:
- The primary key for the `DataKematian_Kelahiran` table will be changed. If it partially fails, the table could be left without primary key constraint.
- You are about to drop the column `uuid` on the `DataKematian_Kelahiran` table. All the data in the column will be lost.
- The primary key for the `GrafikKepuasan` table will be changed. If it partially fails, the table could be left without primary key constraint.
- You are about to drop the column `uuid` on the `GrafikKepuasan` table. All the data in the column will be lost.
- A unique constraint covering the columns `[id]` on the table `DataKematian_Kelahiran` will be added. If there are existing duplicate values, this will fail.
- A unique constraint covering the columns `[id]` on the table `GrafikKepuasan` will be added. If there are existing duplicate values, this will fail.
- Added the required column `doctorSignId` to the `ArtikelKesehatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `firstAidId` to the `ArtikelKesehatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `introductionId` to the `ArtikelKesehatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `mythVsFactId` to the `ArtikelKesehatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `preventionId` to the `ArtikelKesehatan` table without a default value. This is not possible if the table is not empty.
- Added the required column `symptomId` to the `ArtikelKesehatan` table without a default value. This is not possible if the table is not empty.
*/
-- DropIndex
DROP INDEX "DataKematian_Kelahiran_uuid_key";
-- DropIndex
DROP INDEX "GrafikKepuasan_uuid_key";
-- AlterTable
ALTER TABLE "ArtikelKesehatan" ADD COLUMN "doctorSignId" TEXT NOT NULL,
ADD COLUMN "firstAidId" TEXT NOT NULL,
ADD COLUMN "introductionId" TEXT NOT NULL,
ADD COLUMN "mythVsFactId" TEXT NOT NULL,
ADD COLUMN "preventionId" TEXT NOT NULL,
ADD COLUMN "symptomId" TEXT NOT NULL;
-- AlterTable
ALTER TABLE "DataKematian_Kelahiran" DROP CONSTRAINT "DataKematian_Kelahiran_pkey",
DROP COLUMN "uuid",
ALTER COLUMN "id" DROP DEFAULT,
ALTER COLUMN "id" SET DATA TYPE TEXT;
DROP SEQUENCE "DataKematian_Kelahiran_id_seq";
-- AlterTable
ALTER TABLE "GrafikKepuasan" DROP CONSTRAINT "GrafikKepuasan_pkey",
DROP COLUMN "uuid",
ALTER COLUMN "id" DROP DEFAULT,
ALTER COLUMN "id" SET DATA TYPE TEXT;
DROP SEQUENCE "GrafikKepuasan_id_seq";
-- CreateIndex
CREATE UNIQUE INDEX "DataKematian_Kelahiran_id_key" ON "DataKematian_Kelahiran"("id");
-- CreateIndex
CREATE UNIQUE INDEX "GrafikKepuasan_id_key" ON "GrafikKepuasan"("id");
-- AddForeignKey
ALTER TABLE "ArtikelKesehatan" ADD CONSTRAINT "ArtikelKesehatan_introductionId_fkey" FOREIGN KEY ("introductionId") REFERENCES "Introduction"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ArtikelKesehatan" ADD CONSTRAINT "ArtikelKesehatan_symptomId_fkey" FOREIGN KEY ("symptomId") REFERENCES "Symptom"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ArtikelKesehatan" ADD CONSTRAINT "ArtikelKesehatan_preventionId_fkey" FOREIGN KEY ("preventionId") REFERENCES "Prevention"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ArtikelKesehatan" ADD CONSTRAINT "ArtikelKesehatan_firstAidId_fkey" FOREIGN KEY ("firstAidId") REFERENCES "FirstAid"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ArtikelKesehatan" ADD CONSTRAINT "ArtikelKesehatan_mythVsFactId_fkey" FOREIGN KEY ("mythVsFactId") REFERENCES "MythVsFact"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ArtikelKesehatan" ADD CONSTRAINT "ArtikelKesehatan_doctorSignId_fkey" FOREIGN KEY ("doctorSignId") REFERENCES "DoctorSign"("id") ON DELETE RESTRICT ON UPDATE CASCADE;

View File

@@ -73,7 +73,7 @@ model FileStorage {
Puskesmas Puskesmas[]
ProgramKesehatan ProgramKesehatan[]
PenangananDarurat PenangananDarurat[]
KontakDarurat KontakDarurat[]
KontakDarurat KontakDarurat[]
InfoWabahPenyakit InfoWabahPenyakit[]
}
@@ -471,18 +471,24 @@ model Penghargaan {
// ========================================= 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[]
id String @id @default(cuid())
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
informasiumum InformasiUmum @relation(fields: [informasiUmumId], references: [id])
informasiUmumId String
layananunggulan LayananUnggulan @relation(fields: [layananUnggulanId], references: [id])
layananUnggulanId String
dokterdantenagamedis DokterdanTenagaMedis @relation(fields: [dokterdanTenagaMedisId], references: [id])
dokterdanTenagaMedisId String
fasilitaspendukung FasilitasPendukung @relation(fields: [fasilitasPendukungId], references: [id])
fasilitasPendukungId String
prosedurpendaftaran ProsedurPendaftaran @relation(fields: [prosedurPendaftaranId], references: [id])
prosedurPendaftaranId String
tarifdanlayanan TarifDanLayanan @relation(fields: [tarifDanLayananId], references: [id])
tarifDanLayananId String
}
model InformasiUmum {
@@ -490,10 +496,10 @@ model InformasiUmum {
fasilitas String
alamat String
jamOperasional String
FasilitasKesehatan FasilitasKesehatan[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
FasilitasKesehatan FasilitasKesehatan[]
isActive Boolean @default(true)
}
@@ -552,33 +558,47 @@ model TarifDanLayanan {
// ========================================= 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)
id String @id @default(cuid())
content String
informasijadwalkegiatan InformasiJadwalKegiatan @relation(fields: [informasiJadwalKegiatanId], references: [id])
informasiJadwalKegiatanId String
deskripsijadwalkegiatan DeskripsiJadwalKegiatan @relation(fields: [deskripsiJadwalKegiatanId], references: [id])
deskripsiJadwalKegiatanId String
layananjadwalkegiatan LayananJadwalKegiatan @relation(fields: [layananJadwalKegiatanId], references: [id])
layananJadwalKegiatanId String
syaratketentuanjadwalkegiatan SyaratKetentuanJadwalKegiatan @relation(fields: [syaratKetentuanJadwalKegiatanId], references: [id])
syaratKetentuanJadwalKegiatanId String
dokumenjadwalkegiatan DokumenJadwalKegiatan @relation(fields: [dokumenJadwalKegiatanId], references: [id])
dokumenJadwalKegiatanId String
pendaftaranjadwalkegiatan PendaftaranJadwalKegiatan @relation(fields: [pendaftaranJadwalKegiatanId], references: [id])
pendaftaranJadwalKegiatanId 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)
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)
JadwalKegiatan JadwalKegiatan[]
}
model DeskripsiJadwalKegiatan {
id String @id @default(cuid())
deskripsi String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
id String @id @default(cuid())
deskripsi String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
JadwalKegiatan JadwalKegiatan[]
}
model LayananJadwalKegiatan {
@@ -588,6 +608,8 @@ model LayananJadwalKegiatan {
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
JadwalKegiatan JadwalKegiatan[]
}
model SyaratKetentuanJadwalKegiatan {
@@ -597,34 +619,38 @@ model SyaratKetentuanJadwalKegiatan {
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
JadwalKegiatan JadwalKegiatan[]
}
model DokumenJadwalKegiatan {
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
JadwalKegiatan JadwalKegiatan[]
}
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)
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)
JadwalKegiatan JadwalKegiatan[]
}
// ========================================= PERSENTASE KELAHIRAN & KEMATIAN ========================================= //
model DataKematian_Kelahiran {
id Int @id @default(autoincrement())
id String @unique @default(cuid())
tahun String
kematianKasar String
kematianBayi String
@@ -637,7 +663,7 @@ model DataKematian_Kelahiran {
// ========================================= GRAFIK KEPUASAN ========================================= //
model GrafikKepuasan {
id Int @id @default(autoincrement())
id String @unique @default(cuid())
label String
jumlah String
createdAt DateTime @default(now())
@@ -648,56 +674,74 @@ model GrafikKepuasan {
// ========================================= 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)
id String @id @default(cuid())
title String
content String
introduction Introduction @relation(fields: [introductionId], references: [id])
introductionId String
symptom Symptom @relation(fields: [symptomId], references: [id])
symptomId String
prevention Prevention @relation(fields: [preventionId], references: [id])
preventionId String
firstaid FirstAid @relation(fields: [firstAidId], references: [id])
firstAidId String
mythvsfact MythVsFact @relation(fields: [mythVsFactId], references: [id])
mythVsFactId String
doctorsign DoctorSign @relation(fields: [doctorSignId], references: [id])
doctorSignId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
model Introduction {
id Int @id @default(autoincrement())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
ArtikelKesehatan ArtikelKesehatan[]
}
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)
id String @id @default(cuid())
title String
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
ArtikelKesehatan ArtikelKesehatan[]
}
model Prevention {
id Int @id @default(autoincrement())
id String @id @default(cuid())
title String
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
ArtikelKesehatan ArtikelKesehatan[]
}
model FirstAid {
id Int @id @default(autoincrement())
id String @id @default(cuid())
title String
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
ArtikelKesehatan ArtikelKesehatan[]
}
model MythVsFact {
id Int @id @default(autoincrement())
id String @id @default(cuid())
title String
mitos String
fakta String
@@ -705,15 +749,19 @@ model MythVsFact {
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
ArtikelKesehatan ArtikelKesehatan[]
}
model DoctorSign {
id Int @id @default(autoincrement())
id String @id @default(cuid())
content String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
ArtikelKesehatan ArtikelKesehatan[]
}
// ========================================= POSYANDU ========================================= //
@@ -811,16 +859,17 @@ model KontakDarurat {
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
// ========================================= INFO WABAH PENYAKIT ========================================= //
model InfoWabahPenyakit {
id String @id @default(cuid())
name String
id String @id @default(cuid())
name String
deskripsiSingkat String
deskripsiLengkap String
image FileStorage @relation(fields: [imageId], references: [id])
imageId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
image FileStorage @relation(fields: [imageId], references: [id])
imageId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}

View File

@@ -1,339 +1,306 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
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"),
})
const templateForm = z.object({
title: z.string().min(1, "Judul harus diisi"),
content: z.string().min(1, "Content harus diisi"),
introduction: z.object({
content: z.string().min(1, "Content harus diisi"),
}),
symptom: z.object({
title: z.string().min(1, "Judul harus diisi"),
content: z.string().min(1, "Content harus diisi"),
}),
prevention: z.object({
title: z.string().min(1, "Judul harus diisi"),
content: z.string().min(1, "Content harus diisi"),
}),
firstAid: z.object({
title: z.string().min(1, "Judul harus diisi"),
content: z.string().min(1, "Content harus diisi"),
}),
mythVsFact: z.object({
title: z.string().min(1, "Judul harus diisi"),
mitos: z.string().min(1, "Mitos harus diisi"),
fakta: z.string().min(1, "Fakta harus diisi"),
}),
doctorSign: z.object({
content: z.string().min(1, "Content harus diisi"),
}),
});
type Introduction = Prisma.IntroductionGetPayload<{
select: {
content: true;
};
}>;
const defaultForm = {
title: "",
content: "",
introduction: {
content: "",
},
symptom: {
title: "",
content: "",
},
prevention: {
title: "",
content: "",
},
firstAid: {
title: "",
content: "",
},
mythVsFact: {
title: "",
mitos: "",
fakta: "",
},
doctorSign: {
content: "",
},
};
const introduction = proxy({
const artikelKesehatanState = proxy({
create: {
form: {} as Introduction,
form: { ...defaultForm },
loading: false,
async create() {
const cek = templateIntroduction.safeParse(introduction.create.form);
async submit() {
const cek = templateForm.safeParse(this.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
const errMsg = cek.error.issues
.map((v) => `${v.path.join(".")}: ${v.message}`)
.join("\n");
toast.error(errMsg);
return null;
}
try {
introduction.create.loading = true;
const res = await ApiFetch.api.kesehatan.introduction["create"].post(introduction.create.form);
this.loading = true;
const payload = { ...this.form };
const res = await (ApiFetch.api.kesehatan as any)[
"artikel-kesehatan"
].create.post(payload);
if (res.status === 200) {
introduction.findMany.load();
return toast.success("success create");
toast.success("Berhasil menambahkan artikel kesehatan");
this.resetForm();
await artikelKesehatanState.findMany.load();
return res.data;
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} catch (err: any) {
const msg = err?.message || "Terjadi kesalahan saat mengirim data";
toast.error(msg);
console.error("SUBMIT ERROR:", err);
return null;
} finally {
introduction.create.loading = false;
this.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;
}
resetForm() {
this.form = { ...defaultForm };
},
},
findMany: {
data: null as
| Prisma.SymptomGetPayload<{ omit: { isActive: true } }>[]
| Prisma.ArtikelKesehatanGetPayload<{
include: {
introduction: true;
symptom: true;
prevention: true;
firstaid: true;
mythvsfact: true;
doctorsign: true;
};
}>[]
| null,
loading: false,
async load() {
const res = await ApiFetch.api.kesehatan.symptom["find-many"].get();
if (res.status === 200) {
symptom.findMany.data = res.data?.data ?? [];
try {
this.loading = true;
const res = await (ApiFetch.api.kesehatan as any)["artikel-kesehatan"][
"find-many"
].get();
if (res.status === 200) {
this.data = res.data?.data ?? [];
} else {
toast.error("Gagal memuat data artikel kesehatan");
}
return res;
} catch (err) {
toast.error("Terjadi error saat load data");
console.error("LOAD ERROR:", err);
throw err;
} finally {
this.loading = false;
}
},
},
findUnique: {
data: null as Prisma.ArtikelKesehatanGetPayload<{
include: {
introduction: true;
symptom: true;
prevention: true;
firstaid: true;
mythvsfact: true;
doctorsign: true;
};
}> | null,
loading: false,
async load(id: string) {
const res = await fetch(`/api/kesehatan/artikel-kesehatan/${id}`);
if (res.ok) {
const data = await res.json();
artikelKesehatanState.findUnique.data = data.data ?? null;
} else {
toast.error("Gagal load data artikel kesehatan");
}
},
},
edit: {
id: "",
form: { ...defaultForm },
loading: false,
async load(id: string) {
const res = await fetch(`/api/kesehatan/artikel-kesehatan/${id}`);
if (!res.ok) {
toast.error("Gagal load data artikel kesehatan");
return;
}
const result = await res.json();
const data = result.data;
artikelKesehatanState.edit.id = data.id;
artikelKesehatanState.edit.form = {
title: data.title,
content: data.content,
introduction: {
content: data.introduction.content,
},
symptom: {
title: data.symptom.title,
content: data.symptom.content,
},
prevention: {
title: data.prevention.title,
content: data.prevention.content,
},
firstAid: {
title: data.firstaid.title,
content: data.firstaid.content,
},
mythVsFact: {
title: data.mythvsfact.title,
mitos: data.mythvsfact.mitos,
fakta: data.mythvsfact.fakta,
},
doctorSign: {
content: data.doctorsign.content,
},
};
},
async submit() {
const cek = templateForm.safeParse(artikelKesehatanState.edit.form);
if (!cek.success) {
const errMsg = cek.error.issues
.map((v) => `${v.path.join(".")}: ${v.message}`)
.join("\n");
toast.error(errMsg);
return null;
}
try {
artikelKesehatanState.edit.loading = true;
const payload = {
title: artikelKesehatanState.edit.form.title,
content: artikelKesehatanState.edit.form.content,
introduction: {
content: artikelKesehatanState.edit.form.introduction.content,
},
symptom: {
title: artikelKesehatanState.edit.form.symptom.title,
content: artikelKesehatanState.edit.form.symptom.content,
},
prevention: {
title: artikelKesehatanState.edit.form.prevention.title,
content: artikelKesehatanState.edit.form.prevention.content,
},
firstAid: {
title: artikelKesehatanState.edit.form.firstAid.title,
content: artikelKesehatanState.edit.form.firstAid.content,
},
mythVsFact: {
title: artikelKesehatanState.edit.form.mythVsFact.title,
mitos: artikelKesehatanState.edit.form.mythVsFact.mitos,
fakta: artikelKesehatanState.edit.form.mythVsFact.fakta,
},
doctorSign: {
content: artikelKesehatanState.edit.form.doctorSign.content,
},
};
const res = await fetch(
`/api/kesehatan/artikel-kesehatan/${artikelKesehatanState.edit.id}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}
);
if (!res.ok) {
const error = await res.json();
throw new Error(error.message || "Update gagal");
}
toast.success("Berhasil update artikel kesehatan");
await artikelKesehatanState.findMany.load();
return true;
} catch (err) {
toast.error(
err instanceof Error ? err.message : "Terjadi kesalahan saat update"
);
return false;
} finally {
artikelKesehatanState.edit.loading = false;
}
},
resetForm() {
artikelKesehatanState.edit.id = "";
artikelKesehatanState.edit.form = { ...defaultForm };
},
},
delete: {
loading: false,
async byId(id: string) {
try {
artikelKesehatanState.delete.loading = true;
const res = await fetch(
`/api/kesehatan/artikel-kesehatan/del/${id}`,
{
method: "DELETE",
}
);
const result = await res.json();
if (res.ok && result.success) {
toast.success("Artikel kesehatan berhasil dihapus");
await artikelKesehatanState.findMany.load();
} else {
toast.error(result.message || "Gagal menghapus");
}
} catch {
toast.error("Terjadi kesalahan saat menghapus");
} finally {
artikelKesehatanState.delete.loading = false;
}
},
},
});
/* ======================================================================= */
/* 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
export default artikelKesehatanState;

View File

@@ -1,333 +1,308 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
/* Informasi Umum */
const templateInformasiUmum = z.object({
fasilitas: z.string().min(3, "Fasilitas minimal 3 karakter"),
alamat: z.string().min(3, "Alamat minimal 3 karakter"),
jamOperasional: z.string().min(3, "Jam Operasional minimal 3 karakter"),
// Validasi form
const templateForm = z.object({
name: z.string().min(1, "Nama harus diisi"),
informasiUmum: z.object({
fasilitas: z.string().min(1, "Fasilitas harus diisi"),
alamat: z.string().min(1, "Alamat harus diisi"),
jamOperasional: z.string().min(1, "Jam operasional harus diisi"),
}),
layananUnggulan: z.object({
content: z.string().min(1, "Layanan unggulan harus diisi"),
}),
dokterdanTenagaMedis: z.object({
name: z.string().min(1, "Nama dokter harus diisi"),
specialist: z.string().min(1, "Spesialis harus diisi"),
jadwal: z.string().min(1, "Jadwal harus diisi"),
}),
fasilitasPendukung: z.object({
content: z.string().min(1, "Fasilitas pendukung harus diisi"),
}),
prosedurPendaftaran: z.object({
content: z.string().min(1, "Prosedur pendaftaran harus diisi"),
}),
tarifDanLayanan: z.object({
layanan: z.string().min(1, "Layanan harus diisi"),
tarif: z.string().min(1, "Tarif harus diisi"),
}),
});
type InfromasiUmum = Prisma.InformasiUmumGetPayload<{
select: {
fasilitas: true;
alamat: true;
jamOperasional: true;
};
}>;
// Default form kosong
const defaultForm = {
name: "",
informasiUmum: {
fasilitas: "",
alamat: "",
jamOperasional: "",
},
layananUnggulan: {
content: "",
},
dokterdanTenagaMedis: {
name: "",
specialist: "",
jadwal: "",
},
fasilitasPendukung: {
content: "",
},
prosedurPendaftaran: {
content: "",
},
tarifDanLayanan: {
layanan: "",
tarif: "",
},
};
const informasiumum = proxy({
const fasilitasKesehatanState = proxy({
create: {
form: {} as InfromasiUmum,
form: { ...defaultForm },
loading: false,
async create() {
const cek = templateInformasiUmum.safeParse(informasiumum.create.form);
async submit() {
const cek = templateForm.safeParse(this.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
const errMsg = cek.error.issues
.map((v) => `${v.path.join(".")}: ${v.message}`)
.join("\n");
toast.error(errMsg);
return null;
}
try {
informasiumum.create.loading = true;
const res = await ApiFetch.api.kesehatan.informasiumum["create"].post(
informasiumum.create.form
this.loading = true;
const payload = { ...this.form };
const res = await (ApiFetch.api.kesehatan as any)[
"fasilitas-kesehatan"
].create.post(payload);
if (res.status === 200) {
toast.success("Berhasil menambahkan fasilitas kesehatan");
this.resetForm();
await fasilitasKesehatanState.findMany.load();
return res.data;
}
} catch (err: any) {
const msg = err?.message || "Terjadi kesalahan saat mengirim data";
toast.error(msg);
console.error("SUBMIT ERROR:", err);
return null;
} finally {
this.loading = false;
}
},
resetForm() {
this.form = { ...defaultForm };
},
},
findMany: {
data: null as
| Prisma.FasilitasKesehatanGetPayload<{
include: {
informasiumum: true;
layananunggulan: true;
dokterdantenagamedis: true;
fasilitaspendukung: true;
prosedurpendaftaran: true;
tarifdanlayanan: true;
};
}>[]
| null,
loading: false,
async load() {
try {
this.loading = true;
const res = await (ApiFetch.api.kesehatan as any)[
"fasilitas-kesehatan"
]["find-many"].get();
if (res.status === 200) {
this.data = res.data?.data ?? [];
} else {
toast.error("Gagal memuat data fasilitas kesehatan");
}
return res;
} catch (err) {
toast.error("Terjadi error saat load data");
console.error("LOAD ERROR:", err);
throw err;
} finally {
this.loading = false;
}
},
},
findUnique: {
data: null as Prisma.FasilitasKesehatanGetPayload<{
include: {
informasiumum: true;
layananunggulan: true;
dokterdantenagamedis: true;
fasilitaspendukung: true;
prosedurpendaftaran: true;
tarifdanlayanan: true;
};
}> | null,
loading: false,
async load(id: string) {
const res = await fetch(`/api/kesehatan/fasilitas-kesehatan/${id}`);
if (res.ok) {
const data = await res.json();
fasilitasKesehatanState.findUnique.data = data.data ?? null;
} else {
toast.error("Gagal load data fasilitas kesehatan");
}
},
},
edit: {
id: "",
form: { ...defaultForm },
loading: false,
async load(id: string) {
const res = await fetch(`/api/kesehatan/fasilitas-kesehatan/${id}`);
if (!res.ok) {
toast.error("Gagal load data fasilitas kesehatan");
return;
}
const result = await res.json();
const data = result.data;
fasilitasKesehatanState.edit.id = data.id;
fasilitasKesehatanState.edit.form = {
name: data.name,
informasiUmum: {
fasilitas: data.informasiumum.fasilitas,
alamat: data.informasiumum.alamat,
jamOperasional: data.informasiumum.jamOperasional,
},
layananUnggulan: {
content: data.layananunggulan.content,
},
dokterdanTenagaMedis: {
name: data.dokterdantenagamedis.name,
specialist: data.dokterdantenagamedis.specialist,
jadwal: data.dokterdantenagamedis.jadwal,
},
fasilitasPendukung: {
content: data.fasilitaspendukung.content,
},
prosedurPendaftaran: {
content: data.prosedurpendaftaran.content,
},
tarifDanLayanan: {
layanan: data.tarifdanlayanan.layanan,
tarif: data.tarifdanlayanan.tarif,
},
};
},
async submit() {
const cek = templateForm.safeParse(fasilitasKesehatanState.edit.form);
if (!cek.success) {
const errMsg = cek.error.issues
.map((v) => `${v.path.join(".")}: ${v.message}`)
.join("\n");
toast.error(errMsg);
return null;
}
try {
fasilitasKesehatanState.edit.loading = true;
const payload = {
name: fasilitasKesehatanState.edit.form.name,
informasiUmum: {
fasilitas:
fasilitasKesehatanState.edit.form.informasiUmum.fasilitas,
alamat: fasilitasKesehatanState.edit.form.informasiUmum.alamat,
jamOperasional:
fasilitasKesehatanState.edit.form.informasiUmum.jamOperasional,
},
layananUnggulan: {
content: fasilitasKesehatanState.edit.form.layananUnggulan.content,
},
dokterdanTenagaMedis: {
name: fasilitasKesehatanState.edit.form.dokterdanTenagaMedis.name,
specialist:
fasilitasKesehatanState.edit.form.dokterdanTenagaMedis.specialist,
jadwal:
fasilitasKesehatanState.edit.form.dokterdanTenagaMedis.jadwal,
},
fasilitasPendukung: {
content:
fasilitasKesehatanState.edit.form.fasilitasPendukung.content,
},
prosedurPendaftaran: {
content:
fasilitasKesehatanState.edit.form.prosedurPendaftaran.content,
},
tarifDanLayanan: {
layanan: fasilitasKesehatanState.edit.form.tarifDanLayanan.layanan,
tarif: fasilitasKesehatanState.edit.form.tarifDanLayanan.tarif,
},
};
const res = await fetch(
`/api/kesehatan/fasilitas-kesehatan/${fasilitasKesehatanState.edit.id}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}
);
if (res.status === 200) {
informasiumum.findMany.load();
return toast.success("success create");
if (!res.ok) {
const error = await res.json();
throw new Error(error.message || "Update gagal");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
informasiumum.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.InformasiUmumGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.
informasiumum["find-many"].get();
if (res.status === 200) {
informasiumum.findMany.data = res.data?.data ?? [];
}
},
},
});
/* ======================================================================= */
/* Layanan Unggulan */
const templateLayananUnggulanForm = z.object({
content: z.string().min(3, "Content minimal 3 karakter"),
});
type LayananUnggulan = Prisma.LayananUnggulanGetPayload<{
select: {
content: true;
};
}>;
const layananunggulan = proxy({
create: {
form: {} as LayananUnggulan,
loading: false,
async create() {
const cek = templateLayananUnggulanForm.safeParse(layananunggulan.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
layananunggulan.create.loading = true;
const res = await ApiFetch.api.kesehatan.layananunggulan["create"].post(
layananunggulan.create.form
toast.success("Berhasil update fasilitas kesehatan");
await fasilitasKesehatanState.findMany.load();
return true;
} catch (err) {
toast.error(
err instanceof Error ? err.message : "Terjadi kesalahan saat update"
);
if (res.status === 200) {
layananunggulan.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
return false;
} finally {
layananunggulan.create.loading = false;
fasilitasKesehatanState.edit.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.LayananUnggulanGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.
layananunggulan["find-many"].get();
if (res.status === 200) {
layananunggulan.findMany.data = res.data?.data ?? [];
}
resetForm() {
fasilitasKesehatanState.edit.id = "";
fasilitasKesehatanState.edit.form = { ...defaultForm };
},
},
})
/* ======================================================================= */
/* Dokter dan Tenaga Medis */
const templateDokterdanTenagaMedis = z.object({
name: z.string().min(3, "Name minimal 3 karakter"),
specialist: z.string().min(3, "Specialist minimal 3 karakter"),
jadwal: z.string().min(3, "Jadwal minimal 3 karakter"),
})
type DokterdanTenagaMedis = Prisma.DokterdanTenagaMedisGetPayload<{
select: {
name: true;
specialist: true;
jadwal: true;
};
}>;
const dokterdantenagamedis = proxy({
create: {
form: {} as DokterdanTenagaMedis,
delete: {
loading: false,
async create() {
const cek = templateDokterdanTenagaMedis.safeParse(dokterdantenagamedis.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
async byId(id: string){
try {
dokterdantenagamedis.create.loading = true;
const res = await ApiFetch.api.kesehatan.dokterdantenagamedis["create"].post(dokterdantenagamedis.create.form);
if (res.status === 200) {
dokterdantenagamedis.findMany.load();
return toast.success("success create");
fasilitasKesehatanState.delete.loading = true;
const res = await fetch(`/api/kesehatan/fasilitas-kesehatan/del/${id}`, {
method: "DELETE",
});
const result = await res.json();
if (res.ok && result.success) {
toast.success("Fasilitas kesehatan berhasil dihapus");
await fasilitasKesehatanState.findMany.load();
} else {
toast.error(result.message || "Gagal menghapus");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} catch {
toast.error("Terjadi kesalahan saat menghapus");
} finally {
dokterdantenagamedis.create.loading = false;
fasilitasKesehatanState.delete.loading = false;
}
},
}
},
findMany: {
data: null as
| Prisma.DokterdanTenagaMedisGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.dokterdantenagamedis["find-many"].get();
if (res.status === 200) {
dokterdantenagamedis.findMany.data = res.data?.data ?? [];
}
},
},
})
/* ======================================================================= */
});
/* Fasilitas Pendukung */
const templateFasilitasPendukung = z.object({
content: z.string().min(3, "Content minimal 3 karakter"),
})
type FasilitasPendukung = Prisma.FasilitasPendukungGetPayload<{
select: {
content: true;
};
}>;
const fasilitaspendukung = proxy({
create: {
form: {} as FasilitasPendukung,
loading: false,
async create() {
const cek = templateFasilitasPendukung.safeParse(fasilitaspendukung.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
fasilitaspendukung.create.loading = true;
const res = await ApiFetch.api.kesehatan.fasilitaspendukung["create"].post(fasilitaspendukung.create.form);
if (res.status === 200) {
fasilitaspendukung.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
fasilitaspendukung.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.FasilitasPendukungGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.
fasilitaspendukung["find-many"].get();
if (res.status === 200) {
fasilitaspendukung.findMany.data = res.data?.data ?? [];
}
},
},
})
/* ======================================================================= */
/* Tarif dan Layanan */
const templateTarifDanLayanan = z.object({
layanan: z.string().min(3, "Layanan minimal 3 karakter"),
tarif: z.string().min(3, "Tarif minimal 3 karakter"),
})
const tarifdanlayanan = proxy({
create: {
form: {} as Prisma.TarifDanLayananGetPayload<{ select: { layanan: true; tarif: true } }>,
loading: false,
async create() {
const cek = templateTarifDanLayanan.safeParse(tarifdanlayanan.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
tarifdanlayanan.create.loading = true;
const res = await ApiFetch.api.kesehatan.tarifdanlayanan["create"].post(tarifdanlayanan.create.form);
if (res.status === 200) {
tarifdanlayanan.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
tarifdanlayanan.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.TarifDanLayananGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.
tarifdanlayanan["find-many"].get();
if (res.status === 200) {
tarifdanlayanan.findMany.data = res.data?.data ?? [];
}
},
},
})
/* ======================================================================= */
/* Prosedur Pendaftaran */
const templateProsedurPendaftaran = z.object({
content: z.string().min(3, "Content minimal 3 karakter"),
})
const prosedurpendaftaran = proxy({
create: {
form: {} as Prisma.ProsedurPendaftaranGetPayload<{ select: { content: true } }>,
loading: false,
async create() {
const cek = templateProsedurPendaftaran.safeParse(prosedurpendaftaran.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
prosedurpendaftaran.create.loading = true;
const res = await ApiFetch.api.kesehatan.prosedurpendaftaran["create"].post(prosedurpendaftaran.create.form);
if (res.status === 200) {
prosedurpendaftaran.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
prosedurpendaftaran.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.ProsedurPendaftaranGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.
prosedurpendaftaran["find-many"].get();
if (res.status === 200) {
prosedurpendaftaran.findMany.data = res.data?.data ?? [];
}
},
},
})
const stateFasilitasKesehatan = proxy({
informasiumum,
layananunggulan,
dokterdantenagamedis,
fasilitaspendukung,
tarifdanlayanan,
prosedurpendaftaran
})
export default stateFasilitasKesehatan
export default fasilitasKesehatanState;

View File

@@ -6,7 +6,7 @@ import { z } from "zod";
const templateGrafikKepuasan = z.object({
label: z.string().min(2, "Label harus diisi"),
jumlah: z.string().min(2, "Jumlah harus diisi"),
jumlah: z.string().min(1, "Jumlah harus diisi"),
});
type GrafikKepuasan = Prisma.GrafikKepuasanGetPayload<{
@@ -31,24 +31,30 @@ const grafikkepuasan = proxy({
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
toast.error(err);
return null;
}
try {
grafikkepuasan.create.loading = true;
const res = await ApiFetch.api.kesehatan.grafikkepuasan["create"].post(
grafikkepuasan.create.form
);
const res = await ApiFetch.api.kesehatan.grafikkepuasan["create"].post(grafikkepuasan.create.form);
if (res.status === 200) {
grafikkepuasan.create.form = {
label: "",
jumlah: ""
};
grafikkepuasan.findMany.load();
return toast.success("success create");
const id = res.data?.data?.id;
if (id) {
toast.success("Success create");
grafikkepuasan.create.form = {
label: "",
jumlah: "",
};
grafikkepuasan.findMany.load();
return id;
}
}
return toast.error("failed create");
toast.error("failed create");
return null;
} catch (error) {
console.log((error as Error).message);
return null;
} finally {
grafikkepuasan.create.loading = false;
}
@@ -67,10 +73,110 @@ const grafikkepuasan = proxy({
}
},
},
findUnique: {
data: null as Prisma.GrafikKepuasanGetPayload<{
omit: { isActive: true }
}> | null,
async load(id: string) {
try {
const res = await fetch(`/api/kesehatan/grafikkepuasan/${id}`);
if (res.ok) {
const data = await res.json();
grafikkepuasan.findUnique.data = data.data ?? null;
} else {
console.error("Failed to fetch grafikkepuasan:", res.statusText);
grafikkepuasan.findUnique.data = null;
}
} catch (error) {
console.error("Error fetching grafikkepuasan:", error);
grafikkepuasan.findUnique.data = null;
}
},
},
update: {
id: "",
form: {...defaultForm},
loading: false,
async byId() {
},
async submit() {
const id = this.id;
if (!id) {
toast.warn("ID tidak valid");
return null;
}
const cek = templateGrafikKepuasan.safeParse(grafikkepuasan.update.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
this.loading = true;
const response = await fetch(`/api/kesehatan/grafikkepuasan/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(this.form),
});
const result = await response.json();
if (!response.ok || !result?.success) {
throw new Error(result?.message || "Gagal update data");
}
toast.success("Berhasil update data!");
// ✅ Optional: refresh list kalau kamu langsung ke halaman list
await grafikkepuasan.findMany.load();
return result.data;
} catch (error) {
console.error("Error update data:", error);
toast.error("Gagal update data grafik kepuasan");
} finally {
this.loading = false;
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) {
return toast.warn("ID tidak valid");
}
try {
grafikkepuasan.delete.loading = true;
const response = await fetch(`/api/kesehatan/grafikkepuasan/del/${id}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
const result = await response.json();
if (response.ok && result?.success) {
toast.success(
result.message || "Grafik kepuasan berhasil dihapus"
);
await grafikkepuasan.findMany.load(); // refresh list
} else {
toast.error(
result?.message || "Gagal menghapus grafik kepuasan"
);
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus grafik kepuasan");
} finally {
grafikkepuasan.delete.loading = false;
}
}
}
});
const stategrafikKepuasan = proxy({
grafikkepuasan,
});
export default stategrafikKepuasan;
export default grafikkepuasan;

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
@@ -5,362 +6,307 @@ import { proxy } from "valtio";
import { z } from "zod";
/* Informasi Kegiatan */
const templateInformasiKegiatan = z.object({
name: z.string().min(3, "Name minimal 3 karakter"),
tanggal: z.string().min(3, "Tanggal minimal 3 karakter"),
waktu: z.string().min(3, "Waktu minimal 3 karakter"),
lokasi: z.string().min(3, "Lokasi minimal 3 karakter"),
const templateForm = z.object({
content: z.string().min(1, "Content minimal 1 karakter"),
informasiJadwalKegiatan: z.object({
name: z.string().min(1, "Name minimal 1 karakter"),
tanggal: z.string().min(1, "Tanggal minimal 1 karakter"),
waktu: z.string().min(1, "Waktu minimal 1 karakter"),
lokasi: z.string().min(1, "Lokasi minimal 1 karakter"),
}),
deskripsiJadwalKegiatan: z.object({
deskripsi: z.string().min(1, "Deskripsi minimal 1 karakter"),
}),
layananJadwalKegiatan: z.object({
content: z.string().min(1, "Content minimal 1 karakter"),
}),
syaratKetentuanJadwalKegiatan: z.object({
content: z.string().min(1, "Content minimal 1 karakter"),
}),
dokumenJadwalKegiatan: z.object({
content: z.string().min(1, "Content minimal 1 karakter"),
}),
pendaftaranJadwalKegiatan: z.object({
name: z.string().min(1, "Name minimal 1 karakter"),
tanggal: z.string().min(1, "Tanggal minimal 1 karakter"),
namaOrangtua: z.string().min(1, "Nama Orangtua minimal 1 karakter"),
nomor: z.string().min(1, "Nomor minimal 1 karakter"),
alamat: z.string().min(1, "Alamat minimal 1 karakter"),
catatan: z.string().min(1, "Catatan minimal 1 karakter"),
}),
});
type InformasiKegiatan = Prisma.InformasiJadwalKegiatanGetPayload<{
select: {
name: true;
tanggal: true;
waktu: true;
lokasi: true;
};
}>;
const defaultForm = {
content: "",
informasiJadwalKegiatan: {
name: "",
tanggal: "",
waktu: "",
lokasi: "",
},
deskripsiJadwalKegiatan: {
deskripsi: "",
},
layananJadwalKegiatan: {
content: "",
},
syaratKetentuanJadwalKegiatan: {
content: "",
},
dokumenJadwalKegiatan: {
content: "",
},
pendaftaranJadwalKegiatan: {
name: "",
tanggal: "",
namaOrangtua: "",
nomor: "",
alamat: "",
catatan: "",
},
};
const informasiKegiatan = proxy({
const jadwalkegiatanState = proxy({
create: {
form: {} as InformasiKegiatan,
form: { ...defaultForm },
loading: false,
async create() {
const cek = templateInformasiKegiatan.safeParse(
informasiKegiatan.create.form
);
async submit() {
const cek = templateForm.safeParse(this.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
const errMsg = cek.error.issues
.map((v) => `${v.path.join(".")}: ${v.message}`)
.join("\n");
toast.error(errMsg);
return null;
}
try {
informasiKegiatan.create.loading = true;
const res = await ApiFetch.api.kesehatan.informasiJadwalKegiatan[
"create"
].post(informasiKegiatan.create.form);
this.loading = true;
const payload = { ...this.form };
const res = await (ApiFetch.api.kesehatan as any)[
"jadwal-kegiatan"
].create.post(payload);
if (res.status === 200) {
informasiKegiatan.findMany.load();
return toast.success("success create");
toast.success("Berhasil menambahkan jadwal kegiatan");
this.resetForm();
await jadwalkegiatanState.findMany.load();
return res.data;
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} catch (err: any) {
const msg = err?.message || "Terjadi kesalahan saat mengirim data";
toast.error(msg);
console.error("SUBMIT ERROR:", err);
return null;
} finally {
informasiKegiatan.create.loading = false;
this.loading = false;
}
},
resetForm() {
this.form = { ...defaultForm };
},
},
findMany: {
data: null as
| Prisma.InformasiJadwalKegiatanGetPayload<{ omit: { isActive: true } }>[]
| Prisma.JadwalKegiatanGetPayload<{
include: {
informasijadwalkegiatan: true;
deskripsijadwalkegiatan: true;
layananjadwalkegiatan: true;
dokumenjadwalkegiatan: true;
pendaftaranjadwalkegiatan: true;
};
}>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.informasiJadwalKegiatan[
"find-many"
].get();
if (res.status === 200) {
informasiKegiatan.findMany.data = res.data?.data ?? [];
}
},
},
});
/* ======================================================================= */
/* Deskripsi Kegiatan */
const templateDeskripsiKegiatan = z.object({
deskripsi: z.string().min(3, "Content minimal 3 karakter"),
});
type DeskripsiKegiatan = Prisma.DeskripsiJadwalKegiatanGetPayload<{
select: { deskripsi: true };
}>;
const deskripsiKegiatan = proxy({
create: {
form: {} as DeskripsiKegiatan,
loading: false,
async create() {
const cek = templateDeskripsiKegiatan.safeParse(
deskripsiKegiatan.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
async load() {
try {
deskripsiKegiatan.create.loading = true;
const res = await ApiFetch.api.kesehatan.deskripsikegiatan[
"create"
].post(deskripsiKegiatan.create.form);
this.loading = true;
const res = await (ApiFetch.api.kesehatan as any)[
"jadwal-kegiatan"
]["find-many"].get();
if (res.status === 200) {
deskripsiKegiatan.findMany.load();
return toast.success("success create");
this.data = res.data?.data ?? [];
} else {
toast.error("Gagal memuat data jadwal kegiatan");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
return res;
} catch (err) {
toast.error("Terjadi error saat load data");
console.error("LOAD ERROR:", err);
throw err;
} finally {
deskripsiKegiatan.create.loading = false;
this.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.DeskripsiJadwalKegiatanGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.deskripsikegiatan[
"find-many"
].get();
if (res.status === 200) {
deskripsiKegiatan.findMany.data = res.data?.data ?? [];
}
},
},
});
/* ======================================================================= */
/* Layanan Tersedia */
const templateLayananTersedia = z.object({
content: z.string().min(3, "Content minimal 3 karakter"),
});
type LayananTersedia = Prisma.LayananJadwalKegiatanGetPayload<{
select: { content: true };
}>;
const layanantersedia = proxy({
create: {
form: {} as LayananTersedia,
findUnique: {
data: null as Prisma.JadwalKegiatanGetPayload<{
include: {
informasijadwalkegiatan: true;
deskripsijadwalkegiatan: true;
layananjadwalkegiatan: true;
syaratketentuanjadwalkegiatan: true;
dokumenjadwalkegiatan: true;
pendaftaranjadwalkegiatan: true;
};
}> | null,
loading: false,
async create() {
const cek = templateLayananTersedia.safeParse(
layanantersedia.create.form
);
async load(id: string) {
const res = await fetch(`/api/kesehatan/jadwal-kegiatan/${id}`);
if (res.ok) {
const data = await res.json();
jadwalkegiatanState.findUnique.data = data.data ?? null;
} else {
toast.error("Gagal load data jadwal kegiatan");
}
},
},
edit: {
id: "",
form: { ...defaultForm },
loading: false,
async load(id: string) {
const res = await fetch(`/api/kesehatan/jadwal-kegiatan/${id}`);
if (!res.ok) {
toast.error("Gagal load data jadwal kegiatan");
return;
}
const result = await res.json();
const data = result.data;
jadwalkegiatanState.edit.id = data.id;
jadwalkegiatanState.edit.form = {
content: data.content,
informasiJadwalKegiatan: {
name: data.informasijadwalkegiatan.name,
tanggal: data.informasijadwalkegiatan.tanggal,
waktu: data.informasijadwalkegiatan.waktu,
lokasi: data.informasijadwalkegiatan.lokasi,
},
layananJadwalKegiatan: {
content: data.layananjadwalkegiatan.content,
},
deskripsiJadwalKegiatan: {
deskripsi: data.deskripsijadwalkegiatan.deskripsi,
},
syaratKetentuanJadwalKegiatan: {
content: data.syaratketentuanjadwalkegiatan.content,
},
dokumenJadwalKegiatan: {
content: data.dokumenjadwalkegiatan.content,
},
pendaftaranJadwalKegiatan: {
name: data.pendaftaranjadwalkegiatan.name,
tanggal: data.pendaftaranjadwalkegiatan.tanggal,
namaOrangtua: data.pendaftaranjadwalkegiatan.namaOrangtua,
nomor: data.pendaftaranjadwalkegiatan.nomor,
alamat: data.pendaftaranjadwalkegiatan.alamat,
catatan: data.pendaftaranjadwalkegiatan.catatan,
},
};
},
async submit() {
const cek = templateForm.safeParse(jadwalkegiatanState.edit.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
const errMsg = cek.error.issues
.map((v) => `${v.path.join(".")}: ${v.message}`)
.join("\n");
toast.error(errMsg);
return null;
}
try {
layanantersedia.create.loading = true;
const res = await ApiFetch.api.kesehatan.layanantersedia["create"].post(
layanantersedia.create.form
);
if (res.status === 200) {
layanantersedia.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
layanantersedia.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.LayananJadwalKegiatanGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.layanantersedia[
"find-many"
].get();
if (res.status === 200) {
layanantersedia.findMany.data = res.data?.data ?? [];
}
},
},
});
/* ======================================================================= */
jadwalkegiatanState.edit.loading = true;
const payload = {
content: jadwalkegiatanState.edit.form.content,
informasiJadwalKegiatan: {
name: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.name,
tanggal: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.tanggal,
waktu: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.waktu,
lokasi: jadwalkegiatanState.edit.form.informasiJadwalKegiatan.lokasi,
},
layananJadwalKegiatan: {
content: jadwalkegiatanState.edit.form.layananJadwalKegiatan.content,
},
deskripsiJadwalKegiatan: {
deskripsi: jadwalkegiatanState.edit.form.deskripsiJadwalKegiatan.deskripsi,
},
syaratKetentuanJadwalKegiatan: {
content: jadwalkegiatanState.edit.form.syaratKetentuanJadwalKegiatan.content,
},
dokumenJadwalKegiatan: {
content: jadwalkegiatanState.edit.form.dokumenJadwalKegiatan.content,
},
pendaftaranJadwalKegiatan: {
name: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.name,
tanggal: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.tanggal,
namaOrangtua: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.namaOrangtua,
nomor: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.nomor,
alamat: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.alamat,
catatan: jadwalkegiatanState.edit.form.pendaftaranJadwalKegiatan.catatan,
},
};
/* Syarat dan Ketentuan */
const templateSyaratKetentuan = z.object({
content: z.string().min(3, "Content minimal 3 karakter"),
});
type SyaratKetentuan = Prisma.SyaratKetentuanJadwalKegiatanGetPayload<{
select: { content: true };
}>;
const syaratketentuan = proxy({
create: {
form: {} as SyaratKetentuan,
loading: false,
async create() {
const cek = templateSyaratKetentuan.safeParse(
syaratketentuan.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
syaratketentuan.create.loading = true;
const res = await ApiFetch.api.kesehatan.syaratketentuan[
"create"
].post(syaratketentuan.create.form);
if (res.status === 200) {
syaratketentuan.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
syaratketentuan.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.SyaratKetentuanJadwalKegiatanGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.syaratketentuan[
"find-many"
].get();
if (res.status === 200) {
syaratketentuan.findMany.data = res.data?.data ?? [];
}
},
},
});
/* ======================================================================= */
/* Dokumen Yang Diperlukan */
const templateDokumenDiperlukan = z.object({
content: z.string().min(3, "Content minimal 3 karakter"),
});
type DokumenDiperlukan = Prisma.DokumenJadwalKegiatanGetPayload<{
select: { content: true };
}>;
const dokumenjadwalkegiatan = proxy({
create: {
form: {} as DokumenDiperlukan,
loading: false,
async create() {
const cek = templateDokumenDiperlukan.safeParse(
dokumenjadwalkegiatan.create.form
);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
dokumenjadwalkegiatan.create.loading = true;
const res = await ApiFetch.api.kesehatan.dokumendiperlukan[
"create"
].post(dokumenjadwalkegiatan.create.form);
if (res.status === 200) {
dokumenjadwalkegiatan.findMany.load();
return toast.success("success create");
}
return toast.error("failed create");
} catch (error) {
console.log((error as Error).message);
} finally {
dokumenjadwalkegiatan.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.DokumenJadwalKegiatanGetPayload<{ omit: { isActive: true } }>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.dokumendiperlukan[
"find-many"
].get();
if (res.status === 200) {
dokumenjadwalkegiatan.findMany.data = res.data?.data ?? [];
}
},
},
});
/* ======================================================================= */
/* Pendaftaran */
const templatePendaftaran = z.object({
name: z.string().min(3, "Nama minimal 3 karakter"),
tanggal: z.string().min(1),
namaOrangtua: z.string().min(3, "Nama minimal 3 karakter"),
nomor: z.string().min(9, "Nama minimal 9 karakter"),
alamat: z.string().min(7, "Alamat minimal 7 karakter"),
catatan: z.string().min(3, "Catatan minimal 3 karakter"),
})
type Pendaftaran = Prisma.PendaftaranJadwalKegiatanGetPayload<{
select: {
name: true;
tanggal: true;
namaOrangtua: true;
nomor: true;
alamat: true;
catatan: true;
}
}>
const pendaftaranjadwal = proxy({
create: {
form: {} as Pendaftaran,
loading: false,
async create() {
const cek = templatePendaftaran.safeParse(pendaftaranjadwal.create.form);
if(!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
pendaftaranjadwal.create.loading = true;
const res = await ApiFetch.api.kesehatan.pendaftaranJadwalKegiatan["create"].post(
pendaftaranjadwal.create.form);
if (res.status === 200) {
pendaftaranjadwal.findMany.load();
return toast.success("success create")
const res = await fetch(
`/api/kesehatan/jadwal-kegiatan/${jadwalkegiatanState.edit.id}`,
{
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}
return toast.error("failed create")
} catch (error) {
console.log((error as Error).message)
);
if (!res.ok) {
const error = await res.json();
throw new Error(error.message || "Update gagal");
}
toast.success("Berhasil update jadwal kegiatan");
await jadwalkegiatanState.findMany.load();
return true;
} catch (err) {
toast.error(
err instanceof Error ? err.message : "Terjadi kesalahan saat update"
);
return false;
} finally {
pendaftaranjadwal.create.loading = false;
jadwalkegiatanState.edit.loading = false;
}
},
resetForm() {
jadwalkegiatanState.edit.id = "";
jadwalkegiatanState.edit.form = { ...defaultForm };
},
},
delete: {
loading: false,
async byId(id: string){
try {
jadwalkegiatanState.delete.loading = true;
const res = await fetch(`/api/kesehatan/jadwal-kegiatan/del/${id}`, {
method: "DELETE",
});
const result = await res.json();
if (res.ok && result.success) {
toast.success("Jadwal kegiatan berhasil dihapus");
await jadwalkegiatanState.findMany.load();
} else {
toast.error(result.message || "Gagal menghapus");
}
} catch {
toast.error("Terjadi kesalahan saat menghapus");
} finally {
jadwalkegiatanState.delete.loading = false;
}
}
},
findMany: {
data: null as
| Prisma.PendaftaranJadwalKegiatanGetPayload<{omit: {isActive: true}}>[]
| null,
async load() {
const res = await ApiFetch.api.kesehatan.
pendaftaranJadwalKegiatan["find-many"].get();
if(res.status === 200) {
pendaftaranjadwal.findMany.data = res.data?.data ?? [];
}
}
}
})
const stateJadwalKegiatan = proxy({
informasiKegiatan,
deskripsiKegiatan,
layanantersedia,
syaratketentuan,
dokumenjadwalkegiatan,
pendaftaranjadwal
});
export default stateJadwalKegiatan;
export default jadwalkegiatanState;

View File

@@ -6,9 +6,9 @@ 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"),
kematianKasar: z.string().min(1, "Kematian kasar harus diisi"),
kelahiranKasar: z.string().min(1, "Kelahiran kasar harus diisi"),
kematianBayi: z.string().min(1, "Kematian bayi harus diisi"),
});
type Persentase = Prisma.DataKematian_KelahiranGetPayload<{
@@ -34,51 +34,157 @@ const persentasekelahiran = proxy({
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);
const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`;
toast.error(err);
return null;
}
try {
persentasekelahiran.create.loading = true;
const res = await ApiFetch.api.kesehatan.persentasekelahiran[
"create"
].post(persentasekelahiran.create.form);
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");
const id = res.data?.data?.id;
if (id) {
toast.success("Success create");
persentasekelahiran.create.form = { ...defaultForm };
persentasekelahiran.findMany.load();
return id;
}
}
return toast.error("failed create");
toast.error("failed create");
return null;
} catch (error) {
console.log((error as Error).message);
return null;
} finally {
persentasekelahiran.create.loading = false;
}
},
},
findMany: {
data: null as
| Prisma.DataKematian_KelahiranGetPayload<{ omit: { isActive: true } }>[]
| null,
data: null as Prisma.DataKematian_KelahiranGetPayload<{
omit: { isActive: true };
}>[] | null,
async load() {
const res = await ApiFetch.api.kesehatan.persentasekelahiran[
"find-many"
].get();
const res = await ApiFetch.api.kesehatan.persentasekelahiran["find-many"].get();
if (res.status === 200) {
persentasekelahiran.findMany.data = res.data?.data ?? [];
}
},
},
findUnique: {
data: null as Prisma.DataKematian_KelahiranGetPayload<{
omit: { isActive: true };
}> | null,
async load(id: string) {
try {
const res = await fetch(`/api/kesehatan/persentasekelahiran/${id}`);
if (res.ok) {
const data = await res.json();
persentasekelahiran.findUnique.data = data.data ?? null;
} else {
console.error("Failed to fetch persentasekelahiran:", res.statusText);
persentasekelahiran.findUnique.data = null;
}
} catch (error) {
console.error("Error fetching persentasekelahiran:", error);
persentasekelahiran.findUnique.data = null;
}
},
},
update: {
id: "",
form: { ...defaultForm },
loading: false,
async submit() {
const id = this.id;
if (!id) {
toast.warn("ID tidak valid");
return null;
}
const formData = {
tahun: this.form.tahun,
kematianKasar: this.form.kematianKasar,
kelahiranKasar: this.form.kelahiranKasar,
kematianBayi: this.form.kematianBayi,
};
const cek = templatePersentase.safeParse(formData);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return null;
}
try {
this.loading = true;
const res = await fetch(`/api/kesehatan/persentasekelahiran/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(formData),
});
const result = await res.json();
if (!res.ok || !result?.success) {
throw new Error(result?.message || "Gagal update data");
}
toast.success("Berhasil update data!");
await persentasekelahiran.findMany.load();
return result.data;
} catch (error) {
console.error("Update error:", error);
toast.error("Gagal update data persentase kelahiran");
throw error;
} finally {
this.loading = false;
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) return toast.warn("ID tidak valid");
try {
persentasekelahiran.delete.loading = true;
const response = await fetch(`/api/kesehatan/persentasekelahiran/del/${id}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
});
const result = await response.json();
if (response.ok && result?.success) {
toast.success(result.message || "Persentase kelahiran berhasil dihapus");
await persentasekelahiran.findMany.load();
} else {
toast.error(result?.message || "Gagal menghapus persentase kelahiran");
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus persentase kelahiran");
} finally {
persentasekelahiran.delete.loading = false;
}
},
},
});
const statePersentase = proxy({
persentasekelahiran,
});
export default statePersentase;
export default persentasekelahiran;

View File

@@ -0,0 +1,77 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import colors from '@/con/colors';
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import { usePathname, useRouter } from 'next/navigation';
import React, { useEffect, useState } from 'react';
function LayoutTabs({ children }: { children: React.ReactNode }) {
const router = useRouter()
const pathname = usePathname()
const tabs = [
{
label: "Presentase Kelahiran & Kematian",
value: "presentasekelahiran&kematian",
href: "/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian"
},
{
label: "Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik",
value: "grafikhasilkepuasan",
href: "/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan"
},
{
label: "Fasilitas Kesehatan",
value: "fasilitaskesehatan",
href: "/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan"
},
{
label: "Jadwal Kegiatan",
value: "jadwalkegiatan",
href: "/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan"
},
{
label: "Artikel Kesehatan",
value: "artikelkesehatan",
href: "/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan"
},
];
const curentTab = tabs.find(tab => tab.href === pathname)
const [activeTab, setActiveTab] = useState<string | null>(curentTab?.value || tabs[0].value);
const handleTabChange = (value: string | null) => {
const tab = tabs.find(t => t.value === value)
if (tab) {
router.push(tab.href)
}
setActiveTab(value)
}
useEffect(() => {
const match = tabs.find(tab => tab.href === pathname)
if (match) {
setActiveTab(match.value)
}
}, [pathname])
return (
<Stack>
<Title order={3}>Data Kesehatan Warga</Title>
<Tabs color={colors['blue-button']} variant='pills' value={activeTab} onChange={handleTabChange}>
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
{tabs.map((e, i) => (
<TabsTab key={i} value={e.value}>{e.label}</TabsTab>
))}
</TabsList>
{tabs.map((e, i) => (
<TabsPanel key={i} value={e.value}>
{/* Konten dummy, bisa diganti tergantung routing */}
<></>
</TabsPanel>
))}
</Tabs>
{children}
</Stack>
);
}
export default LayoutTabs;

View File

@@ -1,26 +0,0 @@
'use client'
import { Box, Paper, 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';
import colors from '@/con/colors';
function DoctorSignUI() {
const doctorSign = useProxy(stateArtikelKesehatan.doctorSign)
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Text fw={"bold"}>Kapan Harus ke Dokter</Text>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
doctorSign.create.form.content = val
}}
/>
</Paper>
</Box>
);
}
export default DoctorSignUI;

View File

@@ -1,34 +0,0 @@
'use client'
import { Box, Paper, Stack, 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';
import colors from '@/con/colors';
function FirstAidUI() {
const firstAidState = useProxy(stateArtikelKesehatan.firstAid)
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<TextInput
label={<Text fw={"bold"}>Judul Pertolongan Pertama</Text>}
placeholder="Masukkan judul"
onChange={(val) => {
firstAidState.create.form.title = val.target.value
}}
/>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
firstAidState.create.form.content = val
}}
/>
</Stack>
</Paper>
</Box>
);
}
export default FirstAidUI;

View File

@@ -1,28 +0,0 @@
'use client'
import { Box, Paper, Stack, 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';
import colors from '@/con/colors';
function IntoductionUI() {
const introduction = useProxy(stateArtikelKesehatan.introduction)
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Text fw={"bold"}>Pendahuluan</Text>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
introduction.create.form.content = val;
}}
/>
</Stack>
</Paper>
</Box>
);
}
export default IntoductionUI;

View File

@@ -1,40 +0,0 @@
'use client'
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import colors from '@/con/colors';
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
import { useProxy } from 'valtio/utils';
function MythFactUI() {
const mythFact = useProxy(stateArtikelKesehatan.mythFact)
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<TextInput
label={<Text fw={"bold"}>Judul Pertolongan Pertama Penyakit</Text>}
placeholder="Masukkan judul"
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
}}
/>
</Stack>
</Paper>
</Box>
);
}
export default MythFactUI;

View File

@@ -1,172 +0,0 @@
'use client'
import { Box, Button, Center, Group, Paper, 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 >
<Stack gap={"xs"}>
<Title order={4}>Artikel Kesehatan</Title>
<IntoductionUI />
<SymptomUI />
<PreventionUI />
<FirstAidUI />
<MythFactUI />
<DoctorSignUI />
<Group>
<Button bg={colors['blue-button']} mt={10} onClick={submitAllForms}>Submit</Button>
</Group>
</Stack>
</Box>
<Box>
<Stack gap={"xs"}>
<Title order={4}>Data Artikel Kesehatan</Title>
<AllList />
</Stack>
</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 gap={"xs"}>
{/* Introduction */}
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Pendahuluan</Title>
{listState.introduction.findMany.data?.map((item) => (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{ __html: item.content }}></Text>
</Box>
))}
</Paper>
{/* Symptom */}
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Gejala Penyakit</Title>
{listState.symptom.findMany.data?.map((item) => (
<Box key={item.id}>
<Title order={4}>{item.title}</Title>
<Text dangerouslySetInnerHTML={{ __html: item.content }}></Text>
</Box>
))}
</Paper>
{/* Prevention */}
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Pencegahan Penyakit</Title>
{listState.prevention.findMany.data?.map((item) => (
<Box key={item.id}>
<Title order={4}>{item.title}</Title>
<Text dangerouslySetInnerHTML={{ __html: item.content }}></Text>
</Box>
))}
</Paper>
{/* First Aid */}
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Pertolongan Pertama</Title>
{listState.firstAid.findMany.data?.map((item) => (
<Box key={item.id}>
<Title order={4}>{item.title}</Title>
<Text dangerouslySetInnerHTML={{ __html: item.content }}></Text>
</Box>
))}
</Paper>
{/* Myth Fact */}
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Mitos vs Fakta</Title>
{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>
))}
</Paper>
{/* Doctor Sign */}
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Kapan Harus Ke Dokter?</Title>
{listState.doctorSign.findMany.data?.map((item) => (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{ __html: item.content }} />
</Box>
))}
</Paper>
</Stack>
}
export default ArtikelKesehatan;

View File

@@ -1,35 +0,0 @@
'use client'
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
import React from 'react';
import { useProxy } from 'valtio/utils';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import colors from '@/con/colors';
function PreventionUI() {
const preventionState = useProxy(stateArtikelKesehatan.prevention)
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<TextInput
mb={10}
label={<Text fw={"bold"}>Judul Pencegahan Penyakit</Text>}
placeholder="Masukkan judul"
onChange={(val) => {
preventionState.create.form.title = val.target.value
}}
/>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
preventionState.create.form.content = val
}}
/>
</Stack>
</Paper>
</Box>
);
}
export default PreventionUI;

View File

@@ -1,34 +0,0 @@
'use client'
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import colors from '@/con/colors';
function SymptomUI() {
const symptomState = useProxy(stateArtikelKesehatan.symptom)
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<TextInput
mb={10}
label={<Text fw={"bold"}>Judul Gejala Penyakit</Text>}
placeholder='masukkan judul'
onChange={(val) => {
symptomState.create.form.title = val.target.value
}}
/>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
symptomState.create.form.content = val
}}
/>
</Stack>
</Paper>
</Box>
);
}
export default SymptomUI;

View File

@@ -1,89 +0,0 @@
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
import { Center, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import _ from 'lodash';
import { useProxy } from 'valtio/utils';
function DokterdanTenagaMedisList() {
const kesehatanState = useProxy(stateFasilitasKesehatan.dokterdantenagamedis);
useShallowEffect(() => {
kesehatanState.findMany.load();
}, []);
// Penanganan kasus ketika tidak ada data
if (_.isEmpty(kesehatanState.findMany.data)) {
return (
<Stack>
<Table
striped
highlightOnHover
withTableBorder
withColumnBorders
bg={colors['white-1']}
>
<TableThead>
<TableTr>
<TableTh>
<Center>Nama</Center>
</TableTh>
<TableTh>
<Center>Specialist</Center>
</TableTh>
<TableTh >
<Center>Jadwal</Center>
</TableTh>
</TableTr>
</TableThead>
<TableTbody >
<TableTr >
<TableTd colSpan={3}>
<Center>
<Text>Tidak ada data</Text>
</Center>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Stack>
);
}
return (
<Stack>
<Table
striped
highlightOnHover
withTableBorder
withColumnBorders
bg={colors['white-1']}
>
<TableThead >
<TableTr >
<TableTh >
<Center>Nama</Center>
</TableTh>
<TableTh >
<Center>Specialist</Center>
</TableTh>
<TableTh >
<Center>Jadwal</Center>
</TableTh>
</TableTr>
</TableThead>
<TableTbody >
{kesehatanState.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd ta="center">{item.name}</TableTd>
<TableTd ta="center">Specialist {item.specialist}</TableTd>
<TableTd ta="center">{item.jadwal}</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Stack>
);
}
export default DokterdanTenagaMedisList;

View File

@@ -1,41 +0,0 @@
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
import { useProxy } from 'valtio/utils';
function DokterDanTenagaMedis() {
const dokterdantenagamedisState = useProxy(stateFasilitasKesehatan.dokterdantenagamedis)
return (
<Box>
<Paper bg={colors['white-1']} p={"md"}>
<Stack gap={"xs"}>
<Text fw={"bold"}>Dokter & Tenaga Medis</Text>
<TextInput
label="Nama Dokter"
placeholder='masukkan nama dokter'
onChange={(val) => {
dokterdantenagamedisState.create.form.name = val.target.value
}}
/>
<TextInput
label="Specialist"
placeholder='masukkan specialist'
onChange={(val) => {
dokterdantenagamedisState.create.form.specialist = val.target.value
}}
/>
<TextInput
mb={10}
label="Jadwal"
placeholder='masukkan jadwal'
onChange={(val) => {
dokterdantenagamedisState.create.form.jadwal = val.target.value
}}
/>
</Stack>
</Paper>
</Box>
);
}
export default DokterDanTenagaMedis;

View File

@@ -1,21 +0,0 @@
import { Box, Stack, Text } from '@mantine/core';
import React from 'react';
import { useProxy } from 'valtio/utils';
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import { useShallowEffect } from '@mantine/hooks';
function FasilitasPendukungList() {
const fasilitaspendukungState = useProxy(stateFasilitasKesehatan.fasilitaspendukung)
useShallowEffect(() => {
fasilitaspendukungState.findMany.load()
}, [])
return <Stack>
{fasilitaspendukungState.findMany.data?.map((item) => (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{__html: item.content}}/>
</Box>
))}
</Stack>
}
export default FasilitasPendukungList;

View File

@@ -1,21 +0,0 @@
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import { Box, Paper, Text } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import colors from '@/con/colors';
function FasilitasPendukung() {
const fasilitaspendukungState = useProxy(stateFasilitasKesehatan.fasilitaspendukung)
return <Box>
<Paper bg={colors['white-1']} p={'md'}>
<Text fw={"bold"}>Fasilitas Pendukung</Text>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
fasilitaspendukungState.create.form.content = val;
}} />
</Paper>
</Box>
}
export default FasilitasPendukung;

View File

@@ -1,40 +0,0 @@
'use client'
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
import { useProxy } from 'valtio/utils';
function InformasiUmum() {
const infromasiState = useProxy(stateFasilitasKesehatan.informasiumum)
return <Box>
<Paper bg={colors['white-1']} p={'md'}>
<Text fw={"bold"}>Informasi Umum</Text>
<Stack gap={"xs"}>
<TextInput
label="Fasilitas"
placeholder='masukkan nama fasilitas kesehatan'
onChange={(val) => {
infromasiState.create.form.fasilitas = val.target.value
}}
/>
<TextInput
label="Alamat"
placeholder='masukkan alamat'
onChange={(val) => {
infromasiState.create.form.alamat = val.target.value
}}
/>
<TextInput
mb={10}
label="Jam Operasional"
placeholder='masukkan jam operasional'
onChange={(val) => {
infromasiState.create.form.jamOperasional = val.target.value
}}
/>
</Stack>
</Paper>
</Box>
}
export default InformasiUmum;

View File

@@ -1,23 +0,0 @@
'use client'
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import { Box, Paper, Text } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import colors from '@/con/colors';
function LayananUnggulan() {
const informasiumumState = useProxy(stateFasilitasKesehatan.layananunggulan)
return <Box>
<Paper bg={colors['white-1']} p={'md'}>
<Text fw={"bold"}>Layanan Unggulan</Text>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
informasiumumState.create.form.content = val;
}} />
</Paper>
</Box>
;
}
export default LayananUnggulan;

View File

@@ -1,254 +0,0 @@
"use client"
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
import { Box, Button, Center, Paper, SimpleGrid, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
import DokterDanTenagaMedis from './dokterdantenagamedis/page';
import FasilitasPendukung from './fasilitas_pendukung/page';
import InformasiUmum from './informasi_umum/page';
import LayananUnggulan from './layanan_unggulan/page';
import ProsedurPendaftaran from './prosedurpendaftaran/page';
import TarifDanLayanan from './tarifdanlayanan/page';
function FasilitasKesehatan() {
const allState = useProxy(stateFasilitasKesehatan)
const submitAllForms = () => {
if (allState.informasiumum.create.form.fasilitas &&
allState.informasiumum.create.form.alamat &&
allState.informasiumum.create.form.jamOperasional) {
allState.informasiumum.create.create()
}
if (allState.layananunggulan.create.form.content) {
allState.layananunggulan.create.create();
}
if (allState.dokterdantenagamedis.create.form.name &&
allState.dokterdantenagamedis.create.form.specialist &&
allState.dokterdantenagamedis.create.form.jadwal) {
allState.dokterdantenagamedis.create.create()
}
if (allState.fasilitaspendukung.create.form.content) {
allState.fasilitaspendukung.create.create();
}
if (allState.tarifdanlayanan.create.form.tarif &&
allState.tarifdanlayanan.create.form.layanan) {
allState.tarifdanlayanan.create.create()
}
if (allState.prosedurpendaftaran.create.form.content) {
allState.prosedurpendaftaran.create.create();
}
// refreshListData();
// resetAllForms();
}
// const refreshListData = () => {
// allState.informasiumum.findMany.load();
// allState.layananunggulan.findMany.load();
// allState.dokterdantenagamedis.findMany.load();
// allState.fasilitaspendukung.findMany.load();
// allState.tarifdanlayanan.findMany.load();
// allState.prosedurpendaftaran.findMany.load();
// }
// const resetAllForms = () => {
// allState.informasiumum.create.form = {
// fasilitas: '',
// alamat: '',
// jamOperasional: ''
// };
// allState.layananunggulan.create.form = {
// content: ''
// };
// allState.dokterdantenagamedis.create.form = {
// name: '',
// specialist: '',
// jadwal: ''
// };
// allState.fasilitaspendukung.create.form = {
// content: ''
// };
// allState.tarifdanlayanan.create.form = {
// tarif: '',
// layanan: ''
// };
// allState.prosedurpendaftaran.create.form = {
// content: ''
// };
// }
return (
<Stack py={10}>
<SimpleGrid cols={{
base: 1, md: 2
}}>
<Box>
<Stack gap={'xs'}>
<Title order={4}>Tambah Fasilitas Kesehatan</Title>
{/* Informasi Umum */}
<InformasiUmum />
{/* Layanan Unggulan */}
<LayananUnggulan />
{/* Dokter & Tenaga Medis */}
<DokterDanTenagaMedis />
{/* Fasilitas Pendukung */}
<FasilitasPendukung />
{/* Tarif & Layanan */}
<TarifDanLayanan />
{/* Prosedur Pendaftaran */}
<ProsedurPendaftaran />
<Button onClick={submitAllForms}>Submit</Button>
</Stack>
</Box>
<Box>
<Stack gap={"xs"}>
<Title order={4}>Data Fasilitas Kesehatan</Title>
<AllList />
</Stack>
</Box>
</SimpleGrid>
</Stack>
);
}
function AllList() {
const allListState = useProxy(stateFasilitasKesehatan)
useShallowEffect(() => {
allListState.informasiumum.findMany.load();
allListState.layananunggulan.findMany.load();
allListState.dokterdantenagamedis.findMany.load();
allListState.fasilitaspendukung.findMany.load();
allListState.tarifdanlayanan.findMany.load();
allListState.prosedurpendaftaran.findMany.load();
}, [])
if (!allListState.informasiumum.findMany.data
|| !allListState.layananunggulan.findMany.data
|| !allListState.dokterdantenagamedis.findMany.data
|| !allListState.fasilitaspendukung.findMany.data
|| !allListState.tarifdanlayanan.findMany.data
|| !allListState.prosedurpendaftaran.findMany.data
) return <Stack>
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
return <Stack>
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Informasi Umum</Title>
{allListState.informasiumum.findMany.data?.map((item) => (
<Box key={item.id}>
<Text>{item.fasilitas}</Text>
<Text>{item.alamat}</Text>
<Text>{item.jamOperasional}</Text>
</Box>
))}
</Paper>
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Layanan Unggulan</Title>
{allListState.layananunggulan.findMany.data?.map((item) => (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{ __html: item.content }} />
</Box>
))}
</Paper>
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Dokter & Tenaga Medis</Title>
<Table
striped
highlightOnHover
withTableBorder
withColumnBorders
bg={colors['white-1']}
>
<TableThead >
<TableTr >
<TableTh >
<Center>Nama</Center>
</TableTh>
<TableTh >
<Center>Specialist</Center>
</TableTh>
<TableTh >
<Center>Jadwal</Center>
</TableTh>
</TableTr>
</TableThead>
<TableTbody >
{allListState.dokterdantenagamedis.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd ta="center">{item.name}</TableTd>
<TableTd ta="center">Specialist {item.specialist}</TableTd>
<TableTd ta="center">{item.jadwal}</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Paper>
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Fasilitas Pendukung</Title>
{allListState.fasilitaspendukung.findMany.data?.map((item) => (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{ __html: item.content }} />
</Box>
))}
</Paper>
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Tarif & Layanan</Title>
<Table
suppressHydrationWarning
striped
highlightOnHover
withTableBorder
withColumnBorders
bg={colors['white-1']}
>
<TableThead>
<TableTr>
<TableTh>
Layanan
</TableTh>
<TableTh>
Tarif
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{allListState.tarifdanlayanan.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.layanan}</TableTd>
<TableTd>Rp.{item.tarif}</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Paper>
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Prosedur Pendaftaran</Title>
{allListState.prosedurpendaftaran.findMany.data?.map((item) => (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{ __html: item.content }} />
</Box>
))}
</Paper>
</Stack>
}
export default FasilitasKesehatan;

View File

@@ -1,25 +0,0 @@
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import { Box, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
function ListProsedurPendaftaran() {
const prosedurpendaftaranState = useProxy(stateFasilitasKesehatan.prosedurpendaftaran)
useShallowEffect(() => {
prosedurpendaftaranState.findMany.load()
}, [])
if (!prosedurpendaftaranState.findMany.data)return<Stack>
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
return <Stack>
{prosedurpendaftaranState.findMany.data?.map((item) => (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{__html: item.content}}/>
</Box>
))}
</Stack>
}
export default ListProsedurPendaftaran;

View File

@@ -1,22 +0,0 @@
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import { Box, Text, Paper } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import colors from '@/con/colors';
function ProsedurPendaftaran() {
const prosedurpendaftaranState = useProxy(stateFasilitasKesehatan.prosedurpendaftaran)
return <Box>
<Paper bg={colors['white-1']} p={'md'}>
<Text fw={"bold"}>Prosedur Pendaftaran</Text>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
prosedurpendaftaranState.create.form.content = val;
}} />
</Paper>
</Box>
}
export default ProsedurPendaftaran;

View File

@@ -1,82 +0,0 @@
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
import { Center, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import _ from 'lodash';
import { useProxy } from 'valtio/utils';
function TarifDanLayananList() {
const tarifdanlayanan = useProxy(stateFasilitasKesehatan.tarifdanlayanan)
useShallowEffect(() => {
tarifdanlayanan.findMany.load()
}, [])
if (_.isEmpty(tarifdanlayanan.findMany.data)) {
return (
<Stack>
<Table
striped
highlightOnHover
withTableBorder
withColumnBorders
bg={colors['white-1']}
>
<TableThead>
<TableTr>
<TableTh>
<Center>Nama</Center>
</TableTh>
<TableTh>
<Center>Specialist</Center>
</TableTh>
<TableTh >
<Center>Jadwal</Center>
</TableTh>
</TableTr>
</TableThead>
<TableTbody >
<TableTr >
<TableTd colSpan={3}>
<Center>
<Text>Tidak ada data</Text>
</Center>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Stack>
);
}
return <Stack>
<Table
suppressHydrationWarning
striped
highlightOnHover
withTableBorder
withColumnBorders
bg={colors['white-1']}
>
<TableThead>
<TableTr>
<TableTh>
Layanan
</TableTh>
<TableTh>
Tarif
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{tarifdanlayanan.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.layanan}</TableTd>
<TableTd>Rp.{item.tarif}</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Stack>
}
export default TarifDanLayananList;

View File

@@ -1,32 +0,0 @@
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
import { useProxy } from 'valtio/utils';
function TarifDanLayanan() {
const tarifdanlayanan = useProxy(stateFasilitasKesehatan.tarifdanlayanan)
return <Box>
<Paper bg={colors['white-1']} p={'md'}>
<Text fw={"bold"}>Tarif & Layanan</Text>
<Stack gap={"xs"}>
<TextInput
label="Tarif"
placeholder='masukkan tarif'
onChange={(val) => {
tarifdanlayanan.create.form.tarif = val.target.value
}}
/>
<TextInput
mb={10}
label="Layanan"
placeholder='masukkan layanan'
onChange={(val) => {
tarifdanlayanan.create.form.layanan = val.target.value
}}
/>
</Stack>
</Paper>
</Box>
}
export default TarifDanLayanan;

View File

@@ -1,101 +0,0 @@
/* 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, Paper, Skeleton, 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} gap={"xs"}>
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
<Title order={4}>Tambah Grafik Hasil Kepuasan</Title>
<Stack gap={"xs"}>
<TextInput
label="Label"
placeholder='Masukkan label yang diinginkan'
value={grafikkepuasan.create.form.label}
onChange={(val) => {
grafikkepuasan.create.form.label = val.currentTarget.value
}}
/>
<TextInput
label="Jumlah Penderita"
type='number'
placeholder='Masukkan jumlah penderita'
value={grafikkepuasan.create.form.jumlah}
onChange={(val) => {
grafikkepuasan.create.form.jumlah = val.currentTarget.value
}}
/>
<Group>
<Button bg={colors['blue-button']} mt={10}
onClick={async () => {
await grafikkepuasan.create.create();
await grafikkepuasan.findMany.load();
if (grafikkepuasan.findMany.data) {
setChartData(grafikkepuasan.findMany.data);
}
}}
>Submit</Button>
</Group>
</Stack>
</Paper>
{!chartData ? (
<Box style={{ width: '100%', minWidth: 300, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title py={15} order={4}>Data Hasil Kepuasan</Title>
<Skeleton h={400} />
</Paper>
</Box>
) : (
<Box style={{ width: '100%', minWidth: 300, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title py={15} order={4}>Data Hasil Kepuasan</Title>
<BarChart
width={isMobile ? 250 : isTablet ? 300 : 350}
height={380}
data={chartData}
>
<XAxis dataKey="label" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="jumlah" fill={colors['blue-button']} name="Jumlah" />
</BarChart>
</Paper>
</Box>
)}
</Stack>
);
}
export default GrafikHasilKepuasan;

View File

@@ -1,26 +0,0 @@
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import { Box, Paper, Stack, Text } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import colors from '@/con/colors';
function DeskripsiKegiatan() {
const deskripsiKegiatanState = useProxy(stateJadwalKegiatan.deskripsiKegiatan)
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Text pt={10} fw={"bold"}>Deskripsi Kegiatan</Text>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
deskripsiKegiatanState.create.form.deskripsi = val
}}
/>
</Stack>
</Paper>
</Box>
);
}
export default DeskripsiKegiatan;

View File

@@ -1,28 +0,0 @@
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import { Box, Paper, Stack, Text } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import colors from '@/con/colors';
function DokumenYangDiperlukan() {
const dokumenDiperlukan = useProxy(stateJadwalKegiatan.dokumenjadwalkegiatan)
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Text pt={10} fw={"bold"}>Dokumen Yang Diperlukan</Text>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
dokumenDiperlukan.create.form.content = val;
}}
/>
</Stack>
</Paper>
</Box>
);
}
export default DokumenYangDiperlukan;

View File

@@ -1,47 +0,0 @@
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import colors from '@/con/colors';
import { Box, Paper, Stack, TextInput, Title } from '@mantine/core';
import { useProxy } from 'valtio/utils';
function InformasiKegiatan() {
const informasiKegiatanState = useProxy(stateJadwalKegiatan.informasiKegiatan)
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={4}>Informasi Kegiatan</Title>
<TextInput
label="Nama Kegiatan"
placeholder="Masukkan nama kegiatan"
onChange={(val) => {
informasiKegiatanState.create.form.name = val.target.value
}}
/>
<TextInput
label="Tanggal"
placeholder="Masukkan tanggal kegiatan"
onChange={(val) => {
informasiKegiatanState.create.form.tanggal = val.target.value
}}
/>
<TextInput
label="Waktu"
placeholder="Masukkan waktu kegiatan"
onChange={(val) => {
informasiKegiatanState.create.form.waktu = val.target.value
}}
/>
<TextInput
label="Lokasi"
placeholder="Masukkan lokasi kegiatan"
onChange={(val) => {
informasiKegiatanState.create.form.lokasi = val.target.value
}}
/>
</Stack>
</Paper>
</Box>
);
}
export default InformasiKegiatan;

View File

@@ -1,26 +0,0 @@
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import { Box, Paper, Stack, Text } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import colors from '@/con/colors';
function LayananTersedia() {
const layananTersediaState = useProxy(stateJadwalKegiatan.layanantersedia)
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Text pt={10} fw={"bold"}>Layanan Yang Tersedia</Text>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
layananTersediaState.create.form.content = val;
}}
/>
</Stack>
</Paper>
</Box>
);
}
export default LayananTersedia;

View File

@@ -1,192 +0,0 @@
'use client'
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import { Box, Button, Paper, SimpleGrid, Skeleton, Stack, Text, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
import DeskripsiKegiatan from './deskripsi_kegiatan/page';
import DokumenYangDiperlukan from './dokumen_yang_diperlukan/page';
import InformasiKegiatan from './informasi_kegiatan/page';
import LayananTersedia from './layanan_yang_tersedia/page';
import Pendaftaran from './pendaftaran/page';
import SyaratDanKetentuan from './syarat_dan_ketentuan/page';
import colors from '@/con/colors';
function JadwalKegiatan() {
const allState = useProxy(stateJadwalKegiatan)
const submitAllForms = () => {
if (allState.informasiKegiatan.create.form.name &&
allState.informasiKegiatan.create.form.tanggal &&
allState.informasiKegiatan.create.form.waktu &&
allState.informasiKegiatan.create.form.lokasi) {
allState.informasiKegiatan.create.create()
}
if (allState.deskripsiKegiatan.create.form.deskripsi) {
allState.deskripsiKegiatan.create.create();
}
if (allState.layanantersedia.create.form.content) {
allState.layanantersedia.create.create();
}
if (allState.syaratketentuan.create.form.content) {
allState.syaratketentuan.create.create();
}
if (allState.dokumenjadwalkegiatan.create.form.content) {
allState.dokumenjadwalkegiatan.create.create();
}
if (allState.pendaftaranjadwal.create.form.name &&
allState.pendaftaranjadwal.create.form.tanggal &&
allState.pendaftaranjadwal.create.form.namaOrangtua &&
allState.pendaftaranjadwal.create.form.nomor &&
allState.pendaftaranjadwal.create.form.alamat &&
allState.pendaftaranjadwal.create.form.catatan
) {
allState.pendaftaranjadwal.create.create();
}
}
return (
<Stack py={10}>
<SimpleGrid cols={{
base: 1, md: 2
}}>
<Box>
<Stack gap={"xs"}>
<Title order={4}>Tambah Jadwal Kegiatan</Title>
<InformasiKegiatan />
<DeskripsiKegiatan />
<LayananTersedia />
<SyaratDanKetentuan />
<DokumenYangDiperlukan />
<Pendaftaran />
<Button mt={10} onClick={submitAllForms}>
Submit
</Button>
</Stack>
</Box>
<Box>
<Stack gap={"xs"}>
<Title order={4}>Data Jadwal Kegiatan</Title>
<AllList />
</Stack>
</Box>
</SimpleGrid>
</Stack>
);
}
function AllList() {
const allList = useProxy(stateJadwalKegiatan)
useShallowEffect(() => {
allList.informasiKegiatan.findMany.load()
allList.deskripsiKegiatan.findMany.load()
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.pendaftaranjadwal.findMany.data
if (isLoading) {
return (
<Stack>
<Title order={4}>Informasi Kegiatan</Title>
{Array.from({ length: 10 }).map((_, k) => <Skeleton key={k} h={40} />)}
</Stack>
);
}
return (
<Stack gap={"xs"}>
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Informasi Kegiatan</Title>
{allList.informasiKegiatan.findMany.data?.map((item) => {
return (
<Box key={item.id}>
<Text>{item.name}</Text>
<Text>{item.tanggal}</Text>
<Text>{item.waktu}</Text>
<Text>{item.lokasi}</Text>
</Box>
)
})}
</Paper>
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Deskripsi Kegiatan</Title>
{allList.deskripsiKegiatan.findMany.data?.map((item) => {
return (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
</Box>
)
})}
</Paper>
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Layanan Yang Tersedia</Title>
{allList.layanantersedia.findMany.data?.map((item) => {
return (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{ __html: item.content }} />
</Box>
)
})}
</Paper>
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Syarat dan Ketentuan</Title>
{allList.syaratketentuan.findMany.data?.map((item) => {
return (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{ __html: item.content }} />
</Box>
)
})}
</Paper>
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Dokumen Yang Diperlukan</Title>
{allList.dokumenjadwalkegiatan.findMany.data?.map((item) => {
return (
<Box key={item.id}>
<Text dangerouslySetInnerHTML={{ __html: item.content }} />
</Box>
)
})}
</Paper>
<Paper bg={colors['white-1']} p={'md'}>
<Title order={4}>Pendaftaran</Title>
{allList.pendaftaranjadwal.findMany.data?.map((item) => {
return (
<Box key={item.id}>
<Text>{item.name}</Text>
<Text>{item.tanggal}</Text>
<Text>{item.namaOrangtua}</Text>
<Text>{item.nomor}</Text>
<Text>{item.alamat}</Text>
<Text>{item.catatan}</Text>
</Box>
)
})}
</Paper>
</Stack>
)
}
export default JadwalKegiatan;

View File

@@ -1,63 +0,0 @@
'use client'
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import colors from '@/con/colors';
import { Box, Paper, Stack, Text, Textarea, TextInput } from '@mantine/core';
import { useProxy } from 'valtio/utils';
function Pendaftaran() {
const pendaftaran = useProxy(stateJadwalKegiatan.pendaftaranjadwal)
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Text pt={10} fw={"bold"}>Pendaftaran</Text>
<TextInput
label='Nama Balita'
placeholder='Masukkan nama balita'
onChange={(val) => {
pendaftaran.create.form.name = val.target.value
}}
/>
<TextInput
label='Tanggal'
placeholder='Masukkan tanggal'
onChange={(val) => {
pendaftaran.create.form.tanggal = val.target.value
}}
/>
<TextInput
label='Nama Orang Tua / Wali'
placeholder='Masukkan nama orang tua / wali'
onChange={(val) => {
pendaftaran.create.form.namaOrangtua = val.target.value
}}
/>
<TextInput
label='No. Telepon'
placeholder='Masukkan no. telepon'
onChange={(val) => {
pendaftaran.create.form.nomor = val.target.value
}}
/>
<TextInput
label='Alamat'
placeholder='Masukkan alamat'
onChange={(val) => {
pendaftaran.create.form.alamat = val.target.value
}}
/>
<Textarea
label='Catatan Khusus (Opsional)'
placeholder='Masukkan catatan khusus'
onChange={(val) => {
pendaftaran.create.form.catatan = val.target.value
}}
/>
</Stack>
</Paper>
</Box>
);
}
export default Pendaftaran;

View File

@@ -1,26 +0,0 @@
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import { Box, Paper, Stack, Text } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import colors from '@/con/colors';
function SyaratDanKetentuan() {
const syaratKetentuan = useProxy(stateJadwalKegiatan.syaratketentuan)
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Text pt={10} fw={"bold"}>Syarat dan Ketentuan</Text>
<KesehatanEditor
showSubmit={false}
onChange={(val) => {
syaratKetentuan.create.form.content = val;
}}
/>
</Stack>
</Paper>
</Box>
);
}
export default SyaratDanKetentuan;

View File

@@ -1,124 +0,0 @@
'use client'
/* eslint-disable @typescript-eslint/no-explicit-any */
import statePersentase from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Skeleton, 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}>
{/* Form Input */}
<Box>
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
<Title order={4}>Tambah Persentase Data Kelahiran & Kematian</Title>
<Stack gap={"xs"}>
<TextInput
label="Tahun"
type="number"
value={persentase.create.form.tahun}
placeholder="Masukkan tahun"
onChange={(val) => {
persentase.create.form.tahun = val.currentTarget.value;
}}
/>
<TextInput
label="Kematian Kasar"
type="number"
value={persentase.create.form.kematianKasar}
placeholder="Masukkan kematian kasar"
onChange={(val) => {
persentase.create.form.kematianKasar = val.currentTarget.value;
}}
/>
<TextInput
label="Kematian Bayi"
type="number"
value={persentase.create.form.kematianBayi}
placeholder="Masukkan kematian bayi"
onChange={(val) => {
persentase.create.form.kematianBayi = val.currentTarget.value;
}}
/>
<TextInput
label="Kelahiran Kasar"
type="number"
value={persentase.create.form.kelahiranKasar}
placeholder="Masukkan kelahiran kasar"
onChange={(val) => {
persentase.create.form.kelahiranKasar = val.currentTarget.value;
}}
/>
<Group>
<Button
bg={colors['blue-button']}
mt={10}
onClick={async () => {
await persentase.create.create();
await persentase.findMany.load();
if (persentase.findMany.data) {
setChartData(persentase.findMany.data);
}
}}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
{/* Chart */}
{!mounted && !chartData ? (
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title pb={10} order={3}>Data Kelahiran & Kematian</Title>
<Skeleton h={400} />
</Paper>
</Box>
) : (
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title pb={10} order={4}>Data Kelahiran & Kematian</Title>
{mounted && chartData.length > 0 && (
<BarChart width={isMobile ? 450 : isTablet ? 500 : 550} height={350} 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>
)}
</Paper>
</Box>
)}
</Stack>
);
}
export default PersentaseDataKelahiranKematian;

View File

@@ -0,0 +1,374 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
interface ArtikelKesehatanFormBase {
title: string;
content: string;
introduction: {
content: string;
};
symptom: {
title: string;
content: string;
};
prevention: {
title: string;
content: string;
};
firstAid: {
title: string;
content: string;
};
mythVsFact: {
title: string;
mitos: string;
fakta: string;
};
doctorSign: {
content: string;
};
}
function EditArtikelKesehatan() {
const stateArtikelKesehatan = useProxy(artikelKesehatanState);
const router = useRouter();
const params = useParams();
const [formData, setFormData] = useState<ArtikelKesehatanFormBase>({
title: stateArtikelKesehatan.edit.form.title || '',
content: stateArtikelKesehatan.edit.form.content || '',
introduction: {
content: stateArtikelKesehatan.edit.form.introduction?.content || '',
},
symptom: {
title: stateArtikelKesehatan.edit.form.symptom?.title || '',
content: stateArtikelKesehatan.edit.form.symptom?.content || '',
},
prevention: {
title: stateArtikelKesehatan.edit.form.prevention?.title || '',
content: stateArtikelKesehatan.edit.form.prevention?.content || '',
},
firstAid: {
title: stateArtikelKesehatan.edit.form.firstAid?.title || '',
content: stateArtikelKesehatan.edit.form.firstAid?.content || '',
},
mythVsFact: {
title: stateArtikelKesehatan.edit.form.mythVsFact?.title || '',
mitos: stateArtikelKesehatan.edit.form.mythVsFact?.mitos || '',
fakta: stateArtikelKesehatan.edit.form.mythVsFact?.fakta || '',
},
doctorSign: {
content: stateArtikelKesehatan.edit.form.doctorSign?.content || '',
},
});
useEffect(() => {
const loadArtikelKesehatan = async () => {
const id = params?.id as string;
if (!id) return;
try {
await stateArtikelKesehatan.edit.load(id);
const { form } = stateArtikelKesehatan.edit;
if (form) {
setFormData({
title: form.title,
content: form.content,
introduction: {
content: form.introduction?.content || '',
},
symptom: {
title: form.symptom?.title || '',
content: form.symptom?.content || '',
},
prevention: {
title: form.prevention?.title || '',
content: form.prevention?.content || '',
},
firstAid: {
title: form.firstAid?.title || '',
content: form.firstAid?.content || '',
},
mythVsFact: {
title: form.mythVsFact?.title || '',
mitos: form.mythVsFact?.mitos || '',
fakta: form.mythVsFact?.fakta || '',
},
doctorSign: {
content: form.doctorSign?.content || '',
},
});
}
} catch (error) {
console.error("Error loading artikel kesehatan:", error);
toast.error("Gagal memuat data artikel kesehatan");
}
};
loadArtikelKesehatan();
}, [params?.id]);
const handleSubmit = async () => {
try {
stateArtikelKesehatan.edit.form = {
...stateArtikelKesehatan.edit.form,
title: formData.title,
content: formData.content,
introduction: {
content: formData.introduction.content,
},
symptom: {
title: formData.symptom.title,
content: formData.symptom.content,
},
prevention: {
title: formData.prevention.title,
content: formData.prevention.content,
},
firstAid: {
title: formData.firstAid.title,
content: formData.firstAid.content,
},
mythVsFact: {
title: formData.mythVsFact.title,
mitos: formData.mythVsFact.mitos,
fakta: formData.mythVsFact.fakta,
},
doctorSign: {
content: formData.doctorSign.content,
},
};
const success = await stateArtikelKesehatan.edit.submit();
if (success) {
toast.success("Artikel kesehatan berhasil diperbarui!");
router.push("/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan");
}
} catch (error) {
console.error("Error updating artikel kesehatan:", error);
toast.error(error instanceof Error ? error.message : "Gagal memperbarui data artikel kesehatan");
}
};
return (
<Box>
<Box mb={10}>
<Button onClick={() => router.back()} variant="subtle" color="blue">
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Edit Artikel Kesehatan</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
value={formData.title}
onChange={(e) => {
setFormData(prev => ({
...prev,
title: e.target.value
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
value={formData.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
content: e.target.value
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Pendahuluan</Text>}
placeholder="masukkan pendahuluan"
value={formData.introduction.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
introduction: {
...prev.introduction,
content: e.target.value
}
}));
}}
/>
<Box>
<Text fz="md" fw="bold">Gejala</Text>
<Stack gap="xs">
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul gejala penyakit"
value={formData.symptom.title}
onChange={(e) => {
setFormData(prev => ({
...prev,
symptom: {
...prev.symptom,
title: e.target.value
}
}));
}}
/>
<Box>
<Text fz="sm" fw="bold">Deskripsi Gejala</Text>
<EditEditor
value={formData.symptom.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
symptom: {
...prev.symptom,
content: e
}
}));
}}
/>
</Box>
</Stack>
</Box>
<Box>
<Text fz="md" fw="bold">Pencegahan</Text>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
value={formData.prevention.title}
onChange={(e) => {
setFormData(prev => ({
...prev,
prevention: {
...prev.prevention,
title: e.target.value
}
}));
}}
/>
<EditEditor
value={formData.prevention.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
prevention: {
...prev.prevention,
content: e
}
}));
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Pertolongan Pertama</Text>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
value={formData.firstAid.title}
onChange={(e) => {
setFormData(prev => ({
...prev,
firstAid: {
...prev.firstAid,
title: e.target.value
}
}));
}}
/>
<EditEditor
value={formData.firstAid.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
firstAid: {
...prev.firstAid,
content: e
}
}));
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Mitos dan Fakta</Text>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
value={formData.mythVsFact.title}
onChange={(e) => {
setFormData(prev => ({
...prev,
mythVsFact: {
...prev.mythVsFact,
title: e.target.value
}
}));
}}
/>
<Box>
<Text>
Mitos
</Text>
<EditEditor
value={formData.mythVsFact.mitos}
onChange={(e) => {
setFormData(prev => ({
...prev,
mythVsFact: {
...prev.mythVsFact,
mitos: e
}
}));
}}
/>
</Box>
<Box>
<Text>
Fakta
</Text>
<EditEditor
value={formData.mythVsFact.fakta}
onChange={(e) => {
setFormData(prev => ({
...prev,
mythVsFact: {
...prev.mythVsFact,
fakta: e
}
}));
}}
/>
</Box>
</Box>
<Box>
<Text fz="md" fw="bold">Kapan Harus Ke Dokter</Text>
<EditEditor
value={formData.doctorSign.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
doctorSign: {
...prev.doctorSign,
content: e
}
}));
}}
/>
</Box>
<Button onClick={handleSubmit} bg={colors['blue-button']}>
Simpan
</Button>
</Stack>
</Paper>
</Box>
);
}
export default EditArtikelKesehatan;

View File

@@ -0,0 +1,143 @@
'use client'
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import colors from '@/con/colors';
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
function DetailArtikelKesehatan() {
const params = useParams()
const router = useRouter();
const stateArtikelKesehatan = useProxy(artikelKesehatanState)
const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState<string | null>(null)
useShallowEffect(() => {
stateArtikelKesehatan.findUnique.load(params?.id as string)
}, [])
const handleHapus = () => {
if (selectedId) {
stateArtikelKesehatan.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
router.push("/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan")
}
}
if (!stateArtikelKesehatan.findUnique.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
<Stack>
<Text fz={"xl"} fw={"bold"}>Detail Artikel Kesehatan</Text>
{stateArtikelKesehatan.findUnique.data ? (
<Paper bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fz={"lg"} fw={"bold"}>Judul</Text>
<Text fz={"md"}>{stateArtikelKesehatan.findUnique.data.title}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateArtikelKesehatan.findUnique.data.content }} />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Pendahuluan</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateArtikelKesehatan.findUnique.data.introduction.content }} />
</Box>
<Box>
<Stack gap={"xs"}>
<Text fz={"lg"} fw={"bold"}>Gejala</Text>
<Text fz={"md"} fw={"bold"}>Judul Gejala</Text>
<Text fz={"md"}>{stateArtikelKesehatan.findUnique.data.symptom.title}</Text>
<Text fz={"md"} fw={"bold"}>Deskripsi Gejala</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateArtikelKesehatan.findUnique.data.symptom.content }} />
</Stack>
</Box>
<Box>
<Stack gap={"xs"}>
<Text fz={"lg"} fw={"bold"}>Pencegahan</Text>
<Text fz={"md"} fw={"bold"}>Judul Pencegahan</Text>
<Text fz={"md"}>{stateArtikelKesehatan.findUnique.data.prevention.title}</Text>
<Text fz={"md"} fw={"bold"}>Deskripsi Pencegahan</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateArtikelKesehatan.findUnique.data.prevention.content }} />
</Stack>
</Box>
<Box>
<Stack gap={"xs"}>
<Text fz={"lg"} fw={"bold"}>Pertolongan Pertama</Text>
<Text fz={"md"} fw={"bold"}>Judul Pertolongan Pertama</Text>
<Text fz={"md"}>{stateArtikelKesehatan.findUnique.data.firstaid.title}</Text>
<Text fz={"md"} fw={"bold"}>Deskripsi Pertolongan Pertama</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateArtikelKesehatan.findUnique.data.firstaid.content }} />
</Stack>
</Box>
<Box>
<Stack gap={"xs"}>
<Text fz={"lg"} fw={"bold"}>Mitos dan Fakta</Text>
<Text fz={"md"} fw={"bold"}>Judul Mitos dan Fakta</Text>
<Text fz={"md"}>{stateArtikelKesehatan.findUnique.data.mythvsfact.title}</Text>
<Text fz={"md"} fw={"bold"}>Deskripsi Mitos</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateArtikelKesehatan.findUnique.data.mythvsfact.mitos }} />
<Text fz={"md"} fw={"bold"}>Deskripsi Fakta</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateArtikelKesehatan.findUnique.data.mythvsfact.fakta }} />
</Stack>
</Box>
<Box>
<Stack gap={"xs"}>
<Text fz={"lg"} fw={"bold"}>Kapan Harus ke Dokter</Text>
<Text fz={"md"} fw={"bold"}>Deskripsi Kapan Harus ke Dokter</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateArtikelKesehatan.findUnique.data.doctorsign.content }} />
</Stack>
</Box>
<Box>
<Flex gap={"xs"}>
<Button color="red" onClick={() => {
if (stateArtikelKesehatan.findUnique.data) {
setSelectedId(stateArtikelKesehatan.findUnique.data.id)
setModalHapus(true)
}
}}>
<IconX size={20} />
</Button>
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan/${stateArtikelKesehatan.findUnique.data?.id}/edit`)} color="green">
<IconEdit size={20} />
</Button>
</Flex>
</Box>
</Stack>
</Paper>
) : null}
</Stack>
</Paper>
{/* Modal Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text="Apakah anda yakin ingin menghapus artikel kesehatan ini?"
/>
</Box>
);
}
export default DetailArtikelKesehatan;

View File

@@ -0,0 +1,203 @@
'use client'
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
import artikelKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
function CreateArtikelKesehatan() {
const stateArtikelKesehatan = useProxy(artikelKesehatanState)
const router = useRouter();
const resetForm = () => {
stateArtikelKesehatan.create.form = {
title: "",
content: "",
introduction: {
content: "",
},
symptom: {
title: "",
content: "",
},
prevention: {
title: "",
content: "",
},
firstAid: {
title: "",
content: "",
},
mythVsFact: {
title: "",
mitos: "",
fakta: "",
},
doctorSign: {
content: ""
}
};
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await stateArtikelKesehatan.create.submit();
toast.success("Data berhasil disimpan");
resetForm();
// After successful submission, redirect to the list page
router.push('/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan');
}
return (
<Box component="form" onSubmit={handleSubmit}>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Artikel Kesehatan</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
value={stateArtikelKesehatan.create.form.title}
onChange={(e) => {
stateArtikelKesehatan.create.form.title = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
value={stateArtikelKesehatan.create.form.content}
onChange={(e) => {
stateArtikelKesehatan.create.form.content = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Pendahuluan</Text>}
placeholder="masukkan pendahuluan"
value={stateArtikelKesehatan.create.form.introduction.content}
onChange={(e) => {
stateArtikelKesehatan.create.form.introduction.content = e.target.value;
}}
/>
<Box>
<Text fz="md" fw="bold">Gejala</Text>
<Stack gap="xs">
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul gejala penyakit"
value={stateArtikelKesehatan.create.form.symptom.title}
onChange={(e) => {
stateArtikelKesehatan.create.form.symptom.title = e.target.value;
}}
/>
<Box>
<Text fz="sm" fw="bold">Deskripsi Gejala</Text>
<CreateEditor
value={stateArtikelKesehatan.create.form.symptom.content}
onChange={(e) => {
stateArtikelKesehatan.create.form.symptom.content = e;
}}
/>
</Box>
</Stack>
</Box>
<Box>
<Text fz="md" fw="bold">Pencegahan</Text>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
value={stateArtikelKesehatan.create.form.prevention.title}
onChange={(e) => {
stateArtikelKesehatan.create.form.prevention.title = e.target.value;
}}
/>
<CreateEditor
value={stateArtikelKesehatan.create.form.prevention.content}
onChange={(e) => {
stateArtikelKesehatan.create.form.prevention.content = e;
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Pertolongan Pertama</Text>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
value={stateArtikelKesehatan.create.form.firstAid.title}
onChange={(e) => {
stateArtikelKesehatan.create.form.firstAid.title = e.target.value;
}}
/>
<CreateEditor
value={stateArtikelKesehatan.create.form.firstAid.content}
onChange={(e) => {
stateArtikelKesehatan.create.form.firstAid.content = e;
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Mitos dan Fakta</Text>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
value={stateArtikelKesehatan.create.form.mythVsFact.title}
onChange={(e) => {
stateArtikelKesehatan.create.form.mythVsFact.title = e.target.value;
}}
/>
<Box>
<Text>
Mitos
</Text>
<CreateEditor
value={stateArtikelKesehatan.create.form.mythVsFact.mitos}
onChange={(e) => {
stateArtikelKesehatan.create.form.mythVsFact.mitos = e;
}}
/>
</Box>
<Box>
<Text>
Fakta
</Text>
<CreateEditor
value={stateArtikelKesehatan.create.form.mythVsFact.fakta}
onChange={(e) => {
stateArtikelKesehatan.create.form.mythVsFact.fakta = e;
}}
/>
</Box>
</Box>
<Box>
<Text fz="md" fw="bold">Kapan Harus Ke Dokter</Text>
<CreateEditor
value={stateArtikelKesehatan.create.form.doctorSign.content}
onChange={(e) => {
stateArtikelKesehatan.create.form.doctorSign.content = e;
}}
/>
</Box>
<Button onClick={handleSubmit} bg={colors['blue-button']}>
Simpan
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreateArtikelKesehatan;

View File

@@ -0,0 +1,79 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import artikelKesehatanState from '../../../_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
function ArtikelKesehatan() {
return (
<Box>
<HeaderSearch
title='Artikel Kesehatan'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListArtikelKesehatan/>
</Box>
);
}
function ListArtikelKesehatan() {
const stateArtikelKesehatan = useProxy(artikelKesehatanState)
const router = useRouter();
useShallowEffect(() => {
stateArtikelKesehatan.findMany.load()
}, [])
if (!stateArtikelKesehatan.findMany.data) {
return (
<Box py={10}>
<Skeleton h={500}/>
</Box>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Artikel Kesehatan'
href='/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Content</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{stateArtikelKesehatan.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.title}</TableTd>
<TableTd>{item.content}</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default ArtikelKesehatan;

View File

@@ -0,0 +1,359 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
interface FasilitasKesehatanFormBase {
name: string;
informasiUmum: {
fasilitas: string;
alamat: string;
jamOperasional: string;
};
layananUnggulan: {
content: string;
};
dokterdanTenagaMedis: {
name: string;
specialist: string;
jadwal: string;
};
fasilitasPendukung: {
content: string;
};
prosedurPendaftaran: {
content: string;
};
tarifDanLayanan: {
layanan: string;
tarif: string;
};
}
function EditFasilitasKesehatan() {
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState);
const router = useRouter();
const params = useParams();
const [formData, setFormData] = useState<FasilitasKesehatanFormBase>({
name: stateFasilitasKesehatan.edit.form.name || '',
informasiUmum: {
fasilitas: stateFasilitasKesehatan.edit.form.informasiUmum?.fasilitas || '',
alamat: stateFasilitasKesehatan.edit.form.informasiUmum?.alamat || '',
jamOperasional: stateFasilitasKesehatan.edit.form.informasiUmum?.jamOperasional || '',
},
layananUnggulan: {
content: stateFasilitasKesehatan.edit.form.layananUnggulan?.content || '',
},
dokterdanTenagaMedis: {
name: stateFasilitasKesehatan.edit.form.dokterdanTenagaMedis?.name || '',
specialist: stateFasilitasKesehatan.edit.form.dokterdanTenagaMedis?.specialist || '',
jadwal: stateFasilitasKesehatan.edit.form.dokterdanTenagaMedis?.jadwal || '',
},
fasilitasPendukung: {
content: stateFasilitasKesehatan.edit.form.fasilitasPendukung?.content || '',
},
prosedurPendaftaran: {
content: stateFasilitasKesehatan.edit.form.prosedurPendaftaran?.content || '',
},
tarifDanLayanan: {
layanan: stateFasilitasKesehatan.edit.form.tarifDanLayanan?.layanan || '',
tarif: stateFasilitasKesehatan.edit.form.tarifDanLayanan?.tarif || '',
},
});
useEffect(() => {
const loadFasilitasKesehatan = async () => {
const id = params?.id as string;
if (!id) return;
try {
await stateFasilitasKesehatan.edit.load(id);
const { form } = stateFasilitasKesehatan.edit;
if (form) {
setFormData({
name: form.name,
informasiUmum: {
fasilitas: form.informasiUmum?.fasilitas || '',
alamat: form.informasiUmum?.alamat || '',
jamOperasional: form.informasiUmum?.jamOperasional || '',
},
layananUnggulan: {
content: form.layananUnggulan?.content || '',
},
dokterdanTenagaMedis: {
name: form.dokterdanTenagaMedis?.name || '',
specialist: form.dokterdanTenagaMedis?.specialist || '',
jadwal: form.dokterdanTenagaMedis?.jadwal || '',
},
fasilitasPendukung: {
content: form.fasilitasPendukung?.content || '',
},
prosedurPendaftaran: {
content: form.prosedurPendaftaran?.content || '',
},
tarifDanLayanan: {
layanan: form.tarifDanLayanan?.layanan || '',
tarif: form.tarifDanLayanan?.tarif || '',
},
});
}
} catch (error) {
console.error("Error loading fasilitas kesehatan:", error);
toast.error("Gagal memuat data fasilitas kesehatan");
}
};
loadFasilitasKesehatan();
}, [params?.id]);
const handleSubmit = async () => {
try {
stateFasilitasKesehatan.edit.form = {
...stateFasilitasKesehatan.edit.form,
name: formData.name,
informasiUmum: {
fasilitas: formData.informasiUmum.fasilitas,
alamat: formData.informasiUmum.alamat,
jamOperasional: formData.informasiUmum.jamOperasional,
},
layananUnggulan: {
content: formData.layananUnggulan.content,
},
dokterdanTenagaMedis: {
name: formData.dokterdanTenagaMedis.name,
specialist: formData.dokterdanTenagaMedis.specialist,
jadwal: formData.dokterdanTenagaMedis.jadwal,
},
fasilitasPendukung: {
content: formData.fasilitasPendukung.content,
},
prosedurPendaftaran: {
content: formData.prosedurPendaftaran.content,
},
tarifDanLayanan: {
layanan: formData.tarifDanLayanan.layanan,
tarif: formData.tarifDanLayanan.tarif,
},
};
const success = await stateFasilitasKesehatan.edit.submit();
if (success) {
toast.success("Fasilitas kesehatan berhasil diperbarui!");
router.push("/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan");
}
} catch (error) {
console.error("Error updating fasilitas kesehatan:", error);
toast.error(error instanceof Error ? error.message : "Gagal memperbarui data fasilitas kesehatan");
}
};
return (
<Box>
<Box mb={10}>
<Button onClick={() => router.back()} variant="subtle" color="blue">
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Stack gap="xs">
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Edit Fasilitas Kesehatan</Title>
<TextInput
label={<Text fz="sm" fw="bold">Nama Fasilitas Kesehatan</Text>}
placeholder="masukkan nama fasilitas kesehatan"
value={formData.name}
onChange={(e) => {
setFormData(prev => ({
...prev,
name: e.target.value
}));
}}
/>
<Box>
<Text fz="md" fw="bold">Informasi Umum</Text>
<TextInput
label={<Text fz="sm" fw="bold">Fasilitas</Text>}
placeholder="masukkan fasilitas"
value={formData.informasiUmum.fasilitas}
onChange={(e) => {
setFormData(prev => ({
...prev,
informasiUmum: {
...prev.informasiUmum,
fasilitas: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Alamat</Text>}
placeholder="masukkan alamat"
value={formData.informasiUmum.alamat}
onChange={(e) => {
setFormData(prev => ({
...prev,
informasiUmum: {
...prev.informasiUmum,
alamat: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Jam Operasional</Text>}
placeholder="masukkan jam operasional"
value={formData.informasiUmum.jamOperasional}
onChange={(e) => {
setFormData(prev => ({
...prev,
informasiUmum: {
...prev.informasiUmum,
jamOperasional: e.target.value
}
}));
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Layanan Unggulan</Text>
<EditEditor
value={formData.layananUnggulan.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
layananUnggulan: {
...prev.layananUnggulan,
content: e
}
}));
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Dokter dan Tenaga Medis</Text>
<TextInput
label={<Text fz="sm" fw="bold">Nama Dokter</Text>}
placeholder="masukkan nama dokter"
value={formData.dokterdanTenagaMedis.name}
onChange={(e) => {
setFormData(prev => ({
...prev,
dokterdanTenagaMedis: {
...prev.dokterdanTenagaMedis,
name: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Specialist</Text>}
placeholder="masukkan specialist"
value={formData.dokterdanTenagaMedis.specialist}
onChange={(e) => {
setFormData(prev => ({
...prev,
dokterdanTenagaMedis: {
...prev.dokterdanTenagaMedis,
specialist: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Jadwal</Text>}
placeholder="masukkan jadwal"
value={formData.dokterdanTenagaMedis.jadwal}
onChange={(e) => {
setFormData(prev => ({
...prev,
dokterdanTenagaMedis: {
...prev.dokterdanTenagaMedis,
jadwal: e.target.value
}
}));
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Fasilitas Pendukung</Text>
<EditEditor
value={formData.fasilitasPendukung.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
fasilitasPendukung: {
...prev.fasilitasPendukung,
content: e
}
}));
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Prosedur Pendaftaran</Text>
<EditEditor
value={formData.prosedurPendaftaran.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
prosedurPendaftaran: {
...prev.prosedurPendaftaran,
content: e
}
}));
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Tarif dan Layanan</Text>
<TextInput
label={<Text fz="sm" fw="bold">Tarif</Text>}
placeholder="masukkan tarif"
value={formData.tarifDanLayanan.tarif}
onChange={(e) => {
setFormData(prev => ({
...prev,
tarifDanLayanan: {
...prev.tarifDanLayanan,
tarif: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Layanan</Text>}
placeholder="masukkan layanan"
value={formData.tarifDanLayanan.layanan}
onChange={(e) => {
setFormData(prev => ({
...prev,
tarifDanLayanan: {
...prev.tarifDanLayanan,
layanan: e.target.value
}
}));
}}
/>
</Box>
<Button
onClick={handleSubmit}
bg={colors['blue-button']}
loading={stateFasilitasKesehatan.edit.loading}
>
Simpan
</Button>
</Stack>
</Paper>
</Stack>
</Box>
);
}
export default EditFasilitasKesehatan;

View File

@@ -0,0 +1,127 @@
'use client'
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
function DetailFasilitasKesehatan() {
const params = useParams()
const router = useRouter();
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState)
const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState<string | null>(null)
useShallowEffect(() => {
stateFasilitasKesehatan.findUnique.load(params?.id as string)
}, [])
const handleHapus = () => {
if (selectedId) {
stateFasilitasKesehatan.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
router.push("/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan")
}
}
if (!stateFasilitasKesehatan.findUnique.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
<Stack>
<Text fz={"xl"} fw={"bold"}>Detail Fasilitas Kesehatan</Text>
{stateFasilitasKesehatan.findUnique.data ? (
<Paper bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fz={"lg"} fw={"bold"}>Nama Fasilitas Kesehatan</Text>
<Text fz={"md"}>{stateFasilitasKesehatan.findUnique.data.name}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Informasi Umum</Text>
<Text fz={"md"} fw={"bold"}>Fasilitas</Text>
<Text fz={"md"}>{stateFasilitasKesehatan.findUnique.data.informasiumum.fasilitas}</Text>
<Text fz={"md"} fw={"bold"}>Alamat</Text>
<Text fz={"md"}>{stateFasilitasKesehatan.findUnique.data.informasiumum.alamat}</Text>
<Text fz={"md"} fw={"bold"}>Jam Operasional</Text>
<Text fz={"md"}>{stateFasilitasKesehatan.findUnique.data.informasiumum.jamOperasional}</Text>
</Box>
<Box>
<Text fz={"md"} fw={"bold"}>Layanan Unggulan</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateFasilitasKesehatan.findUnique.data.layananunggulan.content }} />
</Box>
<Box>
<Text fz={"md"} fw={"bold"}>Dokter dan Tenaga Medis</Text>
<Text fz={"md"} fw={"bold"}>Nama Dokter</Text>
<Text fz={"md"}>{stateFasilitasKesehatan.findUnique.data.dokterdantenagamedis.name}</Text>
<Text fz={"md"} fw={"bold"}>Specialist</Text>
<Text fz={"md"}>{stateFasilitasKesehatan.findUnique.data.dokterdantenagamedis.specialist}</Text>
<Text fz={"md"} fw={"bold"}>Jadwal</Text>
<Text fz={"md"}>{stateFasilitasKesehatan.findUnique.data.dokterdantenagamedis.jadwal}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Fasilitas Pendukung</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateFasilitasKesehatan.findUnique.data.fasilitaspendukung.content }} />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Prosedur Pendaftaran</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateFasilitasKesehatan.findUnique.data.prosedurpendaftaran.content }} />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Tarif dan Layanan</Text>
<Text fz={"md"} fw={"bold"}>Layanan</Text>
<Text fz={"md"}>{stateFasilitasKesehatan.findUnique.data.tarifdanlayanan.layanan}</Text>
<Text fz={"md"} fw={"bold"}>Tarif</Text>
<Text fz={"md"}>{stateFasilitasKesehatan.findUnique.data.tarifdanlayanan.tarif}</Text>
</Box>
<Box>
<Flex gap={"xs"}>
<Button color="red" onClick={() => {
if (stateFasilitasKesehatan.findUnique.data) {
setSelectedId(stateFasilitasKesehatan.findUnique.data.id)
setModalHapus(true)
}
}}>
<IconX size={20} />
</Button>
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/${stateFasilitasKesehatan.findUnique.data?.id}/edit`)} color="green">
<IconEdit size={20} />
</Button>
</Flex>
</Box>
</Stack>
</Paper>
) : null}
</Stack>
</Paper>
{/* Modal Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text="Apakah anda yakin ingin menghapus fasilitas kesehatan ini?"
/>
</Box>
);
}
export default DetailFasilitasKesehatan;

View File

@@ -0,0 +1,187 @@
'use client'
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
import fasilitasKesehatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
function CreateFasilitasKesehatan() {
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState)
const router = useRouter();
const resetForm = () => {
stateFasilitasKesehatan.create.form = {
name: "",
informasiUmum: {
fasilitas: "",
alamat: "",
jamOperasional: "",
},
layananUnggulan: {
content: "",
},
dokterdanTenagaMedis: {
name: "",
specialist: "",
jadwal: "",
},
fasilitasPendukung: {
content: "",
},
prosedurPendaftaran: {
content: "",
},
tarifDanLayanan: {
layanan: "",
tarif: "",
},
};
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await stateFasilitasKesehatan.create.submit();
toast.success("Data berhasil disimpan");
resetForm();
// After successful submission, redirect to the list page
router.push('/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan');
}
return (
<Box component="form" onSubmit={handleSubmit}>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Fasilitas Kesehatan</Title>
<TextInput
label={<Text fz="sm" fw="bold">Nama Fasilitas Kesehatan</Text>}
placeholder="masukkan nama fasilitas kesehatan"
value={stateFasilitasKesehatan.create.form.name}
onChange={(e) => {
stateFasilitasKesehatan.create.form.name = e.target.value;
}}
/>
<Box>
<Text fz="md" fw="bold">Informasi Umum</Text>
<TextInput
label={<Text fz="sm" fw="bold">Fasilitas</Text>}
placeholder="masukkan fasilitas"
value={stateFasilitasKesehatan.create.form.informasiUmum.fasilitas}
onChange={(e) => {
stateFasilitasKesehatan.create.form.informasiUmum.fasilitas = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Alamat</Text>}
placeholder="masukkan alamat"
value={stateFasilitasKesehatan.create.form.informasiUmum.alamat}
onChange={(e) => {
stateFasilitasKesehatan.create.form.informasiUmum.alamat = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Jam Operasional</Text>}
placeholder="masukkan jam operasional"
value={stateFasilitasKesehatan.create.form.informasiUmum.jamOperasional}
onChange={(e) => {
stateFasilitasKesehatan.create.form.informasiUmum.jamOperasional = e.target.value;
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Layanan Unggulan</Text>
<CreateEditor
value={stateFasilitasKesehatan.create.form.layananUnggulan.content}
onChange={(e) => {
stateFasilitasKesehatan.create.form.layananUnggulan.content = e;
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Dokter dan Tenaga Medis</Text>
<TextInput
label={<Text fz="sm" fw="bold">Nama Dokter</Text>}
placeholder="masukkan nama dokter"
value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.name}
onChange={(e) => {
stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.name = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Specialist</Text>}
placeholder="masukkan specialist"
value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.specialist}
onChange={(e) => {
stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.specialist = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Jadwal</Text>}
placeholder="masukkan jadwal"
value={stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.jadwal}
onChange={(e) => {
stateFasilitasKesehatan.create.form.dokterdanTenagaMedis.jadwal = e.target.value;
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Fasilitas Pendukung</Text>
<CreateEditor
value={stateFasilitasKesehatan.create.form.fasilitasPendukung.content}
onChange={(e) => {
stateFasilitasKesehatan.create.form.fasilitasPendukung.content = e;
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Prosedur Pendaftaran</Text>
<CreateEditor
value={stateFasilitasKesehatan.create.form.prosedurPendaftaran.content}
onChange={(e) => {
stateFasilitasKesehatan.create.form.prosedurPendaftaran.content = e;
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Tarif dan Layanan</Text>
<TextInput
label={<Text fz="sm" fw="bold">Tarif</Text>}
placeholder="masukkan tarif"
value={stateFasilitasKesehatan.create.form.tarifDanLayanan.tarif}
onChange={(e) => {
stateFasilitasKesehatan.create.form.tarifDanLayanan.tarif = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Layanan</Text>}
placeholder="masukkan layanan"
value={stateFasilitasKesehatan.create.form.tarifDanLayanan.layanan}
onChange={(e) => {
stateFasilitasKesehatan.create.form.tarifDanLayanan.layanan = e.target.value;
}}
/>
</Box>
<Button onClick={handleSubmit} bg={colors['blue-button']}>
Simpan
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreateFasilitasKesehatan;

View File

@@ -0,0 +1,81 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import fasilitasKesehatanState from '../../../_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
function FasilitasKesehatan() {
return (
<Box>
<HeaderSearch
title='Fasilitas Kesehatan'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListFasilitasKesehatan/>
</Box>
);
}
function ListFasilitasKesehatan() {
const stateFasilitasKesehatan = useProxy(fasilitasKesehatanState)
const router = useRouter();
useShallowEffect(() => {
stateFasilitasKesehatan.findMany.load()
}, [])
if (!stateFasilitasKesehatan.findMany.data) {
return (
<Box py={10}>
<Skeleton h={500}/>
</Box>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Fasilitas Kesehatan'
href='/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Fasilitas Kesehatan</TableTh>
<TableTh>Alamat</TableTh>
<TableTh>Jam Operasional</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{stateFasilitasKesehatan.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.name}</TableTd>
<TableTd>{item.informasiumum.alamat}</TableTd>
<TableTd>{item.informasiumum.jamOperasional}</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default FasilitasKesehatan;

View File

@@ -0,0 +1,80 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import grafikkepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { useProxy } from 'valtio/utils';
function EditGrafikHasilKepuasan() {
const router = useRouter()
const params = useParams() as { id: string }
const stateGrafikKepuasan = useProxy(grafikkepuasan)
const id = params.id
// Load data saat komponen mount
useEffect(() => {
if (id) {
stateGrafikKepuasan.findUnique.load(id).then(() => {
const data = stateGrafikKepuasan.findUnique.data
if (data) {
stateGrafikKepuasan.update.form = {
label: data.label || '',
jumlah: data.jumlah || '',
}
}
})
}
}, [id])
const handleSubmit = async () => {
// Set the ID before submitting
stateGrafikKepuasan.update.id = id;
await stateGrafikKepuasan.update.submit();
router.push('/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan')
}
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack size={20} />
</Button>
</Box>
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
<Stack>
<Title order={3}>Edit Grafik Hasil Kepuasan</Title>
<TextInput
label="Label"
placeholder="masukkan label"
value={stateGrafikKepuasan.update.form.label}
onChange={(val) => {
stateGrafikKepuasan.update.form.label = val.currentTarget.value;
}}
/>
<TextInput
label="Jumlah"
type="number"
placeholder="masukkan jumlah"
value={stateGrafikKepuasan.update.form.jumlah}
onChange={(val) => {
stateGrafikKepuasan.update.form.jumlah = val.currentTarget.value;
}}
/>
<Button
mt={10}
bg={colors['blue-button']}
onClick={handleSubmit}
>
Simpan
</Button>
</Stack>
</Paper>
</Box>
)
}
export default EditGrafikHasilKepuasan;

View File

@@ -0,0 +1,81 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import grafikkepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
function CreateGrafikHasilKepuasanMasyarakat() {
const stateGrafikKepuasan = useProxy(grafikkepuasan);
const [chartData, setChartData] = useState<any[]>([]);
const router = useRouter()
const resetForm = () => {
stateGrafikKepuasan.create.form = {
label: "",
jumlah: "",
}
}
const handleSubmit = async () => {
const id = await stateGrafikKepuasan.create.create();
if (id) {
const idStr = String(id);
await stateGrafikKepuasan.findUnique.load(idStr);
if (stateGrafikKepuasan.findUnique.data) {
setChartData([stateGrafikKepuasan.findUnique.data]);
}
}
resetForm();
router.push("/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan");
}
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack size={20} />
</Button>
</Box>
<Box>
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
<Title order={4}>Tambah Grafik Hasil Kepuasan Masyarakat</Title>
<Stack gap={"xs"}>
<TextInput
label="Label"
type="text"
value={stateGrafikKepuasan.create.form.label}
placeholder="Masukkan label"
onChange={(val) => {
stateGrafikKepuasan.create.form.label = val.currentTarget.value;
}}
/>
<TextInput
label="Jumlah"
type="number"
value={stateGrafikKepuasan.create.form.jumlah}
placeholder="Masukkan jumlah"
onChange={(val) => {
stateGrafikKepuasan.create.form.jumlah = val.currentTarget.value;
}}
/>
<Group>
<Button
bg={colors['blue-button']}
mt={10}
onClick={handleSubmit}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
</Box>
);
}
export default CreateGrafikHasilKepuasanMasyarakat;

View File

@@ -0,0 +1,148 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/jusulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import grafikkepuasan from '../../../_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
function GrafikHasilKepuasanMasyarakat() {
type PDKMGrafik = {
id: string;
label: string;
jumlah: number;
}
const stateGrafikKepuasan = useProxy(grafikkepuasan);
const [chartData, setChartData] = useState<PDKMGrafik[]>([]);
const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready
const isTablet = useMediaQuery('(max-width: 1024px)')
const isMobile = useMediaQuery('(max-width: 768px)')
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const router = useRouter();
const handleDelete = () => {
if (selectedId) {
stateGrafikKepuasan.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
stateGrafikKepuasan.findMany.load()
}
}
useShallowEffect(() => {
setMounted(true)
stateGrafikKepuasan.findMany.load()
}, [])
useEffect(() => {
setMounted(true);
if (stateGrafikKepuasan.findMany.data) {
setChartData(stateGrafikKepuasan.findMany.data.map((item) => ({
id: item.id,
label: item.label,
jumlah: Number(item.jumlah),
})));
}
}, [stateGrafikKepuasan.findMany.data]);
if (!stateGrafikKepuasan.findMany.data) {
return (
<Stack>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box>
<Stack gap={"xs"}>
{/* Form Input */}
<Paper bg={colors['white-1']} p={'md'}>
<JudulListTab
title='List Grafik Hasil Kepuasan Masyarakat'
href='/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/create'
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Label</TableTh>
<TableTh>Jumlah</TableTh>
<TableTh>Edit</TableTh>
<TableTh>Delete</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{stateGrafikKepuasan.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.label}</TableTd>
<TableTd>{item.jumlah}</TableTd>
<TableTd>
<Button color='green' onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/${item.id}`)}>
<IconEdit size={20} />
</Button>
</TableTd>
<TableTd>
<Button
color='red'
disabled={stateGrafikKepuasan.delete.loading}
onClick={() => {
setSelectedId(item.id)
setModalHapus(true)
}}>
<IconTrash size={20} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Paper>
{/* Chart */}
{!mounted && !chartData ? (
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title pb={10} order={3}>Grafik Hasil Kepuasan Masyarakat</Title>
<Text c='dimmed'>Belum ada data untuk ditampilkan dalam grafik</Text>
</Paper>
</Box>
) : (
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title pb={10} order={4}>Grafik Hasil Kepuasan Masyarakat</Title>
{mounted && chartData.length > 0 && (
<BarChart width={isMobile ? 450 : isTablet ? 500 : 550} height={350} data={chartData} >
<XAxis dataKey="label" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="jumlah" fill={colors['blue-button']} name="Jumlah" />
</BarChart>
)}
</Paper>
</Box>
)}
</Stack>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleDelete}
text='Apakah anda yakin ingin menghapus grafik hasil kepuasan masyarakat ini?'
/>
</Box>
);
}
export default GrafikHasilKepuasanMasyarakat;

View File

@@ -0,0 +1,407 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
import jadwalKegiatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
interface JadwalKegiatanFormBase {
content: string;
informasiJadwalKegiatan: {
name: string;
tanggal: string;
waktu: string;
lokasi: string;
};
deskripsiJadwalKegiatan: {
deskripsi: string;
};
layananJadwalKegiatan: {
content: string;
};
syaratKetentuanJadwalKegiatan: {
content: string;
};
dokumenJadwalKegiatan: {
content: string;
};
pendaftaranJadwalKegiatan: {
name: string;
tanggal: string;
namaOrangtua: string;
nomor: string;
alamat: string;
catatan: string;
};
}
function EditJadwalKegiatan() {
const stateJadwalKegiatan = useProxy(jadwalKegiatanState);
const router = useRouter();
const params = useParams();
const [formData, setFormData] = useState<JadwalKegiatanFormBase>({
content: stateJadwalKegiatan.edit.form.content || '',
informasiJadwalKegiatan: {
name: stateJadwalKegiatan.edit.form.informasiJadwalKegiatan?.name || '',
tanggal: stateJadwalKegiatan.edit.form.informasiJadwalKegiatan?.tanggal || '',
waktu: stateJadwalKegiatan.edit.form.informasiJadwalKegiatan?.waktu || '',
lokasi: stateJadwalKegiatan.edit.form.informasiJadwalKegiatan?.lokasi || '',
},
deskripsiJadwalKegiatan: {
deskripsi: stateJadwalKegiatan.edit.form.deskripsiJadwalKegiatan?.deskripsi || '',
},
layananJadwalKegiatan: {
content: stateJadwalKegiatan.edit.form.layananJadwalKegiatan?.content || '',
},
syaratKetentuanJadwalKegiatan: {
content: stateJadwalKegiatan.edit.form.syaratKetentuanJadwalKegiatan?.content || '',
},
dokumenJadwalKegiatan: {
content: stateJadwalKegiatan.edit.form.dokumenJadwalKegiatan?.content || '',
},
pendaftaranJadwalKegiatan: {
name: stateJadwalKegiatan.edit.form.pendaftaranJadwalKegiatan?.name || '',
tanggal: stateJadwalKegiatan.edit.form.pendaftaranJadwalKegiatan?.tanggal || '',
namaOrangtua: stateJadwalKegiatan.edit.form.pendaftaranJadwalKegiatan?.namaOrangtua || '',
nomor: stateJadwalKegiatan.edit.form.pendaftaranJadwalKegiatan?.nomor || '',
alamat: stateJadwalKegiatan.edit.form.pendaftaranJadwalKegiatan?.alamat || '',
catatan: stateJadwalKegiatan.edit.form.pendaftaranJadwalKegiatan?.catatan || '',
},
});
useEffect(() => {
const loadJadwalKegiatan = async () => {
const id = params?.id as string;
if (!id) return;
try {
await stateJadwalKegiatan.edit.load(id);
const { form } = stateJadwalKegiatan.edit;
if (form) {
setFormData({
content: form.content,
informasiJadwalKegiatan: {
name: form.informasiJadwalKegiatan?.name || '',
tanggal: form.informasiJadwalKegiatan?.tanggal || '',
waktu: form.informasiJadwalKegiatan?.waktu || '',
lokasi: form.informasiJadwalKegiatan?.lokasi || '',
},
deskripsiJadwalKegiatan: {
deskripsi: form.deskripsiJadwalKegiatan?.deskripsi || '',
},
layananJadwalKegiatan: {
content: form.layananJadwalKegiatan?.content || '',
},
syaratKetentuanJadwalKegiatan: {
content: form.syaratKetentuanJadwalKegiatan?.content || '',
},
dokumenJadwalKegiatan: {
content: form.dokumenJadwalKegiatan?.content || '',
},
pendaftaranJadwalKegiatan: {
name: form.pendaftaranJadwalKegiatan?.name || '',
tanggal: form.pendaftaranJadwalKegiatan?.tanggal || '',
namaOrangtua: form.pendaftaranJadwalKegiatan?.namaOrangtua || '',
nomor: form.pendaftaranJadwalKegiatan?.nomor || '',
alamat: form.pendaftaranJadwalKegiatan?.alamat || '',
catatan: form.pendaftaranJadwalKegiatan?.catatan || '',
},
});
}
} catch (error) {
console.error("Error loading jadwal kegiatan:", error);
toast.error("Gagal memuat data jadwal kegiatan");
}
};
loadJadwalKegiatan();
}, [params?.id]);
const handleSubmit = async () => {
try {
stateJadwalKegiatan.edit.form = {
...stateJadwalKegiatan.edit.form,
content: formData.content,
informasiJadwalKegiatan: {
name: formData.informasiJadwalKegiatan.name,
tanggal: formData.informasiJadwalKegiatan.tanggal,
waktu: formData.informasiJadwalKegiatan.waktu,
lokasi: formData.informasiJadwalKegiatan.lokasi,
},
deskripsiJadwalKegiatan: {
deskripsi: formData.deskripsiJadwalKegiatan.deskripsi,
},
layananJadwalKegiatan: {
content: formData.layananJadwalKegiatan.content,
},
syaratKetentuanJadwalKegiatan: {
content: formData.syaratKetentuanJadwalKegiatan.content,
},
dokumenJadwalKegiatan: {
content: formData.dokumenJadwalKegiatan.content,
},
pendaftaranJadwalKegiatan: {
name: formData.pendaftaranJadwalKegiatan.name,
tanggal: formData.pendaftaranJadwalKegiatan.tanggal,
namaOrangtua: formData.pendaftaranJadwalKegiatan.namaOrangtua,
nomor: formData.pendaftaranJadwalKegiatan.nomor,
alamat: formData.pendaftaranJadwalKegiatan.alamat,
catatan: formData.pendaftaranJadwalKegiatan.catatan,
},
};
const success = await stateJadwalKegiatan.edit.submit();
if (success) {
toast.success("Jadwal kegiatan berhasil diperbarui!");
router.push("/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan");
}
} catch (error) {
console.error("Error updating jadwal kegiatan:", error);
toast.error(error instanceof Error ? error.message : "Gagal memperbarui data jadwal kegiatan");
}
};
return (
<Box>
<Box mb={10}>
<Button onClick={() => router.back()} variant="subtle" color="blue">
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Stack gap="xs">
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Edit Jadwal Kegiatan</Title>
<TextInput
label={<Text fz="sm" fw="bold">Nama Jadwal Kegiatan</Text>}
placeholder="masukkan nama jadwal kegiatan"
value={formData.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
content: e.target.value
}));
}}
/>
<Box>
<Text fz="sm" fw="bold">Deskripsi Jadwal Kegiatan</Text>
<EditEditor
value={formData.deskripsiJadwalKegiatan.deskripsi}
onChange={(e) => {
setFormData(prev => ({
...prev,
deskripsiJadwalKegiatan: {
...prev.deskripsiJadwalKegiatan,
deskripsi: e
}
}));
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Informasi Jadwal Kegiatan</Text>
<TextInput
label={<Text fz="sm" fw="bold">Nama</Text>}
placeholder="masukkan nama"
value={formData.informasiJadwalKegiatan.name}
onChange={(e) => {
setFormData(prev => ({
...prev,
informasiJadwalKegiatan: {
...prev.informasiJadwalKegiatan,
name: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Tanggal</Text>}
placeholder="masukkan tanggal"
value={formData.informasiJadwalKegiatan.tanggal}
onChange={(e) => {
setFormData(prev => ({
...prev,
informasiJadwalKegiatan: {
...prev.informasiJadwalKegiatan,
tanggal: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Waktu</Text>}
placeholder="masukkan waktu"
value={formData.informasiJadwalKegiatan.waktu}
onChange={(e) => {
setFormData(prev => ({
...prev,
informasiJadwalKegiatan: {
...prev.informasiJadwalKegiatan,
waktu: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Lokasi</Text>}
placeholder="masukkan lokasi"
value={formData.informasiJadwalKegiatan.lokasi}
onChange={(e) => {
setFormData(prev => ({
...prev,
informasiJadwalKegiatan: {
...prev.informasiJadwalKegiatan,
lokasi: e.target.value
}
}));
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Layanan Jadwal Kegiatan</Text>
<EditEditor
value={formData.layananJadwalKegiatan.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
layananJadwalKegiatan: {
...prev.layananJadwalKegiatan,
content: e
}
}));
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Syarat dan Ketentuan Jadwal Kegiatan</Text>
<EditEditor
value={formData.syaratKetentuanJadwalKegiatan.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
syaratKetentuanJadwalKegiatan: {
...prev.syaratKetentuanJadwalKegiatan,
content: e
}
}));
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Dokumen Jadwal Kegiatan</Text>
<EditEditor
value={formData.dokumenJadwalKegiatan.content}
onChange={(e) => {
setFormData(prev => ({
...prev,
dokumenJadwalKegiatan: {
...prev.dokumenJadwalKegiatan,
content: e
}
}));
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Pendaftaran Jadwal Kegiatan</Text>
<TextInput
label={<Text fz="sm" fw="bold">Nama</Text>}
placeholder="masukkan nama"
value={formData.pendaftaranJadwalKegiatan.name}
onChange={(e) => {
setFormData(prev => ({
...prev,
pendaftaranJadwalKegiatan: {
...prev.pendaftaranJadwalKegiatan,
name: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Tanggal</Text>}
placeholder="masukkan tanggal"
value={formData.pendaftaranJadwalKegiatan.tanggal}
onChange={(e) => {
setFormData(prev => ({
...prev,
pendaftaranJadwalKegiatan: {
...prev.pendaftaranJadwalKegiatan,
tanggal: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Nama Orangtua</Text>}
placeholder="masukkan nama orangtua"
value={formData.pendaftaranJadwalKegiatan.namaOrangtua}
onChange={(e) => {
setFormData(prev => ({
...prev,
pendaftaranJadwalKegiatan: {
...prev.pendaftaranJadwalKegiatan,
namaOrangtua: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Nomor</Text>}
placeholder="masukkan nomor"
value={formData.pendaftaranJadwalKegiatan.nomor}
onChange={(e) => {
setFormData(prev => ({
...prev,
pendaftaranJadwalKegiatan: {
...prev.pendaftaranJadwalKegiatan,
nomor: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Alamat</Text>}
placeholder="masukkan alamat"
value={formData.pendaftaranJadwalKegiatan.alamat}
onChange={(e) => {
setFormData(prev => ({
...prev,
pendaftaranJadwalKegiatan: {
...prev.pendaftaranJadwalKegiatan,
alamat: e.target.value
}
}));
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Catatan</Text>}
placeholder="masukkan catatan"
value={formData.pendaftaranJadwalKegiatan.catatan}
onChange={(e) => {
setFormData(prev => ({
...prev,
pendaftaranJadwalKegiatan: {
...prev.pendaftaranJadwalKegiatan,
catatan: e.target.value
}
}));
}}
/>
</Box>
<Button onClick={handleSubmit} bg={colors['blue-button']}>
Simpan
</Button>
</Stack>
</Paper>
</Stack>
</Box>
);
}
export default EditJadwalKegiatan;

View File

@@ -0,0 +1,126 @@
'use client'
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
import jadwalKegiatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import colors from '@/con/colors';
import { Box, Button, Flex, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
function DetailJadwalKegiatan() {
const params = useParams()
const router = useRouter();
const stateJadwalKegiatan = useProxy(jadwalKegiatanState)
const [modalHapus, setModalHapus] = useState(false);
const [selectedId, setSelectedId] = useState<string | null>(null)
useShallowEffect(() => {
stateJadwalKegiatan.findUnique.load(params?.id as string)
}, [])
const handleHapus = () => {
if (selectedId) {
stateJadwalKegiatan.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
router.push("/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan")
}
}
if (!stateJadwalKegiatan.findUnique.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
<Stack>
<Text fz={"xl"} fw={"bold"}>Detail Jadwal Kegiatan</Text>
{stateJadwalKegiatan.findUnique.data ? (
<Paper bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fz={"lg"} fw={"bold"}>Nama Kegiatan</Text>
<Text fz={"md"}>{stateJadwalKegiatan.findUnique.data.content}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Informasi</Text>
<Text fz={"md"} fw={"bold"}>Nama</Text>
<Text fz={"md"}>{stateJadwalKegiatan.findUnique.data.informasijadwalkegiatan.name}</Text>
<Text fz={"md"} fw={"bold"}>Tanggal</Text>
<Text fz={"md"}>{stateJadwalKegiatan.findUnique.data.informasijadwalkegiatan.tanggal}</Text>
<Text fz={"md"} fw={"bold"}>Waktu</Text>
<Text fz={"md"}>{stateJadwalKegiatan.findUnique.data.informasijadwalkegiatan.waktu}</Text>
<Text fz={"md"} fw={"bold"}>Lokasi</Text>
<Text fz={"md"}>{stateJadwalKegiatan.findUnique.data.informasijadwalkegiatan.lokasi}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateJadwalKegiatan.findUnique.data.deskripsijadwalkegiatan.deskripsi }} />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Layanan</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateJadwalKegiatan.findUnique.data.layananjadwalkegiatan.content }} />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Syarat Ketentuan</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateJadwalKegiatan.findUnique.data.syaratketentuanjadwalkegiatan.content}} />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Dokumen</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateJadwalKegiatan.findUnique.data.dokumenjadwalkegiatan.content }} />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Prosedur Pendaftaran</Text>
<Text fz={"md"}>{stateJadwalKegiatan.findUnique.data.pendaftaranjadwalkegiatan.name}</Text>
<Text fz={"md"}>{stateJadwalKegiatan.findUnique.data.pendaftaranjadwalkegiatan.tanggal}</Text>
<Text fz={"md"}>{stateJadwalKegiatan.findUnique.data.pendaftaranjadwalkegiatan.namaOrangtua}</Text>
<Text fz={"md"}>{stateJadwalKegiatan.findUnique.data.pendaftaranjadwalkegiatan.nomor}</Text>
<Text fz={"md"}>{stateJadwalKegiatan.findUnique.data.pendaftaranjadwalkegiatan.alamat}</Text>
<Text fz={"md"} dangerouslySetInnerHTML={{ __html: stateJadwalKegiatan.findUnique.data.pendaftaranjadwalkegiatan.catatan }} />
</Box>
<Box>
<Flex gap={"xs"}>
<Button color="red" onClick={() => {
if (stateJadwalKegiatan.findUnique.data) {
setSelectedId(stateJadwalKegiatan.findUnique.data.id)
setModalHapus(true)
}
}}>
<IconX size={20} />
</Button>
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan/${stateJadwalKegiatan.findUnique.data?.id}/edit`)} color="green">
<IconEdit size={20} />
</Button>
</Flex>
</Box>
</Stack>
</Paper>
) : null}
</Stack>
</Paper>
{/* Modal Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text="Apakah anda yakin ingin menghapus jadwal kegiatan ini?"
/>
</Box>
);
}
export default DetailJadwalKegiatan;

View File

@@ -0,0 +1,212 @@
'use client'
import CreateEditor from '@/app/admin/(dashboard)/_com/createEditor';
import jadwalKegiatanState from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
function CreateJadwalKegiatan() {
const stateJadwalKegiatan = useProxy(jadwalKegiatanState)
const router = useRouter();
const resetForm = () => {
stateJadwalKegiatan.edit.form = {
content: "",
informasiJadwalKegiatan: {
name: "",
tanggal: "",
waktu: "",
lokasi: "",
},
deskripsiJadwalKegiatan: {
deskripsi: "",
},
layananJadwalKegiatan: {
content: "",
},
syaratKetentuanJadwalKegiatan: {
content: "",
},
dokumenJadwalKegiatan: {
content: "",
},
pendaftaranJadwalKegiatan: {
name: "",
tanggal: "",
namaOrangtua: "",
nomor: "",
alamat: "",
catatan: "",
},
};
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await stateJadwalKegiatan.create.submit();
toast.success("Data berhasil disimpan");
resetForm();
// After successful submission, redirect to the list page
router.push('/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan');
}
return (
<Box component="form" onSubmit={handleSubmit}>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Jadwal Kegiatan</Title>
<TextInput
label={<Text fz="sm" fw="bold">Nama Jadwal Kegiatan</Text>}
placeholder="masukkan nama jadwal kegiatan"
value={stateJadwalKegiatan.create.form.content}
onChange={(e) => {
stateJadwalKegiatan.create.form.content = e.target.value;
}}
/>
<Box>
<Text fz="sm" fw="bold">Deskripsi Jadwal Kegiatan</Text>
<CreateEditor
value={stateJadwalKegiatan.create.form.deskripsiJadwalKegiatan.deskripsi}
onChange={(e) => {
stateJadwalKegiatan.create.form.deskripsiJadwalKegiatan.deskripsi = e;
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Informasi Jadwal Kegiatan</Text>
<TextInput
label={<Text fz="sm" fw="bold">Nama</Text>}
placeholder="masukkan nama"
value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.name}
onChange={(e) => {
stateJadwalKegiatan.create.form.informasiJadwalKegiatan.name = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Tanggal</Text>}
placeholder="masukkan tanggal"
value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.tanggal}
onChange={(e) => {
stateJadwalKegiatan.create.form.informasiJadwalKegiatan.tanggal = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Waktu</Text>}
placeholder="masukkan waktu"
value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.waktu}
onChange={(e) => {
stateJadwalKegiatan.create.form.informasiJadwalKegiatan.waktu = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Lokasi</Text>}
placeholder="masukkan lokasi"
value={stateJadwalKegiatan.create.form.informasiJadwalKegiatan.lokasi}
onChange={(e) => {
stateJadwalKegiatan.create.form.informasiJadwalKegiatan.lokasi = e.target.value;
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Layanan Jadwal Kegiatan</Text>
<CreateEditor
value={stateJadwalKegiatan.create.form.layananJadwalKegiatan.content}
onChange={(e) => {
stateJadwalKegiatan.create.form.layananJadwalKegiatan.content = e;
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Syarat dan Ketentuan Jadwal Kegiatan</Text>
<CreateEditor
value={stateJadwalKegiatan.create.form.syaratKetentuanJadwalKegiatan.content}
onChange={(e) => {
stateJadwalKegiatan.create.form.syaratKetentuanJadwalKegiatan.content = e;
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Dokumen Jadwal Kegiatan</Text>
<CreateEditor
value={stateJadwalKegiatan.create.form.dokumenJadwalKegiatan.content}
onChange={(e) => {
stateJadwalKegiatan.create.form.dokumenJadwalKegiatan.content = e;
}}
/>
</Box>
<Box>
<Text fz="md" fw="bold">Pendaftaran Jadwal Kegiatan</Text>
<TextInput
label={<Text fz="sm" fw="bold">Nama</Text>}
placeholder="masukkan nama"
value={stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.name}
onChange={(e) => {
stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.name = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Tanggal</Text>}
placeholder="masukkan tanggal"
value={stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.tanggal}
onChange={(e) => {
stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.tanggal = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Nama Orangtua</Text>}
placeholder="masukkan nama orangtua"
value={stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.namaOrangtua}
onChange={(e) => {
stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.namaOrangtua = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Nomor</Text>}
placeholder="masukkan nomor"
value={stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.nomor}
onChange={(e) => {
stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.nomor = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Alamat</Text>}
placeholder="masukkan alamat"
value={stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.alamat}
onChange={(e) => {
stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.alamat = e.target.value;
}}
/>
<TextInput
label={<Text fz="sm" fw="bold">Catatan</Text>}
placeholder="masukkan catatan"
value={stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.catatan}
onChange={(e) => {
stateJadwalKegiatan.create.form.pendaftaranJadwalKegiatan.catatan = e.target.value;
}}
/>
</Box>
<Button onClick={handleSubmit} bg={colors['blue-button']}>
Simpan
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreateJadwalKegiatan;

View File

@@ -0,0 +1,82 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../../_com/header';
import JudulList from '../../../_com/judulList';
import jadwalKegiatanState from '../../../_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
function JadwalKegiatan() {
return (
<Box>
<HeaderSearch
title='Jadwal Kegiatan'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListJadwalKegiatan/>
</Box>
);
}
function ListJadwalKegiatan() {
const stateJadwalKegiatan = useProxy(jadwalKegiatanState)
const router = useRouter();
useShallowEffect(() => {
stateJadwalKegiatan.findMany.load()
}, [])
if (!stateJadwalKegiatan.findMany.data) {
return (
<Box py={10}>
<Skeleton h={500}/>
</Box>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Jadwal Kegiatan'
href='/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Nama</TableTh>
<TableTh>Tanggal</TableTh>
<TableTh>Waktu</TableTh>
<TableTh>Lokasi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{stateJadwalKegiatan.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.informasijadwalkegiatan.name}</TableTd>
<TableTd>{item.informasijadwalkegiatan.tanggal}</TableTd>
<TableTd>{item.informasijadwalkegiatan.waktu}</TableTd>
<TableTd>{item.informasijadwalkegiatan.lokasi}</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default JadwalKegiatan;

View File

@@ -0,0 +1,12 @@
'use client'
import LayoutTabs from "./_lib/layoutTabs"
export default function Layout({children} : {children: React.ReactNode}) {
return (
<LayoutTabs>
{children}
</LayoutTabs>
)
}

View File

@@ -1,54 +0,0 @@
import colors from '@/con/colors';
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } 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';
function Page() {
return (
<Stack>
<Title order={3}>Data Kesehatan Warga</Title>
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Persentase Kelahiran & Kematian"}>
<TabsList bg={colors['BG-trans']} p={'xs'}>
<TabsTab value="Persentase Kelahiran & Kematian">
Persentase Kelahiran & Kematian
</TabsTab>
<TabsTab value="Grafik Hasil Kepuasan">
Grafik Hasil Kepuasan
</TabsTab>
<TabsTab value="Fasilitas Kesehatan">
Fasilitas Kesehatan
</TabsTab>
<TabsTab value="Jadwal Kegiatan">
Jadwal Kegiatan
</TabsTab>
<TabsTab value="Artikel Kesehatan">
Artikel Kesehatan
</TabsTab>
</TabsList>
<TabsPanel value="Persentase Kelahiran & Kematian">
<PersentaseDataKelahiranKematian />
</TabsPanel>
<TabsPanel value="Grafik Hasil Kepuasan">
<GrafikHasilKepuasan/>
</TabsPanel>
<TabsPanel value="Fasilitas Kesehatan">
<FasilitasKesehatan/>
</TabsPanel>
<TabsPanel value="Jadwal Kegiatan">
<JadwalKegiatan/>
</TabsPanel>
<TabsPanel value="Artikel Kesehatan">
<ArtikelKesehatan/>
</TabsPanel>
</Tabs>
</Stack>
);
}
export default Page;

View File

@@ -0,0 +1,114 @@
/* eslint-disable react-hooks/exhaustive-deps */
'use client'
import persentasekelahiran from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
function EditPersentaseDataKelahiranKematian() {
const router = useRouter()
const params = useParams() as { id: string }
const statePresentase = useProxy(persentasekelahiran)
const id = params.id
// Load data saat komponen mount
// Di file page.tsx, ubah useEffect-nya menjadi:
useEffect(() => {
if (!id) return;
statePresentase.update.id = id;
statePresentase.findUnique.load(id)
.then(() => {
const data = statePresentase.findUnique.data;
if (data) {
statePresentase.update.form = {
tahun: String(data.tahun || ''),
kematianKasar: String(data.kematianKasar || ''),
kelahiranKasar: String(data.kelahiranKasar || ''),
kematianBayi: String(data.kematianBayi || '')
};
}
})
.catch(error => {
console.error('Error loading data:', error);
toast.error('Gagal memuat data');
});
}, [id]);
// Di handleSubmit, ubah menjadi:
const handleSubmit = async () => {
try {
statePresentase.update.id = id;
await statePresentase.update.submit();
toast.success('Data berhasil diperbarui');
router.push('/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian');
} catch (error) {
console.error('Error updating data:', error);
toast.error('Gagal memperbarui data');
}
};
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack size={20} />
</Button>
</Box>
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
<Stack>
<Title order={3}>Edit Persentase Data Kelahiran & Kematian</Title>
<TextInput
label="Tahun"
placeholder="masukkan tahun"
value={statePresentase.update.form.tahun}
onChange={(val) => {
statePresentase.update.form.tahun = val.currentTarget.value;
}}
/>
<TextInput
label="Kematian Kasar"
type="number"
placeholder="masukkan kematian kasar"
value={statePresentase.update.form.kematianKasar}
onChange={(val) => {
statePresentase.update.form.kematianKasar = val.currentTarget.value;
}}
/>
<TextInput
label="Kematian Bayi"
type="number"
placeholder="masukkan kematian bayi"
value={statePresentase.update.form.kematianBayi}
onChange={(val) => {
statePresentase.update.form.kematianBayi = val.currentTarget.value;
}}
/>
<TextInput
label="Kelahiran Kasar"
type="number"
placeholder="masukkan kelahiran kasar"
value={statePresentase.update.form.kelahiranKasar}
onChange={(val) => {
statePresentase.update.form.kelahiranKasar = val.currentTarget.value;
}}
/>
<Button
mt={10}
bg={colors['blue-button']}
onClick={handleSubmit}
>
Simpan Perubahan
</Button>
</Stack>
</Paper>
</Box>
)
}
export default EditPersentaseDataKelahiranKematian;

View File

@@ -0,0 +1,101 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import persentasekelahiran from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
import { IconArrowBack } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { useProxy } from 'valtio/utils';
function CreatePersentaseDataKelahiranKematian() {
const statePersentase = useProxy(persentasekelahiran);
const [chartData, setChartData] = useState<any[]>([]);
const router = useRouter()
const resetForm = () => {
statePersentase.create.form = {
tahun: "",
kematianBayi: "",
kematianKasar: "",
kelahiranKasar: "",
}
}
const handleSubmit = async () => {
const id = await statePersentase.create.create();
if (id) {
const idStr = String(id);
await statePersentase.findUnique.load(idStr);
if (statePersentase.findUnique.data) {
setChartData([statePersentase.findUnique.data]);
}
}
resetForm();
router.push("/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian");
}
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack size={20} />
</Button>
</Box>
<Box>
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
<Title order={4}>Tambah Persentase Data Kelahiran & Kematian</Title>
<Stack gap={"xs"}>
<TextInput
label="Tahun"
type="number"
value={statePersentase.create.form.tahun}
placeholder="Masukkan tahun"
onChange={(val) => {
statePersentase.create.form.tahun = val.currentTarget.value;
}}
/>
<TextInput
label="Kematian Kasar"
type="number"
value={statePersentase.create.form.kematianKasar}
placeholder="Masukkan kematian kasar"
onChange={(val) => {
statePersentase.create.form.kematianKasar = val.currentTarget.value;
}}
/>
<TextInput
label="Kematian Bayi"
type="number"
value={statePersentase.create.form.kematianBayi}
placeholder="Masukkan kematian bayi"
onChange={(val) => {
statePersentase.create.form.kematianBayi = val.currentTarget.value;
}}
/>
<TextInput
label="Kelahiran Kasar"
type="number"
value={statePersentase.create.form.kelahiranKasar}
placeholder="Masukkan kelahiran kasar"
onChange={(val) => {
statePersentase.create.form.kelahiranKasar = val.currentTarget.value;
}}
/>
<Group>
<Button
bg={colors['blue-button']}
mt={10}
onClick={handleSubmit}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
</Box>
);
}
export default CreatePersentaseDataKelahiranKematian;

View File

@@ -0,0 +1,158 @@
'use client'
import persentasekelahiran from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
import colors from '@/con/colors';
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
import { useProxy } from 'valtio/utils';
import JudulListTab from '../../../_com/jusulListTab';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
function PersentaseDataKelahiranKematian() {
type PDKMGrafik = {
id: string;
tahun: string;
kematianKasar: number;
kematianBayi: number;
kelahiranKasar: number;
}
const statePersentase = useProxy(persentasekelahiran);
const [chartData, setChartData] = useState<PDKMGrafik[]>([]);
const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready
const isTablet = useMediaQuery('(max-width: 1024px)')
const isMobile = useMediaQuery('(max-width: 768px)')
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const router = useRouter();
const handleDelete = () => {
if (selectedId) {
statePersentase.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
statePersentase.findMany.load()
}
}
useShallowEffect(() => {
setMounted(true)
statePersentase.findMany.load()
}, [])
useEffect(() => {
setMounted(true);
if (statePersentase.findMany.data) {
setChartData(statePersentase.findMany.data.map((item) => ({
id: item.id,
tahun: item.tahun,
kematianKasar: Number(item.kematianKasar),
kematianBayi: Number(item.kematianBayi),
kelahiranKasar: Number(item.kelahiranKasar),
})));
}
}, [statePersentase.findMany.data]);
if (!statePersentase.findMany.data) {
return (
<Stack>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box>
<Stack gap={"xs"}>
{/* Form Input */}
<Paper bg={colors['white-1']} p={'md'}>
<JudulListTab
title='List Persentase Data Kelahiran & Kematian'
href='/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/create'
placeholder='pencarian'
searchIcon={<IconSearch size={16} />}
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Tahun</TableTh>
<TableTh>Kematian Kasar</TableTh>
<TableTh>Kematian Bayi</TableTh>
<TableTh>kelahiran Kasar</TableTh>
<TableTh>Edit</TableTh>
<TableTh>Delete</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{statePersentase.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd>{item.tahun}</TableTd>
<TableTd>{item.kematianKasar}</TableTd>
<TableTd>{item.kematianBayi}</TableTd>
<TableTd>{item.kelahiranKasar}</TableTd>
<TableTd>
<Button color='green' onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/${item.id}`)}>
<IconEdit size={20} />
</Button>
</TableTd>
<TableTd>
<Button
color='red'
disabled={statePersentase.delete.loading}
onClick={() => {
setSelectedId(item.id)
setModalHapus(true)
}}>
<IconTrash size={20} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Paper>
{/* Chart */}
{!mounted && !chartData ? (
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title pb={10} order={3}>Data Kelahiran & Kematian</Title>
<Text c='dimmed'>Belum ada data untuk ditampilkan dalam grafik</Text>
</Paper>
</Box>
) : (
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
<Paper bg={colors['white-1']} p={'md'}>
<Title pb={10} order={4}>Data Kelahiran & Kematian</Title>
{mounted && chartData.length > 0 && (
<BarChart width={isMobile ? 450 : isTablet ? 500 : 550} height={350} 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>
)}
</Paper>
</Box>
)}
</Stack>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleDelete}
text='Apakah anda yakin ingin menghapus persentase data kelahiran & kematian ini?'
/>
</Box>
);
}
export default PersentaseDataKelahiranKematian;

View File

@@ -1,64 +0,0 @@
'use client'
import colors from '@/con/colors';
import { Box, Stack, SimpleGrid, Paper, Title, TextInput, Text, Button } from '@mantine/core';
import { KesehatanEditor } from '../../_com/kesehatanEditor';
import { useRouter } from 'next/navigation';
import { IconArrowBack } from '@tabler/icons-react';
function EditPuskesmas() {
const router = useRouter();
return (
<Box>
<Box mb={10}>
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
<IconArrowBack color={colors['blue-button']} size={25}/>
</Button>
</Box>
<Stack gap={"xs"}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={"md"}>
<Stack gap={"xs"}>
<Title order={4}>Edit Puskesmas</Title>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Nama Puskesmas</Text>}
placeholder='Masukkan nama puskesmas'
/>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>No Telp Puskesmas</Text>}
placeholder='Masukkan no telp puskesmas'
/>
<Box>
<Text fw={"bold"} fz={"sm"}>Deskripsi</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Box>
<Text fw={"bold"} fz={"sm"}>Pelayanan Posyandu</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['white-1']} p={"md"}>
<Stack gap={"xs"}>
<Title order={4}>Preview Data Puskesmas</Title>
<Text fw={"bold"} fz={"sm"}>Nama Puskesmas</Text>
<Text fw={"bold"} fz={"sm"}>No Telp Puskesmas</Text>
<Text fw={"bold"} fz={"sm"}>Deskripsi</Text>
<Text fw={"bold"} fz={"sm"}>Pelayanan Posyandu</Text>
</Stack>
</Paper>
</Box>
</SimpleGrid>
</Stack>
</Box>
);
}
export default EditPuskesmas;

View File

@@ -141,7 +141,7 @@ export const navBar = [
{
id: "Kesehatan_2",
name: "Data Kesehatan Warga",
path: "/admin/kesehatan/data-kesehatan-warga"
path: "/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian"
},
{
id: "Kesehatan_3",

View File

@@ -0,0 +1,55 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type ArtikelKesehatanInput = {
title: string;
content: string;
introduction: {content: string};
symptom: {title: string; content: string};
prevention: {title: string; content: string};
firstAid: {title: string; content: string};
mythVsFact: {title: string; mitos: string; fakta: string};
doctorSign: {content: string};
}
const artikelKesehatanCreate = async (context: Context) => {
const body = await context.body as ArtikelKesehatanInput;
const { title, content, introduction, symptom, prevention, firstAid, mythVsFact, doctorSign } = body;
const [createdIntroduction, createdSymptom, createdPrevention, createdFirstAid, createdMythVsFact, createdDoctorSign] = await Promise.all([
prisma.introduction.create({ data: introduction }),
prisma.symptom.create({ data: symptom }),
prisma.prevention.create({ data: prevention }),
prisma.firstAid.create({ data: firstAid }),
prisma.mythVsFact.create({ data: mythVsFact }),
prisma.doctorSign.create({ data: doctorSign }),
])
const artikelKesehatan = await prisma.artikelKesehatan.create({
data: {
title,
content,
introductionId: createdIntroduction.id,
symptomId: createdSymptom.id,
preventionId: createdPrevention.id,
firstAidId: createdFirstAid.id,
mythVsFactId: createdMythVsFact.id,
doctorSignId: createdDoctorSign.id,
},
include: {
introduction: true,
symptom: true,
prevention: true,
firstaid: true,
mythvsfact: true,
doctorsign: true,
}
})
return {
success: true,
message: "Success create artikel kesehatan",
data: artikelKesehatan,
}
}
export default artikelKesehatanCreate; // export the function

View File

@@ -0,0 +1,41 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
const artikelKesehatanDelete = async (context: Context) => {
const id = context.params?.id as string;
if (!id) {
return {
status: 400,
message: "ID tidak ditemukan",
}
}
const artikelKesehatan = await prisma.artikelKesehatan.findUnique({
where: { id },
include: {
introduction: true,
symptom: true,
prevention: true,
firstaid: true,
mythvsfact: true,
doctorsign: true,
}
})
if (!artikelKesehatan) {
return {
status: 404,
message: "Artikel kesehatan tidak ditemukan",
}
}
await prisma.artikelKesehatan.delete({ where: { id } })
return {
status: 200,
success: true,
message: "Artikel kesehatan berhasil dihapus",
}
}
export default artikelKesehatanDelete;

View File

@@ -0,0 +1,30 @@
import prisma from "@/lib/prisma";
export default async function artikelKesehatanFindMany() {
try {
const data = await prisma.artikelKesehatan.findMany({
where: {
isActive: true,
},
include: {
introduction: true,
symptom: true,
prevention: true,
firstaid: true,
mythvsfact: true,
doctorsign: true,
}
})
return {
success: true,
message: "Success fetch artikel kesehatan",
data,
}
} catch (error) {
console.error("Find many error:", error);
return {
success: false,
message: "Failed fetch artikel kesehatan",
}
}
}

View File

@@ -0,0 +1,52 @@
import prisma from "@/lib/prisma";
export async function artikelKesehatanFindUnique(request: Request) {
const url = new URL(request.url);
const pathSegments = url.pathname.split('/');
const id = pathSegments[pathSegments.length - 1];
if (!id) {
return Response.json({
success: false,
message: "ID tidak boleh kosong",
}, { status: 400 })
}
try {
if (typeof id !== 'string') {
return Response.json({
success: false,
message: "ID tidak valid",
}, { status: 400 })
}
const data = await prisma.artikelKesehatan.findUnique({
where: {id},
include: {
introduction: true,
symptom: true,
prevention: true,
firstaid: true,
mythvsfact: true,
doctorsign: true,
}
})
if (!data) {
return Response.json({
success: false,
message: "Data tidak ditemukan",
}, { status: 404 })
}
return Response.json({
success: true,
data,
}, { status: 200 })
} catch (error) {
console.error("Find unique error:", error);
return Response.json({
success: false,
message: "Failed fetch artikel kesehatan",
}, { status: 500 })
}
}

View File

@@ -0,0 +1,85 @@
import Elysia, { t } from "elysia";
import artikelKesehatanCreate from "./create";
import artikelKesehatanFindMany from "./findMany";
import artikelKesehatanDelete from "./del";
import { artikelKesehatanFindUnique } from "./findUnique";
import artikelKesehatanUpdate from "./updt";
const ArtikelKesehatan = new Elysia({
prefix: "artikel-kesehatan",
tags: ["Kesehatan/Artikel Kesehatan"],
})
.post("/create", artikelKesehatanCreate, {
body: t.Object({
title: t.String(),
content: t.String(),
introduction: t.Object({
content: t.String(),
}),
symptom: t.Object({
title: t.String(),
content: t.String(),
}),
prevention: t.Object({
title: t.String(),
content: t.String(),
}),
firstAid: t.Object({
title: t.String(),
content: t.String(),
}),
mythVsFact: t.Object({
title: t.String(),
mitos: t.String(),
fakta: t.String(),
}),
doctorSign: t.Object({
content: t.String(),
}),
}),
})
.get("/find-many", artikelKesehatanFindMany)
.delete("/del/:id", artikelKesehatanDelete)
.get("/:id", async (context) => {
const response = await artikelKesehatanFindUnique(
new Request(context.request)
);
return response;
})
.put(
"/:id",
async (context) => {
const response = await artikelKesehatanUpdate(context);
return response;
},
{
body: t.Object({
title: t.String(),
content: t.String(),
introduction: t.Object({
content: t.String(),
}),
symptom: t.Object({
title: t.String(),
content: t.String(),
}),
prevention: t.Object({
title: t.String(),
content: t.String(),
}),
firstAid: t.Object({
title: t.String(),
content: t.String(),
}),
mythVsFact: t.Object({
title: t.String(),
mitos: t.String(),
fakta: t.String(),
}),
doctorSign: t.Object({
content: t.String(),
}),
}),
}
);
export default ArtikelKesehatan;

View File

@@ -0,0 +1,106 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type ArtikelKesehatanInput = {
title: string;
content: string;
introduction: { content: string };
symptom: { title: string; content: string };
prevention: { title: string; content: string };
firstAid: { title: string; content: string };
mythVsFact: { title: string; mitos: string; fakta: string };
doctorSign: { content: string };
};
const artikelKesehatanUpdate = async (context: Context) => {
const id = context.params?.id as string;
const body = (await context.body) as ArtikelKesehatanInput;
if (!id) {
return new Response(
JSON.stringify({
success: false,
message: "ID is required",
}),
{
status: 400,
headers: {
"Content-Type": "application/json",
},
}
);
}
const existing = await prisma.artikelKesehatan.findUnique({
where: { id },
});
if (!existing) {
return new Response(
JSON.stringify({ success: false, message: "Data not found" }),
{ status: 404, headers: { "Content-Type": "application/json" } }
);
}
const {
title,
content,
introduction,
symptom,
prevention,
firstAid,
mythVsFact,
doctorSign,
} = body;
await Promise.all([
prisma.introduction.update({
where: { id: existing.introductionId },
data: introduction,
}),
prisma.symptom.update({
where: { id: existing.symptomId },
data: symptom,
}),
prisma.prevention.update({
where: { id: existing.preventionId },
data: prevention,
}),
prisma.firstAid.update({
where: { id: existing.firstAidId },
data: firstAid,
}),
prisma.mythVsFact.update({
where: { id: existing.mythVsFactId },
data: mythVsFact,
}),
prisma.doctorSign.update({
where: { id: existing.doctorSignId },
data: doctorSign,
}),
]);
const updated = await prisma.artikelKesehatan.update({
where: { id },
data: {
title,
content
},
include: {
introduction: true,
symptom: true,
prevention: true,
firstaid: true,
mythvsfact: true,
doctorsign: true,
},
});
return {
status: 200,
success: true,
message: "Artikel kesehatan berhasil dihapus",
data: updated,
};
};
export default artikelKesehatanUpdate;

View File

@@ -0,0 +1,64 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FasilitasKesehatanInput = {
name: string;
informasiUmum: { fasilitas: string; alamat: string; jamOperasional: string };
layananUnggulan: { content: string };
dokterdanTenagaMedis: { name: string; specialist: string; jadwal: string };
fasilitasPendukung: { content: string };
prosedurPendaftaran: { content: string };
tarifDanLayanan: { layanan: string; tarif: string };
};
const fasilitasKesehatanCreate = async (context: Context) => {
const body = await context.body as FasilitasKesehatanInput;
const {
name,
informasiUmum,
layananUnggulan,
dokterdanTenagaMedis,
fasilitasPendukung,
prosedurPendaftaran,
tarifDanLayanan,
} = body;
// Buat masing-masing relasi terlebih dahulu
const [createdInformasiUmum, createdLayananUnggulan, createdDokter, createdPendukung, createdProsedur, createdTarif] = await Promise.all([
prisma.informasiUmum.create({ data: informasiUmum }),
prisma.layananUnggulan.create({ data: layananUnggulan }),
prisma.dokterdanTenagaMedis.create({ data: dokterdanTenagaMedis }),
prisma.fasilitasPendukung.create({ data: fasilitasPendukung }),
prisma.prosedurPendaftaran.create({ data: prosedurPendaftaran }),
prisma.tarifDanLayanan.create({ data: tarifDanLayanan }),
]);
const fasilitas = await prisma.fasilitasKesehatan.create({
data: {
name,
informasiUmumId: createdInformasiUmum.id,
layananUnggulanId: createdLayananUnggulan.id,
dokterdanTenagaMedisId: createdDokter.id,
fasilitasPendukungId: createdPendukung.id,
prosedurPendaftaranId: createdProsedur.id,
tarifDanLayananId: createdTarif.id,
},
include: {
informasiumum: true,
layananunggulan: true,
dokterdantenagamedis: true,
fasilitaspendukung: true,
prosedurpendaftaran: true,
tarifdanlayanan: true,
},
});
return {
success: true,
message: "Fasilitas berhasil dibuat",
data: fasilitas,
};
};
export default fasilitasKesehatanCreate;

View File

@@ -0,0 +1,43 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
const fasilitasKesehatanDelete = async (context: Context) => {
const id = context.params?.id as string;
if (!id) {
return {
status: 400,
message: "ID tidak ditemukan",
}
}
const fasilitasKesehatan = await prisma.fasilitasKesehatan.findUnique({
where: { id },
include: {
informasiumum: true,
layananunggulan: true,
dokterdantenagamedis: true,
fasilitaspendukung: true,
prosedurpendaftaran: true,
tarifdanlayanan: true,
}
})
if (!fasilitasKesehatan) {
return {
status: 404,
message: "Fasilitas kesehatan tidak ditemukan",
}
}
await prisma.fasilitasKesehatan.delete({
where: { id },
})
return {
status: 200,
success: true,
message: "Fasilitas kesehatan berhasil dihapus",
}
}
export default fasilitasKesehatanDelete

View File

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

View File

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

View File

@@ -1,18 +0,0 @@
import Elysia, { t } from "elysia";
import dokterDantenagamedisFindMany from "./find-many";
import dokterDantenagamedisCreate from "./create";
const DokterDantenagamedis = new Elysia({
prefix: "/dokterdantenagamedis",
tags: ["Data Kesehatan/Fasilitas Kesehatan/Dokter dan Tenaga Medis"],
})
.get("/find-many", dokterDantenagamedisFindMany)
.post("/create", dokterDantenagamedisCreate, {
body: t.Object({
name: t.String(),
specialist: t.String(),
jadwal: t.String(),
})
})
export default DokterDantenagamedis

View File

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

View File

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

View File

@@ -1,16 +0,0 @@
import Elysia, { t } from "elysia";
import fasilitasPendukungFindMany from "./find-many";
import FasilitasPendukungCreate from "./create";
const FasilitasPendukung = new Elysia({
prefix: "/fasilitaspendukung",
tags: ["Data Kesehatan/Fasilitas Kesehatan/Fasilitas Pendukung"],
})
.get("/find-many", fasilitasPendukungFindMany)
.post("/create", FasilitasPendukungCreate, {
body: t.Object({
content: t.String(),
}),
});
export default FasilitasPendukung;

View File

@@ -0,0 +1,30 @@
import prisma from "@/lib/prisma";
export default async function findManyFasilitasKesehatan() {
try {
const data = await prisma.fasilitasKesehatan.findMany({
where: {
isActive: true,
},
include: {
informasiumum: true,
layananunggulan: true,
dokterdantenagamedis: true,
fasilitaspendukung: true,
prosedurpendaftaran: true,
tarifdanlayanan: true,
}
})
return {
success: true,
message: "Success fetch fasilitas kesehatan",
data,
}
} catch (error) {
console.error("Find many error:", error);
return {
success: false,
message: "Failed fetch fasilitas kesehatan",
}
}
}

View File

@@ -0,0 +1,53 @@
import prisma from "@/lib/prisma";
export default async function findUniqueFasilitasKesehatan(request: Request) {
const url = new URL(request.url);
const pathSegments = url.pathname.split('/');
const id = pathSegments[pathSegments.length - 1];
if (!id) {
return Response.json({
success: false,
message: "ID tidak boleh kosong",
}, { status: 400 })
}
try {
if (typeof id !== 'string') {
return Response.json({
success: false,
message: "ID tidak valid",
}, { status: 400 })
}
const data = await prisma.fasilitasKesehatan.findUnique({
where: {id},
include: {
informasiumum: true,
layananunggulan: true,
dokterdantenagamedis: true,
fasilitaspendukung: true,
prosedurpendaftaran: true,
tarifdanlayanan: true,
}
})
if (!data) {
return Response.json({
success: false,
message: "Data tidak ditemukan",
}, { status: 404 })
}
return Response.json({
success: true,
data,
}, { status: 200 })
} catch (error) {
console.error("Gagal mengambil data fasilitas kesehatan:", error);
return Response.json({
success: false,
message: "Gagal mengambil data fasilitas kesehatan",
}, { status: 500 })
}
}

View File

@@ -0,0 +1,84 @@
import { Elysia, t } from "elysia";
import fasilitasKesehatanCreate from "./create";
import findManyFasilitasKesehatan from "./findMany";
import findUniqueFasilitasKesehatan from "./findUnique";
import fasilitasKesehatanUpdate from "./updt";
import fasilitasKesehatanDelete from "./del";
const FasilitasKesehatan = new Elysia({
prefix: "fasilitas-kesehatan",
tags: ["Kesehatan/Fasilitas Kesehatan"],
})
.post("/create", fasilitasKesehatanCreate, {
body: t.Object({
name: t.String(),
informasiUmum: t.Object({
fasilitas: t.String(),
alamat: t.String(),
jamOperasional: t.String(),
}),
layananUnggulan: t.Object({
content: t.String(),
}),
dokterdanTenagaMedis: t.Object({
name: t.String(),
specialist: t.String(),
jadwal: t.String(),
}),
fasilitasPendukung: t.Object({
content: t.String(),
}),
prosedurPendaftaran: t.Object({
content: t.String(),
}),
tarifDanLayanan: t.Object({
layanan: t.String(),
tarif: t.String(),
}),
}),
})
.get("/find-many", findManyFasilitasKesehatan)
.delete("/del/:id", fasilitasKesehatanDelete)
.get("/:id", async (context) => {
const response = await findUniqueFasilitasKesehatan(
new Request(context.request)
);
return response;
})
.put(
"/:id",
async (context) => {
const response = await fasilitasKesehatanUpdate(context);
return response;
},
{
body: t.Object({
name: t.String(),
informasiUmum: t.Object({
fasilitas: t.String(),
alamat: t.String(),
jamOperasional: t.String(),
}),
layananUnggulan: t.Object({
content: t.String(),
}),
dokterdanTenagaMedis: t.Object({
name: t.String(),
specialist: t.String(),
jadwal: t.String(),
}),
fasilitasPendukung: t.Object({
content: t.String(),
}),
prosedurPendaftaran: t.Object({
content: t.String(),
}),
tarifDanLayanan: t.Object({
layanan: t.String(),
tarif: t.String(),
}),
}),
}
);
export default FasilitasKesehatan;

View File

@@ -1,31 +0,0 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.InformasiUmumGetPayload<{
select: {
fasilitas: true
alamat: true
jamOperasional: true
}
}>
async function informasiUmumCreate(context: Context) {
const body = context.body as FormCreate
await prisma.informasiUmum.create({
data: {
fasilitas: body.fasilitas,
alamat: body.alamat,
jamOperasional: body.jamOperasional,
}
})
return{
success: true,
message: "Success create informasi umum",
data: {
...body
}
}
}
export default informasiUmumCreate

View File

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

View File

@@ -1,18 +0,0 @@
import Elysia, { t } from "elysia";
import informasiUmumFindMany from "./find-many";
import informasiUmumCreate from "./create";
const InformasiUmum = new Elysia({
prefix: "/informasiumum",
tags: ["Data Kesehatan/Fasilitas Kesehatan/Informasi Umum"],
})
.get("/find-many", informasiUmumFindMany)
.post("/create", informasiUmumCreate, {
body: t.Object({
fasilitas: t.String(),
alamat: t.String(),
jamOperasional: t.String(),
}),
});
export default InformasiUmum;

View File

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

View File

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

View File

@@ -1,17 +0,0 @@
import Elysia, { t } from "elysia";
import layananUnggulanCreate from "./create";
import layananUnggulanFindMany from "./find-many";
const LayananUnggulan = new Elysia({
prefix: "/layananunggulan",
tags: ["Data Kesehatan/Fasilitas Kesehatan/Layanan Unggulan"]
})
.get("/find-many", layananUnggulanFindMany)
.post("/create", layananUnggulanCreate, {
body: t.Object({
content: t.String()
})
})
export default LayananUnggulan

View File

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

View File

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

View File

@@ -1,15 +0,0 @@
import Elysia, { t } from "elysia";
import prosedurPendaftaranFindMany from "./find-many";
import prosedurPendaftaranCreate from "./create";
const ProsedurPendaftaran = new Elysia({
prefix: "/prosedurpendaftaran",
tags: ["Data Kesehatan/Fasilitas Kesehatan/Prosedur Pendaftaran"],
})
.get("/find-many", prosedurPendaftaranFindMany)
.post("/create", prosedurPendaftaranCreate, {
body: t.Object({
content: t.String(),
}),
});
export default ProsedurPendaftaran;

View File

@@ -1,30 +0,0 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.TarifDanLayananGetPayload<{
select: {
layanan: true
tarif: true
}
}>
async function tarifdanlayananCreate(context: Context) {
const body = context.body as FormCreate
await prisma.tarifDanLayanan.create({
data: {
layanan: body.layanan,
tarif: body.tarif,
}
})
return {
success: true,
message: "Success create tarif dan layanan",
data: {
...body
}
}
}
export default tarifdanlayananCreate

View File

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

View File

@@ -1,18 +0,0 @@
import Elysia, { t } from "elysia";
import tarifdanlayananFindMany from "./find-many";
import tarifdanlayananCreate from "./create";
const TarifDanLayanan = new Elysia({
prefix: "/tarifdanlayanan",
tags: ["Data Kesehatan/Fasilitas Kesehatan/Tarif dan Layanan"]
})
.get("/find-many", tarifdanlayananFindMany)
.post("/create", tarifdanlayananCreate, {
body: t.Object({
layanan: t.String(),
tarif: t.String(),
})
})
export default TarifDanLayanan;

View File

@@ -0,0 +1,95 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type FasilitasKesehatanInput = {
name: string;
informasiUmum: { fasilitas: string; alamat: string; jamOperasional: string };
layananUnggulan: { content: string };
dokterdanTenagaMedis: { name: string; specialist: string; jadwal: string };
fasilitasPendukung: { content: string };
prosedurPendaftaran: { content: string };
tarifDanLayanan: { layanan: string; tarif: string };
};
const fasilitasKesehatanUpdate = async (context: Context) => {
const id = context.params?.id as string;
const body = await context.body as FasilitasKesehatanInput;
if (!id) {
return new Response(
JSON.stringify({ success: false, message: "ID is required" }),
{ status: 400, headers: { "Content-Type": "application/json" } }
);
}
const existing = await prisma.fasilitasKesehatan.findUnique({
where: { id },
});
if (!existing) {
return new Response(
JSON.stringify({ success: false, message: "Data not found" }),
{ status: 404, headers: { "Content-Type": "application/json" } }
);
}
const {
name,
informasiUmum,
layananUnggulan,
dokterdanTenagaMedis,
fasilitasPendukung,
prosedurPendaftaran,
tarifDanLayanan,
} = body;
// Update data masing-masing relasi
await Promise.all([
prisma.informasiUmum.update({
where: { id: existing.informasiUmumId },
data: informasiUmum,
}),
prisma.layananUnggulan.update({
where: { id: existing.layananUnggulanId },
data: layananUnggulan,
}),
prisma.dokterdanTenagaMedis.update({
where: { id: existing.dokterdanTenagaMedisId },
data: dokterdanTenagaMedis,
}),
prisma.fasilitasPendukung.update({
where: { id: existing.fasilitasPendukungId },
data: fasilitasPendukung,
}),
prisma.prosedurPendaftaran.update({
where: { id: existing.prosedurPendaftaranId },
data: prosedurPendaftaran,
}),
prisma.tarifDanLayanan.update({
where: { id: existing.tarifDanLayananId },
data: tarifDanLayanan,
}),
]);
// Update main record
const updated = await prisma.fasilitasKesehatan.update({
where: { id },
data: { name },
include: {
informasiumum: true,
layananunggulan: true,
dokterdantenagamedis: true,
fasilitaspendukung: true,
prosedurpendaftaran: true,
tarifdanlayanan: true,
},
});
return {
success: true,
message: "Fasilitas berhasil diupdate",
data: updated,
};
};
export default fasilitasKesehatanUpdate;

View File

@@ -11,17 +11,20 @@ type FormCreate = Prisma.GrafikKepuasanGetPayload<{
export default async function grafikKepuasanCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.grafikKepuasan.create({
const created = await prisma.grafikKepuasan.create({
data: {
label: body.label,
jumlah: body.jumlah,
},
select: {
id: true,
label: true,
jumlah: true,
}
});
return {
success: true,
message: "Success create grafik kepuasan",
data: {
...body,
},
data: created,
};
}

View File

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

View File

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

View File

@@ -1,11 +1,18 @@
import Elysia, { t } from "elysia";
import grafikKepuasanCreate from "./create";
import grafikKepuasanFindMany from "./find-many";
import grafikKepuasanFindUnique from "./findUnique";
import grafikKepuasanUpdate from "./updt";
import grafikKepuasanDelete from "./del";
const GrafikKepuasan = new Elysia({
prefix: "/grafikkepuasan",
tags: ["Data Kesehatan/Grafik Kepuasan"]
})
.get("/:id", async (context) => {
const response = await grafikKepuasanFindUnique(new Request(context.request));
return response;
})
.get("/find-many", grafikKepuasanFindMany)
.post("/create", grafikKepuasanCreate, {
body: t.Object({
@@ -13,4 +20,18 @@ const GrafikKepuasan = new Elysia({
jumlah: t.String(),
}),
})
.put("/:id", grafikKepuasanUpdate, {
params: t.Object({
id: t.String(),
}),
body: t.Object({
label: t.String(),
jumlah: t.String(),
}),
})
.delete("/del/:id", grafikKepuasanDelete, {
params: t.Object({
id: t.String(),
}),
})
export default GrafikKepuasan

View File

@@ -0,0 +1,45 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
export default async function grafikKepuasanUpdate(context: Context) {
const id = context.params?.id;
if (!id) {
return {
success: false,
message: "ID tidak ditemukan"
}
}
const {label, jumlah} = context.body as {
label: string;
jumlah: string;
}
const existing = await prisma.grafikKepuasan.findUnique({
where: {
id: id,
},
})
if (!existing) {
return {
success: false,
message: "Data tidak ditemukan",
}
}
const updated = await prisma.grafikKepuasan.update({
where: { id },
data: {
label,
jumlah,
},
})
return {
success: true,
message: "Data berhasil diupdate",
data: updated,
}
}

View File

@@ -0,0 +1,61 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
type JadwalKegiatanInput = {
content: string;
informasiJadwalKegiatan: { name: string; tanggal: string; waktu: string; lokasi: string };
deskripsiJadwalKegiatan: { deskripsi: string };
layananJadwalKegiatan: { content: string };
syaratKetentuanJadwalKegiatan: { content: string };
dokumenJadwalKegiatan: { content: string };
pendaftaranJadwalKegiatan: { name: string, tanggal: string, namaOrangtua: string, nomor: string, alamat: string, catatan: string };
}
const jadwalKegiatanCreate = async (context: Context) => {
const body = await context.body as JadwalKegiatanInput;
const {
content,
informasiJadwalKegiatan,
deskripsiJadwalKegiatan,
layananJadwalKegiatan,
syaratKetentuanJadwalKegiatan,
dokumenJadwalKegiatan,
pendaftaranJadwalKegiatan,
} = body;
const [createdInformasiJadwalKegiatan, createdDeskripsiJadwalKegiatan, createdLayananJadwalKegiatan, createdSyaratKetentuanJadwalKegiatan, createdDokumenJadwalKegiatan, createdPendaftaranJadwalKegiatan] = await Promise.all([
prisma.informasiJadwalKegiatan.create({ data: informasiJadwalKegiatan }),
prisma.deskripsiJadwalKegiatan.create({ data: deskripsiJadwalKegiatan }),
prisma.layananJadwalKegiatan.create({ data: layananJadwalKegiatan }),
prisma.syaratKetentuanJadwalKegiatan.create({ data: syaratKetentuanJadwalKegiatan }),
prisma.dokumenJadwalKegiatan.create({ data: dokumenJadwalKegiatan }),
prisma.pendaftaranJadwalKegiatan.create({ data: pendaftaranJadwalKegiatan }),
])
const jadwalKegiatan = await prisma.jadwalKegiatan.create({
data: {
content,
informasiJadwalKegiatanId: createdInformasiJadwalKegiatan.id,
deskripsiJadwalKegiatanId: createdDeskripsiJadwalKegiatan.id,
layananJadwalKegiatanId: createdLayananJadwalKegiatan.id,
syaratKetentuanJadwalKegiatanId: createdSyaratKetentuanJadwalKegiatan.id,
dokumenJadwalKegiatanId: createdDokumenJadwalKegiatan.id,
pendaftaranJadwalKegiatanId: createdPendaftaranJadwalKegiatan.id,
},
include: {
informasijadwalkegiatan: true,
deskripsijadwalkegiatan: true,
layananjadwalkegiatan: true,
syaratketentuanjadwalkegiatan: true,
dokumenjadwalkegiatan: true,
pendaftaranjadwalkegiatan: true,
}
})
return {
success: true,
message: "Success create jadwal kegiatan",
data: jadwalKegiatan
}
}
export default jadwalKegiatanCreate;

View File

@@ -0,0 +1,43 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
const jadwalKegiatanDelete = async (context: Context) => {
const id = context.params?.id as string;
if (!id) {
return {
status: 400,
message: "ID tidak ditemukan",
}
}
const jadwalKegiatan = await prisma.jadwalKegiatan.findUnique({
where: { id },
include: {
informasijadwalkegiatan: true,
deskripsijadwalkegiatan: true,
layananjadwalkegiatan: true,
syaratketentuanjadwalkegiatan: true,
dokumenjadwalkegiatan: true,
pendaftaranjadwalkegiatan: true,
}
})
if (!jadwalKegiatan) {
return {
status: 404,
message: "Jadwal kegiatan tidak ditemukan",
}
}
await prisma.jadwalKegiatan.delete({
where: { id },
})
return {
status: 200,
success: true,
message: "Jadwal kegiatan berhasil dihapus",
}
}
export default jadwalKegiatanDelete

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