Compare commits

...

9 Commits

Author SHA1 Message Date
5037009c40 Kesehatan : udah fix semua, cuma dibagian kayak detail sama edit uinya belum 2025-06-02 22:23:13 +08:00
8f2b9665a9 Jum'at, 30 May 2025 :
Yang Sudah Di Kerjakan
* Tampilan UI Admin di menu inovasi
* API Create, edit dan delete potensi
* Tampilan UI Landing Page sudah sesuai di mobile

Yang Lagi Dikerjakan:
* Progress Tampilan UI Admin Di Menu lingkungan
* Progress API Create, edit dan delete potensi

Yang Akan Dikerjakan:
* API Create, edit dan delete pengumuman
* Tampilan UI Admin Di Menu Pendidikan
2025-05-30 21:13:55 +08:00
77f99a7c8f Jum'at, 30 May 2025 :
Yang Sudah Di Kerjakan
* Tampilan UI Admin di menu inovasi
* API Create, edit dan delete potensi

Yang Lagi Dikerjakan:
* Progress Tampilan UI Admin Di Menu lingkungan
* Progress API Create, edit dan delete potensi

Yang Akan Dikerjakan:
* API Create, edit dan delete pengumuman
* Tampilan UI Admin Di Menu Pendidikan
2025-05-30 16:57:41 +08:00
d88f168258 Jumat, 30 May 2025 :
Yang Sudah Di Kerjakan
* Tampilan UI Admin di menu inovasi
* API Create, edit dan delete potensi

Yang Lagi Dikerjakan:
* Progress Tampilan UI Admin Di Menu Lingkungan

Yang Akan Dikerjakan:
* API Menu Lain
* Tampilan UI Admin Di Menu Pendidikan
2025-05-30 11:22:31 +08:00
f9bd2cea11 tambahan 2025-05-27 16:18:23 +08:00
5734e5d9a7 Selasa, 27 May 2025 :
Yang Sudah Di Kerjakan
* Tampilan UI Admin di menu ekonomi
* API Create, edit dan delete berita

Yang Lagi Dikerjakan:
* Progress Tampilan UI Admin Di Menu Inovasi
* Progress API ProfilePPID

Yang Akan Dikerjakan:
* API Menu Lain
* Tampilan UI Admin Di Menu Lingkungan
* Tampilan UI Admin Di Menu Pendidikan
2025-05-27 11:23:20 +08:00
3654629bde Senin, 26 May 2025 :
Yang Sudah Di Kerjakan
* Tampilan UI Admin di menu ekonomi
* API Create, edit dan delete berita

Yang Akan Dikerjakan:
* API ProfilePPID
* Tampilan UI Admin Di Menu Inovasi
2025-05-26 17:15:07 +08:00
02738104b5 Senin, 26 May 2025 :
Yang Sudah Di Kerjakan
* Tampilan UI Admin di menu ekonomi
* API Create, dan delete berita

Yang Akan Dikerjakan:
* API Di Menu Desa : Edit Berita
* Tampilan UI Admin Di Menu Inovasi
2025-05-26 14:26:10 +08:00
92de697ae0 Nico-25 Mei 2025:
Membuat create berita dan gambar
Membuat fungsi tombol di mana bisa menghapus konten sesuai idnya
2025-05-25 11:33:50 +08:00
111 changed files with 5726 additions and 1023 deletions

View File

@@ -0,0 +1,582 @@
/*
Warnings:
- You are about to drop the column `image` on the `Berita` table. All the data in the column will be lost.
- You are about to drop the column `katagoryBeritaId` on the `Berita` table. All the data in the column will be lost.
- You are about to drop the column `name` on the `FasilitasPendukung` table. All the data in the column will be lost.
- You are about to drop the column `fasilitasKesehatanId` on the `InformasiUmum` table. All the data in the column will be lost.
- You are about to drop the `KatagoryBerita` table. If the table is not empty, all the data it contains will be lost.
- Added the required column `imageId` to the `Berita` table without a default value. This is not possible if the table is not empty.
- Added the required column `jadwal` to the `DokterdanTenagaMedis` table without a default value. This is not possible if the table is not empty.
- Added the required column `specialist` to the `DokterdanTenagaMedis` table without a default value. This is not possible if the table is not empty.
- Added the required column `content` to the `FasilitasPendukung` table without a default value. This is not possible if the table is not empty.
*/
-- DropForeignKey
ALTER TABLE "Berita" DROP CONSTRAINT "Berita_katagoryBeritaId_fkey";
-- DropForeignKey
ALTER TABLE "InformasiUmum" DROP CONSTRAINT "InformasiUmum_fasilitasKesehatanId_fkey";
-- AlterTable
ALTER TABLE "Berita" DROP COLUMN "image",
DROP COLUMN "katagoryBeritaId",
ADD COLUMN "imageId" TEXT NOT NULL,
ADD COLUMN "kategoriBeritaId" TEXT;
-- AlterTable
ALTER TABLE "DataKematian_Kelahiran" ADD COLUMN "deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
ADD COLUMN "isActive" BOOLEAN NOT NULL DEFAULT true;
-- AlterTable
ALTER TABLE "DokterdanTenagaMedis" ADD COLUMN "jadwal" TEXT NOT NULL,
ADD COLUMN "specialist" TEXT NOT NULL;
-- AlterTable
ALTER TABLE "FasilitasPendukung" DROP COLUMN "name",
ADD COLUMN "content" TEXT NOT NULL;
-- AlterTable
ALTER TABLE "InformasiUmum" DROP COLUMN "fasilitasKesehatanId";
-- DropTable
DROP TABLE "KatagoryBerita";
-- CreateTable
CREATE TABLE "FileStorage" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"realName" TEXT NOT NULL,
"path" TEXT NOT NULL,
"mimeType" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
"deletedAt" TIMESTAMP(3),
"isActive" BOOLEAN NOT NULL DEFAULT true,
"link" TEXT NOT NULL,
CONSTRAINT "FileStorage_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "VisiMisiPPID" (
"id" TEXT NOT NULL,
"visi" TEXT NOT NULL,
"misi" 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 "VisiMisiPPID_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "DasarHukumPPID" (
"id" TEXT NOT NULL,
"judul" TEXT NOT NULL,
"content" 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 "DasarHukumPPID_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ProfilePPID" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"biodata" TEXT NOT NULL,
"riwayat" TEXT NOT NULL,
"pengalaman" TEXT NOT NULL,
"unggulan" TEXT NOT NULL,
"imageUrl" TEXT,
"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 "ProfilePPID_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "DaftarInformasiPublik" (
"id" TEXT NOT NULL,
"nomor" SERIAL NOT NULL,
"jenisInformasi" TEXT NOT NULL,
"deskripsi" TEXT NOT NULL,
"tanggal" 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 "DaftarInformasiPublik_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "PermohonanInformasiPublik" (
"id" TEXT NOT NULL,
"nomor" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"nik" TEXT NOT NULL,
"notelp" TEXT NOT NULL,
"alamat" TEXT NOT NULL,
"email" TEXT NOT NULL,
"jenisInformasiDimintaId" TEXT,
"caraMemperolehInformasiId" TEXT,
"caraMemperolehSalinanInformasiId" TEXT,
"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 "PermohonanInformasiPublik_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "JenisInformasiDiminta" (
"id" TEXT NOT NULL,
"name" 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 "JenisInformasiDiminta_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "CaraMemperolehInformasi" (
"id" TEXT NOT NULL,
"name" 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 "CaraMemperolehInformasi_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "CaraMemperolehSalinanInformasi" (
"id" TEXT NOT NULL,
"name" 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 "CaraMemperolehSalinanInformasi_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "FormulirPermohonanKeberatan" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
"notelp" TEXT NOT NULL,
"alasan" 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 "FormulirPermohonanKeberatan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "IndeksKepuasanMasyarakat" (
"id" SERIAL NOT NULL,
"label" TEXT NOT NULL,
"kepuasan" 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 "IndeksKepuasanMasyarakat_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "GrafikBerdasarkanJenisKelamin" (
"id" TEXT NOT NULL,
"perempuan" TEXT NOT NULL,
"laki" 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 "GrafikBerdasarkanJenisKelamin_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "GrafikBerdasarkanResponden" (
"id" TEXT NOT NULL,
"sangatbaik" TEXT NOT NULL,
"baik" TEXT NOT NULL,
"kurangbaik" TEXT NOT NULL,
"tidakbaik" 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 "GrafikBerdasarkanResponden_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "GrafikBerdasarkanUmur" (
"id" TEXT NOT NULL,
"remaja" TEXT NOT NULL,
"dewasa" TEXT NOT NULL,
"orangtua" TEXT NOT NULL,
"lansia" 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 "GrafikBerdasarkanUmur_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ProfileDesa" (
"id" TEXT NOT NULL,
"sejarah" TEXT NOT NULL,
"visi" TEXT NOT NULL,
"misi" TEXT NOT NULL,
"lambang" TEXT NOT NULL,
"maskot" TEXT NOT NULL,
"profilPerbekelId" TEXT,
"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 "ProfileDesa_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ProfilPerbekel" (
"id" TEXT NOT NULL,
"biodata" TEXT NOT NULL,
"pengalaman" TEXT NOT NULL,
"pengalamanOrganisasi" TEXT NOT NULL,
"programUnggulan" 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 "ProfilPerbekel_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "KategoriBerita" (
"id" TEXT NOT NULL,
"name" 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 "KategoriBerita_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "PotensiDesa" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"deskripsi" TEXT NOT NULL,
"kategori" TEXT NOT NULL,
"imageId" TEXT NOT NULL,
"content" 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 "PotensiDesa_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "TarifDanLayanan" (
"id" TEXT NOT NULL,
"layanan" TEXT NOT NULL,
"tarif" 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 "TarifDanLayanan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "JadwalKegiatan" (
"id" TEXT NOT NULL,
"content" 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 "JadwalKegiatan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "InformasiJadwalKegiatan" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"tanggal" TEXT NOT NULL,
"waktu" TEXT NOT NULL,
"lokasi" 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 "InformasiJadwalKegiatan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "DeskripsiJadwalKegiatan" (
"id" TEXT NOT NULL,
"deskripsi" 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 "DeskripsiJadwalKegiatan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "LayananJadwalKegiatan" (
"id" TEXT NOT NULL,
"content" 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 "LayananJadwalKegiatan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "SyaratKetentuanJadwalKegiatan" (
"id" TEXT NOT NULL,
"content" 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 "SyaratKetentuanJadwalKegiatan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "DokumenJadwalKegiatan" (
"id" TEXT NOT NULL,
"content" 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 "DokumenJadwalKegiatan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "PendaftaranJadwalKegiatan" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"tanggal" TEXT NOT NULL,
"namaOrangtua" TEXT NOT NULL,
"nomor" TEXT NOT NULL,
"alamat" TEXT NOT NULL,
"catatan" 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 "PendaftaranJadwalKegiatan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "GrafikKepuasan" (
"id" SERIAL NOT NULL,
"label" TEXT NOT NULL,
"jumlah" 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 "GrafikKepuasan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ArtikelKesehatan" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"content" 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 "ArtikelKesehatan_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Introduction" (
"id" SERIAL NOT NULL,
"content" 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 "Introduction_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Symptom" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"content" 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 "Symptom_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Prevention" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"content" 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 "Prevention_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "FirstAid" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"content" 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 "FirstAid_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "MythVsFact" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"mitos" TEXT NOT NULL,
"fakta" 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 "MythVsFact_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "DoctorSign" (
"id" SERIAL NOT NULL,
"content" 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 "DoctorSign_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "_FasilitasKesehatanToInformasiUmum" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_FasilitasKesehatanToInformasiUmum_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateTable
CREATE TABLE "_FasilitasKesehatanToTarifDanLayanan" (
"A" TEXT NOT NULL,
"B" TEXT NOT NULL,
CONSTRAINT "_FasilitasKesehatanToTarifDanLayanan_AB_pkey" PRIMARY KEY ("A","B")
);
-- CreateIndex
CREATE UNIQUE INDEX "FileStorage_name_key" ON "FileStorage"("name");
-- CreateIndex
CREATE UNIQUE INDEX "JenisInformasiDiminta_name_key" ON "JenisInformasiDiminta"("name");
-- CreateIndex
CREATE UNIQUE INDEX "CaraMemperolehInformasi_name_key" ON "CaraMemperolehInformasi"("name");
-- CreateIndex
CREATE UNIQUE INDEX "CaraMemperolehSalinanInformasi_name_key" ON "CaraMemperolehSalinanInformasi"("name");
-- CreateIndex
CREATE UNIQUE INDEX "KategoriBerita_name_key" ON "KategoriBerita"("name");
-- CreateIndex
CREATE INDEX "_FasilitasKesehatanToInformasiUmum_B_index" ON "_FasilitasKesehatanToInformasiUmum"("B");
-- CreateIndex
CREATE INDEX "_FasilitasKesehatanToTarifDanLayanan_B_index" ON "_FasilitasKesehatanToTarifDanLayanan"("B");
-- AddForeignKey
ALTER TABLE "PermohonanInformasiPublik" ADD CONSTRAINT "PermohonanInformasiPublik_jenisInformasiDimintaId_fkey" FOREIGN KEY ("jenisInformasiDimintaId") REFERENCES "JenisInformasiDiminta"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PermohonanInformasiPublik" ADD CONSTRAINT "PermohonanInformasiPublik_caraMemperolehInformasiId_fkey" FOREIGN KEY ("caraMemperolehInformasiId") REFERENCES "CaraMemperolehInformasi"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PermohonanInformasiPublik" ADD CONSTRAINT "PermohonanInformasiPublik_caraMemperolehSalinanInformasiId_fkey" FOREIGN KEY ("caraMemperolehSalinanInformasiId") REFERENCES "CaraMemperolehSalinanInformasi"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProfileDesa" ADD CONSTRAINT "ProfileDesa_profilPerbekelId_fkey" FOREIGN KEY ("profilPerbekelId") REFERENCES "ProfilPerbekel"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Berita" ADD CONSTRAINT "Berita_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Berita" ADD CONSTRAINT "Berita_kategoriBeritaId_fkey" FOREIGN KEY ("kategoriBeritaId") REFERENCES "KategoriBerita"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PotensiDesa" ADD CONSTRAINT "PotensiDesa_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_FasilitasKesehatanToInformasiUmum" ADD CONSTRAINT "_FasilitasKesehatanToInformasiUmum_A_fkey" FOREIGN KEY ("A") REFERENCES "FasilitasKesehatan"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_FasilitasKesehatanToInformasiUmum" ADD CONSTRAINT "_FasilitasKesehatanToInformasiUmum_B_fkey" FOREIGN KEY ("B") REFERENCES "InformasiUmum"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_FasilitasKesehatanToTarifDanLayanan" ADD CONSTRAINT "_FasilitasKesehatanToTarifDanLayanan_A_fkey" FOREIGN KEY ("A") REFERENCES "FasilitasKesehatan"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_FasilitasKesehatanToTarifDanLayanan" ADD CONSTRAINT "_FasilitasKesehatanToTarifDanLayanan_B_fkey" FOREIGN KEY ("B") REFERENCES "TarifDanLayanan"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -47,6 +47,23 @@ model AppMenuChild {
appMenuId String?
}
// ========================================= FILE STORAGE ========================================= //
model FileStorage {
id String @id @default(cuid())
name String @unique
realName String
path String
mimeType String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
isActive Boolean @default(true)
link String
Berita Berita[]
PotensiDesa PotensiDesa[]
}
//========================================= MENU PPID ========================================= //
// ========================================= VISI MISI PPID ========================================= //
model VisiMisiPPID {
@@ -263,6 +280,21 @@ model KategoriBerita {
isActive Boolean @default(true)
}
// ========================================= POTENSI DESA ========================================= //
model PotensiDesa {
id String @id @default(cuid())
name String
deskripsi String
kategori String
image FileStorage @relation(fields: [imageId], references: [id])
imageId String
content String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
// ========================================= PENGUMUMAN ========================================= //
model Pengumuman {
id String @id @default(cuid())
@@ -583,19 +615,3 @@ model DoctorSign {
deletedAt DateTime @default(now())
isActive Boolean @default(true)
}
// === BARU
model FileStorage {
id String @id @default(cuid())
name String @unique
realName String
path String
mimeType String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
isActive Boolean @default(true)
link String
Berita Berita[]
}

View File

@@ -8,8 +8,6 @@ import layanan from "./data/list-layanan.json";
import potensi from "./data/list-potensi.json";
import visiMisiPPID from "./data/ppid/visi-misi-ppid/visimisiPPID.json";
import dasarHukumPPID from "./data/ppid/dasar-hukum-ppid/dasarhukumPPID.json";
import profileDesa from "./data/desa/profile/profile_desa.json";
import profilePerbekel from "./data/desa/profile/profil_perbekel.json";
import profilePPID from "./data/ppid/profile-ppid/profilePPid.json";
import path from "path";
import fs from "fs";
@@ -213,53 +211,7 @@ import { v4 as uuid } from "uuid";
}
console.log("dasar hukum PPID success ...");
for (const v of profileDesa) {
await prisma.profileDesa.upsert({
where: {
id: v.id,
},
update: {
sejarah: v.sejarah,
visi: v.visi,
misi: v.misi,
lambang: v.lambang,
maskot: v.maskot,
profilPerbekelId: v.profilPerbekelId,
},
create: {
id: v.id,
sejarah: v.sejarah,
visi: v.visi,
misi: v.misi,
lambang: v.lambang,
maskot: v.maskot,
profilPerbekelId: v.profilPerbekelId,
},
});
}
console.log("profile desa success ...");
for (const v of profilePerbekel) {
await prisma.profilPerbekel.upsert({
where: {
id: v.id,
},
update: {
biodata: v.biodata,
pengalaman: v.pengalaman,
pengalamanOrganisasi: v.pengalamanOrganisasi,
programUnggulan: v.programUnggulan,
},
create: {
id: v.id,
biodata: v.biodata,
pengalaman: v.pengalaman,
pengalamanOrganisasi: v.pengalamanOrganisasi,
programUnggulan: v.programUnggulan,
},
});
}
console.log("profile perbekel success ...");
})()
.then(() => prisma.$disconnect())
.catch((e) => {

View File

Before

Width:  |  Height:  |  Size: 275 KiB

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

View File

@@ -0,0 +1,85 @@
// TestEditor.tsx
import { RichTextEditor, Link } from '@mantine/tiptap';
import { useEditor } from '@tiptap/react';
import Highlight from '@tiptap/extension-highlight';
import StarterKit from '@tiptap/starter-kit';
import Underline from '@tiptap/extension-underline';
import TextAlign from '@tiptap/extension-text-align';
import Superscript from '@tiptap/extension-superscript';
import SubScript from '@tiptap/extension-subscript';
type CreateEditorProps = {
value: string;
onChange: (content: string) => void;
};
export default function CreateEditor({ value, onChange }: CreateEditorProps) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Superscript,
SubScript,
Highlight,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
content: value,
onUpdate: () => {
if (editor) {
onChange(editor.getHTML());
}
},
});
return (
<RichTextEditor editor={editor}>
<RichTextEditor.Toolbar sticky stickyOffset="var(--docs-header-height)">
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold />
<RichTextEditor.Italic />
<RichTextEditor.Underline />
<RichTextEditor.Strikethrough />
<RichTextEditor.ClearFormatting />
<RichTextEditor.Highlight />
<RichTextEditor.Code />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.H1 />
<RichTextEditor.H2 />
<RichTextEditor.H3 />
<RichTextEditor.H4 />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Blockquote />
<RichTextEditor.Hr />
<RichTextEditor.BulletList />
<RichTextEditor.OrderedList />
<RichTextEditor.Subscript />
<RichTextEditor.Superscript />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Link />
<RichTextEditor.Unlink />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.AlignLeft />
<RichTextEditor.AlignCenter />
<RichTextEditor.AlignJustify />
<RichTextEditor.AlignRight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Undo />
<RichTextEditor.Redo />
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
<RichTextEditor.Content />
</RichTextEditor>
);
}

View File

@@ -0,0 +1,102 @@
'use client'
import { RichTextEditor, Link } from '@mantine/tiptap';
import { useEditor } from '@tiptap/react';
import Highlight from '@tiptap/extension-highlight';
import StarterKit from '@tiptap/starter-kit';
import Underline from '@tiptap/extension-underline';
import TextAlign from '@tiptap/extension-text-align';
import Superscript from '@tiptap/extension-superscript';
import SubScript from '@tiptap/extension-subscript';
import { useEffect } from 'react';
type EditEditorProps = {
value: string;
onChange: (content: string) => void;
};
export default function EditEditor({ value, onChange }: EditEditorProps) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Superscript,
SubScript,
Highlight,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
content: value,
// Hapus `immediatelyRender` dan `onMount`
});
// Sinkronisasi konten saat `value` berubah
useEffect(() => {
if (editor && value !== editor.getHTML()) {
editor.commands.setContent(value);
}
}, [value, editor]);
// Sinkronisasi konten ke parent saat diubah
useEffect(() => {
if (!editor) return;
const updateHandler = () => onChange(editor.getHTML());
editor.on('update', updateHandler);
return () => {
editor.off('update', updateHandler);
};
}, [editor, onChange]);
return (
<RichTextEditor editor={editor}>
<RichTextEditor.Toolbar sticky stickyOffset="var(--docs-header-height)">
{/* Toolbar seperti sebelumnya */}
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold />
<RichTextEditor.Italic />
<RichTextEditor.Underline />
<RichTextEditor.Strikethrough />
<RichTextEditor.ClearFormatting />
<RichTextEditor.Highlight />
<RichTextEditor.Code />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.H1 />
<RichTextEditor.H2 />
<RichTextEditor.H3 />
<RichTextEditor.H4 />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Blockquote />
<RichTextEditor.Hr />
<RichTextEditor.BulletList />
<RichTextEditor.OrderedList />
<RichTextEditor.Subscript />
<RichTextEditor.Superscript />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Link />
<RichTextEditor.Unlink />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.AlignLeft />
<RichTextEditor.AlignCenter />
<RichTextEditor.AlignJustify />
<RichTextEditor.AlignRight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Undo />
<RichTextEditor.Redo />
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
<RichTextEditor.Content />
</RichTextEditor>
);
}

View File

@@ -0,0 +1,27 @@
import React from 'react';
import { Grid, GridCol, Paper, TextInput, Title } from '@mantine/core';
import { IconSearch } from '@tabler/icons-react'; // Sesuaikan jika kamu pakai icon lain
import colors from '@/con/colors';
const HeaderSearch = ({ title = "", placeholder = "pencarian", searchIcon = <IconSearch size={20} /> }: { title: string, placeholder?: string, searchIcon?: React.ReactNode }) => {
return (
<Grid>
<GridCol span={{ base: 12, md: 9 }}>
<Title order={3}>{title}</Title>
</GridCol>
<GridCol span={{ base: 12, md: 3 }}>
<Paper radius={"lg"} bg={colors['white-1']}>
<TextInput
radius="lg"
placeholder={placeholder}
leftSection={searchIcon}
w="100%"
/>
</Paper>
</GridCol>
</Grid>
);
};
export default HeaderSearch;

View File

@@ -0,0 +1,30 @@
'use client'
import colors from '@/con/colors';
import { Grid, GridCol, Button, Text } from '@mantine/core';
import { IconCircleDashedPlus } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import React from 'react';
const JudulList = ({ title = "", href = "#" }) => {
const router = useRouter();
const handleNavigate = () => {
router.push(href);
};
return (
<Grid align="center" mb={10}>
<GridCol span={{ base: 12, md: 11 }}>
<Text fz={"xl"} fw={"bold"}>{title}</Text>
</GridCol>
<GridCol span={{ base: 12, md: 1 }} ta="right">
<Button onClick={handleNavigate} bg={colors['blue-button']}>
<IconCircleDashedPlus size={25} />
</Button>
</GridCol>
</Grid>
);
};
export default JudulList;

View File

@@ -0,0 +1,41 @@
'use client'
import colors from '@/con/colors';
import { Grid, GridCol, Button, Text, Paper, TextInput } from '@mantine/core';
import { IconCircleDashedPlus, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import React from 'react';
const JudulListTab = ({ title = "", href = "#", placeholder = "pencarian", searchIcon = <IconSearch size={20} /> }) => {
const router = useRouter();
const handleNavigate = () => {
router.push(href);
};
return (
<Grid mb={10}>
<GridCol span={{ base: 12, md: 8 }}>
<Text fz={{base: "md", md: "xl"}} fw={"bold"}>{title}</Text>
</GridCol>
<GridCol span={{ base: 9, md: 3}} ta="right">
<Paper radius={"lg"} bg={colors['white-1']}>
<TextInput
radius="lg"
placeholder={placeholder}
leftSection={searchIcon}
w="100%"
/>
</Paper>
</GridCol>
<GridCol span={{ base: 3, md: 1}} ta="right">
<Button onClick={handleNavigate} bg={colors['blue-button']}>
<IconCircleDashedPlus size={25} />
</Button>
</GridCol>
</Grid>
);
};
export default JudulListTab;

View File

@@ -0,0 +1,36 @@
// components/modal/ModalKonfirmasiHapus.tsx
import colors from "@/con/colors"
import { Modal, Text, Button, Flex } from "@mantine/core"
interface ModalKonfirmasiHapusProps {
opened: boolean
loading?: boolean
onClose: () => void
onConfirm: () => void
text: string
}
export function ModalKonfirmasiHapus({
opened,
loading = false,
onClose,
onConfirm,
text,
}: ModalKonfirmasiHapusProps) {
return (
<Modal
opened={opened}
onClose={onClose}
title="Konfirmasi Hapus"
centered
>
<Text mb="md">{text}</Text>
<Flex justify="flex-end" gap="sm">
<Button style={{color: "white"}} bg={colors['blue-button']} variant="default" onClick={onClose}>Batal</Button>
<Button color="red" onClick={onConfirm} loading={loading}>
Yakin Hapus
</Button>
</Flex>
</Modal>
)
}

View File

@@ -1,10 +1,10 @@
/* 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";
// 1. Schema validasi dengan Zod
const templateForm = z.object({
judul: z.string().min(3, "Judul minimal 3 karakter"),
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
@@ -13,11 +13,19 @@ const templateForm = z.object({
imageId: z.string().nonempty(),
});
// 2. Default value form berita (hindari uncontrolled input)
const defaultForm = {
judul: "",
deskripsi: "",
imageId: "",
content: "",
kategoriBeritaId: "",
};
// 3. Kategori proxy
const category = proxy({
findMany: {
data: null as
| null
| Prisma.KategoriBeritaGetPayload<{ omit: { isActive: true } }>[],
data: [] as Prisma.KategoriBeritaGetPayload<{ omit: { isActive: true } }>[],
async load() {
const res = await ApiFetch.api.desa.berita.category["find-many"].get();
if (res.status === 200) {
@@ -27,19 +35,10 @@ const category = proxy({
},
});
type BeritaForm = Prisma.BeritaGetPayload<{
select: {
judul: true;
deskripsi: true;
imageId: true;
content: true;
kategoriBeritaId: true;
};
}>;
// 4. Berita proxy
const berita = proxy({
create: {
form: {} as BeritaForm,
form: { ...defaultForm }, // ✅ ini kunci fix-nya
loading: false,
async create() {
const cek = templateForm.safeParse(berita.create.form);
@@ -49,6 +48,7 @@ const berita = proxy({
.join("\n")}] required`;
return toast.error(err);
}
try {
berita.create.loading = true;
const res = await ApiFetch.api.desa.berita["create"].post(
@@ -56,38 +56,200 @@ const berita = proxy({
);
if (res.status === 200) {
berita.findMany.load();
return toast.success("succes create");
return toast.success("Berita berhasil disimpan!");
}
return toast.error("failed create");
return toast.error("Gagal menyimpan berita");
} catch (error) {
console.log((error as Error).message);
} finally {
berita.create.loading = false;
}
},
resetForm() {
berita.create.form = { ...defaultForm };
},
},
findMany: {
data: null as
| Prisma.BeritaGetPayload<{
include: {
image: true,
kategoriBerita: true
}
}>[]
| Prisma.BeritaGetPayload<{
include: {
image: true;
kategoriBerita: true;
};
}>[]
| null,
async load() {
const res = await ApiFetch.api.desa.berita["find-many"].get();
if (res.status === 200) {
berita.findMany.data = (res.data?.data as any) ?? [];
berita.findMany.data = (res.data?.data ) ?? [];
}
},
},
findUnique: {
data: null as
| Prisma.BeritaGetPayload<{
include: {
image: true;
kategoriBerita: true;
};
}> | null,
async load(id: string) {
try {
const res = await fetch(`/api/desa/berita/${id}`);
if (res.ok) {
const data = await res.json();
berita.findUnique.data = data.data ?? null;
} else {
console.error('Failed to fetch berita:', res.statusText);
berita.findUnique.data = null;
}
} catch (error) {
console.error('Error fetching berita:', error);
berita.findUnique.data = null;
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) return toast.warn("ID tidak valid");
try {
berita.delete.loading = true;
const response = await fetch(`/api/desa/berita/delete/${id}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
});
const result = await response.json();
if (response.ok && result?.success) {
toast.success(result.message || "Berita berhasil dihapus");
await berita.findMany.load(); // refresh list
} else {
toast.error(result?.message || "Gagal menghapus berita");
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus berita");
} finally {
berita.delete.loading = false;
}
},
},
edit: {
id: "",
form: { ...defaultForm },
loading: false,
async load(id: string) {
if (!id) {
toast.warn("ID tidak valid");
return null;
}
try {
const response = await fetch(`/api/desa/berita/${id}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result?.success) {
const data = result.data;
this.id = data.id;
this.form = {
judul: data.judul,
deskripsi: data.deskripsi,
content: data.content,
kategoriBeritaId: data.kategoriBeritaId || "",
imageId: data.imageId || "",
};
return data; // Return the loaded data
} else {
throw new Error(result?.message || "Gagal memuat data");
}
} catch (error) {
console.error("Error loading berita:", error);
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
return null;
}
},
async update() {
const cek = templateForm.safeParse(berita.edit.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return false;
}
try {
berita.edit.loading = true;
const response = await fetch(`/api/desa/berita/${this.id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
judul: this.form.judul,
deskripsi: this.form.deskripsi,
content: this.form.content,
kategoriBeritaId: this.form.kategoriBeritaId || null,
imageId: this.form.imageId,
}),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result.success) {
toast.success("Berhasil update berita");
await berita.findMany.load(); // refresh list
return true;
} else {
throw new Error(result.message || "Gagal update berita");
}
} catch (error) {
console.error("Error updating berita:", error);
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat update berita");
return false;
} finally {
berita.edit.loading = false;
}
},
reset() {
berita.edit.id = "";
berita.edit.form = { ...defaultForm };
},
},
});
// 5. State global
const stateDashboardBerita = proxy({
category,
berita,
});
export default stateDashboardBerita;
export default stateDashboardBerita;

View File

@@ -0,0 +1,223 @@
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
const templateForm = z.object({
name: z.string().min(1).max(50),
deskripsi: z.string().min(1).max(50),
kategori: z.string().min(1).max(50),
imageId: z.string().min(1).max(50),
content: z.string().min(1).max(5000),
})
const defaultForm = {
name: "",
deskripsi: "",
kategori: "",
imageId: "",
content: "",
}
const potensiDesaState = proxy({
create: {
form: { ...defaultForm },
loading: false,
async create() {
const cek = templateForm.safeParse(potensiDesaState.create.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
return toast.error(err);
}
try {
potensiDesaState.create.loading = true;
const res = await ApiFetch.api.desa.potensi["create"].post(potensiDesaState.create.form);
if (res.status === 200) {
potensiDesaState.findMany.load();
return toast.success("Potensi berhasil disimpan!");
}
return toast.error("Gagal menyimpan potensi");
} catch (error) {
console.log((error as Error).message);
} finally {
potensiDesaState.create.loading = false;
}
}
},
findMany: {
data: null as
| Prisma.PotensiDesaGetPayload<{
include: {
image: true;
}
}>[]
| null,
async load() {
const res = await ApiFetch.api.desa.potensi["find-many"].get();
if (res.status === 200) {
potensiDesaState.findMany.data = res.data?.data ?? [];
}
}
},
findUnique: {
data: null as
| Prisma.PotensiDesaGetPayload<{
include: {
image: true;
}
}> | null,
async load(id: string) {
try {
const res = await fetch(`/api/desa/potensi/${id}`);
if (res.ok) {
const data = await res.json();
potensiDesaState.findUnique.data = data.data ?? null;
} else {
console.error('Failed to fetch potensi:', res.statusText);
potensiDesaState.findUnique.data = null;
}
} catch (error) {
console.error('Error fetching potensi:', error);
potensiDesaState.findUnique.data = null;
}
},
},
delete: {
loading: false,
async byId(id: string) {
if (!id) return toast.warn("ID tidak valid");
try {
potensiDesaState.delete.loading = true;
const response = await fetch(`/api/desa/potensi/del/${id}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
});
const result = await response.json();
if (response.ok && result?.success) {
toast.success(result.message || "Potensi berhasil dihapus");
await potensiDesaState.findMany.load(); // refresh list
} else {
toast.error(result?.message || "Gagal menghapus potensi");
}
} catch (error) {
console.error("Gagal delete:", error);
toast.error("Terjadi kesalahan saat menghapus potensi");
} finally {
potensiDesaState.delete.loading = false;
}
},
},
edit: {
id: "",
form: { ...defaultForm },
loading: false,
async load(id: string) {
if (!id) {
toast.warn("ID tidak valid");
return null;
}
try {
const response = await fetch(`/api/desa/potensi/${id}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result?.success) {
const data = result.data;
this.id = data.id;
this.form = {
name: data.name,
deskripsi: data.deskripsi,
kategori: data.kategori,
imageId: data.imageId || "",
content: data.content,
};
return data; // Return the loaded data
} else {
throw new Error(result?.message || "Gagal memuat data");
}
} catch (error) {
console.error("Error loading potensi:", error);
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
return null;
}
},
async update() {
const cek = templateForm.safeParse(potensiDesaState.edit.form);
if (!cek.success) {
const err = `[${cek.error.issues
.map((v) => `${v.path.join(".")}`)
.join("\n")}] required`;
toast.error(err);
return false;
}
try {
potensiDesaState.edit.loading = true;
const response = await fetch(`/api/desa/potensi/${this.id}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: this.form.name,
deskripsi: this.form.deskripsi,
kategori: this.form.kategori,
imageId: this.form.imageId,
content: this.form.content,
}),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result.success) {
toast.success("Berhasil update potensi");
await potensiDesaState.findMany.load(); // refresh list
return true;
} else {
throw new Error(result.message || "Gagal update potensi");
}
} catch (error) {
console.error("Error updating potensi:", error);
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat update potensi");
return false;
} finally {
potensiDesaState.edit.loading = false;
}
},
reset() {
potensiDesaState.edit.id = "";
potensiDesaState.edit.form = { ...defaultForm };
}
}
})
export default potensiDesaState

View File

@@ -0,0 +1,241 @@
"use client";
import {
Box,
Button,
Center,
Image,
Paper,
Select,
Skeleton,
Stack,
Text,
TextInput,
Title,
} from "@mantine/core";
import { IconArrowBack, IconImageInPicture } 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";
import EditEditor from "@/app/admin/(dashboard)/_com/editEditor";
import colors from "@/con/colors";
import ApiFetch from "@/lib/api-fetch";
import { FileInput } from "@mantine/core";
import { useShallowEffect } from "@mantine/hooks";
import { Prisma } from "@prisma/client";
import stateDashboardBerita from "../../../../_state/desa/berita";
function EditBerita() {
const beritaState = useProxy(stateDashboardBerita);
const router = useRouter();
const params = useParams();
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const [formData, setFormData] = useState({
judul: beritaState.berita.edit.form.judul || '',
deskripsi: beritaState.berita.edit.form.deskripsi || '',
kategoriBeritaId: beritaState.berita.edit.form.kategoriBeritaId || '',
content: beritaState.berita.edit.form.content || '',
imageId: beritaState.berita.edit.form.imageId || ''
});
// Load berita by id saat pertama kali
useEffect(() => {
const loadBerita = async () => {
const id = params?.id as string;
if (!id) return;
try {
const data = await stateDashboardBerita.berita.edit.load(id); // akses langsung, bukan dari proxy
if (data) {
setFormData({
judul: data.judul || '',
deskripsi: data.deskripsi || '',
kategoriBeritaId: data.kategoriBeritaId || '',
content: data.content || '',
imageId: data.imageId || '',
});
if (data?.image?.link) {
setPreviewImage(data.image.link);
}
}
} catch (error) {
console.error("Error loading berita:", error);
toast.error("Gagal memuat data berita");
}
};
loadBerita();
}, [params?.id]); // ✅ hapus beritaState dari dependency
const handleSubmit = async () => {
try {
// Update global state with form data
beritaState.berita.edit.form = {
...beritaState.berita.edit.form,
judul: formData.judul,
deskripsi: formData.deskripsi,
content: formData.content,
kategoriBeritaId: formData.kategoriBeritaId || '',
imageId: formData.imageId // Keep existing imageId if not changed
};
// Jika ada file baru, upload
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
if (!uploaded?.id) {
return toast.error("Gagal upload gambar");
}
// Update imageId in global state
beritaState.berita.edit.form.imageId = uploaded.id;
}
await beritaState.berita.edit.update();
toast.success("Berita berhasil diperbarui!");
router.push("/admin/desa/berita");
} catch (error) {
console.error("Error updating berita:", error);
toast.error("Terjadi kesalahan saat memperbarui berita");
}
};
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors["blue-button"]} size={30} />
</Button>
</Box>
<Paper bg={"white"} p={"md"} w={{ base: "100%", md: "50%" }}>
<Stack gap={"xs"}>
<Title order={3}>Edit Berita</Title>
<TextInput
value={formData.judul}
onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
placeholder="masukkan judul"
/>
<SelectCategory
value={formData.kategoriBeritaId}
onChange={(val) => {
setFormData({
...formData,
kategoriBeritaId: val?.id || ''
});
}}
/>
<TextInput
value={formData.deskripsi}
onChange={(e) => setFormData({ ...formData, deskripsi: e.target.value })}
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<FileInput
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar Baru (Opsional)</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
"data:image/png;base64," + Buffer.from(buf).toString("base64")
);
setPreviewImage(base64);
}}
/>
{previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg={"gray"}>
<IconImageInPicture />
</Center>
)}
<Box>
<Text fz={"sm"} fw={"bold"}>Konten</Text>
<EditEditor
value={formData.content}
onChange={(htmlContent) => {
setFormData((prev) => ({ ...prev, content: htmlContent }));
beritaState.berita.edit.form.content = htmlContent;
}}
/>
</Box>
<Button onClick={handleSubmit}>Edit Berita</Button>
</Stack>
</Paper>
</Box>
);
}
interface SelectCategoryProps {
onChange: (value: Prisma.KategoriBeritaGetPayload<{
select: {
name: true;
id: true;
};
}> | null) => void;
value?: string | null;
defaultValue?: string | null;
}
function SelectCategory({
onChange,
value,
defaultValue,
}: SelectCategoryProps) {
const categoryState = useProxy(stateDashboardBerita.category);
useShallowEffect(() => {
categoryState.findMany.load().then(() => {
console.log("Kategori berhasil dimuat:", categoryState.findMany.data);
});
}, []);
if (!categoryState.findMany.data) {
return <Skeleton height={38} />;
}
const selectedValue = value || defaultValue;
return (
<Select
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
placeholder="Pilih kategori"
data={categoryState.findMany.data.map((item) => ({
label: item.name,
value: item.id,
}))}
value={selectedValue || null}
onChange={(val: string | null) => {
if (val) {
const selected = categoryState.findMany.data?.find((item) => item.id === val);
if (selected) {
onChange(selected);
}
} else {
onChange(null);
}
}}
searchable
clearable
nothingFoundMessage="Tidak ditemukan"
/>
);
}
export default EditBerita;

View File

@@ -0,0 +1,120 @@
'use client'
import { useProxy } from 'valtio/utils';
import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
import { useParams, useRouter } from 'next/navigation';
import { useState } from 'react';
import colors from '@/con/colors';
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
import stateDashboardBerita from '../../../_state/desa/berita';
function DetailBerita() {
const beritaState = useProxy(stateDashboardBerita)
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const params = useParams()
const router = useRouter()
useShallowEffect(() => {
beritaState.berita.findUnique.load(params?.id as string)
}, [])
const handleHapus = () => {
if (selectedId) {
beritaState.berita.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
router.push("/admin/desa/berita")
}
}
if (!beritaState.berita.findUnique.data) {
return (
<Stack py={10}>
{Array.from({ length: 10 }).map((_, k) => (
<Skeleton key={k} h={40} />
))}
</Stack>
)
}
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
<Stack>
<Text fz={"xl"} fw={"bold"}>Detail Berita</Text>
{beritaState.berita.findUnique.data ? (
<Paper key={beritaState.berita.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fw={"bold"} fz={"lg"}>Kategori</Text>
<Text fz={"lg"}>{beritaState.berita.findUnique.data?.kategoriBerita?.name}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Judul</Text>
<Text fz={"lg"}>{beritaState.berita.findUnique.data?.judul}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
<Text fz={"lg"} >{beritaState.berita.findUnique.data?.deskripsi}</Text>
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
<Image w={{ base: 150, md: 150, lg: 150 }} src={beritaState.berita.findUnique.data?.image?.link} alt="gambar" />
</Box>
<Box>
<Text fw={"bold"} fz={"lg"}>Konten</Text>
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: beritaState.berita.findUnique.data?.content }} />
</Box>
<Flex gap={"xs"} mt={10}>
<Button
onClick={() => {
if (beritaState.berita.findUnique.data) {
setSelectedId(beritaState.berita.findUnique.data.id);
setModalHapus(true);
}
}}
disabled={beritaState.berita.delete.loading || !beritaState.berita.findUnique.data}
color={"red"}
>
<IconX size={20} />
</Button>
<Button
onClick={() => {
if (beritaState.berita.findUnique.data) {
router.push(`/admin/desa/berita/${beritaState.berita.findUnique.data.id}/edit`);
}
}}
disabled={!beritaState.berita.findUnique.data}
color={"green"}
>
<IconEdit size={20} />
</Button>
</Flex>
</Stack>
</Paper>
) : null}
</Stack>
</Paper>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text='Apakah anda yakin ingin menghapus berita ini?'
/>
</Box>
);
}
export default DetailBerita;

View File

@@ -9,7 +9,7 @@ import TextAlign from '@tiptap/extension-text-align';
import Superscript from '@tiptap/extension-superscript';
import SubScript from '@tiptap/extension-subscript';
import { Button, Stack } from '@mantine/core';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
// const content =
// '<h2 style="text-align: center;">Welcome to Mantine rich text editor</h2><p><code>RichTextEditor</code> component focuses on usability and is designed to be as simple as possible to bring a familiar editing experience to regular users. <code>RichTextEditor</code> is based on <a href="https://tiptap.dev/" rel="noopener noreferrer" target="_blank">Tiptap.dev</a> and supports all of its features:</p><ul><li>General text formatting: <strong>bold</strong>, <em>italic</em>, <u>underline</u>, <s>strike-through</s> </li><li>Headings (h1-h6)</li><li>Sub and super scripts (<sup>&lt;sup /&gt;</sup> and <sub>&lt;sub /&gt;</sub> tags)</li><li>Ordered and bullet lists</li><li>Text align&nbsp;</li><li>And all <a href="https://tiptap.dev/extensions" target="_blank" rel="noopener noreferrer">other extensions</a></li></ul>';
@@ -18,11 +18,18 @@ import { useEffect } from 'react';
onEditorReady,
showSubmit = true,
onSubmit,
initialContent = '',
onUpdate,
}: {
onEditorReady?: (editor: any | null) => void;
onSubmit?: (val: string) => void;
showSubmit?: boolean;
initialContent?: string;
onUpdate?: (content: string) => void;
}) {
const [mounted, setMounted] = useState(false);
const [isReady, setIsReady] = useState(false);
const editor = useEditor({
extensions: [
StarterKit,
@@ -33,14 +40,46 @@ import { useEffect } from 'react';
Highlight,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
content: '',
content: initialContent || '<p></p>',
onUpdate: ({ editor }) => {
if (onUpdate) {
onUpdate(editor.getHTML());
}
},
editorProps: {
attributes: {
class: 'prose max-w-none',
},
},
onSelectionUpdate: () => {
if (!isReady && editor) {
setIsReady(true);
onEditorReady?.(editor);
}
},
immediatelyRender: false
});
useEffect(() => {
onEditorReady?.(editor);
}, [editor, onEditorReady] );
if (editor) {
// Set initial content when component mounts
editor.commands.setContent(initialContent || '<p></p>');
// Mark as mounted and notify parent
if (!mounted) {
setMounted(true);
onEditorReady?.(editor);
}
}
return () => {
if (editor) {
editor.destroy();
}
};
}, [editor, initialContent, mounted, onEditorReady]);
if (!editor) return null;
if (!editor) return <div>Loading editor...</div>;
return (

View File

@@ -0,0 +1,168 @@
'use client'
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
import { Box, Button, Center, FileInput, Image, Paper, Select, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { Prisma } from '@prisma/client';
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
import CreateEditor from '../../../_com/createEditor';
import stateDashboardBerita from '../../../_state/desa/berita';
export default function CreateBerita() {
const beritaState = useProxy(stateDashboardBerita);
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const router = useRouter()
const resetForm = () => {
// Reset state di valtio
beritaState.berita.create.form = {
judul: "",
deskripsi: "",
kategoriBeritaId: "",
imageId: "",
content: "",
};
// Reset state lokal
setPreviewImage(null);
setFile(null);
};
const handleSubmit = async () => {
if (!file) {
return toast.warn("Pilih file gambar terlebih dahulu");
}
// Upload gambar dulu
const res = await ApiFetch.api.fileStorage.create.post({
file,
name: file.name,
});
const uploaded = res.data?.data;
if (!uploaded?.id) {
return toast.error("Gagal upload gambar");
}
// Simpan ID gambar ke form
beritaState.berita.create.form.imageId = uploaded.id;
// Submit data berita
await beritaState.berita.create.create();
// Reset form setelah submit
resetForm();
router.push("/admin/desa/berita")
};
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
<Stack gap={"xs"}>
<Title order={3}>Create Berita</Title>
<TextInput
value={beritaState.berita.create.form.judul}
onChange={(val) => {
beritaState.berita.create.form.judul = val.target.value;
}}
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
placeholder="masukkan judul"
/>
<SelectCategory
onChange={(val) => {
beritaState.berita.create.form.kategoriBeritaId = val.id;
}}
/>
<TextInput
value={beritaState.berita.create.form.deskripsi}
onChange={(val) => {
beritaState.berita.create.form.deskripsi = val.target.value;
}}
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<FileInput
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
"data:image/png;base64," + Buffer.from(buf).toString("base64")
);
setPreviewImage(base64);
}}
/>
{previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg={"gray"}>
<IconImageInPicture />
</Center>
)}
<Box>
<Text fz={"sm"} fw={"bold"}>Konten</Text>
<CreateEditor
value={beritaState.berita.create.form.content}
onChange={(htmlContent) => {
beritaState.berita.create.form.content = htmlContent;
}}
/>
</Box>
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan Berita</Button>
</Stack>
</Paper>
</Box>
);
function SelectCategory({
onChange,
}: {
onChange: (value: Prisma.KategoriBeritaGetPayload<{
select: {
name: true;
id: true;
};
}>) => void;
}) {
const categoryState = useProxy(stateDashboardBerita.category);
useShallowEffect(() => {
categoryState.findMany.load();
}, []);
if (!categoryState.findMany.data) {
return <Skeleton height={38} />;
}
return (
<Select
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
placeholder="Pilih kategori"
data={categoryState.findMany.data.map((item) => ({
label: item.name,
value: item.id,
}))}
onChange={(val) => {
const selected = categoryState.findMany.data?.find((item) => item.id === val);
if (selected) {
onChange(selected);
}
}}
searchable
nothingFoundMessage="Tidak ditemukan"
/>
);
}
}

View File

@@ -1,307 +1,189 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import { Box, Button, Center, FileInput, Group, Image, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { Prisma } from '@prisma/client';
import { IconImageInPicture } from '@tabler/icons-react';
import { useProxy } from 'valtio/utils';
import stateDashboardBerita from '../../_state/desa/berita';
import { BeritaEditor } from './_com/BeritaEditor';
import colors from '@/con/colors';
import { Box, Button, Grid, GridCol, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, TextInput, Title } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { toast } from 'react-toastify';
import ApiFetch from '@/lib/api-fetch';
import { useProxy } from 'valtio/utils';
import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus';
import stateDashboardBerita from '../../_state/desa/berita';
function Page() {
return (
<Box>
<Title order={3}>Berita</Title>
<BeritaCreate />
<Grid>
<GridCol span={{ base: 12, md: 9 }}>
<Title order={3}>Berita</Title>
</GridCol>
<GridCol span={{ base: 12, md: 3 }}>
<Paper radius={"lg"} bg={colors['white-1']}>
<TextInput
radius={"lg"}
placeholder='pencarian'
leftSection={<IconSearch size={20} />}
/>
</Paper>
</GridCol>
</Grid>
<BeritaList />
</Box>
);
}
// function BeritaCreate() {
// const beritaState = useProxy(stateDashboardBerita);
// const [previewImage, setPreviewImage] = useState<string | null>(null);
// const [file, setFile] = useState<File | null>(null);
// const [editorInstance, setEditorInstance] = useState<any>(null);
// const handleSubmit = async () => {
// if (!file) {
// return toast.warn("Pilih file gambar terlebih dahulu");
// }
// if (!editorInstance) return toast.error("Editor belum siap");
// const htmlContent = editorInstance.getHTML();
// if (!htmlContent || htmlContent === '<p></p>') return toast.warn("Konten tidak boleh kosong");
// beritaState.berita.create.form.content = htmlContent;
// console.log(beritaState.berita.create.form)
// // Upload gambar dulu
// const res = await ApiFetch.api.fileStorage.create.post({
// file,
// name: file.name,
// });
// function BeritaList() {
// const beritaState = useProxy(stateDashboardBerita)
// useShallowEffect(() => {
// beritaState.berita.findMany.load()
// }, [])
// const uploaded = res.data?.data;
// if (!uploaded?.id) {
// return toast.error("Gagal upload gambar");
// }
// // Simpan ID gambar ke form
// beritaState.berita.create.form.imageId = uploaded.id;
// // Submit data berita
// await beritaState.berita.create.create();
// };
// const router = useRouter()
// if (!beritaState.berita.findMany.data) return <Stack py={10}>
// {Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
// </Stack>
// return (
// <Box py={10}>
// <Paper bg={colors["white-1"]} p={"md"}>
// <Stack gap={"xs"}>
// <SelectCategory
// onChange={(val) => {
// beritaState.berita.create.form.kategoriBeritaId = val.id;
// }}
// />
// <TextInput
// onChange={(val) => {
// beritaState.berita.create.form.judul = val.target.value;
// }}
// label={"Judul"}
// placeholder="masukkan judul"
// />
// <TextInput
// onChange={(val) => {
// beritaState.berita.create.form.deskripsi = val.target.value;
// }}
// label={"Deskripsi"}
// placeholder="masukkan deskripsi"
// />
// <FileInput
// label="Upload Gambar"
// onChange={async (e) => {
// if (!e) return;
// setFile(e);
// const base64 = await e.arrayBuffer().then((buf) =>
// "data:image/png;base64," + Buffer.from(buf).toString("base64")
// );
// setPreviewImage(base64);
// }}
// />
// {previewImage ? (
// <Image alt="" src={previewImage} w={200} h={200} />
// ) : (
// <Center w={200} h={200} bg={"gray"}>
// <IconImageInPicture />
// </Center>
// )}
// <BeritaEditor
// showSubmit={false}
// onEditorReady={(ed) => setEditorInstance(ed)}
// />
// <Button onClick={handleSubmit}>Simpan Berita</Button>
// <Paper bg={colors['white-1']} p={'md'}>
// <Stack>
// <Text fz={"xl"} fw={"bold"}>List Berita</Text>
// <SimpleGrid cols={{ base: 1, md: 4 }}>
// {beritaState.berita.findMany.data?.map((item) => (
// <Paper key={item.id} bg={colors['BG-trans']} p={'md'}>
// <Box >
// <Flex justify="flex-end" mt={10}>
// <ActionIcon
// onClick={() => beritaState.berita.delete.byId(item.id)}
// disabled={beritaState.berita.delete.loading}
// color={colors['blue-button']} variant='transparent'>
// <IconX size={20} />
// </ActionIcon>
// <ActionIcon onClick={() => {
// router.push("/desa/berita/edit");
// }} color={colors['blue-button']} variant='transparent'>
// <IconEdit size={20} />
// </ActionIcon>
// </Flex>
// <Text fw={"bold"} fz={"sm"}>
// Kategori
// </Text>
// <Text>{item.kategoriBerita?.name}</Text>
// <Text fw={"bold"} fz={"sm"}>
// Judul
// </Text>
// <Text>{item.judul}</Text>
// <Text lineClamp={1} fw={"bold"} fz={"sm"}>
// Deskripsi
// </Text>
// <Text size='sm' lineClamp={2}>{item.deskripsi}</Text>
// <Text fw={"bold"} fz={"sm"}>
// Gambar
// </Text>
// <Image w={{ base: 100, md: 150 }} src={item.image?.link} alt="gambar" />
// </Box>
// </Paper>
// ))}
// </SimpleGrid>
// </Stack>
// </Paper>
// </Box>
// );
// )
// }
function BeritaCreate() {
const beritaState = useProxy(stateDashboardBerita);
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const [editorInstance, setEditorInstance] = useState<any>(null);
const resetForm = () => {
// Reset state di valtio
beritaState.berita.create.form = {
judul: "",
deskripsi: "",
kategoriBeritaId: "",
imageId: "",
content: "",
};
// Reset state lokal
setPreviewImage(null);
setFile(null);
if (editorInstance) {
editorInstance.commands.setContent(""); // Kosongkan editor
}
};
const handleSubmit = async () => {
if (!file) {
return toast.warn("Pilih file gambar terlebih dahulu");
}
if (!editorInstance) return toast.error("Editor belum siap");
const htmlContent = editorInstance.getHTML();
if (!htmlContent || htmlContent === "<p></p>") return toast.warn("Konten tidak boleh kosong");
beritaState.berita.create.form.content = htmlContent;
// Upload gambar dulu
const res = await ApiFetch.api.fileStorage.create.post({
file,
name: file.name,
});
const uploaded = res.data?.data;
if (!uploaded?.id) {
return toast.error("Gagal upload gambar");
}
// Simpan ID gambar ke form
beritaState.berita.create.form.imageId = uploaded.id;
// Submit data berita
await beritaState.berita.create.create();
toast.success("Berita berhasil disimpan!");
// Reset form setelah submit
resetForm();
};
return (
<Box py={10}>
<Paper bg={colors["white-1"]} p={"md"} w={{base: "100%", md: "50%"}}>
<Stack gap={"xs"}>
<SelectCategory
onChange={(val) => {
beritaState.berita.create.form.kategoriBeritaId = val.id;
}}
/>
<TextInput
value={beritaState.berita.create.form.judul}
onChange={(val) => {
beritaState.berita.create.form.judul = val.target.value;
}}
label={"Judul"}
placeholder="masukkan judul"
/>
<TextInput
value={beritaState.berita.create.form.deskripsi}
onChange={(val) => {
beritaState.berita.create.form.deskripsi = val.target.value;
}}
label={"Deskripsi"}
placeholder="masukkan deskripsi"
/>
<FileInput
label="Upload Gambar"
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
"data:image/png;base64," + Buffer.from(buf).toString("base64")
);
setPreviewImage(base64);
}}
/>
{previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg={"gray"}>
<IconImageInPicture />
</Center>
)}
<BeritaEditor
showSubmit={false}
onEditorReady={(ed) => setEditorInstance(ed)}
/>
<Button onClick={handleSubmit}>Simpan Berita</Button>
</Stack>
</Paper>
</Box>
);
}
function BeritaList() {
const beritaState = useProxy(stateDashboardBerita)
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
useShallowEffect(() => {
beritaState.berita.findMany.load()
}, [])
if (!beritaState.berita.findMany.data) return <Stack py={10}>
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
const router = useRouter()
const handleHapus = () => {
if (selectedId) {
beritaState.berita.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
}
}
if (!beritaState.berita.findMany.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Text fz={"xl"} fw={"bold"}>List Berita</Text>
<SimpleGrid cols={{ base: 1, md: 4 }}>
{beritaState.berita.findMany.data?.map((item) => (
<Paper key={item.id} bg={colors['BG-trans']} p={'md'}>
<Box >
<Text fw={"bold"} fz={"sm"}>
Kategori
</Text>
<Text>{item.kategoriBerita?.name}</Text>
<Text fw={"bold"} fz={"sm"}>
Judul
</Text>
<Text>{item.judul}</Text>
<Text lineClamp={1} fw={"bold"} fz={"sm"}>
Deskripsi
</Text>
<Text size='sm' lineClamp={2}>{item.deskripsi}</Text>
<Text fw={"bold"} fz={"sm"}>
Gambar
</Text>
<Image w={200} src={item.image?.link} alt="gambar" />
</Box>
</Paper>
))}
</SimpleGrid>
<Grid>
<GridCol span={{ base: 12, md: 11 }}>
<Text fz={"xl"} fw={"bold"}>List Berita</Text>
</GridCol>
<GridCol span={{ base: 12, md: 1 }}>
<Button onClick={() => router.push("/admin/desa/berita/create")} bg={colors['blue-button']}>
<IconCircleDashedPlus size={25} />
</Button>
</GridCol>
</Grid>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh w={250}>Judul</TableTh>
<TableTh w={250}>Kategori</TableTh>
<TableTh w={250}>Image</TableTh>
<TableTh w={200}>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody >
{beritaState.berita.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd >
<Box w={100}>
<Text truncate="end" fz={"sm"}>{item.judul}</Text>
</Box>
</TableTd>
<TableTd >{item.kategoriBerita?.name}</TableTd>
<TableTd>
<Image w={100} src={item.image?.link} alt="gambar" />
</TableTd>
<TableTd>
<Button bg={"green"} onClick={() => router.push(`/admin/desa/berita/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table> </Box>
</Stack>
</Paper>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text='Apakah anda yakin ingin menghapus berita ini?'
/>
</Box>
)
}
function SelectCategory({ onChange }: {
onChange: (value: Prisma.KategoriBeritaGetPayload<{
select: {
name: true,
id: true
}
}>) => void
}) {
const beritaState = useProxy(stateDashboardBerita)
useShallowEffect(() => {
beritaState.category.findMany.load()
}, [])
if (!beritaState.category.findMany.data) return <Skeleton h={40} />
return <Group>
<Select placeholder='pilih kategori' label={<Text fz={"sm"} fw={"bold"}>Pilih Kategori</Text>} data={beritaState.category.findMany.data.map((item) => ({
value: item.id,
label: item.name
}))} onChange={(v) => {
const data = beritaState.category.findMany.data?.find((item) => item.id === v)
if (!data) return
onChange(data)
}} />
</Group>
}
export default Page;

View File

@@ -0,0 +1,128 @@
'use client';
import colors from '@/con/colors';
import ApiFetch from '@/lib/api-fetch';
import { Box, Button, Center, FileInput, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
import { useState } from 'react';
import { toast } from 'react-toastify';
import { useProxy } from 'valtio/utils';
import potensiDesaState from '../../../_state/desa/potensi';
import { useRouter } from 'next/navigation';
import CreateEditor from '../../../_com/createEditor';
function CreatePotensi() {
const potensiState = useProxy(potensiDesaState);
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const router = useRouter();
const handleSubmit = async () => {
if (!file) return toast.warn('Pilih file gambar terlebih dahulu');
const res = await ApiFetch.api.fileStorage.create.post({
file,
name: file.name,
});
const uploaded = res.data?.data;
if (!uploaded?.id) {
return toast.error('Gagal upload gambar');
}
potensiState.create.form.imageId = uploaded.id;
await potensiState.create.create();
resetForm();
router.push('/admin/desa/potensi');
};
const resetForm = () => {
potensiState.create.form = {
name: '',
deskripsi: '',
kategori: '',
imageId: '',
content: '',
};
setPreviewImage(null);
setFile(null);
};
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Potensi</Title>
<TextInput
value={potensiState.create.form.name}
onChange={(val) => (potensiState.create.form.name = val.target.value)}
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
value={potensiState.create.form.deskripsi}
onChange={(val) => (potensiState.create.form.deskripsi = val.target.value)}
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<TextInput
value={potensiState.create.form.kategori}
onChange={(val) => (potensiState.create.form.kategori = val.target.value)}
label={<Text fz="sm" fw="bold">Kategori</Text>}
placeholder="masukkan kategori"
/>
<FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/>
{previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)}
<Box>
<Text fz="sm" fw="bold">Konten</Text>
<CreateEditor
value={potensiState.create.form.content}
onChange={(htmlContent) => {
potensiState.create.form.content = htmlContent;
}}
/>
</Box>
<Button bg={colors['blue-button']} onClick={handleSubmit}>
Simpan Potensi
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreatePotensi;

View File

@@ -0,0 +1,121 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import React from 'react';
import { useProxy } from 'valtio/utils';
import potensiDesaState from '../../../../_state/desa/potensi';
import { useShallowEffect } from '@mantine/hooks';
import { useParams } from 'next/navigation';
import { useState } from 'react';
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
export default function DetailPotensi() {
const router = useRouter()
const params = useParams()
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
const potensiState = useProxy(potensiDesaState)
useShallowEffect(() => {
potensiState.findUnique.load(params?.id as string)
}, [])
const handleHapus = () => {
if (selectedId) {
potensiState.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
router.push("/admin/desa/potensi")
}
}
if (!potensiState.findUnique.data) {
return (
<Stack py={10}>
{Array.from({ length: 10 }).map((_, k) => (
<Skeleton key={k} h={40} />
))}
</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 Potensi</Text>
{potensiState.findUnique.data ? (
<Paper key={potensiState.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fz={"lg"} fw={"bold"}>Judul</Text>
<Text fz={"lg"}>{potensiState.findUnique.data.name}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Kategori</Text>
<Text fz={"lg"}>{potensiState.findUnique.data.kategori}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
<Text fz={"lg"}>{potensiState.findUnique.data.deskripsi}</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Gambar</Text>
<Image src={potensiState.findUnique.data.image?.link} alt="gambar" />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Konten</Text>
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: potensiState.findUnique.data.content }} />
</Box>
<Box>
<Flex gap={"xs"}>
<Button
onClick={() => {
if (potensiState.findUnique.data) {
setSelectedId(potensiState.findUnique.data.id)
setModalHapus(true)
}
}}
disabled={potensiState.delete.loading || !potensiState.findUnique.data}
color="red"
>
<IconX size={20} />
</Button>
<Button
onClick={() => {
if (potensiState.findUnique.data) {
router.push(`/admin/desa/potensi/edit/${potensiState.findUnique.data.id}`)
}
}}
disabled={!potensiState.findUnique.data}
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 potensi ini?"
/>
</Box>
);
}

View File

@@ -0,0 +1,169 @@
'use client'
import EditEditor from "@/app/admin/(dashboard)/_com/editEditor";
import potensiDesaState from "@/app/admin/(dashboard)/_state/desa/potensi";
import colors from "@/con/colors";
import ApiFetch from "@/lib/api-fetch";
import { Box, Button, Paper, Stack, Title, TextInput, FileInput, Center, Text, Image } from "@mantine/core";
import { IconArrowBack, IconImageInPicture } from "@tabler/icons-react";
import { useParams } from "next/navigation";
import { useRouter } from "next/navigation";
import { useState, useEffect } from "react";
import { toast } from "react-toastify";
import { useProxy } from "valtio/utils";
function EditPotensi() {
const potensiState = useProxy(potensiDesaState)
const router = useRouter()
const params = useParams()
const [previewImage, setPreviewImage] = useState<string | null>(null);
const [file, setFile] = useState<File | null>(null);
const [formData, setFormData] = useState({
name: '',
deskripsi: '',
kategori: '',
content: '',
imageId: ''
});
useEffect(() => {
const loadPotensi = async () => {
const id = params?.id as string;
if (!id) return;
try {
const data = await potensiDesaState.edit.load(id); // ambil data dari API
if (data) {
setFormData({
name: data.name || '',
deskripsi: data.deskripsi || '',
kategori: data.kategori || '',
content: data.content || '',
imageId: data.imageId || '',
});
if (data?.image?.link) {
setPreviewImage(data.image.link);
}
}
} catch (error) {
console.error("Error loading potensi:", error);
toast.error("Gagal memuat data potensi");
}
};
loadPotensi();
}, [params?.id]);
const handleSubmit = async () => {
try {
// Sinkronkan semua data dari formData ke state global
potensiState.edit.form = {
...potensiState.edit.form,
name: formData.name,
deskripsi: formData.deskripsi,
kategori: formData.kategori,
content: formData.content,
};
if (file) {
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
const uploaded = res.data?.data;
if (!uploaded?.id) {
return toast.error("Gagal upload gambar");
}
potensiState.edit.form.imageId = uploaded.id;
}
await potensiState.edit.update();
toast.success("Potensi berhasil diperbarui!");
router.push("/admin/desa/potensi");
} catch (error) {
console.error("Error updating potensi:", error);
toast.error("Terjadi kesalahan saat memperbarui potensi");
}
};
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
<Stack gap={"xs"}>
<Title order={3}>Edit Potensi</Title>
<TextInput
value={formData.name}
onChange={(e) => {
const val = e.target.value;
setFormData((prev) => ({ ...prev, name: val }));
potensiState.edit.form.name = val;
}}
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
value={formData.deskripsi}
onChange={(e) => {
const val = e.target.value;
setFormData((prev) => ({ ...prev, deskripsi: val }));
potensiState.edit.form.deskripsi = val;
}}
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<TextInput
value={formData.kategori}
onChange={(e) => {
const val = e.target.value;
setFormData((prev) => ({ ...prev, kategori: val }));
potensiState.edit.form.kategori = val;
}}
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
placeholder="masukkan kategori"
/>
<FileInput
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
"data:image/png;base64," + Buffer.from(buf).toString("base64")
);
setPreviewImage(base64);
}}
/>
{previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg={"gray"}>
<IconImageInPicture />
</Center>
)}
<Box>
<Text fz={"sm"} fw={"bold"}>Konten</Text>
<EditEditor
value={formData.content}
onChange={(htmlContent) => {
setFormData((prev) => ({ ...prev, content: htmlContent }));
potensiState.edit.form.content = htmlContent;
}}
/>
</Box>
<Button bg={colors['blue-button']} onClick={handleSubmit}>Edit Potensi</Button>
</Stack>
</Paper>
</Box>
);
}
export default EditPotensi;

View File

@@ -1,17 +0,0 @@
import colors from '@/con/colors';
import { Box, Paper, Stack, Title } from '@mantine/core';
import React from 'react';
function ListPotensi() {
return (
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<Title order={3}>List Potensi Desa</Title>
</Stack>
</Paper>
</Box>
);
}
export default ListPotensi;

View File

@@ -1,40 +1,89 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, TextInput, Title } from '@mantine/core';
import ListPotensi from './listPage';
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { useShallowEffect } from '@mantine/hooks';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import { useProxy } from 'valtio/utils';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
import potensiDesaState from '../../_state/desa/potensi';
function Potensi() {
return (
<Box py={10}>
<Stack gap={"xs"}>
<Title order={3}>Potensi Desa</Title>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<TextInput
label="Nama Potensi"
placeholder='masukkan nama potensi'
/>
<TextInput
label="Kategori Potensi"
placeholder='masukkan kategori potensi'
/>
<Group>
<Button
mt={10}
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<ListPotensi />
</SimpleGrid>
</Stack>
<Box>
<HeaderSearch
title='Potensi Desa'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListPotensi />
</Box>
);
}
function ListPotensi() {
const potensiState = useProxy(potensiDesaState)
useShallowEffect(() => {
potensiState.findMany.load()
}, [])
const router = useRouter()
if (!potensiState.findMany.data) {
return (
<Stack py={10}>
<Skeleton h={500} />
</Stack>
)
}
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Potensi'
href='/admin/desa/potensi/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Kategori</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{potensiState.findMany.data?.map((item) => (
<TableTr key={item.id}>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>{item.name}</Text>
</Box></TableTd>
<TableTd>{item.kategori}</TableTd>
<TableTd>
<Image w={100} src={item.image?.link} alt="image" />
</TableTd>
<TableTd>
<Button onClick={() => router.push(`/admin/desa/potensi/detail/${item.id}`)}>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default Potensi;

View File

@@ -1,10 +1,46 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import React from 'react';
function Page() {
return (
<div>
demografi-pekerjaan
</div>
<Box>
<Stack gap={"xs"}>
<Box>
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={"md"}>
<Stack gap={"xs"}>
<Title order={3}>Demografi Pekerjaan</Title>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Jumlah Pekerja Laki - Laki</Text>}
placeholder="Masukkan jumlah pekerja laki - laki"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Jumlah Pekerja Perempuan</Text>}
placeholder="Masukkan jumlah pekerja perempuan"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Nama Pekerjaan</Text>}
placeholder="Masukkan nama pekerjaan"
/>
<Group>
<Button
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['white-1']} p={"md"}>
<Stack gap={"xs"}>
<Title order={3}>Grafik Demografi Pekerjaan</Title>
</Stack>
</Paper>
</Box>
</Stack>
</Box>
);
}

View File

@@ -1,10 +1,42 @@
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
import colors from '@/con/colors';
import React from 'react';
function Page() {
return (
<div>
jumlah-penduduk-miskin-2024-2025
</div>
<Box>
<Stack gap={"xs"}>
<Box>
<Paper p={"md"} bg={colors['white-1']} w={{ base: '100%', md: '50%' }}>
<Stack gap={"xs"}>
<Title order={3}>Jumlah Penduduk Miskin 2024-2025</Title>
<TextInput
label="Tahun"
placeholder="masukkan tahun"
/>
<TextInput
label="Jumlah Penduduk Miskin"
placeholder="masukkan jumlah penduduk miskin"
/>
<Group>
<Button
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper p={"md"} bg={colors['white-1']} >
<Stack gap={"xs"}>
<Title order={3}>Grafik Jumlah Penduduk Miskin 2024-2025</Title>
</Stack>
</Paper>
</Box>
</Stack>
</Box>
);
}

View File

@@ -1,11 +1,37 @@
import React from 'react';
import colors from '@/con/colors';
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import DataPengangguran from './ui/dataPengangguran/page';
import GrafikDataPengangguran from './ui/grafikDataPengangguran/page';
import DetailDataPengangguran from './ui/detailDataPengangguran/page';
function Page() {
return (
<div>
jumlah-pengangguran-2024-2025
</div>
);
<Stack>
<Title order={3}>Jumlah Pengangguran 2024-2025</Title>
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Data Pengangguran Desa"}>
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
<TabsTab value="Data Pengangguran Desa">
Data Pengangguran Desa
</TabsTab>
<TabsTab value="Grafik Data Pengangguran Desa">
Grafik Data Pengangguran Desa
</TabsTab>
<TabsTab value="Detail Data Pengangguran Desa">
Detail Data Pengangguran Desa
</TabsTab>
</TabsList>
<TabsPanel value="Data Pengangguran Desa">
<DataPengangguran/>
</TabsPanel>
<TabsPanel value="Grafik Data Pengangguran Desa">
<GrafikDataPengangguran/>
</TabsPanel>
<TabsPanel value="Detail Data Pengangguran Desa">
<DetailDataPengangguran/>
</TabsPanel>
</Tabs>
</Stack>
)
}
export default Page;

View File

@@ -0,0 +1,51 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
function DataPengangguran() {
return (
<Box>
<Box py={15}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>Data Pengangguran</Title>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Total Pengangguran</Text>}
placeholder="masukkan total pengangguran"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Pengangguran Terdidik</Text>}
placeholder="masukkan pengangguran terdidik"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Usia Produktif</Text>}
placeholder="masukkan usia produktif"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Sedang Mencari Kerja</Text>}
placeholder="masukkan jumlah sedang mencari kerja"
/>
<Group>
<Button bg={colors['blue-button']}>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>List Data Pengangguran</Title>
</Stack>
</Paper>
</Box>
</SimpleGrid>
</Box>
</Box>
);
}
export default DataPengangguran;

View File

@@ -0,0 +1,18 @@
import colors from '@/con/colors';
import { Box, Paper, Stack, Title } from '@mantine/core';
function DetailDataPengangguran() {
return (
<Box>
<Box py={15}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>Detail Data Pengangguran</Title>
</Stack>
</Paper>
</Box>
</Box>
);
}
export default DetailDataPengangguran;

View File

@@ -0,0 +1,49 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
function GrafikDataPengangguran() {
return (
<Box>
<Box py={15}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>Data Pengangguran</Title>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Total Pengangguran</Text>}
placeholder="masukkan total pengangguran"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Pengangguran Terdidik</Text>}
placeholder="masukkan pengangguran terdidik"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Usia Produktif</Text>}
placeholder="masukkan usia produktif"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Sedang Mencari Kerja</Text>}
placeholder="masukkan jumlah sedang mencari kerja"
/>
<Group>
<Button bg={colors['blue-button']}>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>List Data Pengangguran</Title>
</Stack>
</Paper>
</Box>
</Box>
</Box>
);
}
export default GrafikDataPengangguran;

View File

@@ -1,10 +1,30 @@
import React from 'react';
import colors from '@/con/colors';
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import ProgramKemiskinan from './ui/program/page';
import StatistikKemiskinan from './ui/statistik/page';
function Page() {
return (
<div>
program-kemiskinan
</div>
<Stack>
<Title order={3}>Program Kemiskinan</Title>
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Program Kemiskinan"}>
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
<TabsTab value="Program Kemiskinan">
Program Kemiskinan
</TabsTab>
<TabsTab value="Statistik Kemiskinan">
Statistik Kemiskinan
</TabsTab>
</TabsList>
<TabsPanel value="Program Kemiskinan">
<ProgramKemiskinan />
</TabsPanel>
<TabsPanel value="Statistik Kemiskinan">
<StatistikKemiskinan />
</TabsPanel>
</Tabs>
</Stack>
);
}

View File

@@ -0,0 +1,43 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
import React from 'react';
function ProgramKemiskinan() {
return (
<Box py={15}>
<SimpleGrid cols={{base: 1, md: 2}}>
<Box>
<Paper p={"md"} bg={colors['white-1']}>
<Stack gap={"xs"}>
<Title order={3}>Program Kemiskinan</Title>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Nama Program</Text>}
placeholder="Masukkan nama program"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Deskripsi Program</Text>}
placeholder="Masukkan deskripsi program"
/>
<Group>
<Button
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper p={"md"} bg={colors['white-1']}>
<Stack gap={"xs"}>
<Title order={3}>List Data Program Kemiskinan</Title>
</Stack>
</Paper>
</Box>
</SimpleGrid>
</Box>
);
}
export default ProgramKemiskinan;

View File

@@ -0,0 +1,43 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import React from 'react';
function StatistikKemiskinan() {
return (
<Box py={15}>
<Stack gap={"xs"}>
<Box>
<Paper w={{base: '100%', md: '50%'}} p={"md"} bg={colors['white-1']}>
<Stack gap={"xs"}>
<Title order={3}>Statistik Kemiskinan</Title>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Tahun</Text>}
placeholder="Masukkan tahun"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Jumlah Penduduk Miskin</Text>}
placeholder="Masukkan jumlah penduduk miskin"
/>
<Group>
<Button
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper p={"md"} bg={colors['white-1']}>
<Stack gap={"xs"}>
<Title order={3}>Statistik Kemiskinan</Title>
</Stack>
</Paper>
</Box>
</Stack>
</Box>
);
}
export default StatistikKemiskinan;

View File

@@ -1,10 +1,31 @@
import colors from '@/con/colors';
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import React from 'react';
import DataSektorUnggulan from './ui/data_sektor_unggulan/page';
import GrafikDataSektor from './ui/grafik_data_sektor/page';
function Page() {
return (
<div>
sektor-unggulan-desa
</div>
<Stack>
<Title order={3}>Sektor Unggulan Desa</Title>
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Data Sektor Unggulan Desa"}>
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
<TabsTab value="Data Sektor Unggulan Desa">
Data Sektor Unggulan Desa
</TabsTab>
<TabsTab value="Grafik Data Sektor Unggulan Desa">
Grafik Data Sektor Unggulan Desa
</TabsTab>
</TabsList>
<TabsPanel value="Data Sektor Unggulan Desa">
<DataSektorUnggulan />
</TabsPanel>
<TabsPanel value="Grafik Data Sektor Unggulan Desa">
<GrafikDataSektor />
</TabsPanel>
</Tabs>
</Stack>
);
}

View File

@@ -0,0 +1,45 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
import React from 'react';
function DataSektorUnggulan() {
return (
<Box py={15}>
<Stack gap={"xs"}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper p={"md"} bg={colors['white-1']}>
<Stack gap={"xs"}>
<Title order={3}>Data Sektor Unggulan</Title>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Nama Sektor Unggulan</Text>}
placeholder="Masukkan nama sektor unggulan"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Deskripsi Sektor Unggulan</Text>}
placeholder="Masukkan deskripsi sektor unggulan"
/>
<Group>
<Button
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper p={"md"} bg={colors['white-1']}>
<Stack gap={"xs"}>
<Title order={3}>Data Sektor Unggulan</Title>
</Stack>
</Paper>
</Box>
</SimpleGrid>
</Stack>
</Box>
);
}
export default DataSektorUnggulan;

View File

@@ -0,0 +1,44 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import React from 'react';
function GrafikDataSektor() {
return (
<Box py={15}>
<Stack gap={"xs"}>
<Box>
<Paper w={{base: '100%', md: '50%'}} p={"md"} bg={colors['white-1']}>
<Stack gap={"xs"}>
<Title order={3}>Grafik Data Sektor Unggulan</Title>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Nama Sektor Unggulan</Text>}
placeholder="Masukkan nama sektor unggulan"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Jumlah Sektor Unggulan</Text>}
placeholder="Masukkan jumlah sektor unggulan"
/>
<Group>
<Button
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper p={"md"} bg={colors['white-1']}>
<Stack gap={"xs"}>
<Title order={3}>Grafik Sektor Unggulan</Title>
</Stack>
</Paper>
</Box>
</Stack>
</Box>
);
}
export default GrafikDataSektor;

View File

@@ -1,10 +1,43 @@
import colors from '@/con/colors';
import { Box, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Title } from '@mantine/core';
import React from 'react';
function Page() {
return (
<div>
ajukan-ide-inovatif
</div>
<Box py={5}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>Ajukan Ide Inovatif</Title>
<Box>
<Table striped withRowBorders withColumnBorders withTableBorder>
<TableThead>
<TableTr>
<TableTh>No</TableTh>
<TableTh>Nama</TableTh>
<TableTh>Alamat</TableTh>
<TableTh>Nama Ide Inovatif</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Masalah yang ingin diatasi</TableTh>
<TableTh>Benefit</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>1</TableTd>
<TableTd>nama</TableTd>
<TableTd>alamat</TableTd>
<TableTd>ide inovatif</TableTd>
<TableTd>deskripsi</TableTd>
<TableTd>masalah</TableTd>
<TableTd>benefit</TableTd>
</TableTr>
</TableTbody>
</Table> </Box>
</Stack>
</Paper>
</Box>
);
}

View File

@@ -1,10 +1,49 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconImageInPicture } from '@tabler/icons-react';
import React from 'react';
function Page() {
return (
<div>
desa-digital-smart-village
</div>
<Box>
<Stack gap={"xs"}>
<Box>
<Paper w={{ base: '100%', md: '50%' }} p={"md"} bg={colors['white-1']}>
<Stack gap={"xs"}>
<Title order={3}>Create Data Desa Digital Smart Village</Title>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Nama Desa Digital Smart Village</Text>}
placeholder="Masukkan nama desa digital smart village"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Deskripsi Desa Digital Smart Village</Text>}
placeholder="Masukkan deskripsi desa digital smart village"
/>
<Box>
<Text fz={"sm"} fw={"bold"}>
Upload Gambar Desa Digital Smart Village
</Text>
<IconImageInPicture size={24} />
</Box>
<Group>
<Button
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper p={"md"} bg={colors['white-1']}>
<Stack gap={"xs"}>
<Title order={3}>Create Data Desa Digital Smart Village</Title>
</Stack>
</Paper>
</Box>
</Stack>
</Box>
);
}

View File

@@ -0,0 +1,35 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconImageInPicture } from '@tabler/icons-react';
import React from 'react';
function CreateInfoTeknologiTepatGuna() {
return (
<Box>
<Stack gap={'xs'}>
<Paper w={{base: "100%", md: "50%"}} p={"md"} bg={colors['white-1']}>
<Stack gap={"xs"}>
<Title order={4}>Create Info Teknologi Tepat Guna</Title>
<Box>
<Text fw={"bold"} fz={"sm"}>Masukkan Image</Text>
<IconImageInPicture size={24}/>
</Box>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Nama Info Teknologi Tepat Guna</Text>}
placeholder="Masukkan nama info teknologi tepat guna"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Deskripsi Info Teknologi Tepat Guna</Text>}
placeholder="Masukkan deskripsi info teknologi tepat guna"
/>
<Group>
<Button bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
</Stack>
</Box>
);
}
export default CreateInfoTeknologiTepatGuna;

View File

@@ -0,0 +1,31 @@
import colors from '@/con/colors';
import { Box, Paper, SimpleGrid, Stack, Title } from '@mantine/core';
import React from 'react';
function ListDataInfoTeknologiTepatGuna() {
return (
<Box>
<Stack gap={'xs'}>
<Paper p={'md'} bg={colors['BG-trans']}>
<Title order={4}>List Data Info Teknologi Tepat Guna</Title>
<SimpleGrid py={10} cols={{ base: 1, md: 4 }}>
<Paper p={'md'} bg={colors['white-1']}>
Data 1
</Paper>
<Paper p={'md'} bg={colors['white-1']}>
Data 2
</Paper>
<Paper p={'md'} bg={colors['white-1']}>
Data 3
</Paper>
<Paper p={'md'} bg={colors['white-1']}>
Data 4
</Paper>
</SimpleGrid>
</Paper>
</Stack>
</Box>
);
}
export default ListDataInfoTeknologiTepatGuna;

View File

@@ -1,10 +1,17 @@
import { Box, Stack, Title } from '@mantine/core';
import React from 'react';
import CreateInfoTeknologiTepatGuna from './create/page';
import ListDataInfoTeknologiTepatGuna from './listData/page';
function Page() {
return (
<div>
info-teknologi-tepat-guna
</div>
<Box>
<Stack gap={'xs'}>
<Title order={3}>Info Teknologi Tepat Guna</Title>
<CreateInfoTeknologiTepatGuna/>
<ListDataInfoTeknologiTepatGuna/>
</Stack>
</Box>
);
}

View File

@@ -1,10 +1,34 @@
import colors from '@/con/colors';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import React from 'react';
import KolaborasiInovasi from './ui/kolaborasiInovasi/page';
import MitraKolaborasi from './ui/mitraKolaborasi/page';
function Page() {
return (
<div>
kolaborasi-inovasi
</div>
<Box>
<Stack>
<Title order={3}>Kolaborasi Inovasi</Title>
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Kolaborasi Inovasi"}>
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
<TabsTab value="Kolaborasi Inovasi">
Kolaborasi Inovasi
</TabsTab>
<TabsTab value="Mitra Kolaborasi">
Mitra Kolaborasi
</TabsTab>
</TabsList>
<TabsPanel value="Kolaborasi Inovasi">
<KolaborasiInovasi/>
</TabsPanel>
<TabsPanel value="Mitra Kolaborasi">
<MitraKolaborasi/>
</TabsPanel>
</Tabs>
</Stack>
</Box>
);
}

View File

@@ -0,0 +1,53 @@
import colors from '@/con/colors';
import { Box, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
import React from 'react';
function KolaborasiInovasi() {
return (
<Box py={15}>
<Stack gap={"xs"}>
<Box>
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={4}>Create Kolaborasi Inovasi</Title>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Tahun</Text>}
placeholder="Masukkan tahun"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Nama Kolaborasi Inovasi</Text>}
placeholder="Masukkan nama kolaborasi inovasi"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Deskripsi Kolaborasi Inovasi</Text>}
placeholder="Masukkan deskripsi kolaborasi inovasi"
/>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['BG-trans']} p={'md'}>
<Title order={4}>List Data Kolaborasi Inovasi</Title>
<SimpleGrid py={10} cols={{ base: 1, md: 4 }}>
<Paper p={'md'} bg={colors['white-1']}>
Data 1
</Paper>
<Paper p={'md'} bg={colors['white-1']}>
Data 2
</Paper>
<Paper p={'md'} bg={colors['white-1']}>
Data 3
</Paper>
<Paper p={'md'} bg={colors['white-1']}>
Data 4
</Paper>
</SimpleGrid>
</Paper>
</Box>
</Stack>
</Box>
);
}
export default KolaborasiInovasi;

View File

@@ -0,0 +1,52 @@
import React from 'react';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
import colors from '@/con/colors';
import { IconImageInPicture } from '@tabler/icons-react';
function MitraKolaborasi() {
return (
<Box py={15}>
<Stack gap={'xs'}>
<Box>
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
<Stack gap={'xs'}>
<Title order={4}>Create Mitra Kolaborasi</Title>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Nama Mitra Kolaborasi</Text>}
placeholder="Masukkan nama mitra kolaborasi"
/>
<Box>
<Text fw={"bold"} fz={"sm"}>Masukkan Image</Text>
<IconImageInPicture size={50}/>
</Box>
<Group>
<Button bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['BG-trans']} p={'md'}>
<Title order={4}>List Data Kolaborasi Inovasi</Title>
<SimpleGrid py={10} cols={{ base: 1, md: 4 }}>
<Paper p={'md'} bg={colors['white-1']}>
Foto 1
</Paper>
<Paper p={'md'} bg={colors['white-1']}>
Foto 2
</Paper>
<Paper p={'md'} bg={colors['white-1']}>
Foto 3
</Paper>
<Paper p={'md'} bg={colors['white-1']}>
Foto 4
</Paper>
</SimpleGrid>
</Paper>
</Box>
</Stack>
</Box>
);
}
export default MitraKolaborasi;

View File

@@ -1,10 +1,40 @@
import colors from '@/con/colors';
import { Box, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconImageInPicture } from '@tabler/icons-react';
import React from 'react';
function Page() {
return (
<div>
layanan-online-desa
</div>
<Box>
<Stack gap={"xs"}>
<Box>
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
<Stack gap={'xs'}>
<Title order={3}>Layanan Online Desa</Title>
<TextInput
label={<Text fz={'sm'} fw={'bold'}>Nama Layanan</Text>}
placeholder="Masukkan nama layanan"
/>
<TextInput
label={<Text fz={'sm'} fw={'bold'}>Deskripsi Layanan</Text>}
placeholder="Masukkan deskripsi layanan"
/>
<Box>
<Text fz={'sm'} fw={'bold'}>Upload Gambar Layanan</Text>
<IconImageInPicture size={24} />
</Box>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={'xs'}>
<Title order={3}>List Data Layanan Online Desa</Title>
</Stack>
</Paper>
</Box>
</Stack>
</Box>
);
}

View File

@@ -0,0 +1,37 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconImageInPicture } from '@tabler/icons-react';
import React from 'react';
function ProgramKreatifCreate() {
return (
<Box>
<Stack gap={"xs"}>
<Paper w={{base: "100%", md: "50%"}} p={"md"} bg={colors["white-1"]}>
<Stack gap={"xs"}>
<Title order={4}>Create Program Kreatif Desa</Title>
<Box>
<Text fz={"sm"} fw={"bold"}>Gambar</Text>
<IconImageInPicture size={24}/>
</Box>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Nama Program</Text>}
placeholder="Masukkan nama program"
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
placeholder="Masukkan deskripsi"
/>
<Group>
<Button
bg={colors["blue-button"]}
>Simpan</Button>
</Group>
</Stack>
</Paper>
</Stack>
</Box>
);
}
export default ProgramKreatifCreate;

View File

@@ -0,0 +1,29 @@
import colors from '@/con/colors';
import { Box, Paper, SimpleGrid, Stack, Title } from '@mantine/core';
import React from 'react';
function ListDataProgramKreatifDesa() {
return (
<Box>
<Stack gap={"xs"}>
<Title order={3}>List Data Program Kreatif Desa</Title>
<SimpleGrid cols={{ base: 1, md: 4 }}>
<Paper p={"md"} bg={colors["white-1"]}>
Data 1
</Paper>
<Paper p={"md"} bg={colors["white-1"]}>
Data 2
</Paper>
<Paper p={"md"} bg={colors["white-1"]}>
Data 3
</Paper>
<Paper p={"md"} bg={colors["white-1"]}>
Data 4
</Paper>
</SimpleGrid>
</Stack>
</Box>
);
}
export default ListDataProgramKreatifDesa;

View File

@@ -1,10 +1,17 @@
import { Box, Stack, Title } from '@mantine/core';
import React from 'react';
import ProgramKreatifCreate from './create/page';
import ListDataProgramKreatifDesa from './listData/page';
function Page() {
return (
<div>
program-kreatif-desa
</div>
<Box>
<Stack gap={"xs"}>
<Title order={3}>Program Kreatif Desa</Title>
<ProgramKreatifCreate />
<ListDataProgramKreatifDesa />
</Stack>
</Box>
);
}

View File

@@ -0,0 +1,77 @@
'use client'
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 { KesehatanEditor } from '../../_com/kesehatanEditor';
function CreateInfoWabahPenyakit() {
const router = useRouter();
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Info Wabah Penyakit</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<TextInput
label={<Text fz="sm" fw="bold">Kategori</Text>}
placeholder="masukkan kategori"
/>
{/* <FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/> */}
{/* {previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)} */}
<Box>
<Text fz="sm" fw="bold">Konten</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Button bg={colors['blue-button']}>
Simpan Potensi
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreateInfoWabahPenyakit;

View File

@@ -1,36 +1,64 @@
import colors from '@/con/colors';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import React from 'react';
import CreateInfoWabahPenyakit from './ui/tambah_wabah_penyakit/page';
import ListInfoWabahPenyakit from './ui/list_wabah_penyakit/page';
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import JudulList from '../../_com/judulList';
import HeaderSearch from '../../_com/header';
function Page() {
function InfoWabahPenyakit() {
return (
<Box>
<Stack gap={'xs'}>
<Title order={3}>Info Wabah/Penyakit</Title>
<Tabs defaultValue="create" color={colors['blue-button']} variant='pills'>
<TabsList mb={10} bg={colors['BG-trans']} p={'xs'}>
<TabsTab value="create" >
Tambah Info Wabah/Penyakit
</TabsTab>
<TabsTab value="list" >
List Info Wabah/Penyakit
</TabsTab>
</TabsList>
<TabsPanel value="create">
<CreateInfoWabahPenyakit/>
</TabsPanel>
<TabsPanel value="list">
<ListInfoWabahPenyakit/>
</TabsPanel>
</Tabs>
</Stack>
<HeaderSearch
title='Info Wabah Penyakit'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListInfoWabahPenyakit/>
</Box>
);
}
export default Page;
function ListInfoWabahPenyakit() {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Info Wabah Penyakit'
href='/admin/kesehatan/info-wabah-penyakit/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Kategori</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>Test</Text>
</Box></TableTd>
<TableTd>Test</TableTd>
<TableTd>
<Image w={100} src={"/"} alt="image" />
</TableTd>
<TableTd>
<Button>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default InfoWabahPenyakit;

View File

@@ -1,45 +0,0 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconImageInPicture } from '@tabler/icons-react';
import React from 'react';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
function CreateInfoWabahPenyakit() {
return (
<Box>
<Stack gap={'xs'}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={4}>Tambah Info Wabah/Penyakit</Title>
<Box>
<Text fw={"bold"} fz={"sm"}>Masukkan Image</Text>
<IconImageInPicture size={50} />
</Box>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Nama Info Wabah/Penyakit</Text>}
placeholder='Masukkan nama info wabah/penyakit'
/>
<Box>
<Text fw={"bold"} fz={"sm"}>Deskripsi Info Wabah/Penyakit</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Group>
<Button bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
</Box>
</SimpleGrid>
</Stack>
</Box>
);
}
export default CreateInfoWabahPenyakit;

View File

@@ -0,0 +1,77 @@
'use client'
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 { KesehatanEditor } from '../../_com/kesehatanEditor';
function CreateKontakDarurat() {
const router = useRouter();
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Kontak Darurat</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<TextInput
label={<Text fz="sm" fw="bold">Kategori</Text>}
placeholder="masukkan kategori"
/>
{/* <FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/> */}
{/* {previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)} */}
<Box>
<Text fz="sm" fw="bold">Konten</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Button bg={colors['blue-button']}>
Simpan Potensi
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreateKontakDarurat;

View File

@@ -1,35 +1,64 @@
import colors from '@/con/colors';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import React from 'react';
import CreateKontakDarurat from './ui/tambah_kontak_darurat/page';
import ListKontakDarurat from './ui/list_kontak_darurat/page';
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import JudulList from '../../_com/judulList';
import HeaderSearch from '../../_com/header';
function Page() {
function KontakDarurat() {
return (
<Box>
<Stack gap={'xs'}>
<Title order={3}>Kontak Darurat</Title>
<Tabs defaultValue="create" color={colors['blue-button']} variant='pills'>
<TabsList mb={10} bg={colors['BG-trans']} p={'xs'}>
<TabsTab value="create" >
Tambah Kontak Darurat
</TabsTab>
<TabsTab value="list" >
List Kontak Darurat
</TabsTab>
</TabsList>
<TabsPanel value="create">
<CreateKontakDarurat />
</TabsPanel>
<TabsPanel value="list">
<ListKontakDarurat/>
</TabsPanel>
</Tabs>
</Stack>
<HeaderSearch
title='KontakDarurat'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListKontakDarurat/>
</Box>
);
}
export default Page;
function ListKontakDarurat() {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Kontak Darurat'
href='/admin/kesehatan/kontak-darurat/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Kategori</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>Test</Text>
</Box></TableTd>
<TableTd>Test</TableTd>
<TableTd>
<Image w={100} src={"/"} alt="image" />
</TableTd>
<TableTd>
<Button>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default KontakDarurat;

View File

@@ -1,33 +0,0 @@
import { Box, Paper, Stack, Table, TableTbody, TableTh, TableThead, TableTr, Title } from '@mantine/core';
import colors from '@/con/colors';
import React from 'react';
function ListKontakDarurat() {
return (
<Box>
<Paper bg={colors['white-1']} p={'md'} radius={10}>
<Stack gap={'xs'}>
<Title order={3}>List Kontak Darurat</Title>
<Box>
<Table striped withTableBorder withColumnBorders withRowBorders>
<TableThead>
<TableTr>
<TableTh>Image</TableTh>
<TableTh>Nama Kontak Darurat</TableTh>
<TableTh>Deskripsi Kontak Darurat</TableTh>
<TableTh>Aksi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
);
}
export default ListKontakDarurat;

View File

@@ -1,45 +0,0 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconImageInPicture } from '@tabler/icons-react';
import React from 'react';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
function CreateKontakDarurat() {
return (
<Box>
<Stack gap={'xs'}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={4}>Tambah Kontak Darurat</Title>
<Box>
<Text fw={"bold"} fz={"sm"}>Masukkan Image</Text>
<IconImageInPicture size={50} />
</Box>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Nama Kontak Darurat</Text>}
placeholder='Masukkan nama kontak darurat'
/>
<Box>
<Text fw={"bold"} fz={"sm"}>Deskripsi Kontak Darurat</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Group>
<Button bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
</Box>
</SimpleGrid>
</Stack>
</Box>
);
}
export default CreateKontakDarurat;

View File

@@ -0,0 +1,77 @@
'use client'
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 { KesehatanEditor } from '../../_com/kesehatanEditor';
function CreatePenangananDarurat() {
const router = useRouter();
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Penanganan Darurat</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<TextInput
label={<Text fz="sm" fw="bold">Kategori</Text>}
placeholder="masukkan kategori"
/>
{/* <FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/> */}
{/* {previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)} */}
<Box>
<Text fz="sm" fw="bold">Konten</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Button bg={colors['blue-button']}>
Simpan Potensi
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreatePenangananDarurat;

View File

@@ -1,35 +1,64 @@
import colors from '@/con/colors';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import React from 'react';
import CreatePenangananDarurat from './ui/tambah_penanganan_darurat/page';
import ListPenangananDarurat from './ui/list_penanganan_darurat/page';
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import JudulList from '../../_com/judulList';
import HeaderSearch from '../../_com/header';
function Page() {
function PenangananDarurat() {
return (
<Box>
<Stack gap={'xs'}>
<Title order={3}>Penanganan Darurat</Title>
<Tabs defaultValue="create" color={colors['blue-button']} variant='pills'>
<TabsList mb={10} bg={colors['BG-trans']} p={'xs'}>
<TabsTab value="create" >
Tambah Penanganan Darurat
</TabsTab>
<TabsTab value="list" >
List Penanganan Darurat
</TabsTab>
</TabsList>
<TabsPanel value="create">
<CreatePenangananDarurat/>
</TabsPanel>
<TabsPanel value="list">
<ListPenangananDarurat/>
</TabsPanel>
</Tabs>
</Stack>
<HeaderSearch
title='PenangananDarurat'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListPenangananDarurat/>
</Box>
);
}
export default Page;
function ListPenangananDarurat() {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Penanganan Darurat'
href='/admin/kesehatan/penanganan-darurat/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Kategori</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>Test</Text>
</Box></TableTd>
<TableTd>Test</TableTd>
<TableTd>
<Image w={100} src={"/"} alt="image" />
</TableTd>
<TableTd>
<Button>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default PenangananDarurat;

View File

@@ -1,45 +0,0 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconImageInPicture } from '@tabler/icons-react';
import React from 'react';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
function CreatePenangananDarurat() {
return (
<Box>
<Stack gap={'xs'}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={4}>Tambah Penanganan Darurat</Title>
<Box>
<Text fw={"bold"} fz={"sm"}>Masukkan Image</Text>
<IconImageInPicture size={50} />
</Box>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Nama Penanganan Darurat</Text>}
placeholder='Masukkan nama penanganan darurat'
/>
<Box>
<Text fw={"bold"} fz={"sm"}>Deskripsi Penanganan Darurat</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Group>
<Button bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
</Box>
</SimpleGrid>
</Stack>
</Box>
);
}
export default CreatePenangananDarurat;

View File

@@ -0,0 +1,45 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import React from 'react';
import { KesehatanEditor } from '../../_com/kesehatanEditor';
function CreatePosyandu() {
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>
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={4}>Create Posyandu</Title>
<Box>
<Text fw={"bold"} fz={"sm"}>Masukkan Image</Text>
<IconImageInPicture size={50} />
</Box>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Nama Posyandu</Text>}
placeholder='Masukkan nama posyandu'
/>
<Box>
<Text fw={"bold"} fz={"sm"}>Deskripsi Posyandu</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Group>
<Button bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
</Box>
);
}
export default CreatePosyandu;

View File

@@ -0,0 +1,70 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Flex, Text, Image } from '@mantine/core';
import { IconArrowBack, IconX, IconEdit } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import React from 'react';
// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
function DetailPosyandu() {
const router = useRouter();
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 Potensi</Text>
<Paper bg={colors['BG-trans']} p={'md'}>
<Stack gap={"xs"}>
<Box>
<Text fz={"lg"} fw={"bold"}>Judul</Text>
<Text fz={"lg"}>Test Judul</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Kategori</Text>
<Text fz={"lg"}>Test Kategori</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
<Text fz={"lg"}>Test Deskripsi</Text>
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Gambar</Text>
<Image src={"/"} alt="gambar" />
</Box>
<Box>
<Text fz={"lg"} fw={"bold"}>Konten</Text>
<Text fz={"lg"} >Test Konten</Text>
</Box>
<Box>
<Flex gap={"xs"}>
<Button color="red">
<IconX size={20} />
</Button>
<Button color="green">
<IconEdit size={20} />
</Button>
</Flex>
</Box>
</Stack>
</Paper>
</Stack>
</Paper>
{/* Modal Hapus
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text="Apakah anda yakin ingin menghapus potensi ini?"
/> */}
</Box>
);
}
export default DetailPosyandu;

View File

@@ -0,0 +1,45 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import React from 'react';
import { KesehatanEditor } from '../../_com/kesehatanEditor';
function EditPosyandu() {
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>
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={4}>Edit Posyandu</Title>
<Box>
<Text fw={"bold"} fz={"sm"}>Masukkan Image</Text>
<IconImageInPicture size={50} />
</Box>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Nama Posyandu</Text>}
placeholder='Masukkan nama posyandu'
/>
<Box>
<Text fw={"bold"} fz={"sm"}>Deskripsi Posyandu</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Group>
<Button bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
</Box>
);
}
export default EditPosyandu;

View File

@@ -1,52 +1,53 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
import React from 'react';
import { KesehatanEditor } from '../_com/kesehatanEditor';
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
import HeaderSearch from '../../_com/header';
import JudulList from '../../_com/judulList';
function Posyandu() {
return (
<Box>
<Title order={3}>Kesehatan</Title>
<SimpleGrid py={10} cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={"md"}>
<Stack gap={"xs"}>
<Title order={3}>Posyandu</Title>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Nama Posyandu</Text>}
placeholder='masukkan nama posyandu'
/>
<TextInput
label={<Text fz={"sm"} fw={"bold"}>Nomor Telepon Posyandu</Text>}
placeholder='masukkan nomor telepon posyandu'
/>
<Box>
<Text fz={"sm"} fw={"bold"}>Deskripsi Posyandu</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Box>
<Text fz={"sm"} fw={"bold"}>Pelayanan Posyandu</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Group>
<Button bg={colors['blue-button']}>Submit</Button>
</Group>
<HeaderSearch
title='Posyandu'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListPosyandu/>
</Box>
);
}
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['white-1']} p={"md"}>
<Stack>
<Title order={3}>Preview Data Posyandu</Title>
</Stack>
</Paper>
</Box>
</SimpleGrid>
function ListPosyandu() {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<JudulList
title='List Posyandu'
href='/admin/kesehatan/posyandu/create'
/>
<Table striped withTableBorder withRowBorders>
<TableThead>
<TableTr>
<TableTh>Nama Posyandu</TableTh>
<TableTh>Nomor Posyandu</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>Posyandu 1</TableTd>
<TableTd>0896232831883</TableTd>
<TableTd>Posyandu 1</TableTd>
<TableTd>
<Button>
<IconDeviceImac size={20} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Paper>
</Box>
);
}

View File

@@ -0,0 +1,77 @@
'use client'
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 { KesehatanEditor } from '../../_com/kesehatanEditor';
function CreateProgramKesehatan() {
const router = useRouter();
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Program Kesehatan</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<TextInput
label={<Text fz="sm" fw="bold">Kategori</Text>}
placeholder="masukkan kategori"
/>
{/* <FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/> */}
{/* {previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)} */}
<Box>
<Text fz="sm" fw="bold">Konten</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Button bg={colors['blue-button']}>
Simpan Potensi
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreateProgramKesehatan;

View File

@@ -1,34 +1,64 @@
import colors from '@/con/colors';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import CreateProgramKesehatan from './ui/tambah_program_kesehatan/page';
import ListProgramKesehatan from './ui/list_program_kesehatan/page';
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import JudulList from '../../_com/judulList';
import HeaderSearch from '../../_com/header';
function Page() {
function ProgramKesehatan() {
return (
<Box>
<Stack gap={'xs'}>
<Title order={3}>Program Kesehatan</Title>
<Tabs defaultValue="create" color={colors['blue-button']} variant='pills'>
<TabsList mb={10} bg={colors['BG-trans']} p={'xs'}>
<TabsTab value="create" >
Tambah Program Kesehatan
</TabsTab>
<TabsTab value="list" >
List Program Kesehatan
</TabsTab>
</TabsList>
<TabsPanel value="create">
<CreateProgramKesehatan />
</TabsPanel>
<TabsPanel value="list">
<ListProgramKesehatan/>
</TabsPanel>
</Tabs>
</Stack>
<HeaderSearch
title='ProgramKesehatan'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListProgramKesehatan/>
</Box>
);
}
export default Page;
function ListProgramKesehatan() {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Program Kesehatan'
href='/admin/kesehatan/program-kesehatan/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Kategori</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>Test</Text>
</Box></TableTd>
<TableTd>Test</TableTd>
<TableTd>
<Image w={100} src={"/"} alt="image" />
</TableTd>
<TableTd>
<Button>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default ProgramKesehatan;

View File

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

View File

@@ -1,33 +0,0 @@
import { Box, Paper, Stack, Table, TableTbody, TableTh, TableThead, TableTr, Title } from '@mantine/core';
import colors from '@/con/colors';
import React from 'react';
function ListProgramKesehatan() {
return (
<Box>
<Paper bg={colors['white-1']} p={'md'} radius={10}>
<Stack gap={'xs'}>
<Title order={3}>List Program Kesehatan</Title>
<Box>
<Table striped withRowBorders withColumnBorders withTableBorder>
<TableThead>
<TableTr>
<TableTh>Deskripsi Program Kesehatan</TableTh>
<TableTh>Nama Konten Program Kesehatan</TableTh>
<TableTh>Deskripsi Konten Program Kesehatan</TableTh>
<TableTh>Aksi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
);
}
export default ListProgramKesehatan;

View File

@@ -1,62 +0,0 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
import React from 'react';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import { IconImageInPicture } from '@tabler/icons-react';
function CreateProgramKesehatan() {
return (
<Box>
<Stack gap={'xs'}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={'xs'}>
<Title order={4}>Tambah Program Kesehatan</Title>
<Box>
<Text fw={"bold"} fz={"sm"}>Deskripsi Program Kesehatan</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Box>
<Text>Masukkan Image</Text>
<IconImageInPicture size={50} />
</Box>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Nama Konten Program Kesehatan</Text>}
placeholder='Masukkan nama konten program kesehatan'
/>
<Box>
<Text fw={"bold"} fz={"sm"}>Deskripsi Konten Program Kesehatan</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Group>
<Button bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
<Paper bg={colors['white-1']} p={"md"}>
<Stack gap={"xs"}>
<Title order={4}>Preview Data Program Kesehatan</Title>
<Text fw={"bold"} fz={"sm"}>Deskripsi Program Kesehatan</Text>
<Box>
<Text fw={"bold"} fz={"sm"}>Image</Text>
<IconImageInPicture size={50} />
</Box>
<Text fw={"bold"} fz={"sm"}>Nama Konten Program Kesehatan</Text>
<Text fw={"bold"} fz={"sm"}>Deskripsi Konten Program Kesehatan</Text>
</Stack>
</Paper>
</Box>
</SimpleGrid>
</Stack>
</Box>
);
}
export default CreateProgramKesehatan;

View File

@@ -0,0 +1,77 @@
'use client'
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 { KesehatanEditor } from '../../_com/kesehatanEditor';
function CreatePuskesmas() {
const router = useRouter();
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Puskesmas</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<TextInput
label={<Text fz="sm" fw="bold">Kategori</Text>}
placeholder="masukkan kategori"
/>
{/* <FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/> */}
{/* {previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)} */}
<Box>
<Text fz="sm" fw="bold">Konten</Text>
<KesehatanEditor
showSubmit={false}
/>
</Box>
<Button bg={colors['blue-button']}>
Simpan Potensi
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreatePuskesmas;

View File

@@ -1,7 +1,7 @@
import colors from '@/con/colors';
import { Box, Stack, SimpleGrid, Paper, Title, TextInput, Text } from '@mantine/core';
import React from 'react';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
import { KesehatanEditor } from '../../_com/kesehatanEditor';
function UpdatePuskesmas() {
return (

View File

@@ -1,34 +1,64 @@
import colors from '@/con/colors';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import UpdatePuskesmas from './ui/Edit-Puskesmas/updatePuskesmas';
import CreatePuskesmas from './ui/Tambah-Puskesmas/createPuskesmas';
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import JudulList from '../../_com/judulList';
import HeaderSearch from '../../_com/header';
function Puskesmas() {
return (
<Stack gap={"xs"}>
<Box>
<Title order={3}>Puskesmas</Title>
<Tabs defaultValue="create" color={colors['blue-button']} variant='pills'>
<TabsList mb={10} bg={colors['BG-trans']} p={'xs'}>
<TabsTab value="create" >
Tambah Puskesmas
</TabsTab>
<TabsTab value="update" >
Edit Puskesmas
</TabsTab>
</TabsList>
<TabsPanel value="create">
<CreatePuskesmas />
</TabsPanel>
<TabsPanel value="update">
<UpdatePuskesmas />
</TabsPanel>
</Tabs>
</Box>
</Stack>
<Box>
<HeaderSearch
title='Puskesmas'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListPuskesmas/>
</Box>
);
}
function ListPuskesmas() {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Puskesmas'
href='/admin/kesehatan/puskesmas/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Kategori</TableTh>
<TableTh>Image</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>Test</Text>
</Box></TableTd>
<TableTd>Test</TableTd>
<TableTd>
<Image w={100} src={"/"} alt="image" />
</TableTd>
<TableTd>
<Button>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default Puskesmas;

View File

@@ -1,55 +0,0 @@
import colors from '@/con/colors';
import { Box, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
import React from 'react';
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
function CreatePuskesmas() {
return (
<Box>
<Stack gap={"xs"}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={"md"}>
<Stack gap={"xs"}>
<Title order={4}>Tambah 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 CreatePuskesmas;

View File

@@ -0,0 +1,73 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import React from 'react';
function CreateDataLingkunganDesa() {
const router = useRouter()
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Data Lingkungan Desa</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<Box>
<Text fz="sm" fw="bold">Gambar</Text>
<IconImageInPicture size={25} />
</Box>
{/* <FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/> */}
{/* {previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)} */}
<Box>
<Text fz="sm" fw="bold">Konten</Text>
{/* <CreateEditor
value={potensiState.create.form.content}
onChange={(htmlContent) => {
potensiState.create.form.content = htmlContent;
}}
/> */}
</Box>
<Button bg={colors['blue-button']} >
Simpan Data
</Button>
</Stack>
</Paper>
</Box>
);
}
export default CreateDataLingkunganDesa;

View File

@@ -1,11 +1,66 @@
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import React from 'react';
import HeaderSearch from '../../_com/header';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import colors from '@/con/colors';
import JudulList from '../../_com/judulList';
function Page() {
function DataLingkunganDesa() {
return (
<div>
data-lingkungan-desa
</div>
<Box>
<HeaderSearch
title='Data Lingkungan Desa'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListDataLingkunganDesa/>
</Box>
);
}
export default Page;
function ListDataLingkunganDesa() {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p="md">
<Stack>
<JudulList
title='List Data Lingkungan Desa'
href='/admin/lingkungan/data-lingkungan-desa/create'
/>
<Box style={{overflowX: 'auto'}}>
<Table striped withRowBorders withTableBorder style={{minWidth: '700px'}}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Gambar</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>Judul</Text>
</Box>
</TableTd>
<TableTd>
<Image w={100} alt="image" />
</TableTd>
<TableTd>Deskripsi</TableTd>
<TableTd>
<Button>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default DataLingkunganDesa;

View File

@@ -0,0 +1,61 @@
'use client'
import React from 'react';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
function Page() {
const router = useRouter()
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Edukasi Lingkungan</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
{/* <FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/> */}
{/* {previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)} */}
<Box>
<Text fz="sm" fw="bold">Gambar</Text>
<IconImageInPicture size={25} />
</Box>
<Button bg={colors['blue-button']} >
Simpan
</Button>
</Stack>
</Paper>
</Box>
);
}
export default Page;

View File

@@ -1,11 +1,66 @@
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import React from 'react';
import HeaderSearch from '../../_com/header';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import colors from '@/con/colors';
import JudulList from '../../_com/judulList';
function Page() {
function Page() {
return (
<div>
edukasi-lingkungan
</div>
<Box py={10}>
<HeaderSearch
title='Edukasi Lingkungan'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListEdukasiLingkungan/>
</Box>
);
}
function ListEdukasiLingkungan() {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Edukasi Lingkungan'
href='/admin/lingkungan/edukasi-lingkungan/create'
/>
<Box style={{overflowX: 'auto'}}>
<Table striped withRowBorders withTableBorder style={{minWidth: '700px'}}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Gambar</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>Judul</Text>
</Box>
</TableTd>
<TableTd>
<Image w={100} src="/" alt="image" />
</TableTd>
<TableTd>Deskripsi</TableTd>
<TableTd>
<Button>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default Page;

View File

@@ -0,0 +1,65 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
import React from 'react';
function Page() {
const router = useRouter()
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Gotong Royong</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
label={<Text fz="sm" fw="bold">Kategori</Text>}
placeholder="masukkan kategori"
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
{/* <FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/> */}
{/* {previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)} */}
<Box>
<Text fz="sm" fw="bold">Gambar</Text>
<IconImageInPicture size={25} />
</Box>
<Button bg={colors['blue-button']} >
Simpan
</Button>
</Stack>
</Paper>
</Box>
);
}
export default Page;

View File

@@ -1,11 +1,68 @@
import React from 'react';
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import HeaderSearch from '../../_com/header';
import colors from '@/con/colors';
import JudulList from '../../_com/judulList';
function Page() {
function GotongRoyong() {
return (
<div>
gotong-royong
</div>
<Box>
<HeaderSearch
title='Gotong Royong'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListGotongRoyong/>
</Box>
);
}
export default Page;
function ListGotongRoyong() {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Gotong Royong'
href='/admin/lingkungan/gotong-royong/create'
/>
<Box style={{ overflowX: 'auto'}}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px'}}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Kategori</TableTh>
<TableTh>Image</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>Judul</Text>
</Box>
</TableTd>
<TableTd>Kategori</TableTd>
<TableTd>
<Image w={100} src="/" alt="image" />
</TableTd>
<TableTd>Deskripsi</TableTd>
<TableTd>
<Button>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default GotongRoyong;

View File

@@ -0,0 +1,62 @@
'use client'
import React from 'react';
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
function Page() {
const router = useRouter()
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Konservasi Adat Bali</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
{/* <FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/>
*/}
{/* {previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)} */}
<Box>
<Text fz="sm" fw="bold">Gambar</Text>
<IconImageInPicture size={25} />
</Box>
<Button bg={colors['blue-button']}>
Simpan
</Button>
</Stack>
</Paper>
</Box>
);
}
export default Page;

View File

@@ -1,11 +1,66 @@
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import React from 'react';
import HeaderSearch from '../../_com/header';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import colors from '@/con/colors';
import JudulList from '../../_com/judulList';
function Page() {
function KonservasiAdatBali() {
return (
<div>
konservasi-adat-bali
</div>
<Box py={10}>
<HeaderSearch
title='Konservasi Adat Bali'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListKonservasiAdatBali />
</Box>
);
}
export default Page;
function ListKonservasiAdatBali() {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Konservasi Adat Bali'
href='/admin/lingkungan/konservasi-adat-bali/create'
/>
<Box style={{overflowX: 'auto'}}>
<Table striped withRowBorders withTableBorder style={{minWidth: '700px'}}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Gambar</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>Judul</Text>
</Box>
</TableTd>
<TableTd>
<Image w={100} src="/" alt="image" />
</TableTd>
<TableTd>Deskripsi</TableTd>
<TableTd>
<Button>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default KonservasiAdatBali;

View File

@@ -0,0 +1,71 @@
'use client'
import colors from "@/con/colors";
import { Box, Button, Paper, Stack, Title, TextInput, Text } from "@mantine/core";
import { IconArrowBack } from "@tabler/icons-react";
import { useRouter } from "next/navigation";
export default function CreatePengelolaanSampahBankSampah() {
const router = useRouter()
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap="xs">
<Title order={3}>Create Mekanisme Bank Sampah</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
{/* <FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/> */}
{/* {previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)} */}
<Box>
<Text fz="sm" fw="bold">Konten</Text>
{/* <CreateEditor
value={potensiState.create.form.content}
onChange={(htmlContent) => {
potensiState.create.form.content = htmlContent;
}}
/> */}
</Box>
<Button bg={colors['blue-button']} >
Simpan
</Button>
</Stack>
</Paper>
</Box>
)
}

View File

@@ -1,11 +1,33 @@
import React from 'react';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import ListPage from './ui/list_page/listPage';
import colors from '@/con/colors';
function Page() {
function PengelolaanSampahBankSampah() {
return (
<div>
pengelolaan-sampah-bank-sampah
</div>
);
<Box>
<Stack>
<Title order={3}>Pengelolaan Sampah Bank Sampah</Title>
<Tabs defaultValue="list" color={colors['blue-button']} variant="pills">
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
<TabsTab value="list">
List Pengelolaan Sampah Bank Sampah
</TabsTab>
<TabsTab value="maps">
Maps
</TabsTab>
</TabsList>
<TabsPanel value="list">
<ListPage />
</TabsPanel>
<TabsPanel value="maps">
Maps
</TabsPanel>
</Tabs>
</Stack>
</Box>
)
}
export default Page;
export default PengelolaanSampahBankSampah;

View File

@@ -0,0 +1,66 @@
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import React from 'react';
import HeaderSearch from '@/app/admin/(dashboard)/_com/header';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import colors from '@/con/colors';
import JudulList from '@/app/admin/(dashboard)/_com/judulList';
function ListPage() {
return (
<Box py={10}>
<HeaderSearch
title='Mekanisme Bank Sampah'
placeholder='pencarian'
searchIcon={<IconSearch size={20} />}
/>
<ListPengelolaanSampahBankSampah />
</Box>
);
}
function ListPengelolaanSampahBankSampah() {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Mekanisme Bank Sampah'
href='/admin/lingkungan/pengelolaan-sampah-bank-sampah/create'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Gambar</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>Judul</Text>
</Box>
</TableTd>
<TableTd>
<Image w={100} alt="image" />
</TableTd>
<TableTd>Deskripsi</TableTd>
<TableTd>
<Button>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default ListPage;

View File

@@ -0,0 +1,73 @@
'use client'
import colors from '@/con/colors';
import { Box, Button, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
import { useRouter } from 'next/navigation';
function Page() {
const router = useRouter()
return (
<Box>
<Box mb={10}>
<Button variant="subtle" onClick={() => router.back()}>
<IconArrowBack color={colors['blue-button']} size={25} />
</Button>
</Box>
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
<Stack gap={"xs"}>
<Title order={3}>Create Program Penghijauan</Title>
<TextInput
label={<Text fz="sm" fw="bold">Judul</Text>}
placeholder="masukkan judul"
/>
<TextInput
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
<Text fz="sm" fw="bold">Gambar</Text>
<IconImageInPicture size={25} />
{/* <FileInput
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
);
setPreviewImage(base64);
}}
/> */}
{/* {previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg="gray">
<IconImageInPicture />
</Center>
)} */}
<Box>
<Text fz="sm" fw="bold">Konten</Text>
{/* <CreateEditor
value={potensiState.create.form.content}
onChange={(htmlContent) => {
potensiState.create.form.content = htmlContent;
}}
/> */}
</Box>
<Button bg={colors['blue-button']}>
Simpan Potensi
</Button>
</Stack>
</Paper>
</Box>
);
}
export default Page;

View File

@@ -1,11 +1,68 @@
import React from 'react';
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
import HeaderSearch from '../../_com/header';
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
import colors from '@/con/colors';
import JudulList from '../../_com/judulList';
function Page() {
function ProgramPenghijauan() {
return (
<div>
program-penghijauan
</div>
<Box>
<HeaderSearch
title='Program Penghijauan'
placeholder='pencarian'
searchIcon={<IconSearch size={20}/>}
/>
<ListManfaatPenghijauan/>
</Box>
);
}
export default Page;
function ListManfaatPenghijauan() {
return (
<Box py={10}>
<Paper bg={colors['white-1']} p={'md'}>
<Stack>
<JudulList
title='List Manfaat Program Penghijauan'
/>
<Box style={{ overflowX: "auto" }}>
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
<TableThead>
<TableTr>
<TableTh>Judul</TableTh>
<TableTh>Gambar</TableTh>
<TableTh>Jumlah</TableTh>
<TableTh>Deskripsi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
<TableTr>
<TableTd>
<Box w={100}>
<Text truncate="end" fz={"sm"}>Judul</Text>
</Box>
</TableTd>
<TableTd>
<Text truncate="end" fz={"sm"}>Jumlah</Text>
</TableTd>
<TableTd>
<Image w={100} alt="image" />
</TableTd>
<TableTd>Deskripsi</TableTd>
<TableTd>
<Button>
<IconDeviceImacCog size={25} />
</Button>
</TableTd>
</TableTr>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
)
}
export default ProgramPenghijauan;

View File

@@ -36,7 +36,6 @@ function GrafikHasilKepuasan() {
<Stack>
<Title order={3}>Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik</Title>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Label"
placeholder="masukkan label"
value={grafikHasilKepuasan.create.form.label}
@@ -45,7 +44,6 @@ function GrafikHasilKepuasan() {
}}
/>
<TextInput
w={{ base: '100%', md: '50%' }}
label="Jumlah Kepuasan"
type="number"
placeholder="masukkan jumlah kepuasan"

View File

@@ -42,7 +42,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
}}
padding={'md'}
>
<AppShellHeader bg={colors["white-trans-1"]}>
<AppShellHeader bg={colors["white-1"]}>
<Group px={10} align="center">
{!desktopOpened && (
<ActionIcon variant="light" onClick={toggleDesktop}>

View File

@@ -13,9 +13,6 @@ type FormCreate = Prisma.BeritaGetPayload<{
}>;
async function beritaCreate(context: Context) {
const body = context.body as FormCreate;
console.log(body)
// console.log(body)
await prisma.berita.create({
data: {

View File

@@ -0,0 +1,55 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
import fs from "fs/promises";
import path from "path";
const beritaDelete = async (context: Context) => {
const id = context.params?.id as string;
if (!id) {
return {
status: 400,
body: "ID tidak diberikan",
};
}
const berita = await prisma.berita.findUnique({
where: { id },
include: {
image: true,
kategoriBerita: true, // pastikan relasi image sudah ada di prisma schema
},
});
if (!berita) {
return {
status: 404,
body: "Berita tidak ditemukan",
};
}
// Hapus file gambar dari filesystem jika ada
if (berita.image) {
try {
const filePath = path.join(berita.image.path, berita.image.name);
await fs.unlink(filePath);
await prisma.fileStorage.delete({
where: { id: berita.image.id },
});
} catch (err) {
console.error("Gagal hapus file image:", err);
}
}
// Hapus berita dari DB
await prisma.berita.delete({
where: { id },
});
return {
success: true,
message: "Berita dan file terkait berhasil dihapus",
};
};
export default beritaDelete;

View File

@@ -0,0 +1,59 @@
import prisma from "@/lib/prisma";
export default async function handler(
request: Request
) {
// Extract the ID from the URL path
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 {
// Validate that the ID is a valid UUID or whatever format you're using
if (typeof id !== 'string') {
return Response.json({
success: false,
message: "ID tidak valid",
}, { status: 400 });
}
const data = await prisma.berita.findUnique({
where: { id },
include: {
image: true,
kategoriBerita: true,
},
});
if (!data) {
return Response.json({
success: false,
message: "Berita tidak ditemukan",
}, { status: 404 });
}
// Ensure we're returning a proper Response object
return Response.json({
success: true,
message: "Success fetch berita by ID",
data,
}, {
status: 200,
});
} catch (e) {
console.error("Find by ID error:", e);
return Response.json({
success: false,
message: "Gagal mengambil berita: " + (e instanceof Error ? e.message : 'Unknown error'),
}, {
status: 500,
});
}
}

View File

@@ -2,10 +2,17 @@ import Elysia, { t } from "elysia";
import kategoriBeritaFindMany from "./category";
import beritaFindMany from "./find-many";
import beritaCreate from "./create";
import beritaDelete from "./del";
import beritaUpdate from "./updt";
import findBeritaById from "./find-by-id";
const Berita = new Elysia({ prefix: "/berita", tags: ["Desa/Berita"] })
.get("/category/find-many", kategoriBeritaFindMany)
.get("/find-many", beritaFindMany)
.get("/:id", async (context) => {
const response = await findBeritaById(new Request(context.request));
return response;
})
.post("/create", beritaCreate, {
body: t.Object({
judul: t.String(),
@@ -14,6 +21,23 @@ const Berita = new Elysia({ prefix: "/berita", tags: ["Desa/Berita"] })
content: t.String(),
kategoriBeritaId: t.Union([t.String(), t.Null()]),
}),
});
})
.delete("/delete/:id", beritaDelete)
.put(
"/:id",
async (context) => {
const response = await beritaUpdate(context);
return response;
},
{
body: t.Object({
judul: t.String(),
deskripsi: t.String(),
imageId: t.String(),
content: t.String(),
kategoriBeritaId: t.Union([t.String(), t.Null()]),
}),
}
);
export default Berita;

View File

@@ -0,0 +1,98 @@
import prisma from "@/lib/prisma";
import { Context } from "elysia";
import { Prisma } from "@prisma/client";
import fs from "fs/promises";
import path from "path";
type FormUpdate = Prisma.BeritaGetPayload<{
select: {
id: true;
judul: true;
deskripsi: true;
content: true;
kategoriBeritaId: true;
imageId: true;
};
}>;
async function beritaUpdate(context: Context) {
try {
const id = context.params?.id as string; // ambil dari URL
const body = (await context.body) as Omit<FormUpdate, "id">;
const {
judul,
deskripsi,
content,
kategoriBeritaId,
imageId,
} = body;
if (!id) {
return new Response(
JSON.stringify({ success: false, message: "ID tidak boleh kosong" }),
{ status: 400, headers: { 'Content-Type': 'application/json' } }
);
}
const existing = await prisma.berita.findUnique({
where: { id },
include: {
image: true,
kategoriBerita: true,
},
});
if (!existing) {
return new Response(
JSON.stringify({ success: false, message: "Berita tidak ditemukan" }),
{ status: 404, headers: { 'Content-Type': 'application/json' } }
);
}
if (existing.imageId && existing.imageId !== imageId) {
const oldImage = existing.image;
if (oldImage) {
try {
const filePath = path.join(oldImage.path, oldImage.name);
await fs.unlink(filePath);
await prisma.fileStorage.delete({
where: { id: oldImage.id },
});
} catch (err) {
console.error("Gagal hapus gambar lama:", err);
}
}
}
const updated = await prisma.berita.update({
where: { id },
data: {
judul,
deskripsi,
content,
kategoriBeritaId: kategoriBeritaId || null,
imageId,
},
});
return new Response(
JSON.stringify({
success: true,
message: "Berita berhasil diupdate",
data: updated,
}),
{ status: 200, headers: { 'Content-Type': 'application/json' } }
);
} catch (error) {
console.error("Error updating berita:", error);
return new Response(
JSON.stringify({
success: false,
message: "Terjadi kesalahan saat mengupdate berita",
}),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
}
export default beritaUpdate;

View File

@@ -2,11 +2,12 @@ import Elysia from "elysia";
import Berita from "./berita";
import Pengumuman from "./pengumuman";
import ProfileDesa from "./profile/profile_desa";
import PotensiDesa from "./potensi";
const Desa = new Elysia({ prefix: "/api/desa", tags: ["Desa"] })
.use(Berita)
.use(Pengumuman)
.use(ProfileDesa)
.use(PotensiDesa)
export default Desa;

View File

@@ -0,0 +1,33 @@
import prisma from "@/lib/prisma";
import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.PotensiDesaGetPayload<{
select: {
name: true;
deskripsi: true;
kategori: true;
imageId: true;
content: true;
}
}>
export default async function potensiDesaCreate(context: Context) {
const body = context.body as FormCreate;
await prisma.potensiDesa.create({
data: {
name: body.name,
deskripsi: body.deskripsi,
kategori: body.kategori,
imageId: body.imageId,
content: body.content,
},
});
return {
success: true,
message: "Success create potensi desa",
data: {
...body,
},
};
}

View File

@@ -0,0 +1,54 @@
import prisma from "@/lib/prisma";
import path from "path";
import { Context } from "vm";
import fs from "fs/promises";
const potensiDesaDelete = async (context: Context) => {
const id = context.params?.id as string;
if (!id) {
return {
status: 400,
body: "ID tidak diberikan",
};
}
const potensiDesa = await prisma.potensiDesa.findUnique({
where: { id },
include: {
image: true,
},
});
if (!potensiDesa) {
return {
status: 404,
body: "Potensi Desa tidak ditemukan",
};
}
// Hapus file gambar dari filesystem jika ada
if (potensiDesa.image) {
try {
const filePath = path.join(potensiDesa.image.path, potensiDesa.image.name);
await fs.unlink(filePath);
await prisma.fileStorage.delete({
where: { id: potensiDesa.image.id },
});
} catch (err) {
console.error("Gagal hapus file image:", err);
}
}
// Hapus potensi desa dari DB
await prisma.potensiDesa.delete({
where: { id },
});
return {
success: true,
message: "Potensi Desa dan file terkait berhasil dihapus",
};
};
export default potensiDesaDelete;

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