Compare commits
10 Commits
nico/17-ju
...
nico/24-ju
| Author | SHA1 | Date | |
|---|---|---|---|
| 27fa7ac0fc | |||
| fc08b2e790 | |||
| 4a5524ce88 | |||
| 899883ca2a | |||
| 10ecc13ad7 | |||
| 58f538425c | |||
| d2f53ff69b | |||
| 40f0294595 | |||
| 6ed0246cea | |||
| af726043bd |
@@ -23,7 +23,7 @@
|
||||
"@mantine/charts": "^7.17.1",
|
||||
"@mantine/core": "^7.17.4",
|
||||
"@mantine/dates": "^8.1.0",
|
||||
"@mantine/dropzone": "^7.17.0",
|
||||
"@mantine/dropzone": "^8.1.1",
|
||||
"@mantine/form": "^8.1.0",
|
||||
"@mantine/hooks": "^7.17.4",
|
||||
"@mantine/tiptap": "^7.17.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"id": "edit",
|
||||
"judul": "Lambang Desa",
|
||||
"deskripsi" : "<ul><li>Memperkokoh kerukunan hidup masyarakat dalam jalinan adat, budaya, olahraga, dan agama.</li><li>Meningkatkan kualitas pelayanan publik dengan menerapkan teknologi informasi dan komunikasi terintegrasi.</li><li>Meningkatkan tata kelola pemerintah desa dengan menerapkan prinsip good governance dan good clean government.</li><li>Meningkatkan kualitas pendidikan, kesehatan, Keluarga Berencana serta pengelolaan kependudukan.</li><li>Memperkuat usaha mikro kecil dan menengah (UMKM) dan BUMDesa sebagai pilar ekonomi masyarakat.</li><li>Mewujudkan tatanan kehidupan bermasyarakat yang menjunjung tinggi penegakan hukum dan HAM.</li><li>Meningkatkan perlindungan dan pengelolaan terhadap sumber daya alam dan lingkungan hidup.</li><li>Memperkuat daya saing desa melalui peningkatan mutu sumber daya manusia dan infrastruktur desa berbasis potensi desa.</li><li>Meningkatkan sinergisitas potensi budaya, pertanian dalam arti luas dan pariwisata.</li><li>Memperkuat daya saing desa melalui peningkatan mutu sumber daya manusia dan infrastruktur desa berbasis potensi desa.</li><li>Meningkatkan sinergisitas potensi budaya, pertanian dalam arti luas dan pariwisata.</li></ul>"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"id": "edit",
|
||||
"judul": "Maskot Desa",
|
||||
"deskripsi" : "<p>Pudak adalah bunga dari tanaman sejenis pandan (Pandanaceae). Bentuk bunga ini tersusun dalam beberapa lapisan, terbungkus oleh kelopak warna putih (semacam daun lonjong) yang ujungnya meruncing.</p><p>Bunga Pudak berwarna kuning dan akan terlihat jika kelopak atau pelepahnya telah mekar. Kekhasan dari bunga pudak, yaitu mempunyai aroma wangi yang semerbak nan lembut (tidak menyengat), dan dapat menebar keharuman sepanjang pagi atau pun sore hari. Tanaman ini dapat tumbuh di sepanjang pantai, aliran sungai, di atas batu-batu karang, dan juga di tanah ladang.</p><p>Dalam Kamus Jawa Kuna- Indonesia kata “Pudak” berarti bunga pandan atau Pandanus Moschatus (Mardiwarsito: 1981: 442). Selain itu bunga pudak juga dapat disebut ketaka atau ketaki (Mardiwarsito, 1981: 276). Sedangkan kata “Sategal” berasal dari kata dasar “Tegal” yang berarti ladang (Mardiwarsito, 1981: 593). Jadi Pudak Sategal dapat diartikan sebagai satu ladang luas yang dipenuhi bunga pudak dan menabar keharuman.</p><p>Pada sebuah kesempatan, Ida Pedanda Putu Pemaron menjelaskan mengenai makna dari istilah Pudak Sategal dengan sebuah analogi bahwa, sekuntum bunga pudak memiliki aroma wangi atau keharuman yang sangat kuat, apalagi jika satu ladang penuh bunga pudak, maka dapat dipastikan aroma keharumannya akan membumbung menyebar ke segala penjuru (Wawancara, 18 Mei 2019 di Geria Putra Mandara Kenderan, Tegallalang). “Pudak” ialah sebuah bunga yang memiliki aroma wangi atau keharuman yang semerbak, lembut, dan khas.</p><p>Garapan Tari Maskot Desa Darmasaba Sekar Pudak diwujudkan ke dalam bentuk tari kreasi yang ditarikan secara berkelompok dengan jumlah lima orang penari perempuan (putri).</p><p>Pemilihan penari perempuan dimaksudkan untuk mempresentasikan keindahan, keluwesan, dan keharuman dari bunga pudak. Sedangkan penetapan jumlah penari lima orang didasarkan atas pertimbangan kebutuhan koreografi agar dapat membentuk desain-desain komposisi lantai yang menarik dan dinamis, baik ketika ditarikan di area panggung yang luas atau pun area panggung yang kecil. Penyajian tari maskot ini dirancang dengan durasi waktu 9 menit.</p>"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"id": "edit",
|
||||
"biodata": "<p>I.B Surya Prabhawa Manuaba, S.H., M.H., adalah Perbekel Darmasaba periode 2021-2027, seorang advokat, pendiri Mantra Legal Consultants & Advocates, serta aktif di bidang musik dan akademis. Dia menempuh pendidikan hukum di Universitas Udayana dan Universitas Mahasaraswati Denpasar serta memiliki pengalaman luas di berbagai organisasi dan kepemimpinan.</p>",
|
||||
"pengalaman": "<ul><li>2021 - 2027: Perbekel Desa Darmasaba</li><li>2015 - Sekarang: Founder & Managing Director Mantra Legal Consultants & Advocates</li><li>2020 - Sekarang: Founder Ugawa Record Music Studio</li><li>2010 - 2016: Dosen Fakultas Hukum Universitas Mahasaraswati Denpasar</li></ul>",
|
||||
"pengalamanOrganisasi": "<ul> <li>1996 – 1997: Ketua OSIS SMP Negeri 1 Abiansemal</li><li>1999 – 2000: Ketua OSIS SMA Negeri 1 Mengwi</li> <li>2008 – 2009: Ketua BEM Universitas Mahasaraswati Denpasar</li> <li>2008 – 2010: Ketua Sekaa Taruna Sila Dharma, Banjar Tengah, Desa Adat Tegal, Darmasaba</li> <li>2020 – Sekarang: Pengurus Young Lawyer Committee Peradi Denpasar</li> <li>2021 – Sekarang: Dewan Kehormatan Himpunan Pengusaha Muda Indonesia (HIPMI) Badung</li> <li>2023 – 2028: Komite Tetap Advokasi – Bidang Hukum dan Regulasi Kamar Dagang dan Industri Badung</li> </ul>",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"id": "edit",
|
||||
"judul": "Sejarah Desa",
|
||||
"deskripsi": "<p>Asal – usul nama Darmasaba tertuang dalam lontar Usada Bali. Seperti di tulis dalam monografi Desa Darmasaba tahun 1980 silam, nama Darmasaba berkaitan dengan keturunan Danghyang Nirarta diceritakan, Sang kawi-wiku asal Daha (Jawa Timur) itu memiliki cucu bernama Ida Pedanda Sakti Manuaba yang tigggal di Desa Kendran Tegalalang Gianyar. Merasa tidak disenangi sang ayah, Ida Pedanda Sakti Manuaba pergi mengembara bersama dua orang pengiringnya. Pengembaraan sang pendeta sampai di pura Sarin Buana di Jimbaran. Saat mengadakan semedi di tempat ini sang pendeta melihat sinar api. Yang sangat jauh di utara. Timbul keinginan Ida Pedanda Manuaba untuk mengunjungi tempat itu. Sampailah sang Pedanda di pura Batan Bila Peguyangan. Disini Ida Pedanda Manuaba singgah menghadap Ida Pedanda Budha yang tinggal disana. Selanjutnya, kedua pendeta bersama-sama menuju arah utara dan singgah di Taman Cang Ana, sebuah taman milik Arya Lanang Blusung. Di tempat ini kedua pendeta bersama-sama melaksanakan semedi dan menetap untuk sementara waktu.</p>"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"id" : "1",
|
||||
"id" : "edit",
|
||||
"visi" : "<p>Mewujudkan Desa Darmasaba yang sejahtera, unggul, religius, berbudaya, dan aman dengan berlandaskan Tri Hita Karana</p>",
|
||||
"misi" : "<ul><li>Memperkokoh kerukunan hidup masyarakat dalam jalinan adat, budaya, olahraga, dan agama.</li><li>Meningkatkan kualitas pelayanan publik dengan menerapkan teknologi informasi dan komunikasi terintegrasi.</li><li>Meningkatkan tata kelola pemerintah desa dengan menerapkan prinsip good governance dan good clean government.</li><li>Meningkatkan kualitas pendidikan, kesehatan, Keluarga Berencana serta pengelolaan kependudukan.</li><li>Memperkuat usaha mikro kecil dan menengah (UMKM) dan BUMDesa sebagai pilar ekonomi masyarakat.</li><li>Mewujudkan tatanan kehidupan bermasyarakat yang menjunjung tinggi penegakan hukum dan HAM.</li><li>Meningkatkan perlindungan dan pengelolaan terhadap sumber daya alam dan lingkungan hidup.</li><li>Memperkuat daya saing desa melalui peningkatan mutu sumber daya manusia dan infrastruktur desa berbasis potensi desa.</li><li>Meningkatkan sinergisitas potensi budaya, pertanian dalam arti luas dan pariwisata.</li></ul>"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[
|
||||
{
|
||||
"id": "1",
|
||||
"id": "edit",
|
||||
"name": "I.B Surya Prabhawa Manuaba, S.H., M.H.",
|
||||
"biodata": "<p>I.B Surya Prabhawa Manuaba, S.H., M.H., adalah Perbekel Darmasaba periode 2021-2027, seorang advokat, pendiri Mantra Legal Consultants & Advocates, serta aktif di bidang musik dan akademis. Dia menempuh pendidikan hukum di Universitas Udayana dan Universitas Mahasaraswati Denpasar, serta memiliki pengalaman luas di berbagai organisasi dan kepemimpinan.</p>",
|
||||
"riwayat": "<ul> <li>2021 - 2027: Perbekel Desa Darmasaba</li> <li>2015 - Sekarang: Founder & Managing Director Mantra Legal Consultants & Advocates</li> <li>2020 - Sekarang: Founder Ugawa Record Music Studio</li> <li>2010 - 2016: Dosen Fakultas Hukum Universitas Mahasaraswati Denpasar</li> </ul>",
|
||||
|
||||
139
prisma/migrations/20250624034125_24_jun2025/migration.sql
Normal file
139
prisma/migrations/20250624034125_24_jun2025/migration.sql
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The primary key for the `DataKematian_Kelahiran` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "DataKematian_Kelahiran" DROP CONSTRAINT "DataKematian_Kelahiran_pkey",
|
||||
ALTER COLUMN "id" DROP DEFAULT,
|
||||
ALTER COLUMN "id" SET DATA TYPE TEXT,
|
||||
ADD CONSTRAINT "DataKematian_Kelahiran_pkey" PRIMARY KEY ("id");
|
||||
DROP SEQUENCE "DataKematian_Kelahiran_id_seq";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "ProfilePPID" ADD COLUMN "imageUrl" TEXT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Puskesmas" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"alamat" TEXT NOT NULL,
|
||||
"jamId" TEXT NOT NULL,
|
||||
"imageId" TEXT NOT NULL,
|
||||
"kontakId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "Puskesmas_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "JamOperasional" (
|
||||
"id" TEXT NOT NULL,
|
||||
"workDays" TEXT NOT NULL,
|
||||
"weekDays" TEXT NOT NULL,
|
||||
"holiday" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "JamOperasional_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "KontakPuskesmas" (
|
||||
"id" TEXT NOT NULL,
|
||||
"kontakPuskesmas" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"facebook" TEXT NOT NULL,
|
||||
"kontakUGD" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "KontakPuskesmas_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ProgramKesehatan" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"deskripsiSingkat" TEXT NOT NULL,
|
||||
"deskripsi" TEXT NOT NULL,
|
||||
"imageId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "ProgramKesehatan_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "PenangananDarurat" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"deskripsi" TEXT NOT NULL,
|
||||
"imageId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "PenangananDarurat_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "KontakDarurat" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"deskripsi" TEXT NOT NULL,
|
||||
"imageId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "KontakDarurat_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "InfoWabahPenyakit" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"deskripsiSingkat" TEXT NOT NULL,
|
||||
"deskripsiLengkap" TEXT NOT NULL,
|
||||
"imageId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
|
||||
CONSTRAINT "InfoWabahPenyakit_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Puskesmas" ADD CONSTRAINT "Puskesmas_jamId_fkey" FOREIGN KEY ("jamId") REFERENCES "JamOperasional"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Puskesmas" ADD CONSTRAINT "Puskesmas_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Puskesmas" ADD CONSTRAINT "Puskesmas_kontakId_fkey" FOREIGN KEY ("kontakId") REFERENCES "KontakPuskesmas"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ProgramKesehatan" ADD CONSTRAINT "ProgramKesehatan_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "PenangananDarurat" ADD CONSTRAINT "PenangananDarurat_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "KontakDarurat" ADD CONSTRAINT "KontakDarurat_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "InfoWabahPenyakit" ADD CONSTRAINT "InfoWabahPenyakit_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
12
prisma/migrations/20250624061829_24jun2025_2/migration.sql
Normal file
12
prisma/migrations/20250624061829_24jun2025_2/migration.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The primary key for the `GrafikKepuasan` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "GrafikKepuasan" DROP CONSTRAINT "GrafikKepuasan_pkey",
|
||||
ALTER COLUMN "id" DROP DEFAULT,
|
||||
ALTER COLUMN "id" SET DATA TYPE TEXT,
|
||||
ADD CONSTRAINT "GrafikKepuasan_pkey" PRIMARY KEY ("id");
|
||||
DROP SEQUENCE "GrafikKepuasan_id_seq";
|
||||
@@ -50,26 +50,32 @@ model AppMenuChild {
|
||||
// ========================================= 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[]
|
||||
Posyandu Posyandu[]
|
||||
StrukturPPID StrukturPPID[]
|
||||
GalleryFoto GalleryFoto[]
|
||||
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[]
|
||||
Posyandu Posyandu[]
|
||||
StrukturPPID StrukturPPID[]
|
||||
GalleryFoto GalleryFoto[]
|
||||
PelayananSuratKeterangan PelayananSuratKeterangan[]
|
||||
Penghargaan Penghargaan[]
|
||||
ProfileDesaImage ProfileDesaImage[]
|
||||
ProfilePPID ProfilePPID[]
|
||||
ProfilPerbekel ProfilPerbekel[]
|
||||
Penghargaan Penghargaan[]
|
||||
ProfileDesaImage ProfileDesaImage[]
|
||||
ProfilePPID ProfilePPID[]
|
||||
ProfilPerbekel ProfilPerbekel[]
|
||||
Puskesmas Puskesmas[]
|
||||
ProgramKesehatan ProgramKesehatan[]
|
||||
PenangananDarurat PenangananDarurat[]
|
||||
KontakDarurat KontakDarurat[]
|
||||
|
||||
InfoWabahPenyakit InfoWabahPenyakit[]
|
||||
}
|
||||
|
||||
//========================================= MENU PPID ========================================= //
|
||||
@@ -116,6 +122,7 @@ model ProfilePPID {
|
||||
riwayat String @db.Text
|
||||
pengalaman String @db.Text
|
||||
unggulan String @db.Text
|
||||
imageUrl String?
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
@@ -278,37 +285,37 @@ model LambangDesa {
|
||||
}
|
||||
|
||||
model MaskotDesa {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
judul String
|
||||
deskripsi String @db.Text
|
||||
deskripsi String @db.Text
|
||||
images ProfileDesaImage[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model ProfileDesaImage {
|
||||
id String @id @default(cuid())
|
||||
label String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
MaskotDesa MaskotDesa @relation(fields: [maskotDesaId], references: [id])
|
||||
id String @id @default(cuid())
|
||||
label String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
MaskotDesa MaskotDesa @relation(fields: [maskotDesaId], references: [id])
|
||||
maskotDesaId String
|
||||
}
|
||||
|
||||
model ProfilPerbekel {
|
||||
id String @id @default(cuid())
|
||||
biodata String @db.Text
|
||||
pengalaman String @db.Text
|
||||
pengalamanOrganisasi String @db.Text
|
||||
programUnggulan String @db.Text
|
||||
id String @id @default(cuid())
|
||||
biodata String @db.Text
|
||||
pengalaman String @db.Text
|
||||
pengalamanOrganisasi String @db.Text
|
||||
programUnggulan String @db.Text
|
||||
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||
imageId String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= BERITA ========================================= //
|
||||
@@ -617,7 +624,7 @@ model PendaftaranJadwalKegiatan {
|
||||
|
||||
// ========================================= PERSENTASE KELAHIRAN & KEMATIAN ========================================= //
|
||||
model DataKematian_Kelahiran {
|
||||
id Int @id @default(autoincrement())
|
||||
id String @id @default(cuid())
|
||||
tahun String
|
||||
kematianKasar String
|
||||
kematianBayi String
|
||||
@@ -630,7 +637,7 @@ model DataKematian_Kelahiran {
|
||||
|
||||
// ========================================= GRAFIK KEPUASAN ========================================= //
|
||||
model GrafikKepuasan {
|
||||
id Int @id @default(autoincrement())
|
||||
id String @id @default(cuid())
|
||||
label String
|
||||
jumlah String
|
||||
createdAt DateTime @default(now())
|
||||
@@ -722,3 +729,98 @@ model Posyandu {
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= PUSKESMAS ========================================= //
|
||||
model Puskesmas {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
alamat String
|
||||
jam JamOperasional @relation(fields: [jamId], references: [id])
|
||||
jamId String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
kontak KontakPuskesmas @relation(fields: [kontakId], references: [id])
|
||||
kontakId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
model JamOperasional {
|
||||
id String @id @default(cuid())
|
||||
workDays String
|
||||
weekDays String
|
||||
holiday String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
Puskesmas Puskesmas[]
|
||||
}
|
||||
|
||||
model KontakPuskesmas {
|
||||
id String @id @default(cuid())
|
||||
kontakPuskesmas String
|
||||
email String
|
||||
facebook String
|
||||
kontakUGD String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
Puskesmas Puskesmas[]
|
||||
}
|
||||
|
||||
// ========================================= PROGRAM KESSEHATAN ========================================= //
|
||||
model ProgramKesehatan {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
deskripsiSingkat String
|
||||
deskripsi String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= PENANGANAN DARURAT ========================================= //
|
||||
model PenangananDarurat {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
deskripsi String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
// ========================================= KONTAK DARURAT ========================================= //
|
||||
model KontakDarurat {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
deskripsi String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
// ========================================= INFO WABAH PENYAKIT ========================================= //
|
||||
model InfoWabahPenyakit {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
deskripsiSingkat String
|
||||
deskripsiLengkap String
|
||||
image FileStorage @relation(fields: [imageId], references: [id])
|
||||
imageId String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deletedAt DateTime @default(now())
|
||||
isActive Boolean @default(true)
|
||||
}
|
||||
|
||||
BIN
public/bungapudak.png
Normal file
BIN
public/bungapudak.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 221 KiB |
BIN
public/klimakstari.png
Normal file
BIN
public/klimakstari.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 217 KiB |
BIN
public/pohonpudak.png
Normal file
BIN
public/pohonpudak.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 253 KiB |
|
Before Width: | Height: | Size: 195 KiB After Width: | Height: | Size: 195 KiB |
BIN
public/tarisekar.png
Normal file
BIN
public/tarisekar.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 208 KiB |
@@ -6,7 +6,7 @@ import { z } from "zod";
|
||||
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(1).max(50),
|
||||
deskripsi: z.string().min(1).max(50),
|
||||
deskripsi: z.string().min(1).max(5000),
|
||||
kategori: z.string().min(1).max(50),
|
||||
imageId: z.string().min(1).max(50),
|
||||
content: z.string().min(1).max(5000),
|
||||
|
||||
@@ -38,13 +38,13 @@ const sejarahDesa = proxy({
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/profile/sejarah-desa/${id}`);
|
||||
|
||||
const response = await fetch(`/api/desa/profile/sejarah/${id}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
if (result.success) {
|
||||
this.data = result.data;
|
||||
return result.data;
|
||||
@@ -91,64 +91,69 @@ const sejarahDesa = proxy({
|
||||
},
|
||||
|
||||
async submit() {
|
||||
// Validate form
|
||||
const validation = sejarahDesaForm.safeParse(this.form);
|
||||
|
||||
if (!validation.success) {
|
||||
const errors = validation.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return false;
|
||||
// Validate form
|
||||
const validation = sejarahDesaForm.safeParse(this.form);
|
||||
|
||||
if (!validation.success) {
|
||||
const errors = validation.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/desa/profile/sejarah/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/profile/sejarah-desa/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
|
||||
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 profile");
|
||||
// Refresh profile data
|
||||
await sejarahDesa.findUnique.load(this.id);
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update profile");
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
this.error = errorMessage;
|
||||
console.error("Update profile error:", errorMessage);
|
||||
toast.error("Terjadi kesalahan saat update profile");
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// Reset form
|
||||
reset() {
|
||||
this.id = "";
|
||||
this.form = { ...sejarahDesaDefaultForm };
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
this.isReadOnly = false;
|
||||
);
|
||||
|
||||
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 profile");
|
||||
// Refresh profile data
|
||||
await sejarahDesa.findUnique.load(this.id);
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update profile");
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
this.error = errorMessage;
|
||||
console.error("Update profile error:", errorMessage);
|
||||
toast.error("Terjadi kesalahan saat update profile");
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// Reset form
|
||||
reset() {
|
||||
this.id = "";
|
||||
this.form = { ...sejarahDesaDefaultForm };
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
this.isReadOnly = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ========================================= VISI MISI DESA ========================================= //
|
||||
@@ -187,12 +192,12 @@ const visiMisiDesa = proxy({
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/profile/visi-misi/${id}`);
|
||||
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
if (result.success) {
|
||||
this.data = result.data;
|
||||
return result.data;
|
||||
@@ -239,64 +244,66 @@ const visiMisiDesa = proxy({
|
||||
},
|
||||
|
||||
async submit() {
|
||||
// Validate form
|
||||
const validation = visiMisiDesaForm.safeParse(this.form);
|
||||
|
||||
if (!validation.success) {
|
||||
const errors = validation.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/profile/visi-misi/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
|
||||
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 visi misi desa");
|
||||
// Refresh profile data
|
||||
await visiMisiDesa.findUnique.load(this.id);
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update visi misi desa");
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
this.error = errorMessage;
|
||||
console.error("Update visi misi desa error:", errorMessage);
|
||||
toast.error("Terjadi kesalahan saat update visi misi desa");
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// Reset form
|
||||
reset() {
|
||||
this.id = "";
|
||||
this.form = { ...visiMisiDesaDefaultForm };
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
this.isReadOnly = false;
|
||||
// Validate form
|
||||
const validation = visiMisiDesaForm.safeParse(this.form);
|
||||
|
||||
if (!validation.success) {
|
||||
const errors = validation.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/profile/visi-misi/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
|
||||
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 visi misi desa");
|
||||
// Refresh profile data
|
||||
await visiMisiDesa.findUnique.load(this.id);
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update visi misi desa");
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
this.error = errorMessage;
|
||||
console.error("Update visi misi desa error:", errorMessage);
|
||||
toast.error("Terjadi kesalahan saat update visi misi desa");
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// Reset form
|
||||
reset() {
|
||||
this.id = "";
|
||||
this.form = { ...visiMisiDesaDefaultForm };
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
this.isReadOnly = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ========================================= LAMBANG DESA ========================================= //
|
||||
@@ -334,13 +341,13 @@ const lambangDesa = proxy({
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/profile/lambang-desa/${id}`);
|
||||
|
||||
const response = await fetch(`/api/desa/profile/lambang/${id}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
if (result.success) {
|
||||
this.data = result.data;
|
||||
return result.data;
|
||||
@@ -387,64 +394,69 @@ const lambangDesa = proxy({
|
||||
},
|
||||
|
||||
async submit() {
|
||||
// Validate form
|
||||
const validation = lambangDesaForm.safeParse(this.form);
|
||||
|
||||
if (!validation.success) {
|
||||
const errors = validation.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return false;
|
||||
// Validate form
|
||||
const validation = lambangDesaForm.safeParse(this.form);
|
||||
|
||||
if (!validation.success) {
|
||||
const errors = validation.error.issues
|
||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||
.join(", ");
|
||||
toast.error(`Form tidak valid: ${errors}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/desa/profile/lambang/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/profile/lambang-desa/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
|
||||
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 lambang desa");
|
||||
// Refresh profile data
|
||||
await lambangDesa.findUnique.load(this.id);
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update lambang desa");
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
this.error = errorMessage;
|
||||
console.error("Update lambang desa error:", errorMessage);
|
||||
toast.error("Terjadi kesalahan saat update lambang desa");
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// Reset form
|
||||
reset() {
|
||||
this.id = "";
|
||||
this.form = { ...lambangDesaDefaultForm };
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
this.isReadOnly = false;
|
||||
);
|
||||
|
||||
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 lambang desa");
|
||||
// Refresh profile data
|
||||
await lambangDesa.findUnique.load(this.id);
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update lambang desa");
|
||||
}
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
this.error = errorMessage;
|
||||
console.error("Update lambang desa error:", errorMessage);
|
||||
toast.error("Terjadi kesalahan saat update lambang desa");
|
||||
return false;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// Reset form
|
||||
reset() {
|
||||
this.id = "";
|
||||
this.form = { ...lambangDesaDefaultForm };
|
||||
this.error = null;
|
||||
this.loading = false;
|
||||
this.isReadOnly = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// ========================================= MASKOT DESA ========================================= //
|
||||
@@ -502,7 +514,7 @@ const maskotDesa = proxy({
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/profile/maskot-desa/${id}`);
|
||||
const response = await fetch(`/api/desa/profile/maskot/${id}`);
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result.success) {
|
||||
@@ -577,7 +589,7 @@ const maskotDesa = proxy({
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/desa/profile/maskot-desa/${this.id}`,
|
||||
`/api/desa/profile/maskot/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
@@ -678,7 +690,7 @@ const profilPerbekel = proxy({
|
||||
this.error = null;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/desa/profile/profil-perbekel/${id}`);
|
||||
const response = await fetch(`/api/desa/profile/profileperbekel/${id}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
@@ -750,7 +762,7 @@ const profilPerbekel = proxy({
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
`/api/desa/profile/profil-perbekel/${this.id}`,
|
||||
`/api/desa/profile/profileperbekel/${this.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
@@ -806,11 +818,12 @@ const profilPerbekel = proxy({
|
||||
},
|
||||
});
|
||||
|
||||
const stateProfileDesa = {
|
||||
const stateProfileDesa = proxy({
|
||||
lambangDesa,
|
||||
maskotDesa,
|
||||
profilPerbekel,
|
||||
visiMisiDesa,
|
||||
sejarahDesa,
|
||||
};
|
||||
});
|
||||
|
||||
export default stateProfileDesa;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { z } from "zod";
|
||||
|
||||
const templateGrafikKepuasan = z.object({
|
||||
label: z.string().min(2, "Label harus diisi"),
|
||||
jumlah: z.string().min(2, "Jumlah harus diisi"),
|
||||
jumlah: z.string().min(1, "Jumlah harus diisi"),
|
||||
});
|
||||
|
||||
type GrafikKepuasan = Prisma.GrafikKepuasanGetPayload<{
|
||||
@@ -31,24 +31,30 @@ const grafikkepuasan = proxy({
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
grafikkepuasan.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.grafikkepuasan["create"].post(
|
||||
grafikkepuasan.create.form
|
||||
);
|
||||
const res = await ApiFetch.api.kesehatan.grafikkepuasan["create"].post(grafikkepuasan.create.form);
|
||||
|
||||
if (res.status === 200) {
|
||||
grafikkepuasan.create.form = {
|
||||
label: "",
|
||||
jumlah: ""
|
||||
};
|
||||
grafikkepuasan.findMany.load();
|
||||
return toast.success("success create");
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
grafikkepuasan.create.form = {
|
||||
label: "",
|
||||
jumlah: "",
|
||||
};
|
||||
grafikkepuasan.findMany.load();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return toast.error("failed create");
|
||||
toast.error("failed create");
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
return null;
|
||||
} finally {
|
||||
grafikkepuasan.create.loading = false;
|
||||
}
|
||||
@@ -67,10 +73,110 @@ const grafikkepuasan = proxy({
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.GrafikKepuasanGetPayload<{
|
||||
omit: { isActive: true }
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/grafikkepuasan/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
grafikkepuasan.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch grafikkepuasan:", res.statusText);
|
||||
grafikkepuasan.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching grafikkepuasan:", error);
|
||||
grafikkepuasan.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: {...defaultForm},
|
||||
loading: false,
|
||||
async byId() {
|
||||
},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
const cek = templateGrafikKepuasan.safeParse(grafikkepuasan.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
this.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/grafikkepuasan/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
}
|
||||
|
||||
toast.success("Berhasil update data!");
|
||||
|
||||
// ✅ Optional: refresh list kalau kamu langsung ke halaman list
|
||||
await grafikkepuasan.findMany.load();
|
||||
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error("Error update data:", error);
|
||||
toast.error("Gagal update data grafik kepuasan");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) {
|
||||
return toast.warn("ID tidak valid");
|
||||
}
|
||||
try {
|
||||
grafikkepuasan.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/kesehatan/grafikkepuasan/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "Grafik kepuasan berhasil dihapus"
|
||||
);
|
||||
await grafikkepuasan.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus grafik kepuasan"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus grafik kepuasan");
|
||||
} finally {
|
||||
grafikkepuasan.delete.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const stategrafikKepuasan = proxy({
|
||||
grafikkepuasan,
|
||||
});
|
||||
|
||||
export default stategrafikKepuasan;
|
||||
export default grafikkepuasan;
|
||||
|
||||
@@ -6,9 +6,9 @@ import { z } from "zod";
|
||||
|
||||
const templatePersentase = z.object({
|
||||
tahun: z.string().min(4, "Tahun harus diisi"),
|
||||
kematianKasar: z.string().min(2, "Kematian kasar harus diisi"),
|
||||
kelahiranKasar: z.string().min(2, "Kelahiran kasar harus diisi"),
|
||||
kematianBayi: z.string().min(2, "Kematian bayi harus diisi"),
|
||||
kematianKasar: z.string().min(1, "Kematian kasar harus diisi"),
|
||||
kelahiranKasar: z.string().min(1, "Kelahiran kasar harus diisi"),
|
||||
kematianBayi: z.string().min(1, "Kematian bayi harus diisi"),
|
||||
});
|
||||
|
||||
type Persentase = Prisma.DataKematian_KelahiranGetPayload<{
|
||||
@@ -37,26 +37,32 @@ const persentasekelahiran = proxy({
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
toast.error(err);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
persentasekelahiran.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.persentasekelahiran[
|
||||
"create"
|
||||
].post(persentasekelahiran.create.form);
|
||||
const res = await ApiFetch.api.kesehatan.persentasekelahiran["create"].post(persentasekelahiran.create.form);
|
||||
|
||||
if (res.status === 200) {
|
||||
persentasekelahiran.create.form = {
|
||||
tahun: "",
|
||||
kematianKasar: "",
|
||||
kelahiranKasar: "",
|
||||
kematianBayi: "",
|
||||
};
|
||||
persentasekelahiran.findMany.load();
|
||||
return toast.success("success create");
|
||||
const id = res.data?.data?.id;
|
||||
if (id) {
|
||||
toast.success("Success create");
|
||||
persentasekelahiran.create.form = {
|
||||
tahun: "",
|
||||
kematianKasar: "",
|
||||
kelahiranKasar: "",
|
||||
kematianBayi: "",
|
||||
};
|
||||
persentasekelahiran.findMany.load();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return toast.error("failed create");
|
||||
toast.error("failed create");
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
return null;
|
||||
} finally {
|
||||
persentasekelahiran.create.loading = false;
|
||||
}
|
||||
@@ -75,10 +81,111 @@ const persentasekelahiran = proxy({
|
||||
}
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.DataKematian_KelahiranGetPayload<{
|
||||
omit: { isActive: true };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/persentasekelahiran/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
persentasekelahiran.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch persentasekelahiran:", res.statusText);
|
||||
persentasekelahiran.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching persentasekelahiran:", error);
|
||||
persentasekelahiran.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
update: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async byId() {},
|
||||
async submit() {
|
||||
const id = this.id;
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
const cek = templatePersentase.safeParse(persentasekelahiran.update.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
try {
|
||||
this.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/persentasekelahiran/${id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(this.form),
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok || !result?.success) {
|
||||
throw new Error(result?.message || "Gagal update data");
|
||||
}
|
||||
|
||||
toast.success("Berhasil update data!");
|
||||
|
||||
// ✅ Optional: refresh list kalau kamu langsung ke halaman list
|
||||
await persentasekelahiran.findMany.load();
|
||||
|
||||
return result.data;
|
||||
} catch (error) {
|
||||
console.error("Error update data:", error);
|
||||
toast.error("Gagal update data persentase kelahiran");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
persentasekelahiran.delete.loading = true;
|
||||
|
||||
const response = await fetch(
|
||||
`/api/kesehatan/persentasekelahiran/del/${id}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(
|
||||
result.message || "Persentase kelahiran berhasil dihapus"
|
||||
);
|
||||
await persentasekelahiran.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(
|
||||
result?.message || "Gagal menghapus persentase kelahiran"
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus persentase kelahiran");
|
||||
} finally {
|
||||
persentasekelahiran.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const statePersentase = proxy({
|
||||
persentasekelahiran,
|
||||
});
|
||||
|
||||
export default statePersentase;
|
||||
export default persentasekelahiran;
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
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(3, "Judul minimal 3 karakter"),
|
||||
deskripsiSingkat: z.string().min(3, "Deskripsi singkat minimal 3 karakter"),
|
||||
deskripsiLengkap: z.string().min(3, "Deskripsi lengkap minimal 3 karakter"),
|
||||
imageId: z.string().nonempty(),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
deskripsiSingkat: "",
|
||||
deskripsiLengkap: "",
|
||||
imageId: "",
|
||||
};
|
||||
|
||||
const infoWabahPenyakit = proxy({
|
||||
findMany: {
|
||||
data: [] as Prisma.InfoWabahPenyakitGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.infowabahpenyakit[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
infoWabahPenyakit.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
create: {
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateForm.safeParse(infoWabahPenyakit.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
infoWabahPenyakit.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.infowabahpenyakit[
|
||||
"create"
|
||||
].post(infoWabahPenyakit.create.form);
|
||||
if (res.status === 200) {
|
||||
infoWabahPenyakit.findMany.load();
|
||||
return toast.success("Info wabah penyakit berhasil disimpan!");
|
||||
}
|
||||
|
||||
return toast.error("Gagal menyimpan info wabah penyakit");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
infoWabahPenyakit.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
infoWabahPenyakit.create.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.InfoWabahPenyakitGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/infowabahpenyakit/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
infoWabahPenyakit.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch info wabah penyakit:", res.statusText);
|
||||
infoWabahPenyakit.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching info wabah penyakit:", error);
|
||||
infoWabahPenyakit.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
infoWabahPenyakit.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/kesehatan/infowabahpenyakit/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Info wabah penyakit berhasil dihapus");
|
||||
await infoWabahPenyakit.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus info wabah penyakit");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus info wabah penyakit");
|
||||
} finally {
|
||||
infoWabahPenyakit.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/infowabahpenyakit/${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,
|
||||
deskripsiSingkat: data.deskripsiSingkat,
|
||||
deskripsiLengkap: data.deskripsiLengkap,
|
||||
imageId: data.imageId,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching info wabah penyakit:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateForm.safeParse(infoWabahPenyakit.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
infoWabahPenyakit.edit.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/infowabahpenyakit/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
deskripsiSingkat: this.form.deskripsiSingkat,
|
||||
deskripsiLengkap: this.form.deskripsiLengkap,
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success(result.message || "Info wabah penyakit berhasil diupdate");
|
||||
await infoWabahPenyakit.findMany.load();
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update info wabah penyakit");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal update:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat mengupdate info wabah penyakit");
|
||||
return false;
|
||||
} finally {
|
||||
infoWabahPenyakit.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
infoWabahPenyakit.edit.id = "";
|
||||
infoWabahPenyakit.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default infoWabahPenyakit;
|
||||
@@ -0,0 +1,208 @@
|
||||
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(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
imageId: z.string().nonempty(),
|
||||
})
|
||||
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
}
|
||||
|
||||
const kontakDarurat = proxy({
|
||||
findMany: {
|
||||
data: [] as Prisma.KontakDaruratGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.kontakdarurat[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
kontakDarurat.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
create:{
|
||||
form: {...defaultForm},
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateForm.safeParse(kontakDarurat.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
kontakDarurat.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.kontakdarurat[
|
||||
"create"
|
||||
].post(kontakDarurat.create.form);
|
||||
if (res.status === 200) {
|
||||
kontakDarurat.findMany.load();
|
||||
return toast.success("Kontak Darurat berhasil disimpan!");
|
||||
}
|
||||
|
||||
return toast.error("Gagal menyimpan kontak darurat");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
kontakDarurat.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
kontakDarurat.create.form = {...defaultForm};
|
||||
}
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.KontakDaruratGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/kontakdarurat/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
kontakDarurat.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
kontakDarurat.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
kontakDarurat.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
try {
|
||||
kontakDarurat.delete.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/kontakdarurat/del/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Kontak darurat berhasil dihapus");
|
||||
await kontakDarurat.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus kontak darurat");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus kontak darurat");
|
||||
} finally {
|
||||
kontakDarurat.delete.loading = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/kontakdarurat/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
imageId: data.imageId,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching kontak darurat:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateForm.safeParse(kontakDarurat.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
kontakDarurat.edit.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/kontakdarurat/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success(result.message || "Kontak darurat berhasil diupdate");
|
||||
await kontakDarurat.findMany.load();
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update kontak darurat");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal update:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat mengupdate kontak darurat");
|
||||
return false;
|
||||
} finally {
|
||||
kontakDarurat.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
kontakDarurat.edit.id = "";
|
||||
kontakDarurat.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default kontakDarurat
|
||||
@@ -0,0 +1,208 @@
|
||||
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(3, "Judul minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
imageId: z.string().nonempty(),
|
||||
})
|
||||
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
}
|
||||
|
||||
const penangananDarurat = proxy({
|
||||
findMany: {
|
||||
data: [] as Prisma.PenangananDaruratGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.penanganandarurat[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
penangananDarurat.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
create:{
|
||||
form: {...defaultForm},
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateForm.safeParse(penangananDarurat.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
penangananDarurat.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.penanganandarurat[
|
||||
"create"
|
||||
].post(penangananDarurat.create.form);
|
||||
if (res.status === 200) {
|
||||
penangananDarurat.findMany.load();
|
||||
return toast.success("Penanganan Darurat berhasil disimpan!");
|
||||
}
|
||||
|
||||
return toast.error("Gagal menyimpan penanganan darurat");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
penangananDarurat.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
penangananDarurat.create.form = {...defaultForm};
|
||||
}
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.PenangananDaruratGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/penanganandarurat/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
penangananDarurat.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch data", res.status, res.statusText);
|
||||
penangananDarurat.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
penangananDarurat.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
try {
|
||||
penangananDarurat.delete.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/penanganandarurat/del/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Penanganan darurat berhasil dihapus");
|
||||
await penangananDarurat.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus penanganan darurat");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus penanganan darurat");
|
||||
} finally {
|
||||
penangananDarurat.delete.loading = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/penanganandarurat/${id}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result?.success) {
|
||||
const data = result.data;
|
||||
this.id = data.id;
|
||||
this.form = {
|
||||
name: data.name,
|
||||
deskripsi: data.deskripsi,
|
||||
imageId: data.imageId,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching penanganan darurat:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateForm.safeParse(penangananDarurat.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
penangananDarurat.edit.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/penanganandarurat/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success(result.message || "Penanganan darurat berhasil diupdate");
|
||||
await penangananDarurat.findMany.load();
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update penanganan darurat");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal update:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat mengupdate penanganan darurat");
|
||||
return false;
|
||||
} finally {
|
||||
penangananDarurat.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
penangananDarurat.edit.id = "";
|
||||
penangananDarurat.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default penangananDarurat
|
||||
217
src/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu.ts
Normal file
217
src/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
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, { message: "Name is required" }),
|
||||
nomor: z.string().min(1, { message: "Nomor is required" }),
|
||||
deskripsi: z.string().min(1, { message: "Deskripsi is required" }),
|
||||
imageId: z.string().nonempty(),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
nomor: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
};
|
||||
|
||||
const posyandustate = proxy({
|
||||
create: {
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateForm.safeParse(posyandustate.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
posyandustate.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.posyandu["create"].post(posyandustate.create.form);
|
||||
if (res.status === 200) {
|
||||
posyandustate.findMany.load();
|
||||
return toast.success("Posyandu berhasil disimpan!");
|
||||
}
|
||||
return toast.error("Gagal menyimpan posyandu");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
posyandustate.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm(){
|
||||
posyandustate.create.form = { ...defaultForm };
|
||||
}
|
||||
},
|
||||
findMany: {
|
||||
data: null as
|
||||
| Prisma.PosyanduGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
}
|
||||
}>[]
|
||||
| null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.posyandu["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
posyandustate.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
}
|
||||
},
|
||||
findUnique: {
|
||||
data: null as
|
||||
| Prisma.PosyanduGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
}
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/posyandu/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
posyandustate.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch posyandu:", res.statusText);
|
||||
posyandustate.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching posyandu:", error);
|
||||
posyandustate.findUnique.data = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
try {
|
||||
posyandustate.delete.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/posyandu/del/${id}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Posyandu berhasil dihapus");
|
||||
await posyandustate.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus posyandu");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus posyandu");
|
||||
} finally {
|
||||
posyandustate.delete.loading = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: {...defaultForm},
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if(!id){
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/posyandu/${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,
|
||||
nomor: data.nomor,
|
||||
deskripsi: data.deskripsi,
|
||||
imageId: data.imageId || "",
|
||||
};
|
||||
return data;
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching posyandu:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||
return null;
|
||||
}
|
||||
},
|
||||
async update() {
|
||||
const cek = templateForm.safeParse(posyandustate.edit.form);
|
||||
if(!cek.success){
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
posyandustate.edit.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/posyandu/${this.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
nomor: this.form.nomor,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
});
|
||||
|
||||
if(!response.ok){
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if(result.success){
|
||||
toast.success(result.message || "Posyandu berhasil diperbarui");
|
||||
await posyandustate.findMany.load(); // refresh list
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching posyandu:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||
return false;
|
||||
} finally {
|
||||
posyandustate.edit.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
posyandustate.edit.id = "";
|
||||
posyandustate.edit.form = {...defaultForm};
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default posyandustate;
|
||||
@@ -0,0 +1,214 @@
|
||||
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(3, "Judul minimal 3 karakter"),
|
||||
deskripsiSingkat: z.string().min(3, "Deskripsi singkat minimal 3 karakter"),
|
||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||
imageId: z.string().nonempty(),
|
||||
});
|
||||
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
deskripsiSingkat: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
};
|
||||
|
||||
const programKesehatan = proxy({
|
||||
findMany: {
|
||||
data: [] as Prisma.ProgramKesehatanGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}>[],
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.programkesehatan[
|
||||
"find-many"
|
||||
].get();
|
||||
if (res.status === 200) {
|
||||
programKesehatan.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
create: {
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async create() {
|
||||
const cek = templateForm.safeParse(programKesehatan.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
programKesehatan.create.loading = true;
|
||||
const res = await ApiFetch.api.kesehatan.programkesehatan[
|
||||
"create"
|
||||
].post(programKesehatan.create.form);
|
||||
if (res.status === 200) {
|
||||
programKesehatan.findMany.load();
|
||||
return toast.success("Program Kesehatan berhasil disimpan!");
|
||||
}
|
||||
|
||||
return toast.error("Gagal menyimpan program kesehatan");
|
||||
} catch (error) {
|
||||
console.log((error as Error).message);
|
||||
} finally {
|
||||
programKesehatan.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
programKesehatan.create.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
findUnique: {
|
||||
data: null as Prisma.ProgramKesehatanGetPayload<{
|
||||
include: {
|
||||
image: true;
|
||||
};
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
try {
|
||||
const res = await fetch(`/api/kesehatan/programkesehatan/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
programKesehatan.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
console.error("Failed to fetch program kesehatan:", res.statusText);
|
||||
programKesehatan.findUnique.data = null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching program kesehatan:", error);
|
||||
programKesehatan.findUnique.data = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
if (!id) return toast.warn("ID tidak valid");
|
||||
|
||||
try {
|
||||
programKesehatan.delete.loading = true;
|
||||
|
||||
const response = await fetch(`/api/kesehatan/programkesehatan/del/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
if (response.ok && result?.success) {
|
||||
toast.success(result.message || "Program kesehatan berhasil dihapus");
|
||||
await programKesehatan.findMany.load(); // refresh list
|
||||
} else {
|
||||
toast.error(result?.message || "Gagal menghapus program kesehatan");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal delete:", error);
|
||||
toast.error("Terjadi kesalahan saat menghapus program kesehatan");
|
||||
} finally {
|
||||
programKesehatan.delete.loading = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
if (!id) {
|
||||
toast.warn("ID tidak valid");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/kesehatan/programkesehatan/${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,
|
||||
deskripsiSingkat: data.deskripsiSingkat,
|
||||
deskripsi: data.deskripsi,
|
||||
imageId: data.imageId,
|
||||
};
|
||||
return data; // Return the loaded data
|
||||
} else {
|
||||
throw new Error(result?.message || "Gagal memuat data");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching program kesehatan:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
async update() {
|
||||
const cek = templateForm.safeParse(programKesehatan.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues
|
||||
.map((v) => `${v.path.join(".")}`)
|
||||
.join("\n")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
programKesehatan.edit.loading = true;
|
||||
const response = await fetch(`/api/kesehatan/programkesehatan/${this.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: this.form.name,
|
||||
deskripsiSingkat: this.form.deskripsiSingkat,
|
||||
deskripsi: this.form.deskripsi,
|
||||
imageId: this.form.imageId,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
toast.success(result.message || "Program kesehatan berhasil diupdate");
|
||||
await programKesehatan.findMany.load();
|
||||
return true;
|
||||
} else {
|
||||
throw new Error(result.message || "Gagal update program kesehatan");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Gagal update:", error);
|
||||
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat mengupdate program kesehatan");
|
||||
return false;
|
||||
} finally {
|
||||
programKesehatan.edit.loading = false;
|
||||
}
|
||||
},
|
||||
reset() {
|
||||
programKesehatan.edit.id = "";
|
||||
programKesehatan.edit.form = { ...defaultForm };
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default programKesehatan;
|
||||
@@ -0,0 +1,298 @@
|
||||
import ApiFetch from "@/lib/api-fetch";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { toast } from "react-toastify";
|
||||
import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
// Validasi form
|
||||
const templateForm = z.object({
|
||||
name: z.string().min(1),
|
||||
alamat: z.string().min(1),
|
||||
imageId: z.string().min(1),
|
||||
jam: z.object({
|
||||
workDays: z.string().min(1),
|
||||
weekDays: z.string().min(1),
|
||||
holiday: z.string().min(1),
|
||||
}),
|
||||
kontak: z.object({
|
||||
kontakPuskesmas: z.string().min(1),
|
||||
email: z.string().min(1),
|
||||
facebook: z.string().min(1),
|
||||
kontakUGD: z.string().min(1),
|
||||
}),
|
||||
});
|
||||
|
||||
// Default form
|
||||
const defaultForm = {
|
||||
name: "",
|
||||
alamat: "",
|
||||
imageId: "",
|
||||
jam: {
|
||||
workDays: "",
|
||||
weekDays: "",
|
||||
holiday: "",
|
||||
},
|
||||
kontak: {
|
||||
kontakPuskesmas: "",
|
||||
email: "",
|
||||
facebook: "",
|
||||
kontakUGD: "",
|
||||
},
|
||||
image: undefined,
|
||||
};
|
||||
|
||||
const puskesmasState = proxy({
|
||||
create: {
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
async submit() {
|
||||
const cek = templateForm.safeParse(puskesmasState.create.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues.map((v) => v.path.join(".")).join(", ")}] required`;
|
||||
return toast.error(err);
|
||||
}
|
||||
|
||||
try {
|
||||
puskesmasState.create.loading = true;
|
||||
|
||||
console.log('Form data:', puskesmasState.create.form);
|
||||
interface ErrorResponse {
|
||||
message?: string;
|
||||
error?: string;
|
||||
errors?: Record<string, string[]>;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
name: puskesmasState.create.form.name,
|
||||
alamat: puskesmasState.create.form.alamat,
|
||||
imageId: puskesmasState.create.form.imageId,
|
||||
jam: {
|
||||
workDays: puskesmasState.create.form.jam.workDays,
|
||||
weekDays: puskesmasState.create.form.jam.weekDays,
|
||||
holiday: puskesmasState.create.form.jam.holiday,
|
||||
},
|
||||
kontak: {
|
||||
kontakPuskesmas: puskesmasState.create.form.kontak.kontakPuskesmas,
|
||||
email: puskesmasState.create.form.kontak.email,
|
||||
facebook: puskesmasState.create.form.kontak.facebook,
|
||||
kontakUGD: puskesmasState.create.form.kontak.kontakUGD,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
console.log('Sending payload:', JSON.stringify(payload, null, 2));
|
||||
|
||||
try {
|
||||
const res = await ApiFetch.api.kesehatan.puskesmas.create.post(payload);
|
||||
console.log('API Response:', res);
|
||||
|
||||
if (res.status === 200) {
|
||||
await puskesmasState.findMany.load();
|
||||
toast.success("Berhasil menambahkan puskesmas");
|
||||
return res;
|
||||
} else {
|
||||
console.error('API Error Response:', {
|
||||
status: res.status,
|
||||
data: res.data
|
||||
});
|
||||
|
||||
const errorData = res.data as ErrorResponse;
|
||||
let errorMessage = 'Gagal menambahkan puskesmas';
|
||||
|
||||
if (errorData?.message) {
|
||||
errorMessage = errorData.message;
|
||||
} else if (errorData?.error) {
|
||||
errorMessage = errorData.error;
|
||||
} else if (errorData?.errors) {
|
||||
errorMessage = Object.entries(errorData.errors)
|
||||
.map(([field, messages]) => `${field}: ${Array.isArray(messages) ? messages.join(', ') : messages}`)
|
||||
.join('; ');
|
||||
}
|
||||
|
||||
console.error('Extracted error message:', errorMessage);
|
||||
toast.error(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in API call:', {
|
||||
error,
|
||||
errorString: String(error),
|
||||
jsonError: error instanceof Error ? {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack
|
||||
} : 'Not an Error instance'
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error in puskesmas submit:", {
|
||||
error,
|
||||
errorString: String(error),
|
||||
errorType: typeof error,
|
||||
isErrorInstance: error instanceof Error,
|
||||
errorDetails: error instanceof Error ? {
|
||||
name: error.name,
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
cause: error.cause
|
||||
} : null
|
||||
});
|
||||
|
||||
let errorMessage = "Terjadi kesalahan saat menambahkan puskesmas";
|
||||
if (error instanceof Error) {
|
||||
errorMessage = error.message || errorMessage;
|
||||
} else if (error && typeof error === 'object' && 'message' in error) {
|
||||
errorMessage = String((error as { message: unknown }).message);
|
||||
} else if (typeof error === 'string') {
|
||||
errorMessage = error;
|
||||
}
|
||||
|
||||
console.error('Displaying error to user:', errorMessage);
|
||||
toast.error(errorMessage);
|
||||
throw error;
|
||||
} finally {
|
||||
puskesmasState.create.loading = false;
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
puskesmasState.create.form = { ...defaultForm };
|
||||
}
|
||||
},
|
||||
|
||||
findMany: {
|
||||
data: null as Prisma.PuskesmasGetPayload<{
|
||||
include: { image: true; jam: true; kontak: true };
|
||||
}>[] | null,
|
||||
async load() {
|
||||
const res = await ApiFetch.api.kesehatan.puskesmas["find-many"].get();
|
||||
if (res.status === 200) {
|
||||
puskesmasState.findMany.data = res.data?.data ?? [];
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
findUnique: {
|
||||
data: null as Prisma.PuskesmasGetPayload<{
|
||||
include: { image: true; jam: true; kontak: true };
|
||||
}> | null,
|
||||
async load(id: string) {
|
||||
const res = await fetch(`/api/kesehatan/puskesmas/${id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
puskesmasState.findUnique.data = data.data ?? null;
|
||||
} else {
|
||||
toast.error("Gagal load data puskesmas");
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
edit: {
|
||||
id: "",
|
||||
form: { ...defaultForm },
|
||||
loading: false,
|
||||
|
||||
async load(id: string) {
|
||||
const res = await fetch(`/api/kesehatan/puskesmas/${id}`);
|
||||
if (!res.ok) {
|
||||
toast.error("Gagal memuat data");
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await res.json();
|
||||
const data = result.data;
|
||||
|
||||
puskesmasState.edit.id = data.id;
|
||||
puskesmasState.edit.form = {
|
||||
name: data.name,
|
||||
alamat: data.alamat,
|
||||
imageId: data.imageId,
|
||||
jam: {
|
||||
workDays: data.jam.workDays,
|
||||
weekDays: data.jam.weekDays,
|
||||
holiday: data.jam.holiday,
|
||||
},
|
||||
kontak: {
|
||||
kontakPuskesmas: data.kontak.kontakPuskesmas,
|
||||
email: data.kontak.email,
|
||||
facebook: data.kontak.facebook,
|
||||
kontakUGD: data.kontak.kontakUGD,
|
||||
},
|
||||
image: data.image,
|
||||
};
|
||||
},
|
||||
|
||||
async submit() {
|
||||
const cek = templateForm.safeParse(puskesmasState.edit.form);
|
||||
if (!cek.success) {
|
||||
const err = `[${cek.error.issues.map((v) => v.path.join(".")).join(", ")}] required`;
|
||||
toast.error(err);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
puskesmasState.edit.loading = true;
|
||||
const payload = {
|
||||
name: puskesmasState.edit.form.name,
|
||||
alamat: puskesmasState.edit.form.alamat,
|
||||
imageId: puskesmasState.edit.form.imageId,
|
||||
jam: { ...puskesmasState.edit.form.jam },
|
||||
kontak: { ...puskesmasState.edit.form.kontak }
|
||||
};
|
||||
|
||||
const res = await fetch(`/api/kesehatan/puskesmas/${puskesmasState.edit.id}`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const error = await res.json();
|
||||
throw new Error(error.message || "Update gagal");
|
||||
}
|
||||
|
||||
toast.success("Berhasil update puskesmas");
|
||||
await puskesmasState.findMany.load();
|
||||
return true;
|
||||
} catch (err) {
|
||||
toast.error(err instanceof Error ? err.message : "Terjadi kesalahan saat update");
|
||||
return false;
|
||||
} finally {
|
||||
puskesmasState.edit.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
reset() {
|
||||
puskesmasState.edit.id = "";
|
||||
puskesmasState.edit.form = { ...defaultForm };
|
||||
}
|
||||
},
|
||||
|
||||
delete: {
|
||||
loading: false,
|
||||
async byId(id: string) {
|
||||
try {
|
||||
puskesmasState.delete.loading = true;
|
||||
const res = await fetch(`/api/kesehatan/puskesmas/del/${id}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
const result = await res.json();
|
||||
if (res.ok && result.success) {
|
||||
toast.success("Puskesmas berhasil dihapus");
|
||||
await puskesmasState.findMany.load();
|
||||
} else {
|
||||
toast.error(result.message || "Gagal menghapus");
|
||||
}
|
||||
} catch {
|
||||
toast.error("Terjadi kesalahan saat menghapus");
|
||||
} finally {
|
||||
puskesmasState.delete.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default puskesmasState;
|
||||
@@ -5,9 +5,9 @@ import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateGrafikJenisKelamin = z.object({
|
||||
laki: z.string().min(2, "Data laki-laki harus diisi"),
|
||||
perempuan: z.string().min(2, "Data perempuan harus diisi"),
|
||||
});
|
||||
laki: z.string().min(1, "Data laki-laki harus diisi"),
|
||||
perempuan: z.string().min(1, "Data perempuan harus diisi"),
|
||||
});
|
||||
|
||||
type GrafikJenisKelamin = Prisma.GrafikBerdasarkanJenisKelaminGetPayload<{
|
||||
select: {
|
||||
|
||||
@@ -5,10 +5,10 @@ import { proxy } from "valtio";
|
||||
import { z } from "zod";
|
||||
|
||||
const templateGrafikUmur = z.object({
|
||||
remaja: z.string().min(2, "Data remaja harus diisi"),
|
||||
dewasa: z.string().min(2, "Data dewasa harus diisi"),
|
||||
orangtua: z.string().min(2, "Data orangtua harus diisi"),
|
||||
lansia: z.string().min(2, "Data lansia harus diisi"),
|
||||
remaja: z.string().min(1, "Data remaja harus diisi"),
|
||||
dewasa: z.string().min(1, "Data dewasa harus diisi"),
|
||||
orangtua: z.string().min(1, "Data orangtua harus diisi"),
|
||||
lansia: z.string().min(1, "Data lansia harus diisi"),
|
||||
});
|
||||
|
||||
type GrafikUmur = Prisma.GrafikBerdasarkanUmurGetPayload<{
|
||||
|
||||
@@ -6,7 +6,7 @@ import { z } from "zod";
|
||||
|
||||
const templateGrafikHasilKepuasanMasyarakat = z.object({
|
||||
label: z.string().min(2, "Label harus diisi"),
|
||||
kepuasan: z.string().min(2, "Kepuasan harus diisi"),
|
||||
kepuasan: z.string().min(1, "Kepuasan harus diisi"),
|
||||
});
|
||||
|
||||
type GrafikHasilKepuasanMasyarakat = Prisma.IndeksKepuasanMasyarakatGetPayload<{
|
||||
|
||||
@@ -125,16 +125,6 @@ function EditBerita() {
|
||||
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 })}
|
||||
@@ -174,6 +164,16 @@ function EditBerita() {
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<SelectCategory
|
||||
value={formData.kategoriBeritaId}
|
||||
onChange={(val) => {
|
||||
setFormData({
|
||||
...formData,
|
||||
kategoriBeritaId: val?.id || ''
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button onClick={handleSubmit}>Edit Berita</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
@@ -63,9 +63,9 @@ export default function CreateBerita() {
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
<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"}>
|
||||
@@ -79,8 +79,9 @@ export default function CreateBerita() {
|
||||
placeholder="masukkan judul"
|
||||
/>
|
||||
<SelectCategory
|
||||
value={beritaState.berita.create.form.kategoriBeritaId}
|
||||
onChange={(val) => {
|
||||
beritaState.berita.create.form.kategoriBeritaId = val.id;
|
||||
beritaState.berita.create.form.kategoriBeritaId = val?.id || "";
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
@@ -114,10 +115,10 @@ export default function CreateBerita() {
|
||||
<Box>
|
||||
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||
<CreateEditor
|
||||
value={beritaState.berita.create.form.content}
|
||||
onChange={(htmlContent) => {
|
||||
beritaState.berita.create.form.content = htmlContent;
|
||||
}}
|
||||
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>
|
||||
@@ -126,26 +127,37 @@ export default function CreateBerita() {
|
||||
</Box>
|
||||
);
|
||||
|
||||
function SelectCategory({
|
||||
onChange,
|
||||
}: {
|
||||
interface SelectCategoryProps {
|
||||
onChange: (value: Prisma.KategoriBeritaGetPayload<{
|
||||
select: {
|
||||
name: true;
|
||||
id: true;
|
||||
};
|
||||
}>) => void;
|
||||
}) {
|
||||
}> | null) => void;
|
||||
value?: string | null;
|
||||
defaultValue?: string | null;
|
||||
}
|
||||
|
||||
function SelectCategory({
|
||||
onChange,
|
||||
value,
|
||||
defaultValue,
|
||||
}: SelectCategoryProps) {
|
||||
const categoryState = useProxy(stateDashboardBerita.category);
|
||||
|
||||
|
||||
useShallowEffect(() => {
|
||||
categoryState.findMany.load();
|
||||
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>}
|
||||
@@ -154,13 +166,19 @@ export default function CreateBerita() {
|
||||
label: item.name,
|
||||
value: item.id,
|
||||
}))}
|
||||
onChange={(val) => {
|
||||
const selected = categoryState.findMany.data?.find((item) => item.id === val);
|
||||
if (selected) {
|
||||
onChange(selected);
|
||||
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"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import colors from '@/con/colors';
|
||||
import { Box, SimpleGrid, Paper, Stack, Title, Group, Button, Text } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import { DesaEditor } from '../../../_com/desaEditor';
|
||||
|
||||
function LambangDesa() {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>Lambang Desa</Title>
|
||||
<Text fw={"bold"}>Deskripsi Lambang Desa</Text>
|
||||
<DesaEditor showSubmit={false} />
|
||||
<Group>
|
||||
<Button
|
||||
mt={10}
|
||||
bg={colors['blue-button']}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>List Lambang Desa</Title>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default LambangDesa;
|
||||
@@ -1,13 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import LayoutTabsEdit from "../_lib/layoutTabsEdit"
|
||||
|
||||
function Layout({children}: {children: React.ReactNode}) {
|
||||
return (
|
||||
<LayoutTabsEdit>
|
||||
{children}
|
||||
</LayoutTabsEdit>
|
||||
);
|
||||
}
|
||||
|
||||
export default Layout;
|
||||
@@ -1,39 +0,0 @@
|
||||
import colors from '@/con/colors';
|
||||
import { Box, SimpleGrid, Paper, Stack, Title, Group, Button, Text } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import { DesaEditor } from '../../../_com/desaEditor';
|
||||
|
||||
function MaskotDesa() {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>Maskot Desa</Title>
|
||||
<Text fw={"bold"}>Deskripsi Maskot Desa</Text>
|
||||
<DesaEditor showSubmit={false} />
|
||||
<Group>
|
||||
<Button
|
||||
mt={10}
|
||||
bg={colors['blue-button']}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>List Maskot Desa</Title>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default MaskotDesa;
|
||||
@@ -1,49 +0,0 @@
|
||||
import colors from '@/con/colors';
|
||||
import { Box, SimpleGrid, Paper, Stack, Title, Group, Button, TextInput, Text } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import { DesaEditor } from '../../../_com/desaEditor';
|
||||
|
||||
function ProfilePerbekel() {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>Profil Perbekel</Title>
|
||||
<TextInput
|
||||
label="Nama Perbekel"
|
||||
placeholder="masukkan nama perbekel"
|
||||
/>
|
||||
<Text fz={"sm"} fw={"bold"}>Biodata</Text>
|
||||
<DesaEditor showSubmit={false} />
|
||||
<Text fz={"sm"} fw={"bold"}>Pengalaman</Text>
|
||||
<DesaEditor showSubmit={false} />
|
||||
<Text fz={"sm"} fw={"bold"}>Pengalaman Organisasi</Text>
|
||||
<DesaEditor showSubmit={false} />
|
||||
<Text fz={"sm"} fw={"bold"}>Program Unggulan</Text>
|
||||
<DesaEditor showSubmit={false} />
|
||||
<Group>
|
||||
<Button
|
||||
mt={10}
|
||||
bg={colors['blue-button']}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>List Profil Perbekel</Title>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProfilePerbekel;
|
||||
@@ -1,34 +0,0 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core';
|
||||
|
||||
|
||||
|
||||
function SejarahDesa() {
|
||||
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>Sejarah Desa</Title>
|
||||
<Text fw={"bold"}>Deskripsi Sejarah Desa</Text>
|
||||
<Group>
|
||||
<Button
|
||||
mt={10}
|
||||
bg={colors['blue-button']}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default SejarahDesa;
|
||||
@@ -1,25 +0,0 @@
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Stack } from '@mantine/core';
|
||||
|
||||
function Page() {
|
||||
return (
|
||||
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
||||
<Stack gap={"22"}>
|
||||
<Box>
|
||||
<Stack gap={'lg'}>
|
||||
<Paper p={"xl"} bg={colors['BG-trans']}>
|
||||
{/* <Box px={{ base: 0, md: 30 }}>
|
||||
<Text ta={"center"} fz={{ base: "h3", md: "h2" }} fw={"bold"} dangerouslySetInnerHTML={{ __html: listDasarHukum.findById.data.judul }} />
|
||||
</Box>
|
||||
<Box px={{ base: 0, md: 30 }}>
|
||||
<Text fz={{ base: "md", md: "h3" }} ta={"justify"} dangerouslySetInnerHTML={{ __html: listDasarHukum.findById.data.content }} />
|
||||
</Box> */}
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -1,64 +0,0 @@
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core';
|
||||
import { DesaEditor } from '../../../_com/desaEditor';
|
||||
|
||||
function VisiMisiDesa() {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>Visi Desa</Title>
|
||||
<Text fw={"bold"}>Deskripsi Visi Desa</Text>
|
||||
<DesaEditor showSubmit={false} />
|
||||
<Group>
|
||||
<Button
|
||||
mt={10}
|
||||
bg={colors['blue-button']}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>List Visi Desa</Title>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
<SimpleGrid py={30} cols={{ base: 1, md: 2 }}>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>Misi Desa</Title>
|
||||
<Text fw={"bold"}>Deskripsi Misi Desa</Text>
|
||||
<DesaEditor showSubmit={false}/>
|
||||
<Group>
|
||||
<Button
|
||||
mt={10}
|
||||
bg={colors['blue-button']}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>List Misi Desa</Title>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default VisiMisiDesa;
|
||||
@@ -0,0 +1,126 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Center, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function Page() {
|
||||
const lambangState = useProxy(stateProfileDesa.lambangDesa)
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) {
|
||||
toast.error("ID tidak valid");
|
||||
router.push("/admin/desa/profile/profile-desa");
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await lambangState.findUnique.load(id);
|
||||
if (data) {
|
||||
lambangState.update.initialize(data);
|
||||
}
|
||||
};
|
||||
|
||||
loadData();
|
||||
|
||||
return () => {
|
||||
lambangState.update.reset();
|
||||
lambangState.findUnique.reset(); // opsional: reset juga data lama
|
||||
};
|
||||
}, [params?.id, router]);
|
||||
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (isSubmitting || !lambangState.update.form.judul.trim()) {
|
||||
toast.error("Judul wajib diisi");
|
||||
return;
|
||||
}
|
||||
setIsSubmitting(true)
|
||||
try {
|
||||
const success = await lambangState.update.submit()
|
||||
if (success) {
|
||||
toast.success("Data berhasil disimpan");
|
||||
router.push("/admin/desa/profile/profile-desa");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error update lambang desa:", error);
|
||||
toast.error("Terjadi kesalahan saat update lambang desa");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
if (
|
||||
lambangState.findUnique.loading ||
|
||||
!lambangState.findUnique.data ||
|
||||
lambangState.update.loading
|
||||
) {
|
||||
return (
|
||||
<Box>
|
||||
<Center h={400}>
|
||||
<Text>Memuat data...</Text>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Stack gap={'xs'}>
|
||||
<Group>
|
||||
<Button variant="subtle" onClick={handleBack}>
|
||||
<IconArrowBack color={colors['blue-button']} size={20} />
|
||||
</Button>
|
||||
</Group>
|
||||
<Paper bg={colors['white-1']} p={'xs'} w={{ base: '100%', md: '50%' }}>
|
||||
<Stack gap={'xs'}>
|
||||
<Box>
|
||||
<Box>
|
||||
<Stack>
|
||||
<Title order={3}>Edit Lambang Desa</Title>
|
||||
<TextInput
|
||||
label={<Text fz={"md"} fw={"bold"}>Judul</Text>}
|
||||
placeholder="Judul"
|
||||
value={lambangState.update.form.judul}
|
||||
onChange={(e) => lambangState.update.form.judul = e.currentTarget.value}
|
||||
error={!lambangState.update.form.judul && "Judul wajib diisi"}
|
||||
/>
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Deskripsi</Text>
|
||||
<EditEditor
|
||||
value={lambangState.update.form.deskripsi}
|
||||
onChange={(val) => lambangState.update.form.deskripsi = val}
|
||||
/>
|
||||
</Box>
|
||||
<Group>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
bg={colors['blue-button']}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,244 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import { Box, Button, Group, Image, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { Dropzone } from '@mantine/dropzone';
|
||||
import { IconArrowBack, IconPhoto, IconUpload, IconX } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function Page() {
|
||||
const maskotState = useProxy(stateProfileDesa.maskotDesa)
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
|
||||
const [images, setImages] = useState<
|
||||
Array<{ file: File; preview: string; label: string }>
|
||||
>([]);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
judul: maskotState.update.form.judul || '',
|
||||
deskripsi: maskotState.update.form.deskripsi || '',
|
||||
images: [] as Array<{ label: string; imageId: string }>
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await maskotState.findUnique.load(id);
|
||||
if (data) {
|
||||
// 🔥 INI YANG KURANG!
|
||||
maskotState.update.initialize(data);
|
||||
|
||||
setFormData({
|
||||
judul: data.judul || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
images: (data.images || []).map((img: any) => ({
|
||||
label: img.label,
|
||||
imageId: img.image?.id ?? '',
|
||||
})),
|
||||
});
|
||||
|
||||
if (data?.images?.length > 0 && data.images[0].image?.link) {
|
||||
setImages(data.images.map((img: any) => ({
|
||||
file: null,
|
||||
preview: img.image.link,
|
||||
label: img.label,
|
||||
})));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading berita:", error);
|
||||
toast.error("Gagal memuat data berita");
|
||||
}
|
||||
};
|
||||
|
||||
loadData();
|
||||
}, [params?.id]);
|
||||
|
||||
|
||||
const handleBack = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const uploadedImages = [];
|
||||
|
||||
// Upload semua gambar baru
|
||||
for (const img of images) {
|
||||
if (!img.file || !(img.file instanceof File)) {
|
||||
toast.error("File tidak valid untuk di-upload");
|
||||
continue; // atau return kalau kamu mau hentikan semua
|
||||
}
|
||||
|
||||
const res = await ApiFetch.api.fileStorage.create.post({
|
||||
file: img.file,
|
||||
name: img.file.name,
|
||||
});
|
||||
|
||||
const uploaded = res.data?.data;
|
||||
if (!uploaded?.id) {
|
||||
toast.error("Gagal upload salah satu gambar");
|
||||
return;
|
||||
}
|
||||
|
||||
uploadedImages.push({
|
||||
imageId: uploaded.id,
|
||||
label: img.label || 'main',
|
||||
});
|
||||
}
|
||||
|
||||
// Update ke global state
|
||||
maskotState.update.updateField("judul", formData.judul);
|
||||
maskotState.update.updateField("deskripsi", formData.deskripsi);
|
||||
maskotState.update.updateField("images", uploadedImages);
|
||||
|
||||
const success = await maskotState.update.submit();
|
||||
|
||||
if (success) {
|
||||
toast.success("Maskot berhasil diperbarui!");
|
||||
router.push("/admin/desa/profile/profile-desa");
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error update maskot:", error);
|
||||
toast.error("Gagal update maskot");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Stack gap={'xs'}>
|
||||
<Group>
|
||||
<Button variant="subtle" onClick={handleBack}>
|
||||
<IconArrowBack color={colors['blue-button']} size={20} />
|
||||
</Button>
|
||||
</Group>
|
||||
<Paper bg={colors['white-1']} p={'xs'} w={{ base: '100%', md: '100%' }}>
|
||||
<Stack gap={'xs'}>
|
||||
<Box>
|
||||
<Box>
|
||||
<Stack>
|
||||
<Title order={3}>Edit Maskot Desa</Title>
|
||||
<TextInput
|
||||
w={{ base: '100%', md: '50%' }}
|
||||
label={<Text fz={"md"} fw={"bold"}>Judul</Text>}
|
||||
placeholder="Masukkan judul"
|
||||
value={formData.judul}
|
||||
onChange={(val) => setFormData({ ...formData, judul: val.currentTarget.value })}
|
||||
/>
|
||||
<Box w={{ base: '100%', md: '50%' }}>
|
||||
<Text fz={"md"} fw={"bold"}>Deskripsi</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => setFormData({ ...formData, deskripsi: val })}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Gambar</Text>
|
||||
<Box w={{ base: '100%', md: '50%' }}>
|
||||
<Dropzone
|
||||
|
||||
onDrop={(files) => {
|
||||
const newImages = files.map((file) => ({
|
||||
file,
|
||||
preview: URL.createObjectURL(file),
|
||||
label: '',
|
||||
}));
|
||||
setImages((prev) => [...prev, ...newImages]);
|
||||
}}
|
||||
>
|
||||
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
|
||||
<Dropzone.Accept>
|
||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX size={52} color="var(--mantine-color-red-6)" stroke={1.5} />
|
||||
</Dropzone.Reject>
|
||||
<Dropzone.Idle>
|
||||
<IconPhoto size={52} color="var(--mantine-color-dimmed)" stroke={1.5} />
|
||||
</Dropzone.Idle>
|
||||
|
||||
<div>
|
||||
<Text size="xl" inline>
|
||||
Drag images here or click to select files
|
||||
</Text>
|
||||
<Text size="sm" c="dimmed" inline mt={7}>
|
||||
Attach as many files as you like, each file should not exceed 5mb
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
</Dropzone>
|
||||
</Box>
|
||||
</Box>
|
||||
<SimpleGrid cols={{ base: 2, md: 4 }} >
|
||||
{images.map((img, index) => (
|
||||
<Box key={index} mb="md">
|
||||
<Paper p="sm" radius="md" withBorder style={{ position: 'relative', maxWidth: 300 }}>
|
||||
<Stack gap={'xs'}>
|
||||
<Group>
|
||||
<Button
|
||||
size="xs"
|
||||
color="red"
|
||||
variant="light"
|
||||
onClick={() => {
|
||||
const updated = [...images];
|
||||
updated.splice(index, 1);
|
||||
setImages(updated);
|
||||
}}
|
||||
>
|
||||
Hapus
|
||||
</Button>
|
||||
</Group>
|
||||
<Image
|
||||
src={img.preview}
|
||||
alt={`Preview ${index}`}
|
||||
width={280}
|
||||
height={180}
|
||||
fit="cover"
|
||||
radius="sm"
|
||||
/>
|
||||
<TextInput
|
||||
label={`Label Gambar ${index + 1}`}
|
||||
placeholder="Contoh: Logo, Maskot, Dll"
|
||||
value={img.label}
|
||||
onChange={(e) => {
|
||||
const updated = [...images];
|
||||
updated[index].label = e.currentTarget.value;
|
||||
setImages(updated);
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
))}
|
||||
</SimpleGrid>
|
||||
<Group>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
bg={colors['blue-button']}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,126 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Center, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function Page() {
|
||||
const sejarahState = useProxy(stateProfileDesa.sejarahDesa)
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) {
|
||||
toast.error("ID tidak valid");
|
||||
router.push("/admin/desa/profile/profile-desa");
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await sejarahState.findUnique.load(id);
|
||||
if (data) {
|
||||
sejarahState.update.initialize(data);
|
||||
}
|
||||
};
|
||||
|
||||
loadData();
|
||||
|
||||
return () => {
|
||||
sejarahState.update.reset();
|
||||
sejarahState.findUnique.reset(); // opsional: reset juga data lama
|
||||
};
|
||||
}, [params?.id, router]);
|
||||
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (isSubmitting || !sejarahState.update.form.judul.trim()) {
|
||||
toast.error("Judul wajib diisi");
|
||||
return;
|
||||
}
|
||||
setIsSubmitting(true)
|
||||
try {
|
||||
const success = await sejarahState.update.submit()
|
||||
if (success) {
|
||||
toast.success("Data berhasil disimpan");
|
||||
router.push("/admin/desa/profile/profile-desa");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error update sejarah desa:", error);
|
||||
toast.error("Terjadi kesalahan saat update sejarah desa");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
if (
|
||||
sejarahState.findUnique.loading ||
|
||||
!sejarahState.findUnique.data ||
|
||||
sejarahState.update.loading
|
||||
) {
|
||||
return (
|
||||
<Box>
|
||||
<Center h={400}>
|
||||
<Text>Memuat data...</Text>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Stack gap={'xs'}>
|
||||
<Group>
|
||||
<Button variant="subtle" onClick={handleBack}>
|
||||
<IconArrowBack color={colors['blue-button']} size={20} />
|
||||
</Button>
|
||||
</Group>
|
||||
<Paper bg={colors['white-1']} p={'xs'} w={{ base: '100%', md: '50%' }}>
|
||||
<Stack gap={'xs'}>
|
||||
<Box>
|
||||
<Box>
|
||||
<Stack>
|
||||
<Title order={3}>Edit Sejarah Desa</Title>
|
||||
<TextInput
|
||||
label={<Text fz={"md"} fw={"bold"}>Judul</Text>}
|
||||
placeholder="Judul"
|
||||
value={sejarahState.update.form.judul}
|
||||
onChange={(e) => sejarahState.update.form.judul = e.currentTarget.value}
|
||||
error={!sejarahState.update.form.judul && "Judul wajib diisi"}
|
||||
/>
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Deskripsi</Text>
|
||||
<EditEditor
|
||||
value={sejarahState.update.form.deskripsi}
|
||||
onChange={(val) => sejarahState.update.form.deskripsi = val}
|
||||
/>
|
||||
</Box>
|
||||
<Group>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
bg={colors['blue-button']}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,124 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Center, Group, Paper, Stack, Text, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function Page() {
|
||||
const visiMisiState = useProxy(stateProfileDesa.visiMisiDesa)
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) {
|
||||
toast.error("ID tidak valid");
|
||||
router.push("/admin/desa/profile/profile-desa");
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await visiMisiState.findUnique.load(id);
|
||||
if (data) {
|
||||
visiMisiState.update.initialize(data);
|
||||
}
|
||||
};
|
||||
|
||||
loadData();
|
||||
|
||||
return () => {
|
||||
visiMisiState.update.reset();
|
||||
visiMisiState.findUnique.reset(); // opsional: reset juga data lama
|
||||
};
|
||||
}, [params?.id, router]);
|
||||
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (isSubmitting || !visiMisiState.update.form.visi.trim()) {
|
||||
toast.error("Visi wajib diisi");
|
||||
return;
|
||||
}
|
||||
setIsSubmitting(true)
|
||||
try {
|
||||
const success = await visiMisiState.update.submit()
|
||||
if (success) {
|
||||
toast.success("Data berhasil disimpan");
|
||||
router.push("/admin/desa/profile/profile-desa");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error update sejarah desa:", error);
|
||||
toast.error("Terjadi kesalahan saat update sejarah desa");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
if (
|
||||
visiMisiState.findUnique.loading ||
|
||||
!visiMisiState.findUnique.data ||
|
||||
visiMisiState.update.loading
|
||||
) {
|
||||
return (
|
||||
<Box>
|
||||
<Center h={400}>
|
||||
<Text>Memuat data...</Text>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Stack gap={'xs'}>
|
||||
<Group>
|
||||
<Button variant="subtle" onClick={handleBack}>
|
||||
<IconArrowBack color={colors['blue-button']} size={20} />
|
||||
</Button>
|
||||
</Group>
|
||||
<Paper bg={colors['white-1']} p={'xs'} w={{ base: '100%', md: '50%' }}>
|
||||
<Stack gap={'xs'}>
|
||||
<Box>
|
||||
<Box>
|
||||
<Stack>
|
||||
<Title order={3}>Edit Visi Misi Desa</Title>
|
||||
<Text fz={"md"} fw={"bold"}>Visi</Text>
|
||||
<EditEditor
|
||||
value={visiMisiState.update.form.visi}
|
||||
onChange={(val) => visiMisiState.update.form.visi = val}
|
||||
/>
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Misi</Text>
|
||||
<EditEditor
|
||||
value={visiMisiState.update.form.misi}
|
||||
onChange={(val) => visiMisiState.update.form.misi = val}
|
||||
/>
|
||||
</Box>
|
||||
<Group>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
bg={colors['blue-button']}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -1,131 +1,168 @@
|
||||
'use client'
|
||||
|
||||
import colors from '@/con/colors';
|
||||
import { Paper, Stack, Grid, GridCol, Title, Button, Box, Text, Center, Image, SimpleGrid } from '@mantine/core';
|
||||
import { Box, Button, Card, Center, Grid, GridCol, Group, Image, Paper, Stack, Text, Title } from '@mantine/core';
|
||||
import { useSnapshot } from 'valtio';
|
||||
import stateProfileDesa from '../../../_state/desa/profile';
|
||||
import { useEffect } from 'react';
|
||||
import { IconEdit } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React from 'react';
|
||||
|
||||
function Page() {
|
||||
const router = useRouter()
|
||||
const router = useRouter();
|
||||
const snap = useSnapshot(stateProfileDesa);
|
||||
|
||||
// Panggil load data sekali saat komponen mount
|
||||
useEffect(() => {
|
||||
stateProfileDesa.sejarahDesa.findUnique.load("edit");
|
||||
stateProfileDesa.visiMisiDesa.findUnique.load("edit");
|
||||
stateProfileDesa.lambangDesa.findUnique.load("edit");
|
||||
stateProfileDesa.maskotDesa.findUnique.load("edit");
|
||||
}, []);
|
||||
|
||||
const sejarah = snap.sejarahDesa.findUnique.data;
|
||||
const visiMisi = snap.visiMisiDesa.findUnique.data;
|
||||
const lambang = snap.lambangDesa.findUnique.data;
|
||||
const maskot = snap.maskotDesa.findUnique.data;
|
||||
|
||||
return (
|
||||
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
||||
<Stack gap={"22"}>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 11 }}>
|
||||
<Title order={2}>Preview Profile Desa</Title>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 1 }}>
|
||||
<Button bg={colors['blue-button']} onClick={() => router.push('/admin/desa/profile/edit/sejarah_desa')}>
|
||||
<IconEdit size={16} />
|
||||
</Button>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack gap={"lg"}>
|
||||
<Title order={2}>Preview Profile Desa</Title>
|
||||
{/* Sejarah Desa */}
|
||||
<Box>
|
||||
<Stack gap={'lg'}>
|
||||
<Paper p={"xl"} bg={colors['BG-trans']}>
|
||||
<Box pb={30}>
|
||||
<Center>
|
||||
<Image src={"/darmasaba-icon.png"} alt="" w={{ base: 200, md: 300 }} />
|
||||
</Center>
|
||||
<Text c={colors['blue-button']} ta={"center"} fw={"bold"} fz={"2.5rem"}>Sejarah Desa</Text>
|
||||
</Box>
|
||||
<Paper p={"xl"} bg={colors['white-trans-1']} w={{ base: "100%", md: "100%" }}>
|
||||
<Text fz={{ base: "md", md: "h3" }} ta={"justify"}>
|
||||
Test
|
||||
</Text>
|
||||
{sejarah && (
|
||||
<Box>
|
||||
<Stack gap={'lg'}>
|
||||
<Paper p={"md"} bg={colors['BG-trans']}>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 11 }}>
|
||||
<Title order={2}>Preview Sejarah Desa</Title>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 1 }}>
|
||||
<Button bg={colors['blue-button']} onClick={() => router.push(`/admin/desa/profile/profile-desa/${sejarah.id}/sejarah_desa`)}>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Box pb={30}>
|
||||
<Center>
|
||||
<Image src={"/darmasaba-icon.png"} alt="" w={{ base: 200, md: 300 }} />
|
||||
</Center>
|
||||
<Text c={colors['blue-button']} ta={"center"} fw={"bold"} fz={"2.5rem"}>{sejarah.judul}</Text>
|
||||
</Box>
|
||||
<Paper p={"xl"} bg={colors['white-trans-1']}>
|
||||
<Text fz={{ base: "md", md: "h3" }} ta={"justify"} dangerouslySetInnerHTML={{ __html: sejarah.deskripsi }} />
|
||||
</Paper>
|
||||
</Paper>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Visi Misi Desa */}
|
||||
<Box>
|
||||
<Stack gap={'lg'}>
|
||||
<Paper p={"xl"} bg={colors['BG-trans']}>
|
||||
<Box pb={30}>
|
||||
<Center>
|
||||
<Image src={"/darmasaba-icon.png"} alt="" w={{ base: 200, md: 300 }} />
|
||||
</Center>
|
||||
</Box>
|
||||
<Paper p={"xl"} bg={colors['white-trans-1']} w={{ base: "100%", md: "100%" }}>
|
||||
<Text c={colors['blue-button']} ta={"center"} fw={"bold"} fz={"2.5rem"}>Visi Desa</Text>
|
||||
<Text fz={{ base: "md", md: "h3" }} ta={"justify"}>
|
||||
Test
|
||||
</Text>
|
||||
{visiMisi && (
|
||||
<Box>
|
||||
<Stack gap={'lg'}>
|
||||
<Paper p={"md"} bg={colors['BG-trans']}>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 11 }}>
|
||||
<Title order={2}>Preview Visi Misi Desa</Title>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 1 }}>
|
||||
<Button onClick={() => router.push(`/admin/desa/profile/profile-desa/${visiMisi.id}/visi_misi_desa`)} bg={colors['blue-button']}>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Box pb={30}>
|
||||
<Center>
|
||||
<Image src={"/darmasaba-icon.png"} alt="" w={{ base: 200, md: 300 }} />
|
||||
</Center>
|
||||
<Text c={colors['blue-button']} ta={"center"} fw={"bold"} fz={"2.5rem"}>Visi Misi Desa</Text>
|
||||
</Box>
|
||||
<Paper p={"xl"} bg={colors['white-trans-1']}>
|
||||
<Text fw={"bold"} fz={{ base: "lg", md: "h2" }}>Visi Desa</Text>
|
||||
<Text fz={{ base: "md", md: "h3" }} ta={"justify"} dangerouslySetInnerHTML={{ __html: visiMisi.visi }} />
|
||||
<Text fw={"bold"} fz={{ base: "lg", md: "h2" }}>Misi Desa</Text>
|
||||
<Text fz={{ base: "md", md: "h3" }} ta={"justify"} dangerouslySetInnerHTML={{ __html: visiMisi.misi }} />
|
||||
</Paper>
|
||||
</Paper>
|
||||
<Paper p={"xl"} bg={colors['white-trans-1']} w={{ base: "100%", md: "100%" }}>
|
||||
<Text c={colors['blue-button']} ta={"center"} fw={"bold"} fz={"2.5rem"}>Misi Desa</Text>
|
||||
<Text fz={{ base: "md", md: "h3" }} ta={"justify"}>
|
||||
Test
|
||||
</Text>
|
||||
</Paper>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Lambang Desa */}
|
||||
<Box>
|
||||
<Stack gap={'lg'}>
|
||||
<Paper p={"xl"} bg={colors['BG-trans']}>
|
||||
<Box pb={30}>
|
||||
<Center>
|
||||
<Image src={"/darmasaba-icon.png"} alt="" w={{ base: 200, md: 300 }} />
|
||||
</Center>
|
||||
<Text c={colors['blue-button']} ta={"center"} fw={"bold"} fz={"2.5rem"}>Lambang Desa</Text>
|
||||
</Box>
|
||||
<Paper p={"xl"} bg={colors['white-trans-1']} w={{ base: "100%", md: "100%" }}>
|
||||
<Text fz={{ base: "md", md: "h3" }} ta={"justify"}>
|
||||
Test
|
||||
</Text>
|
||||
{lambang && (
|
||||
<Box>
|
||||
<Stack gap={'lg'}>
|
||||
<Paper p={"md"} bg={colors['BG-trans']}>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 11 }}>
|
||||
<Title order={2}>Preview Lambang Desa</Title>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 1 }}>
|
||||
<Button onClick={() => router.push(`/admin/desa/profile/profile-desa/${lambang.id}/lambang_desa`)} bg={colors['blue-button']}>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Box pb={30}>
|
||||
<Center>
|
||||
<Image src={"/darmasaba-icon.png"} alt="" w={{ base: 200, md: 300 }} />
|
||||
</Center>
|
||||
<Text c={colors['blue-button']} ta={"center"} fw={"bold"} fz={"2.5rem"}>Lambang Desa</Text>
|
||||
</Box>
|
||||
<Paper p={"xl"} bg={colors['white-trans-1']}>
|
||||
<Text fz={{ base: "md", md: "h3" }} ta={"justify"} dangerouslySetInnerHTML={{ __html: lambang.deskripsi }} />
|
||||
</Paper>
|
||||
</Paper>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Maskot Desa */}
|
||||
<Box>
|
||||
<Stack gap={'lg'}>
|
||||
<Paper p={"xl"} bg={colors['BG-trans']}>
|
||||
<Box pb={30}>
|
||||
<Center>
|
||||
<Image src={"/darmasaba-icon.png"} alt="" w={{ base: 200, md: 300 }} />
|
||||
</Center>
|
||||
<Text c={colors['blue-button']} ta={"center"} fw={"bold"} fz={"2.5rem"}>Maskot Desa</Text>
|
||||
</Box>
|
||||
<Paper p={"xl"} bg={colors['white-trans-1']} w={{ base: "100%", md: "100%" }}>
|
||||
<Text fz={{ base: "md", md: "h3" }} ta={"justify"}>
|
||||
Test
|
||||
</Text>
|
||||
{maskot && (
|
||||
<Box>
|
||||
<Stack gap={'lg'}>
|
||||
<Paper p={"md"} bg={colors['BG-trans']}>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 11 }}>
|
||||
<Title order={2}>Preview Maskot Desa</Title>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 1 }}>
|
||||
<Button onClick={() => router.push(`/admin/desa/profile/profile-desa/${maskot.id}/maskot_desa`)} bg={colors['blue-button']}>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
<Box pb={30}>
|
||||
<Center>
|
||||
<Image src={"/pudak-icon.png"} alt="" w={{ base: 200, md: 300 }} />
|
||||
</Center>
|
||||
<Text c={colors['blue-button']} ta={"center"} fw={"bold"} fz={"2.5rem"}>Maskot Desa</Text>
|
||||
</Box>
|
||||
<Paper p={"xl"} bg={colors['white-trans-1']}>
|
||||
<Text fz={{ base: "md", md: "h3" }} ta={"justify"} dangerouslySetInnerHTML={{ __html: maskot.deskripsi }} />
|
||||
<Group wrap="wrap" gap="md">
|
||||
{maskot.images.map((img, index) => (
|
||||
<Card key={index} p="xs" w={220}>
|
||||
<Image
|
||||
src={img.image.link}
|
||||
alt={img.label}
|
||||
w={200}
|
||||
h={200}
|
||||
fit="cover"
|
||||
radius="md"
|
||||
style={{ border: '1px solid #ccc', objectFit: 'cover' }}
|
||||
/>
|
||||
<Text ta="center" mt="xs" fw="bold">{img.label}</Text>
|
||||
</Card>
|
||||
))}
|
||||
</Group>
|
||||
</Paper>
|
||||
</Paper>
|
||||
<SimpleGrid
|
||||
cols={{
|
||||
base: 1,
|
||||
md: 2,
|
||||
}}
|
||||
>
|
||||
<Center>
|
||||
<Box>
|
||||
<Paper p={"lg"}>
|
||||
<Image src={"/api/img/pohonpudak.png"} alt="" w={{ base: 150, md: 250 }} />
|
||||
<Text ta={"center"} fw={"bold"} c={colors["blue-button"]} fz={{ base: "md", md: "h3" }}>Pohon Pudak</Text>
|
||||
</Paper>
|
||||
</Box>
|
||||
</Center>
|
||||
<Center>
|
||||
<Box>
|
||||
<Paper p={"lg"}>
|
||||
<Image src={"/api/img/bungapudak.png"} alt="" w={{ base: 150, md: 250 }} />
|
||||
<Text ta={"center"} fw={"bold"} c={colors["blue-button"]} fz={{ base: "md", md: "h3" }}>Bunga Pudak</Text>
|
||||
</Paper>
|
||||
</Box>
|
||||
</Center>
|
||||
</SimpleGrid>
|
||||
<Paper p={"xl"} bg={colors['white-trans-1']} w={{ base: "100%", md: "100%" }}>
|
||||
<Text fz={{ base: "md", md: "h3" }} ta={"justify"}>
|
||||
Test
|
||||
</Text>
|
||||
</Paper>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
'use client'
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile';
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import { Box, Button, Center, FileInput, Group, Image, Paper, Stack, Text, 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';
|
||||
|
||||
function ProfilePerbekel() {
|
||||
const perbekelState = useProxy(stateProfileDesa.profilPerbekel)
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) {
|
||||
toast.error("ID tidak valid");
|
||||
router.push("/admin/desa/profile/profile-perbekel");
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await perbekelState.findUnique.load(id);
|
||||
if (data) {
|
||||
perbekelState.edit.initialize(data);
|
||||
}
|
||||
if (data?.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
};
|
||||
|
||||
loadData();
|
||||
|
||||
return () => {
|
||||
perbekelState.edit.reset();
|
||||
perbekelState.findUnique.reset(); // opsional: reset juga data lama
|
||||
};
|
||||
}, [params?.id, router]);
|
||||
|
||||
const handleFileChange = (newFile: File | null) => {
|
||||
if (!newFile) {
|
||||
setFile(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setFile(newFile);
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (event) => {
|
||||
setPreviewImage(event.target?.result as string);
|
||||
};
|
||||
reader.readAsDataURL(newFile);
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (isSubmitting || !perbekelState.edit.form.biodata.trim()) {
|
||||
toast.error("Biodata wajib diisi");
|
||||
return;
|
||||
}
|
||||
setIsSubmitting(true)
|
||||
try {
|
||||
if (file) {
|
||||
const uploadResponse = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
|
||||
const uploaded = uploadResponse.data?.data;
|
||||
|
||||
if (!uploaded?.id) {
|
||||
toast.error("Gagal upload gambar");
|
||||
return;
|
||||
}
|
||||
|
||||
perbekelState.edit.form.imageId = uploaded.id;
|
||||
}
|
||||
const success = await perbekelState.edit.submit()
|
||||
if (success) {
|
||||
toast.success("Data berhasil disimpan");
|
||||
router.push("/admin/desa/profile/profile-perbekel");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error update sejarah desa:", error);
|
||||
toast.error("Terjadi kesalahan saat update sejarah desa");
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
router.back()
|
||||
}
|
||||
|
||||
if (
|
||||
perbekelState.findUnique.loading ||
|
||||
!perbekelState.findUnique.data ||
|
||||
perbekelState.edit.loading
|
||||
) {
|
||||
return (
|
||||
<Box>
|
||||
<Center h={400}>
|
||||
<Text>Memuat data...</Text>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Stack gap={'xs'}>
|
||||
<Group>
|
||||
<Button variant="subtle" onClick={handleBack}>
|
||||
<IconArrowBack color={colors['blue-button']} size={20} />
|
||||
</Button>
|
||||
</Group>
|
||||
<Paper bg={colors['white-1']} p={'xs'} w={{ base: '100%', md: '50%' }}>
|
||||
<Stack gap={'xs'}>
|
||||
<Box>
|
||||
<Box>
|
||||
<Stack>
|
||||
<Title order={3}>Edit Profil Perbekel</Title>
|
||||
<Text fz={"md"} fw={"bold"}>Biodata</Text>
|
||||
<EditEditor
|
||||
value={perbekelState.edit.form.biodata}
|
||||
onChange={(val) => perbekelState.edit.form.biodata = val}
|
||||
/>
|
||||
{/* File Upload */}
|
||||
<FileInput
|
||||
label={<Text fz="sm" fw="bold">Upload Gambar Baru (Opsional)</Text>}
|
||||
value={file}
|
||||
onChange={handleFileChange}
|
||||
accept="image/*"
|
||||
/>
|
||||
|
||||
{/* Preview Gambar */}
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold" mb="xs">Preview Gambar</Text>
|
||||
{previewImage ? (
|
||||
<Image alt="Profile preview" src={previewImage} w={200} h={200} fit="cover" radius="md" />
|
||||
) : (
|
||||
<Center w={200} h={200} bg="gray.2">
|
||||
<Stack align="center" gap="xs">
|
||||
<IconImageInPicture size={48} color="gray" />
|
||||
<Text size="sm" c="gray">Tidak ada gambar</Text>
|
||||
</Stack>
|
||||
</Center>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Pengalaman</Text>
|
||||
<EditEditor
|
||||
value={perbekelState.edit.form.pengalaman}
|
||||
onChange={(val) => perbekelState.edit.form.pengalaman = val}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Pengalaman Organisasi</Text>
|
||||
<EditEditor
|
||||
value={perbekelState.edit.form.pengalamanOrganisasi}
|
||||
onChange={(val) => perbekelState.edit.form.pengalamanOrganisasi = val}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"md"} fw={"bold"}>Program Unggulan</Text>
|
||||
<EditEditor
|
||||
value={perbekelState.edit.form.programUnggulan}
|
||||
onChange={(val) => perbekelState.edit.form.programUnggulan = val}
|
||||
/>
|
||||
</Box>
|
||||
<Group>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
bg={colors['blue-button']}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProfilePerbekel;
|
||||
@@ -1,10 +1,110 @@
|
||||
'use client'
|
||||
|
||||
import colors from '@/con/colors';
|
||||
import { Paper, Text } from '@mantine/core';
|
||||
import { Box, Button, Center, Divider, Grid, GridCol, Image, Paper, Stack, Text, Title } from '@mantine/core';
|
||||
import { IconEdit } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useSnapshot } from 'valtio';
|
||||
import stateProfileDesa from '../../../_state/desa/profile';
|
||||
|
||||
function Page() {
|
||||
const router = useRouter();
|
||||
const snap = useSnapshot(stateProfileDesa);
|
||||
|
||||
// Panggil load data sekali saat komponen mount
|
||||
useEffect(() => {
|
||||
stateProfileDesa.profilPerbekel.findUnique.load("edit");
|
||||
}, []);
|
||||
|
||||
const perbekel = snap.profilPerbekel.findUnique.data;
|
||||
|
||||
return (
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Text>Test</Text>
|
||||
<Stack gap={"xs"}>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 11 }}>
|
||||
<Title order={3}>Preview Profile PPID</Title>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 1 }}>
|
||||
<Button bg={colors['blue-button']} onClick={() => router.push(`/admin/desa/profile/profile-perbekel/${snap.profilPerbekel.findUnique.data?.id}`)}>
|
||||
<IconEdit size={16} />
|
||||
</Button>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
{perbekel && (
|
||||
<Box >
|
||||
<Paper p={"xl"} bg={colors['BG-trans']}>
|
||||
<Box px={{ base: "md", md: 100 }}>
|
||||
<Grid>
|
||||
<GridCol span={{ base: 12, md: 12 }}>
|
||||
<Center>
|
||||
<Image src={"/darmasaba-icon.png"} w={{ base: 100, md: 150 }} alt='' />
|
||||
</Center>
|
||||
</GridCol>
|
||||
<GridCol span={{ base: 12, md: 12 }}>
|
||||
<Text ta={"center"} fz={{ base: "1.2rem", md: "1.8rem" }} fw={'bold'}>PROFIL PIMPINAN BADAN PUBLIK DESA DARMASABA </Text>
|
||||
</GridCol>
|
||||
</Grid>
|
||||
</Box>
|
||||
<Divider my={"md"} color={colors['blue-button']} />
|
||||
{/* biodata perbekel */}
|
||||
<Box px={{ base: 0, md: 50 }} pb={30}>
|
||||
<Box pb={20} px={{ base: 0, md: 50 }}>
|
||||
<Paper bg={colors['BG-trans']} w={{ base: "100%", md: "100%" }}>
|
||||
<Stack gap={0}>
|
||||
<Center>
|
||||
<Image
|
||||
pt={{ base: 0, md: 90 }}
|
||||
src={perbekel.image?.link || "/perbekel.png"}
|
||||
w={{ base: 250, md: 350 }}
|
||||
alt='Foto Profil PPID'
|
||||
onError={(e) => {
|
||||
e.currentTarget.src = "/perbekel.png";
|
||||
}}
|
||||
/>
|
||||
</Center>
|
||||
<Paper
|
||||
bg={colors['blue-button']}
|
||||
py={20}
|
||||
className="glass3"
|
||||
px={{ base: 10, md: 10 }}
|
||||
|
||||
>
|
||||
<Text ta={"center"} c={colors['white-1']} fw={"bolder"} fz={{ base: "1.2rem", md: "1.6rem" }}>
|
||||
I.B. Surya Prabhawa Manuaba, S.H.,M.H.,NL.P.
|
||||
</Text>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
<Box pt={10}>
|
||||
<Box>
|
||||
<Text fz={{ base: "1.125rem", md: "1.6rem" }} fw={'bold'}>Biodata</Text>
|
||||
<Text fz={{ base: "1rem", md: "1.5rem" }} ta={"justify"} dangerouslySetInnerHTML={{ __html: perbekel.biodata }} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={{ base: "1.125rem", md: "1.6rem" }} fw={'bold'}>Pengalaman</Text>
|
||||
<Text fz={{ base: "1rem", md: "1.5rem" }} dangerouslySetInnerHTML={{ __html: perbekel.pengalaman }} />
|
||||
</Box>
|
||||
</Box>
|
||||
<Box pb={30}>
|
||||
<Text fz={{ base: "1.125rem", md: "1.6rem" }} fw={'bold'}>Pengalaman Organisasi</Text>
|
||||
<Box px={20}>
|
||||
<Text fz={{ base: "1rem", md: "1.5rem" }} ta={"justify"} dangerouslySetInnerHTML={{ __html: perbekel.pengalamanOrganisasi }} />
|
||||
</Box>
|
||||
</Box>
|
||||
<Box pb={20}>
|
||||
<Text fz={{ base: "1.125rem", md: "1.6rem" }} fw={'bold'}>Program Kerja Unggulan</Text>
|
||||
<Box px={20}>
|
||||
<Text fz={{ base: "1rem", md: "1.5rem" }} ta={"justify"} dangerouslySetInnerHTML={{ __html: perbekel.programUnggulan }} />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
function LayoutTabs({ children }: { children: React.ReactNode }) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const tabs = [
|
||||
{
|
||||
label: "Presentase Kelahiran & Kematian",
|
||||
value: "presentasekelahiran&kematian",
|
||||
href: "/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian"
|
||||
},
|
||||
{
|
||||
label: "Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik",
|
||||
value: "grafikhasilkepuasan",
|
||||
href: "/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan"
|
||||
},
|
||||
{
|
||||
label: "Fasilitas Kesehatan",
|
||||
value: "fasilitaskesehatan",
|
||||
href: "/admin/kesehatan/data-kesehatan-warga/fasilitas_kesehatan"
|
||||
},
|
||||
{
|
||||
label: "Jadwal Kegiatan",
|
||||
value: "jadwalkegiatan",
|
||||
href: "/admin/kesehatan/data-kesehatan-warga/jadwal_kegiatan"
|
||||
},
|
||||
{
|
||||
label: "Artikel Kesehatan",
|
||||
value: "artikelkesehatan",
|
||||
href: "/admin/kesehatan/data-kesehatan-warga/artikel_kesehatan"
|
||||
},
|
||||
];
|
||||
const curentTab = tabs.find(tab => tab.href === pathname)
|
||||
const [activeTab, setActiveTab] = useState<string | null>(curentTab?.value || tabs[0].value);
|
||||
|
||||
const handleTabChange = (value: string | null) => {
|
||||
const tab = tabs.find(t => t.value === value)
|
||||
if (tab) {
|
||||
router.push(tab.href)
|
||||
}
|
||||
setActiveTab(value)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const match = tabs.find(tab => tab.href === pathname)
|
||||
if (match) {
|
||||
setActiveTab(match.value)
|
||||
}
|
||||
}, [pathname])
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={3}>Data Kesehatan Warga</Title>
|
||||
<Tabs color={colors['blue-button']} variant='pills' value={activeTab} onChange={handleTabChange}>
|
||||
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
||||
{tabs.map((e, i) => (
|
||||
<TabsTab key={i} value={e.value}>{e.label}</TabsTab>
|
||||
))}
|
||||
</TabsList>
|
||||
{tabs.map((e, i) => (
|
||||
<TabsPanel key={i} value={e.value}>
|
||||
{/* Konten dummy, bisa diganti tergantung routing */}
|
||||
<></>
|
||||
</TabsPanel>
|
||||
))}
|
||||
</Tabs>
|
||||
{children}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default LayoutTabs;
|
||||
@@ -1,101 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client'
|
||||
import stategrafikKepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Skeleton, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function GrafikHasilKepuasan() {
|
||||
const grafikkepuasan = useProxy(stategrafikKepuasan.grafikkepuasan)
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const isTablet = useMediaQuery('(max-width: 1024px)')
|
||||
const isMobile = useMediaQuery('(max-width: 768px)')
|
||||
const [chartData, setChartData] = useState<any[]>([])
|
||||
|
||||
useShallowEffect(() => {
|
||||
const fetchData = async () => {
|
||||
await grafikkepuasan.findMany.load();
|
||||
if (grafikkepuasan.findMany.data && grafikkepuasan.findMany.data.length > 0) {
|
||||
setChartData(grafikkepuasan.findMany.data);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true); // setelah komponen siap di client
|
||||
}, []);
|
||||
|
||||
if (!mounted) {
|
||||
return null; // Jangan render apa-apa dulu sebelum mounted
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Stack py={10} gap={"xs"}>
|
||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
||||
<Title order={4}>Tambah Grafik Hasil Kepuasan</Title>
|
||||
<Stack gap={"xs"}>
|
||||
<TextInput
|
||||
label="Label"
|
||||
placeholder='Masukkan label yang diinginkan'
|
||||
value={grafikkepuasan.create.form.label}
|
||||
onChange={(val) => {
|
||||
grafikkepuasan.create.form.label = val.currentTarget.value
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Jumlah Penderita"
|
||||
type='number'
|
||||
placeholder='Masukkan jumlah penderita'
|
||||
value={grafikkepuasan.create.form.jumlah}
|
||||
onChange={(val) => {
|
||||
grafikkepuasan.create.form.jumlah = val.currentTarget.value
|
||||
}}
|
||||
/>
|
||||
<Group>
|
||||
<Button bg={colors['blue-button']} mt={10}
|
||||
onClick={async () => {
|
||||
await grafikkepuasan.create.create();
|
||||
await grafikkepuasan.findMany.load();
|
||||
if (grafikkepuasan.findMany.data) {
|
||||
setChartData(grafikkepuasan.findMany.data);
|
||||
}
|
||||
}}
|
||||
>Submit</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
{!chartData ? (
|
||||
<Box style={{ width: '100%', minWidth: 300, minHeight: 300 }}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Title py={15} order={4}>Data Hasil Kepuasan</Title>
|
||||
<Skeleton h={400} />
|
||||
</Paper>
|
||||
</Box>
|
||||
) : (
|
||||
<Box style={{ width: '100%', minWidth: 300, minHeight: 300 }}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Title py={15} order={4}>Data Hasil Kepuasan</Title>
|
||||
<BarChart
|
||||
width={isMobile ? 250 : isTablet ? 300 : 350}
|
||||
height={380}
|
||||
data={chartData}
|
||||
>
|
||||
<XAxis dataKey="label" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Bar dataKey="jumlah" fill={colors['blue-button']} name="Jumlah" />
|
||||
</BarChart>
|
||||
</Paper>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default GrafikHasilKepuasan;
|
||||
@@ -1,124 +0,0 @@
|
||||
'use client'
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import statePersentase from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Skeleton, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function PersentaseDataKelahiranKematian() {
|
||||
const persentase = useProxy(statePersentase.persentasekelahiran);
|
||||
const [chartData, setChartData] = useState<any[]>([]);
|
||||
const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready
|
||||
const isTablet = useMediaQuery('(max-width: 1024px)')
|
||||
const isMobile = useMediaQuery('(max-width: 768px)')
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
useShallowEffect(() => {
|
||||
const fetchData = async () => {
|
||||
await persentase.findMany.load();
|
||||
if (persentase.findMany.data && persentase.findMany.data.length > 0) {
|
||||
setChartData(persentase.findMany.data);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Stack py={10}>
|
||||
{/* Form Input */}
|
||||
<Box>
|
||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
||||
<Title order={4}>Tambah Persentase Data Kelahiran & Kematian</Title>
|
||||
<Stack gap={"xs"}>
|
||||
<TextInput
|
||||
label="Tahun"
|
||||
type="number"
|
||||
value={persentase.create.form.tahun}
|
||||
placeholder="Masukkan tahun"
|
||||
onChange={(val) => {
|
||||
persentase.create.form.tahun = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Kematian Kasar"
|
||||
type="number"
|
||||
value={persentase.create.form.kematianKasar}
|
||||
placeholder="Masukkan kematian kasar"
|
||||
onChange={(val) => {
|
||||
persentase.create.form.kematianKasar = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Kematian Bayi"
|
||||
type="number"
|
||||
value={persentase.create.form.kematianBayi}
|
||||
placeholder="Masukkan kematian bayi"
|
||||
onChange={(val) => {
|
||||
persentase.create.form.kematianBayi = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Kelahiran Kasar"
|
||||
type="number"
|
||||
value={persentase.create.form.kelahiranKasar}
|
||||
placeholder="Masukkan kelahiran kasar"
|
||||
onChange={(val) => {
|
||||
persentase.create.form.kelahiranKasar = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<Group>
|
||||
<Button
|
||||
bg={colors['blue-button']}
|
||||
mt={10}
|
||||
onClick={async () => {
|
||||
await persentase.create.create();
|
||||
await persentase.findMany.load();
|
||||
if (persentase.findMany.data) {
|
||||
setChartData(persentase.findMany.data);
|
||||
}
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
|
||||
{/* Chart */}
|
||||
{!mounted && !chartData ? (
|
||||
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Title pb={10} order={3}>Data Kelahiran & Kematian</Title>
|
||||
<Skeleton h={400} />
|
||||
</Paper>
|
||||
</Box>
|
||||
) : (
|
||||
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Title pb={10} order={4}>Data Kelahiran & Kematian</Title>
|
||||
{mounted && chartData.length > 0 && (
|
||||
<BarChart width={isMobile ? 450 : isTablet ? 500 : 550} height={350} data={chartData} >
|
||||
<XAxis dataKey="tahun" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Bar dataKey="kematianKasar" fill="#f03e3e" name="Kematian Kasar" />
|
||||
<Bar dataKey="kematianBayi" fill="#ff922b" name="Kematian Bayi" />
|
||||
<Bar dataKey="kelahiranKasar" fill="#4dabf7" name="Kelahiran Kasar" />
|
||||
</BarChart>
|
||||
)}
|
||||
</Paper>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default PersentaseDataKelahiranKematian;
|
||||
@@ -1,10 +1,9 @@
|
||||
'use client'
|
||||
import { Box, Paper, Text } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Text } from '@mantine/core';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function DoctorSignUI() {
|
||||
const doctorSign = useProxy(stateArtikelKesehatan.doctorSign)
|
||||
@@ -1,10 +1,9 @@
|
||||
'use client'
|
||||
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
|
||||
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
|
||||
function FirstAidUI() {
|
||||
const firstAidState = useProxy(stateArtikelKesehatan.firstAid)
|
||||
@@ -2,9 +2,9 @@
|
||||
import { Box, Paper, Stack, Text } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
|
||||
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
|
||||
import colors from '@/con/colors';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
|
||||
|
||||
function IntoductionUI() {
|
||||
const introduction = useProxy(stateArtikelKesehatan.introduction)
|
||||
@@ -169,4 +169,4 @@ function AllList() {
|
||||
</Stack>
|
||||
}
|
||||
|
||||
export default ArtikelKesehatan;
|
||||
export default ArtikelKesehatan;
|
||||
@@ -1,10 +1,9 @@
|
||||
'use client'
|
||||
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
|
||||
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
|
||||
import React from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
|
||||
function PreventionUI() {
|
||||
const preventionState = useProxy(stateArtikelKesehatan.prevention)
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
import stateArtikelKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/artikelKesehatan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
|
||||
import colors from '@/con/colors';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
|
||||
function SymptomUI() {
|
||||
const symptomState = useProxy(stateArtikelKesehatan.symptom)
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client'
|
||||
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
|
||||
@@ -1,8 +1,9 @@
|
||||
'use client'
|
||||
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Text } from '@mantine/core';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
|
||||
import colors from '@/con/colors';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
|
||||
function FasilitasPendukung() {
|
||||
const fasilitaspendukungState = useProxy(stateFasilitasKesehatan.fasilitaspendukung)
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Text } from '@mantine/core';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
|
||||
import colors from '@/con/colors';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||
|
||||
function LayananUnggulan() {
|
||||
const informasiumumState = useProxy(stateFasilitasKesehatan.layananunggulan)
|
||||
@@ -1,8 +1,9 @@
|
||||
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||
import { Box, Text, Paper } from '@mantine/core';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Text } from '@mantine/core';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
|
||||
function ProsedurPendaftaran() {
|
||||
const prosedurpendaftaranState = useProxy(stateFasilitasKesehatan.prosedurpendaftaran)
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client'
|
||||
import stateFasilitasKesehatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/fasilitasKesehatan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Stack, Text, TextInput } from '@mantine/core';
|
||||
@@ -0,0 +1,80 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
|
||||
import grafikkepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function EditGrafikHasilKepuasan() {
|
||||
const router = useRouter()
|
||||
const params = useParams() as { id: string }
|
||||
const stateGrafikKepuasan = useProxy(grafikkepuasan)
|
||||
|
||||
const id = params.id
|
||||
|
||||
// Load data saat komponen mount
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
stateGrafikKepuasan.findUnique.load(id).then(() => {
|
||||
const data = stateGrafikKepuasan.findUnique.data
|
||||
if (data) {
|
||||
stateGrafikKepuasan.update.form = {
|
||||
label: data.label || '',
|
||||
jumlah: data.jumlah || '',
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [id])
|
||||
|
||||
const handleSubmit = async () => {
|
||||
// Set the ID before submitting
|
||||
stateGrafikKepuasan.update.id = id;
|
||||
await stateGrafikKepuasan.update.submit();
|
||||
router.push('/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan')
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack size={20} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>Edit Grafik Hasil Kepuasan</Title>
|
||||
<TextInput
|
||||
label="Label"
|
||||
placeholder="masukkan label"
|
||||
value={stateGrafikKepuasan.update.form.label}
|
||||
onChange={(val) => {
|
||||
stateGrafikKepuasan.update.form.label = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Jumlah"
|
||||
type="number"
|
||||
placeholder="masukkan jumlah"
|
||||
value={stateGrafikKepuasan.update.form.jumlah}
|
||||
onChange={(val) => {
|
||||
stateGrafikKepuasan.update.form.jumlah = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
mt={10}
|
||||
bg={colors['blue-button']}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default EditGrafikHasilKepuasan;
|
||||
@@ -0,0 +1,81 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client'
|
||||
import grafikkepuasan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function CreateGrafikHasilKepuasanMasyarakat() {
|
||||
const stateGrafikKepuasan = useProxy(grafikkepuasan);
|
||||
const [chartData, setChartData] = useState<any[]>([]);
|
||||
const router = useRouter()
|
||||
|
||||
const resetForm = () => {
|
||||
stateGrafikKepuasan.create.form = {
|
||||
label: "",
|
||||
jumlah: "",
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const id = await stateGrafikKepuasan.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await stateGrafikKepuasan.findUnique.load(idStr);
|
||||
if (stateGrafikKepuasan.findUnique.data) {
|
||||
setChartData([stateGrafikKepuasan.findUnique.data]);
|
||||
}
|
||||
}
|
||||
resetForm();
|
||||
router.push("/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan");
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack size={20} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Box>
|
||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
||||
<Title order={4}>Tambah Grafik Hasil Kepuasan Masyarakat</Title>
|
||||
<Stack gap={"xs"}>
|
||||
<TextInput
|
||||
label="Label"
|
||||
type="text"
|
||||
value={stateGrafikKepuasan.create.form.label}
|
||||
placeholder="Masukkan label"
|
||||
onChange={(val) => {
|
||||
stateGrafikKepuasan.create.form.label = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Jumlah"
|
||||
type="number"
|
||||
value={stateGrafikKepuasan.create.form.jumlah}
|
||||
placeholder="Masukkan jumlah"
|
||||
onChange={(val) => {
|
||||
stateGrafikKepuasan.create.form.jumlah = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<Group>
|
||||
<Button
|
||||
bg={colors['blue-button']}
|
||||
mt={10}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreateGrafikHasilKepuasanMasyarakat;
|
||||
@@ -0,0 +1,148 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
|
||||
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
|
||||
import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import JudulListTab from '../../../_com/jusulListTab';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import grafikkepuasan from '../../../_state/kesehatan/data_kesehatan_warga/grafikKepuasan';
|
||||
|
||||
function GrafikHasilKepuasanMasyarakat() {
|
||||
type PDKMGrafik = {
|
||||
id: string;
|
||||
label: string;
|
||||
jumlah: number;
|
||||
}
|
||||
const stateGrafikKepuasan = useProxy(grafikkepuasan);
|
||||
const [chartData, setChartData] = useState<PDKMGrafik[]>([]);
|
||||
const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready
|
||||
const isTablet = useMediaQuery('(max-width: 1024px)')
|
||||
const isMobile = useMediaQuery('(max-width: 768px)')
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const router = useRouter();
|
||||
|
||||
const handleDelete = () => {
|
||||
if (selectedId) {
|
||||
stateGrafikKepuasan.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
|
||||
stateGrafikKepuasan.findMany.load()
|
||||
}
|
||||
}
|
||||
|
||||
useShallowEffect(() => {
|
||||
setMounted(true)
|
||||
stateGrafikKepuasan.findMany.load()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
if (stateGrafikKepuasan.findMany.data) {
|
||||
setChartData(stateGrafikKepuasan.findMany.data.map((item) => ({
|
||||
id: item.id,
|
||||
label: item.label,
|
||||
jumlah: Number(item.jumlah),
|
||||
})));
|
||||
}
|
||||
}, [stateGrafikKepuasan.findMany.data]);
|
||||
|
||||
if (!stateGrafikKepuasan.findMany.data) {
|
||||
return (
|
||||
<Stack>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
{/* Form Input */}
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulListTab
|
||||
title='List Grafik Hasil Kepuasan Masyarakat'
|
||||
href='/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/create'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={16} />}
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Label</TableTh>
|
||||
<TableTh>Jumlah</TableTh>
|
||||
<TableTh>Edit</TableTh>
|
||||
<TableTh>Delete</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{stateGrafikKepuasan.findMany.data?.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.label}</TableTd>
|
||||
<TableTd>{item.jumlah}</TableTd>
|
||||
<TableTd>
|
||||
<Button color='green' onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/grafik_hasil_kepuasan/${item.id}`)}>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button
|
||||
color='red'
|
||||
disabled={stateGrafikKepuasan.delete.loading}
|
||||
onClick={() => {
|
||||
setSelectedId(item.id)
|
||||
setModalHapus(true)
|
||||
}}>
|
||||
<IconTrash size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
|
||||
|
||||
{/* Chart */}
|
||||
{!mounted && !chartData ? (
|
||||
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Title pb={10} order={3}>Grafik Hasil Kepuasan Masyarakat</Title>
|
||||
<Text c='dimmed'>Belum ada data untuk ditampilkan dalam grafik</Text>
|
||||
</Paper>
|
||||
</Box>
|
||||
) : (
|
||||
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Title pb={10} order={4}>Grafik Hasil Kepuasan Masyarakat</Title>
|
||||
{mounted && chartData.length > 0 && (
|
||||
<BarChart width={isMobile ? 450 : isTablet ? 500 : 550} height={350} data={chartData} >
|
||||
<XAxis dataKey="label" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Bar dataKey="jumlah" fill={colors['blue-button']} name="Jumlah" />
|
||||
</BarChart>
|
||||
)}
|
||||
</Paper>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleDelete}
|
||||
text='Apakah anda yakin ingin menghapus grafik hasil kepuasan masyarakat ini?'
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default GrafikHasilKepuasanMasyarakat;
|
||||
@@ -1,8 +1,9 @@
|
||||
'use client'
|
||||
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Stack, Text } from '@mantine/core';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
|
||||
import colors from '@/con/colors';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
|
||||
function DeskripsiKegiatan() {
|
||||
const deskripsiKegiatanState = useProxy(stateJadwalKegiatan.deskripsiKegiatan)
|
||||
@@ -1,8 +1,9 @@
|
||||
'use client'
|
||||
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Stack, Text } from '@mantine/core';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
|
||||
import colors from '@/con/colors';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
|
||||
function DokumenYangDiperlukan() {
|
||||
const dokumenDiperlukan = useProxy(stateJadwalKegiatan.dokumenjadwalkegiatan)
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client'
|
||||
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
@@ -1,8 +1,9 @@
|
||||
'use client'
|
||||
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Stack, Text } from '@mantine/core';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
|
||||
import colors from '@/con/colors';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
|
||||
function LayananTersedia() {
|
||||
const layananTersediaState = useProxy(stateJadwalKegiatan.layanantersedia)
|
||||
@@ -1,8 +1,9 @@
|
||||
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Paper, Stack, Text } from '@mantine/core';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { KesehatanEditor } from '../../../_com/kesehatanEditor';
|
||||
import colors from '@/con/colors';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
import stateJadwalKegiatan from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/jadwalKegiatan';
|
||||
|
||||
function SyaratDanKetentuan() {
|
||||
const syaratKetentuan = useProxy(stateJadwalKegiatan.syaratketentuan)
|
||||
@@ -0,0 +1,12 @@
|
||||
'use client'
|
||||
|
||||
import LayoutTabs from "./_lib/layoutTabs"
|
||||
|
||||
|
||||
export default function Layout({children} : {children: React.ReactNode}) {
|
||||
return (
|
||||
<LayoutTabs>
|
||||
{children}
|
||||
</LayoutTabs>
|
||||
)
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import colors from '@/con/colors';
|
||||
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||
import ArtikelKesehatan from './_ui/artikel_kesehatan/page';
|
||||
import FasilitasKesehatan from './_ui/fasilitas_kesehatan/page';
|
||||
import GrafikHasilKepuasan from './_ui/grafik_hasil_kepuasan/page';
|
||||
import JadwalKegiatan from './_ui/jadwal_kegiatan/page';
|
||||
import PersentaseDataKelahiranKematian from './_ui/persentase_data_kelahiran_kematian/page';
|
||||
|
||||
|
||||
function Page() {
|
||||
return (
|
||||
<Stack>
|
||||
<Title order={3}>Data Kesehatan Warga</Title>
|
||||
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Persentase Kelahiran & Kematian"}>
|
||||
<TabsList bg={colors['BG-trans']} p={'xs'}>
|
||||
<TabsTab value="Persentase Kelahiran & Kematian">
|
||||
Persentase Kelahiran & Kematian
|
||||
</TabsTab>
|
||||
<TabsTab value="Grafik Hasil Kepuasan">
|
||||
Grafik Hasil Kepuasan
|
||||
</TabsTab>
|
||||
<TabsTab value="Fasilitas Kesehatan">
|
||||
Fasilitas Kesehatan
|
||||
</TabsTab>
|
||||
<TabsTab value="Jadwal Kegiatan">
|
||||
Jadwal Kegiatan
|
||||
</TabsTab>
|
||||
<TabsTab value="Artikel Kesehatan">
|
||||
Artikel Kesehatan
|
||||
</TabsTab>
|
||||
</TabsList>
|
||||
|
||||
<TabsPanel value="Persentase Kelahiran & Kematian">
|
||||
<PersentaseDataKelahiranKematian />
|
||||
</TabsPanel>
|
||||
<TabsPanel value="Grafik Hasil Kepuasan">
|
||||
<GrafikHasilKepuasan/>
|
||||
</TabsPanel>
|
||||
<TabsPanel value="Fasilitas Kesehatan">
|
||||
<FasilitasKesehatan/>
|
||||
</TabsPanel>
|
||||
<TabsPanel value="Jadwal Kegiatan">
|
||||
<JadwalKegiatan/>
|
||||
</TabsPanel>
|
||||
<TabsPanel value="Artikel Kesehatan">
|
||||
<ArtikelKesehatan/>
|
||||
</TabsPanel>
|
||||
|
||||
</Tabs>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,101 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
|
||||
import persentasekelahiran from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function EditPersentaseDataKelahiranKematian() {
|
||||
const router = useRouter()
|
||||
const params = useParams() as { id: string }
|
||||
const statePresentase = useProxy(persentasekelahiran)
|
||||
|
||||
const id = params.id
|
||||
|
||||
// Load data saat komponen mount
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
statePresentase.findUnique.load(id).then(() => {
|
||||
const data = statePresentase.findUnique.data
|
||||
if (data) {
|
||||
statePresentase.update.form = {
|
||||
tahun: data.tahun || '',
|
||||
kematianKasar: data.kematianKasar || '',
|
||||
kelahiranKasar: data.kelahiranKasar || '',
|
||||
kematianBayi: data.kematianBayi || '',
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [id])
|
||||
|
||||
const handleSubmit = async () => {
|
||||
// Set the ID before submitting
|
||||
statePresentase.update.id = id;
|
||||
await statePresentase.update.submit();
|
||||
router.push('/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian')
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack size={20} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
|
||||
<Stack>
|
||||
<Title order={3}>Edit Persentase Data Kelahiran & Kematian</Title>
|
||||
<TextInput
|
||||
label="Tahun"
|
||||
placeholder="masukkan tahun"
|
||||
value={statePresentase.update.form.tahun}
|
||||
onChange={(val) => {
|
||||
statePresentase.update.form.tahun = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Kematian Kasar"
|
||||
type="number"
|
||||
placeholder="masukkan kematian kasar"
|
||||
value={statePresentase.update.form.kematianKasar}
|
||||
onChange={(val) => {
|
||||
statePresentase.update.form.kematianKasar = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Kematian Bayi"
|
||||
type="number"
|
||||
placeholder="masukkan kematian bayi"
|
||||
value={statePresentase.update.form.kematianBayi}
|
||||
onChange={(val) => {
|
||||
statePresentase.update.form.kematianBayi = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Kelahiran Kasar"
|
||||
type="number"
|
||||
placeholder="masukkan kelahiran kasar"
|
||||
value={statePresentase.update.form.kelahiranKasar}
|
||||
onChange={(val) => {
|
||||
statePresentase.update.form.kelahiranKasar = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
mt={10}
|
||||
bg={colors['blue-button']}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Simpan Perubahan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default EditPersentaseDataKelahiranKematian;
|
||||
@@ -0,0 +1,101 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
'use client'
|
||||
import persentasekelahiran from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useState } from 'react';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function CreatePersentaseDataKelahiranKematian() {
|
||||
const statePersentase = useProxy(persentasekelahiran);
|
||||
const [chartData, setChartData] = useState<any[]>([]);
|
||||
const router = useRouter()
|
||||
|
||||
const resetForm = () => {
|
||||
statePersentase.create.form = {
|
||||
tahun: "",
|
||||
kematianBayi: "",
|
||||
kematianKasar: "",
|
||||
kelahiranKasar: "",
|
||||
}
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
const id = await statePersentase.create.create();
|
||||
if (id) {
|
||||
const idStr = String(id);
|
||||
await statePersentase.findUnique.load(idStr);
|
||||
if (statePersentase.findUnique.data) {
|
||||
setChartData([statePersentase.findUnique.data]);
|
||||
}
|
||||
}
|
||||
resetForm();
|
||||
router.push("/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian");
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack size={20} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Box>
|
||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
||||
<Title order={4}>Tambah Persentase Data Kelahiran & Kematian</Title>
|
||||
<Stack gap={"xs"}>
|
||||
<TextInput
|
||||
label="Tahun"
|
||||
type="number"
|
||||
value={statePersentase.create.form.tahun}
|
||||
placeholder="Masukkan tahun"
|
||||
onChange={(val) => {
|
||||
statePersentase.create.form.tahun = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Kematian Kasar"
|
||||
type="number"
|
||||
value={statePersentase.create.form.kematianKasar}
|
||||
placeholder="Masukkan kematian kasar"
|
||||
onChange={(val) => {
|
||||
statePersentase.create.form.kematianKasar = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Kematian Bayi"
|
||||
type="number"
|
||||
value={statePersentase.create.form.kematianBayi}
|
||||
placeholder="Masukkan kematian bayi"
|
||||
onChange={(val) => {
|
||||
statePersentase.create.form.kematianBayi = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
label="Kelahiran Kasar"
|
||||
type="number"
|
||||
value={statePersentase.create.form.kelahiranKasar}
|
||||
placeholder="Masukkan kelahiran kasar"
|
||||
onChange={(val) => {
|
||||
statePersentase.create.form.kelahiranKasar = val.currentTarget.value;
|
||||
}}
|
||||
/>
|
||||
<Group>
|
||||
<Button
|
||||
bg={colors['blue-button']}
|
||||
mt={10}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreatePersentaseDataKelahiranKematian;
|
||||
@@ -0,0 +1,158 @@
|
||||
'use client'
|
||||
import persentasekelahiran from '@/app/admin/(dashboard)/_state/kesehatan/data_kesehatan_warga/persentaseKelahiran';
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title } from '@mantine/core';
|
||||
import { useMediaQuery, useShallowEffect } from '@mantine/hooks';
|
||||
import { IconEdit, IconSearch, IconTrash } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Bar, BarChart, Legend, Tooltip, XAxis, YAxis } from 'recharts';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import JudulListTab from '../../../_com/jusulListTab';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
|
||||
function PersentaseDataKelahiranKematian() {
|
||||
type PDKMGrafik = {
|
||||
id: string;
|
||||
tahun: string;
|
||||
kematianKasar: number;
|
||||
kematianBayi: number;
|
||||
kelahiranKasar: number;
|
||||
}
|
||||
const statePersentase = useProxy(persentasekelahiran);
|
||||
const [chartData, setChartData] = useState<PDKMGrafik[]>([]);
|
||||
const [mounted, setMounted] = useState(false); // untuk memastikan DOM sudah ready
|
||||
const isTablet = useMediaQuery('(max-width: 1024px)')
|
||||
const isMobile = useMediaQuery('(max-width: 768px)')
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const router = useRouter();
|
||||
|
||||
const handleDelete = () => {
|
||||
if (selectedId) {
|
||||
statePersentase.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
|
||||
statePersentase.findMany.load()
|
||||
}
|
||||
}
|
||||
|
||||
useShallowEffect(() => {
|
||||
setMounted(true)
|
||||
statePersentase.findMany.load()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
if (statePersentase.findMany.data) {
|
||||
setChartData(statePersentase.findMany.data.map((item) => ({
|
||||
id: item.id,
|
||||
tahun: item.tahun,
|
||||
kematianKasar: Number(item.kematianKasar),
|
||||
kematianBayi: Number(item.kematianBayi),
|
||||
kelahiranKasar: Number(item.kelahiranKasar),
|
||||
})));
|
||||
}
|
||||
}, [statePersentase.findMany.data]);
|
||||
|
||||
if (!statePersentase.findMany.data) {
|
||||
return (
|
||||
<Stack>
|
||||
<Skeleton h={500} />
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Stack gap={"xs"}>
|
||||
{/* Form Input */}
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<JudulListTab
|
||||
title='List Persentase Data Kelahiran & Kematian'
|
||||
href='/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/create'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={16} />}
|
||||
/>
|
||||
<Table striped withTableBorder withRowBorders>
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Tahun</TableTh>
|
||||
<TableTh>Kematian Kasar</TableTh>
|
||||
<TableTh>Kematian Bayi</TableTh>
|
||||
<TableTh>kelahiran Kasar</TableTh>
|
||||
<TableTh>Edit</TableTh>
|
||||
<TableTh>Delete</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{statePersentase.findMany.data?.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>{item.tahun}</TableTd>
|
||||
<TableTd>{item.kematianKasar}</TableTd>
|
||||
<TableTd>{item.kematianBayi}</TableTd>
|
||||
<TableTd>{item.kelahiranKasar}</TableTd>
|
||||
<TableTd>
|
||||
<Button color='green' onClick={() => router.push(`/admin/kesehatan/data-kesehatan-warga/persentase_data_kelahiran_kematian/${item.id}`)}>
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button
|
||||
color='red'
|
||||
disabled={statePersentase.delete.loading}
|
||||
onClick={() => {
|
||||
setSelectedId(item.id)
|
||||
setModalHapus(true)
|
||||
}}>
|
||||
<IconTrash size={20} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Paper>
|
||||
|
||||
|
||||
{/* Chart */}
|
||||
{!mounted && !chartData ? (
|
||||
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Title pb={10} order={3}>Data Kelahiran & Kematian</Title>
|
||||
<Text c='dimmed'>Belum ada data untuk ditampilkan dalam grafik</Text>
|
||||
</Paper>
|
||||
</Box>
|
||||
) : (
|
||||
<Box style={{ width: '100%', minWidth: 300, height: 400, minHeight: 300 }}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Title pb={10} order={4}>Data Kelahiran & Kematian</Title>
|
||||
{mounted && chartData.length > 0 && (
|
||||
<BarChart width={isMobile ? 450 : isTablet ? 500 : 550} height={350} data={chartData} >
|
||||
<XAxis dataKey="tahun" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Bar dataKey="kematianKasar" fill="#f03e3e" name="Kematian Kasar" />
|
||||
<Bar dataKey="kematianBayi" fill="#ff922b" name="Kematian Bayi" />
|
||||
<Bar dataKey="kelahiranKasar" fill="#4dabf7" name="Kelahiran Kasar" />
|
||||
</BarChart>
|
||||
)}
|
||||
</Paper>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{/* Modal Konfirmasi Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleDelete}
|
||||
text='Apakah anda yakin ingin menghapus persentase data kelahiran & kematian ini?'
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default PersentaseDataKelahiranKematian;
|
||||
@@ -0,0 +1,150 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import infoWabahPenyakit from '@/app/admin/(dashboard)/_state/kesehatan/info-wabah-penyakit/infoWabahPenyakit';
|
||||
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 { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
function EditInfoWabahPenyakit() {
|
||||
const infoWabahPenyakitState = useProxy(infoWabahPenyakit)
|
||||
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: infoWabahPenyakitState.edit.form.name || '',
|
||||
deskripsiSingkat: infoWabahPenyakitState.edit.form.deskripsiSingkat || '',
|
||||
deskripsi: infoWabahPenyakitState.edit.form.deskripsiLengkap || '',
|
||||
imageId: infoWabahPenyakitState.edit.form.imageId || '',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const loadInfoWabahPenyakit = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await infoWabahPenyakitState.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
deskripsiSingkat: data.deskripsiSingkat || '',
|
||||
deskripsi: data.deskripsiLengkap || '',
|
||||
imageId: data.imageId || '',
|
||||
});
|
||||
|
||||
if (data?.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading program kesehatan:", error);
|
||||
toast.error("Gagal memuat data program kesehatan");
|
||||
}
|
||||
};
|
||||
|
||||
loadInfoWabahPenyakit();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
infoWabahPenyakitState.edit.form = {
|
||||
...infoWabahPenyakitState.edit.form,
|
||||
name: formData.name,
|
||||
deskripsiSingkat: formData.deskripsiSingkat,
|
||||
deskripsiLengkap: formData.deskripsi,
|
||||
imageId: formData.imageId,
|
||||
};
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
infoWabahPenyakitState.edit.form.imageId = uploaded.id;
|
||||
}
|
||||
|
||||
await infoWabahPenyakitState.edit.update();
|
||||
toast.success("Info wabah penyakit berhasil diperbarui!");
|
||||
router.push("/admin/kesehatan/info-wabah-penyakit");
|
||||
} catch (error) {
|
||||
console.error("Error updating info wabah penyakit:", error);
|
||||
toast.error("Gagal memuat data info wabah penyakit");
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
|
||||
<Stack gap="xs">
|
||||
<Title order={3}>Edit Info Wabah Penyakit</Title>
|
||||
<TextInput
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
label={<Text fz="sm" fw="bold">Judul</Text>}
|
||||
placeholder="masukkan judul"
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
value={formData.deskripsiSingkat}
|
||||
onChange={(e) => setFormData({ ...formData, deskripsiSingkat: e.target.value })}
|
||||
label={<Text fz="sm" fw="bold">Deskripsi Singkat</Text>}
|
||||
placeholder="masukkan deskripsi"
|
||||
/>
|
||||
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold">Deskripsi</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => setFormData({ ...formData, deskripsi: val })}
|
||||
/>
|
||||
</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>
|
||||
)}
|
||||
|
||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>
|
||||
Simpan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box >
|
||||
);
|
||||
}
|
||||
|
||||
export default EditInfoWabahPenyakit;
|
||||
@@ -0,0 +1,103 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Paper, Stack, Flex, Text, Image, Skeleton } from '@mantine/core';
|
||||
import { IconArrowBack, IconX, IconEdit } from '@tabler/icons-react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import React, { useState } from 'react';
|
||||
import infoWabahPenyakit from '../../../_state/kesehatan/info-wabah-penyakit/infoWabahPenyakit';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
|
||||
function DetailInfoWabahPenyakit() {
|
||||
const infoWabahPenyakitState = useProxy(infoWabahPenyakit)
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const router = useRouter();
|
||||
const params = useParams()
|
||||
|
||||
useShallowEffect(() => {
|
||||
infoWabahPenyakitState.findUnique.load(params?.id as string)
|
||||
}, [])
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
infoWabahPenyakitState.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/kesehatan/info-wabah-penyakit")
|
||||
}
|
||||
}
|
||||
|
||||
if (!infoWabahPenyakitState.findUnique.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={400} />
|
||||
</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 Info Wabah Penyakit</Text>
|
||||
{infoWabahPenyakitState.findUnique.data ? (
|
||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Judul</Text>
|
||||
<Text fz={"lg"}>{infoWabahPenyakitState.findUnique.data.name}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Deskripsi Singkat</Text>
|
||||
<Text fz={"lg"}>{infoWabahPenyakitState.findUnique.data.deskripsiSingkat}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
|
||||
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: infoWabahPenyakitState.findUnique.data.deskripsiLengkap }} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Gambar</Text>
|
||||
<Image src={infoWabahPenyakitState.findUnique.data.image?.link} alt="gambar" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Flex gap={"xs"}>
|
||||
<Button color="red" onClick={() => {
|
||||
if (infoWabahPenyakitState.findUnique.data) {
|
||||
setSelectedId(infoWabahPenyakitState.findUnique.data.id)
|
||||
setModalHapus(true)
|
||||
}
|
||||
}}
|
||||
disabled={infoWabahPenyakitState.delete.loading || !infoWabahPenyakitState.findUnique.data}
|
||||
>
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button onClick={() => router.push(`/admin/kesehatan/info-wabah-penyakit/${infoWabahPenyakitState.findUnique.data?.id}/edit`)} color="green">
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text="Apakah anda yakin ingin menghapus info wabah penyakit ini?"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailInfoWabahPenyakit;
|
||||
@@ -1,43 +1,101 @@
|
||||
'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 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 { useRouter } from 'next/navigation';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import CreateEditor from '../../../_com/createEditor';
|
||||
import infoWabahPenyakit from '../../../_state/kesehatan/info-wabah-penyakit/infoWabahPenyakit';
|
||||
|
||||
function CreateInfoWabahPenyakit() {
|
||||
const router = useRouter();
|
||||
const infoWabahPenyakitState = useProxy(infoWabahPenyakit)
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
const resetForm = () => {
|
||||
// Reset state di valtio
|
||||
infoWabahPenyakitState.create.form = {
|
||||
name: "",
|
||||
deskripsiSingkat: "",
|
||||
deskripsiLengkap: "",
|
||||
imageId: "",
|
||||
};
|
||||
|
||||
// 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
|
||||
infoWabahPenyakitState.create.form.imageId = uploaded.id;
|
||||
|
||||
// Submit data berita
|
||||
await infoWabahPenyakitState.create.create();
|
||||
|
||||
// Reset form setelah submit
|
||||
resetForm();
|
||||
router.push("/admin/kesehatan/info-wabah-penyakit")
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<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
|
||||
|
||||
value={infoWabahPenyakitState.create.form.name}
|
||||
onChange={(val) => {
|
||||
infoWabahPenyakitState.create.form.name = val.target.value;
|
||||
}}
|
||||
label={<Text fz="sm" fw="bold">Judul</Text>}
|
||||
placeholder="masukkan judul"
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
|
||||
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
|
||||
value={infoWabahPenyakitState.create.form.deskripsiSingkat}
|
||||
onChange={(val) => {
|
||||
infoWabahPenyakitState.create.form.deskripsiSingkat = val.target.value;
|
||||
}}
|
||||
label={<Text fz="sm" fw="bold">Deskripsi Singkat</Text>}
|
||||
placeholder="masukkan deskripsi"
|
||||
/>
|
||||
|
||||
<TextInput
|
||||
|
||||
label={<Text fz="sm" fw="bold">Kategori</Text>}
|
||||
placeholder="masukkan kategori"
|
||||
/>
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold">Deskripsi</Text>
|
||||
<CreateEditor
|
||||
value={infoWabahPenyakitState.create.form.deskripsiLengkap}
|
||||
onChange={(val) => {
|
||||
infoWabahPenyakitState.create.form.deskripsiLengkap = val;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
{/* <FileInput
|
||||
<FileInput
|
||||
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
|
||||
value={file}
|
||||
onChange={async (e) => {
|
||||
@@ -48,25 +106,18 @@ function CreateInfoWabahPenyakit() {
|
||||
);
|
||||
setPreviewImage(base64);
|
||||
}}
|
||||
/> */}
|
||||
/>
|
||||
|
||||
{/* {previewImage ? (
|
||||
{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 onClick={handleSubmit} bg={colors['blue-button']}>
|
||||
Simpan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
'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 DetailInfoWabahPenyakit() {
|
||||
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 Info Wabah/Penyakit</Text>
|
||||
|
||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Nama Info Wabah/Penyakit</Text>
|
||||
<Text fz={"lg"}>Test Judul</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Deskripsi Info Wabah/Penyakit</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 onClick={() => router.push('/admin/kesehatan/info-wabah-penyakit/edit')} 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 penanganan darurat ini?"
|
||||
/> */}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailInfoWabahPenyakit;
|
||||
@@ -1,62 +0,0 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Stack, SimpleGrid, Paper, Title, TextInput, Text, Button, Image } from '@mantine/core';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
|
||||
function EditInfoWabahPenyakit() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Stack gap={"xs"}>
|
||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={"md"}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Edit Info Wabah/Penyakit</Title>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Nama Info Wabah/Penyakit</Text>}
|
||||
placeholder='Masukkan nama Info Wabah/Penyakit'
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Deskripsi Info Wabah/Penyakit</Text>}
|
||||
placeholder='Masukkan deskripsi Info Wabah/Penyakit'
|
||||
/>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi</Text>
|
||||
<KesehatanEditor
|
||||
showSubmit={false}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Gambar</Text>
|
||||
<Image src={"/"} alt="gambar" />
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={"md"}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={4}>Preview Data Info Wabah/Penyakit</Title>
|
||||
<Text fw={"bold"} fz={"sm"}>Nama Info Wabah/Penyakit</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Info Wabah/Penyakit</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>Gambar</Text>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditInfoWabahPenyakit;
|
||||
@@ -1,10 +1,13 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||
import JudulList from '../../_com/judulList';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import infoWabahPenyakit from '../../_state/kesehatan/info-wabah-penyakit/infoWabahPenyakit';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
|
||||
function InfoWabahPenyakit() {
|
||||
return (
|
||||
@@ -20,7 +23,20 @@ function InfoWabahPenyakit() {
|
||||
}
|
||||
|
||||
function ListInfoWabahPenyakit() {
|
||||
const infoWabahPenyakitState = useProxy(infoWabahPenyakit)
|
||||
const router = useRouter()
|
||||
|
||||
useShallowEffect(() => {
|
||||
infoWabahPenyakitState.findMany.load()
|
||||
}, [])
|
||||
|
||||
if (!infoWabahPenyakitState.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
@@ -34,27 +50,32 @@ function ListInfoWabahPenyakit() {
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Judul</TableTh>
|
||||
<TableTh>Kategori</TableTh>
|
||||
<TableTh>Deskripsi Singkat</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 onClick={() => router.push('/admin/kesehatan/info-wabah-penyakit/detail')}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
{infoWabahPenyakitState.findMany.data?.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
<Text truncate="end" fz={"sm"}>{item.name}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text truncate="end" fz={"sm"}>{item.deskripsiSingkat}</Text>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Image w={100} src={item.image?.link} alt="image" />
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/kesehatan/info-wabah-penyakit/${item.id}`)}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import kontakDarurat from '@/app/admin/(dashboard)/_state/kesehatan/kontak-darurat/kontakDarurat';
|
||||
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 { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
|
||||
|
||||
function EditKontakDarurat() {
|
||||
const kontakDaruratState = useProxy(kontakDarurat)
|
||||
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: kontakDaruratState.edit.form.name || '',
|
||||
deskripsi: kontakDaruratState.edit.form.deskripsi || '',
|
||||
imageId: kontakDaruratState.edit.form.imageId || '',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const loadProgramKesehatan = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await kontakDaruratState.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
});
|
||||
|
||||
if (data?.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading program kesehatan:", error);
|
||||
toast.error("Gagal memuat data program kesehatan");
|
||||
}
|
||||
};
|
||||
|
||||
loadProgramKesehatan();
|
||||
}, [params?.id]);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
kontakDaruratState.edit.form = {
|
||||
...kontakDaruratState.edit.form,
|
||||
name: formData.name,
|
||||
deskripsi: formData.deskripsi,
|
||||
imageId: formData.imageId,
|
||||
};
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
kontakDaruratState.edit.form.imageId = uploaded.id;
|
||||
}
|
||||
|
||||
await kontakDaruratState.edit.update();
|
||||
toast.success("Kontak darurat berhasil diperbarui!");
|
||||
router.push("/admin/kesehatan/kontak-darurat");
|
||||
} catch (error) {
|
||||
console.error("Error updating kontak darurat:", error);
|
||||
toast.error("Gagal memuat data kontak darurat");
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
|
||||
<Stack gap="xs">
|
||||
<Title order={3}>Edit Kontak Darurat</Title>
|
||||
<TextInput
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
label={<Text fz="sm" fw="bold">Judul</Text>}
|
||||
placeholder="masukkan judul"
|
||||
/>
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold">Deskripsi</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => setFormData({ ...formData, deskripsi: val })}
|
||||
/>
|
||||
</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>
|
||||
)}
|
||||
|
||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>
|
||||
Simpan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Box >
|
||||
);
|
||||
}
|
||||
|
||||
export default EditKontakDarurat;
|
||||
@@ -0,0 +1,99 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
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 { useProxy } from 'valtio/utils';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import kontakDarurat from '../../../_state/kesehatan/kontak-darurat/kontakDarurat';
|
||||
|
||||
function DetailKontakDarurat() {
|
||||
const kontakDaruratState = useProxy(kontakDarurat)
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const router = useRouter();
|
||||
const params = useParams()
|
||||
|
||||
useShallowEffect(() => {
|
||||
kontakDaruratState.findUnique.load(params?.id as string)
|
||||
}, [])
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
kontakDaruratState.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/kesehatan/kontak-darurat")
|
||||
}
|
||||
}
|
||||
|
||||
if (!kontakDaruratState.findUnique.data) {
|
||||
return (
|
||||
<Stack py={10}>
|
||||
<Skeleton h={400} />
|
||||
</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 Kontak Darurat</Text>
|
||||
{kontakDaruratState.findUnique.data ? (
|
||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Judul</Text>
|
||||
<Text fz={"lg"}>{kontakDaruratState.findUnique.data.name}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
|
||||
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: kontakDaruratState.findUnique.data.deskripsi }} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Gambar</Text>
|
||||
<Image src={kontakDaruratState.findUnique.data.image?.link} alt="gambar" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Flex gap={"xs"}>
|
||||
<Button color="red" onClick={() => {
|
||||
if (kontakDaruratState.findUnique.data) {
|
||||
setSelectedId(kontakDaruratState.findUnique.data.id)
|
||||
setModalHapus(true)
|
||||
}
|
||||
}}
|
||||
disabled={kontakDaruratState.delete.loading || !kontakDaruratState.findUnique.data}
|
||||
>
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button onClick={() => router.push(`/admin/kesehatan/kontak-darurat/${kontakDaruratState.findUnique.data?.id}/edit`)} color="green">
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text="Apakah anda yakin ingin menghapus kontak darurat ini?"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailKontakDarurat;
|
||||
@@ -1,77 +1,113 @@
|
||||
'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 { Box, Button, Center, FileInput, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import kontakDarurat from '../../../_state/kesehatan/kontak-darurat/kontakDarurat';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import CreateEditor from '../../../_com/createEditor';
|
||||
|
||||
|
||||
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
|
||||
const router = useRouter();
|
||||
const kontakDaruratState = useProxy(kontakDarurat)
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
const resetForm = () => {
|
||||
kontakDaruratState.create.form = {
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
};
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
};
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
kontakDaruratState.create.form.imageId = uploaded.id;
|
||||
|
||||
await kontakDaruratState.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push("/admin/kesehatan/kontak-darurat")
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
</Box>
|
||||
|
||||
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
|
||||
<Stack gap="xs">
|
||||
<Title order={3}>Create Kontak Darurat</Title>
|
||||
|
||||
<TextInput
|
||||
value={kontakDaruratState.create.form.name}
|
||||
onChange={(val) => {
|
||||
kontakDaruratState.create.form.name = val.target.value;
|
||||
}}
|
||||
label={<Text fz="sm" fw="bold">Judul</Text>}
|
||||
placeholder="masukkan judul"
|
||||
/>
|
||||
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold">Deskripsi</Text>
|
||||
<CreateEditor
|
||||
value={kontakDaruratState.create.form.deskripsi}
|
||||
onChange={(val) => {
|
||||
kontakDaruratState.create.form.deskripsi = val;
|
||||
}}
|
||||
/>
|
||||
</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>
|
||||
)}
|
||||
|
||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>
|
||||
Simpan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default CreateKontakDarurat;
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
'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 DetailKontakDarurat() {
|
||||
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 Kontak Darurat</Text>
|
||||
|
||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Nama Kontak Darurat</Text>
|
||||
<Text fz={"lg"}>Test Judul</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Deskripsi Kontak Darurat</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 onClick={() => router.push('/admin/kesehatan/kontak-darurat/edit')} 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 penanganan darurat ini?"
|
||||
/> */}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailKontakDarurat;
|
||||
@@ -1,62 +0,0 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Stack, SimpleGrid, Paper, Title, TextInput, Text, Button, Image } from '@mantine/core';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
|
||||
function EditKontakDarurat() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Stack gap={"xs"}>
|
||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={"md"}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Edit Kontak Darurat</Title>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Nama Kontak Darurat</Text>}
|
||||
placeholder='Masukkan nama Kontak Darurat'
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Deskripsi Kontak Darurat</Text>}
|
||||
placeholder='Masukkan deskripsi Kontak Darurat'
|
||||
/>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi</Text>
|
||||
<KesehatanEditor
|
||||
showSubmit={false}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Gambar</Text>
|
||||
<Image src={"/"} alt="gambar" />
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={"md"}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={4}>Preview Data Kontak Darurat</Title>
|
||||
<Text fw={"bold"} fz={"sm"}>Nama Kontak Darurat</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Kontak Darurat</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>Gambar</Text>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditKontakDarurat;
|
||||
@@ -1,66 +1,88 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||
import JudulList from '../../_com/judulList';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import kontakDarurat from '../../_state/kesehatan/kontak-darurat/kontakDarurat';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
|
||||
function KontakDarurat() {
|
||||
return (
|
||||
<Box>
|
||||
<HeaderSearch
|
||||
title='KontakDarurat'
|
||||
title='Kontak Darurat'
|
||||
placeholder='pencarian'
|
||||
searchIcon={<IconSearch size={20} />}
|
||||
/>
|
||||
<ListKontakDarurat/>
|
||||
<ListKontakDarurat />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function ListKontakDarurat() {
|
||||
const kontakDaruratState = useProxy(kontakDarurat)
|
||||
const router = useRouter();
|
||||
|
||||
useShallowEffect(() => {
|
||||
kontakDaruratState.findMany.load()
|
||||
}, [])
|
||||
|
||||
if (!kontakDaruratState.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
<Stack>
|
||||
<JudulList
|
||||
title='List 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 onClick={() => router.push('/admin/kesehatan/kontak-darurat/detail')}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
<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>Deskripsi</TableTh>
|
||||
<TableTh>Image</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
{kontakDaruratState.findMany.data?.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
<Text truncate="end" fz={"sm"}>{item.name}</Text>
|
||||
</Box>
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Image w={100} src={item.image?.link} alt="image" />
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/kesehatan/kontak-darurat/${item.id}`)}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import penangananDarurat from '@/app/admin/(dashboard)/_state/kesehatan/penanganan-darurat/penangananDarurat';
|
||||
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 { useParams, useRouter } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
|
||||
function EditPenangananDarurat() {
|
||||
const penangananDaruratState = useProxy(penangananDarurat)
|
||||
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: penangananDaruratState.edit.form.name || '',
|
||||
deskripsi: penangananDaruratState.edit.form.deskripsi || '',
|
||||
imageId: penangananDaruratState.edit.form.imageId || '',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const loadPenangananDarurat = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await penangananDaruratState.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
})
|
||||
|
||||
if (data?.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading penanganan darurat:', error);
|
||||
toast.error('Gagal memuat data penanganan darurat');
|
||||
}
|
||||
}
|
||||
|
||||
loadPenangananDarurat();
|
||||
}, [params?.id])
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
penangananDaruratState.edit.form = {
|
||||
...penangananDaruratState.edit.form,
|
||||
name: formData.name,
|
||||
deskripsi: formData.deskripsi,
|
||||
imageId: formData.imageId,
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
penangananDaruratState.edit.form.imageId = uploaded.id;
|
||||
}
|
||||
|
||||
await penangananDaruratState.edit.update();
|
||||
toast.success("Penanganan darurat berhasil diperbarui!");
|
||||
router.push("/admin/kesehatan/penanganan-darurat");
|
||||
} catch (error) {
|
||||
console.error("Error updating penanganan darurat:", error);
|
||||
toast.error("Gagal memuat data penanganan darurat");
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Stack gap={"xs"}>
|
||||
<Paper bg={colors['white-1']} p={"md"} w={{ base: '100%', md: '50%' }}>
|
||||
<Stack gap="xs">
|
||||
<Title order={3}>Edit Penanganan Darurat</Title>
|
||||
|
||||
<TextInput
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
label={<Text fz="sm" fw="bold">Judul</Text>}
|
||||
placeholder="masukkan judul"
|
||||
/>
|
||||
|
||||
<Box>
|
||||
<Text fz="sm" fw="bold">Deskripsi</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(val) => setFormData({ ...formData, deskripsi: val })}
|
||||
/>
|
||||
</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>
|
||||
)}
|
||||
|
||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>
|
||||
Simpan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack >
|
||||
</Box >
|
||||
);
|
||||
}
|
||||
|
||||
export default EditPenangananDarurat;
|
||||
@@ -0,0 +1,101 @@
|
||||
'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, { useState } from 'react';
|
||||
// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
import penangananDarurat from '../../../_state/kesehatan/penanganan-darurat/penangananDarurat';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { Skeleton } from '@mantine/core';
|
||||
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||
|
||||
function DetailPenangananDarurat() {
|
||||
const penangananDaruratState = useProxy(penangananDarurat)
|
||||
const [modalHapus, setModalHapus] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||
const router = useRouter();
|
||||
const params = useParams()
|
||||
|
||||
useShallowEffect(() => {
|
||||
penangananDaruratState.findUnique.load(params?.id as string)
|
||||
}, [])
|
||||
|
||||
const handleHapus = () => {
|
||||
if (selectedId) {
|
||||
penangananDaruratState.delete.byId(selectedId)
|
||||
setModalHapus(false)
|
||||
setSelectedId(null)
|
||||
router.push("/admin/kesehatan/penanganan-darurat")
|
||||
}
|
||||
}
|
||||
|
||||
if (!penangananDaruratState.findUnique.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
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 Penanganan Darurat</Text>
|
||||
{penangananDaruratState.findUnique.data ? (
|
||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Nama Penanganan Darurat</Text>
|
||||
<Text fz={"lg"}>{penangananDaruratState.findUnique.data.name}</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
|
||||
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: penangananDaruratState.findUnique.data.deskripsi }} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Gambar</Text>
|
||||
<Image src={penangananDaruratState.findUnique.data.image?.link} alt="gambar" />
|
||||
</Box>
|
||||
<Box>
|
||||
<Flex gap={"xs"}>
|
||||
<Button color="red" onClick={() => {
|
||||
if (penangananDaruratState.findUnique.data) {
|
||||
setSelectedId(penangananDaruratState.findUnique.data.id)
|
||||
setModalHapus(true)
|
||||
}
|
||||
}}
|
||||
disabled={penangananDaruratState.delete.loading || !penangananDaruratState.findUnique.data}>
|
||||
<IconX size={20} />
|
||||
</Button>
|
||||
<Button onClick={() => router.push(`/admin/kesehatan/penanganan-darurat/${penangananDaruratState.findUnique.data?.id}/edit`)} color="green">
|
||||
<IconEdit size={20} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
{/* Modal Hapus */}
|
||||
<ModalKonfirmasiHapus
|
||||
opened={modalHapus}
|
||||
onClose={() => setModalHapus(false)}
|
||||
onConfirm={handleHapus}
|
||||
text="Apakah anda yakin ingin menghapus penanganan darurat ini?"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailPenangananDarurat;
|
||||
@@ -1,14 +1,56 @@
|
||||
'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 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 { useRouter } from 'next/navigation';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import CreateEditor from '../../../_com/createEditor';
|
||||
import penangananDarurat from '../../../_state/kesehatan/penanganan-darurat/penangananDarurat';
|
||||
|
||||
|
||||
function CreatePenangananDarurat() {
|
||||
const router = useRouter();
|
||||
const penangananDaruratState = useProxy(penangananDarurat)
|
||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||
const [file, setFile] = useState<File | null>(null);
|
||||
|
||||
const resetForm = () => {
|
||||
penangananDaruratState.create.form = {
|
||||
name: "",
|
||||
deskripsi: "",
|
||||
imageId: "",
|
||||
};
|
||||
setPreviewImage(null);
|
||||
setFile(null);
|
||||
};
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
penangananDaruratState.create.form.imageId = uploaded.id;
|
||||
|
||||
await penangananDaruratState.create.create();
|
||||
|
||||
resetForm();
|
||||
router.push("/admin/kesehatan/penanganan-darurat")
|
||||
}
|
||||
return (
|
||||
<Box>
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
@@ -20,54 +62,48 @@ function CreatePenangananDarurat() {
|
||||
<Title order={3}>Create Penanganan Darurat</Title>
|
||||
|
||||
<TextInput
|
||||
|
||||
value={penangananDaruratState.create.form.name}
|
||||
onChange={(val) => {
|
||||
penangananDaruratState.create.form.name = val.target.value;
|
||||
}}
|
||||
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}
|
||||
<Text fz="sm" fw="bold">Deskripsi</Text>
|
||||
<CreateEditor
|
||||
value={penangananDaruratState.create.form.deskripsi}
|
||||
onChange={(val) => {
|
||||
penangananDaruratState.create.form.deskripsi = val;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Button bg={colors['blue-button']}>
|
||||
Simpan Potensi
|
||||
</Button>
|
||||
<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>
|
||||
)}
|
||||
|
||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>
|
||||
Simpan
|
||||
</Button>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
'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 DetailPenangananDarurat() {
|
||||
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 Penanganan Darurat</Text>
|
||||
|
||||
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||
<Stack gap={"xs"}>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Nama Penanganan Darurat</Text>
|
||||
<Text fz={"lg"}>Test Judul</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fz={"lg"} fw={"bold"}>Deskripsi Penanganan Darurat</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 onClick={() => router.push('/admin/kesehatan/penanganan-darurat/edit')} 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 penanganan darurat ini?"
|
||||
/> */}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default DetailPenangananDarurat;
|
||||
@@ -1,62 +0,0 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Stack, SimpleGrid, Paper, Title, TextInput, Text, Button, Image } from '@mantine/core';
|
||||
import { KesehatanEditor } from '../../_com/kesehatanEditor';
|
||||
import { IconArrowBack } from '@tabler/icons-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
|
||||
function EditPenangananDarurat() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<Box>
|
||||
<Box mb={10}>
|
||||
<Button variant="subtle" onClick={() => router.back()}>
|
||||
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||
</Button>
|
||||
</Box>
|
||||
<Stack gap={"xs"}>
|
||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={"md"}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={3}>Edit Penanganan Darurat</Title>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Nama Penanganan Darurat</Text>}
|
||||
placeholder='Masukkan nama Penanganan Darurat'
|
||||
/>
|
||||
<TextInput
|
||||
label={<Text fw={"bold"} fz={"sm"}>Deskripsi Penanganan Darurat</Text>}
|
||||
placeholder='Masukkan deskripsi Penanganan Darurat'
|
||||
/>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi</Text>
|
||||
<KesehatanEditor
|
||||
showSubmit={false}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Gambar</Text>
|
||||
<Image src={"/"} alt="gambar" />
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
<Box>
|
||||
<Paper bg={colors['white-1']} p={"md"}>
|
||||
<Stack gap={"xs"}>
|
||||
<Title order={4}>Preview Data Penanganan Darurat</Title>
|
||||
<Text fw={"bold"} fz={"sm"}>Nama Penanganan Darurat</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>No Telp Penanganan Darurat</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi</Text>
|
||||
<Text fw={"bold"} fz={"sm"}>Pelayanan Posyandu</Text>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
</SimpleGrid>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditPenangananDarurat;
|
||||
@@ -1,10 +1,14 @@
|
||||
'use client'
|
||||
import colors from '@/con/colors';
|
||||
import { Box, Button, Image, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||
import JudulList from '../../_com/judulList';
|
||||
import HeaderSearch from '../../_com/header';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useProxy } from 'valtio/utils';
|
||||
import { useShallowEffect } from '@mantine/hooks';
|
||||
import penangananDarurat from '../../_state/kesehatan/penanganan-darurat/penangananDarurat';
|
||||
|
||||
|
||||
function PenangananDarurat() {
|
||||
return (
|
||||
@@ -20,7 +24,21 @@ function PenangananDarurat() {
|
||||
}
|
||||
|
||||
function ListPenangananDarurat() {
|
||||
const penangananDaruratState = useProxy(penangananDarurat)
|
||||
const router = useRouter();
|
||||
|
||||
useShallowEffect(() => {
|
||||
penangananDaruratState.findMany.load()
|
||||
}, [])
|
||||
|
||||
if (!penangananDaruratState.findMany.data) {
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Skeleton h={500} />
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box py={10}>
|
||||
<Paper bg={colors['white-1']} p={'md'}>
|
||||
@@ -34,27 +52,31 @@ function ListPenangananDarurat() {
|
||||
<TableThead>
|
||||
<TableTr>
|
||||
<TableTh>Judul</TableTh>
|
||||
<TableTh>Kategori</TableTh>
|
||||
<TableTh>Deskripsi</TableTh>
|
||||
<TableTh>Image</TableTh>
|
||||
<TableTh>Detail</TableTh>
|
||||
</TableTr>
|
||||
</TableThead>
|
||||
<TableTbody>
|
||||
<TableTr>
|
||||
{penangananDaruratState.findMany.data?.map((item) => (
|
||||
<TableTr key={item.id}>
|
||||
<TableTd>
|
||||
<Box w={100}>
|
||||
<Text truncate="end" fz={"sm"}>Test</Text>
|
||||
<Text truncate="end" fz={"sm"}>{item.name}</Text>
|
||||
</Box></TableTd>
|
||||
<TableTd>Test</TableTd>
|
||||
<TableTd>
|
||||
<Image w={100} src={"/"} alt="image" />
|
||||
<Text truncate="end" fz={"sm"} dangerouslySetInnerHTML={{ __html: item.deskripsi }} />
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push('/admin/kesehatan/penanganan-darurat/detail')}>
|
||||
<Image w={100} src={item.image?.link} alt="image" />
|
||||
</TableTd>
|
||||
<TableTd>
|
||||
<Button onClick={() => router.push(`/admin/kesehatan/penanganan-darurat/${item.id}`)}>
|
||||
<IconDeviceImacCog size={25} />
|
||||
</Button>
|
||||
</TableTd>
|
||||
</TableTr>
|
||||
))}
|
||||
</TableTbody>
|
||||
</Table>
|
||||
</Box>
|
||||
|
||||
147
src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx
Normal file
147
src/app/admin/(dashboard)/kesehatan/posyandu/[id]/edit/page.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
'use client'
|
||||
import EditEditor from '@/app/admin/(dashboard)/_com/editEditor';
|
||||
import posyandustate from '@/app/admin/(dashboard)/_state/kesehatan/posyandu/posyandu';
|
||||
import colors from '@/con/colors';
|
||||
import ApiFetch from '@/lib/api-fetch';
|
||||
import { Box, Button, Center, FileInput, Group, Image, Paper, 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';
|
||||
|
||||
|
||||
function EditPosyandu() {
|
||||
const statePosyandu = useProxy(posyandustate)
|
||||
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: statePosyandu.edit.form.name || '',
|
||||
nomor: statePosyandu.edit.form.nomor || '',
|
||||
deskripsi: statePosyandu.edit.form.deskripsi || '',
|
||||
imageId: statePosyandu.edit.form.imageId || '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const loadPosyandu = async () => {
|
||||
const id = params?.id as string;
|
||||
if (!id) return;
|
||||
|
||||
try {
|
||||
const data = await statePosyandu.edit.load(id);
|
||||
if (data) {
|
||||
setFormData({
|
||||
name: data.name || '',
|
||||
nomor: data.nomor || '',
|
||||
deskripsi: data.deskripsi || '',
|
||||
imageId: data.imageId || '',
|
||||
});
|
||||
|
||||
if (data?.image?.link) {
|
||||
setPreviewImage(data.image.link);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading posyandu:", error);
|
||||
toast.error("Gagal memuat data posyandu");
|
||||
}
|
||||
}
|
||||
loadPosyandu();
|
||||
}, [params?.id])
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
statePosyandu.edit.form = {
|
||||
...statePosyandu.edit.form,
|
||||
name: formData.name,
|
||||
nomor: formData.nomor,
|
||||
deskripsi: formData.deskripsi,
|
||||
imageId: formData.imageId,
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
statePosyandu.edit.form.imageId = uploaded.id;
|
||||
}
|
||||
|
||||
await statePosyandu.edit.update();
|
||||
toast.success("Posyandu berhasil diperbarui!");
|
||||
router.push("/admin/kesehatan/posyandu");
|
||||
} catch (error) {
|
||||
console.error("Error updating posyandu:", error);
|
||||
toast.error("Gagal memuat data posyandu");
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
{previewImage ? (
|
||||
<Image alt="" src={previewImage} w={200} h={200} />
|
||||
) : (
|
||||
<Center w={200} h={200} bg={"gray"}>
|
||||
<IconImageInPicture />
|
||||
</Center>
|
||||
)}
|
||||
<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);
|
||||
}}
|
||||
/>
|
||||
<TextInput
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Nama Posyandu</Text>}
|
||||
placeholder='Masukkan nama posyandu'
|
||||
/>
|
||||
<TextInput
|
||||
value={formData.nomor}
|
||||
onChange={(e) => setFormData({ ...formData, nomor: e.target.value })}
|
||||
label={<Text fw={"bold"} fz={"sm"}>Nomor Posyandu</Text>}
|
||||
placeholder='Masukkan nomor posyandu'
|
||||
/>
|
||||
<Box>
|
||||
<Text fw={"bold"} fz={"sm"}>Deskripsi Posyandu</Text>
|
||||
<EditEditor
|
||||
value={formData.deskripsi}
|
||||
onChange={(htmlContent) => {
|
||||
setFormData({ ...formData, deskripsi: htmlContent });
|
||||
statePosyandu.edit.form.deskripsi = htmlContent;
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Group>
|
||||
<Button onClick={handleSubmit} bg={colors['blue-button']}>Submit</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default EditPosyandu;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user