diff --git a/prisma/data/desa/profile/lambang_desa.json b/prisma/data/desa/profile/lambang_desa.json new file mode 100644 index 00000000..c2198f1e --- /dev/null +++ b/prisma/data/desa/profile/lambang_desa.json @@ -0,0 +1,7 @@ +[ + { + "id": "1", + "judul": "Lambang Desa", + "deskripsi" : "" + } +] \ No newline at end of file diff --git a/prisma/data/desa/profile/maskot_desa.json b/prisma/data/desa/profile/maskot_desa.json new file mode 100644 index 00000000..3cf73b9a --- /dev/null +++ b/prisma/data/desa/profile/maskot_desa.json @@ -0,0 +1,7 @@ +[ + { + "id": "1", + "judul": "Maskot Desa", + "deskripsi" : "

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.

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.

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.

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.

Garapan Tari Maskot Desa Darmasaba Sekar Pudak diwujudkan ke dalam bentuk tari kreasi yang ditarikan secara berkelompok dengan jumlah lima orang penari perempuan (putri).

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.

" + } +] \ No newline at end of file diff --git a/prisma/data/desa/profile/profile_desa.json b/prisma/data/desa/profile/profile_desa.json deleted file mode 100644 index 29ed4d8a..00000000 --- a/prisma/data/desa/profile/profile_desa.json +++ /dev/null @@ -1,11 +0,0 @@ -[ - { - "id": "1", - "sejarah" : "

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.

", - "visi" : "

Mewujudkan Desa Darmasaba yang sejahtera, unggul, religius, berbudaya, dan aman dengan berlandaskan Tri Hita Karana

", - "misi" : "", - "lambang" : "", - "maskot" : "

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.

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.

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.

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.

Garapan Tari Maskot Desa Darmasaba Sekar Pudak diwujudkan ke dalam bentuk tari kreasi yang ditarikan secara berkelompok dengan jumlah lima orang penari perempuan (putri).

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.

", - "profilPerbekelId" : "1" - } -] \ No newline at end of file diff --git a/prisma/data/desa/profile/sejarah_desa.json b/prisma/data/desa/profile/sejarah_desa.json new file mode 100644 index 00000000..c0dd3e5f --- /dev/null +++ b/prisma/data/desa/profile/sejarah_desa.json @@ -0,0 +1,7 @@ +[ + { + "id": "1", + "judul": "Sejarah Desa", + "deskripsi": "

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.

" + } +] \ No newline at end of file diff --git a/prisma/data/desa/profile/visi_misi_desa.json b/prisma/data/desa/profile/visi_misi_desa.json new file mode 100644 index 00000000..4a6b978c --- /dev/null +++ b/prisma/data/desa/profile/visi_misi_desa.json @@ -0,0 +1,7 @@ +[ + { + "id" : "1", + "visi" : "

Mewujudkan Desa Darmasaba yang sejahtera, unggul, religius, berbudaya, dan aman dengan berlandaskan Tri Hita Karana

", + "misi" : "" + } +] \ No newline at end of file diff --git a/prisma/migrations/20250616155255_16_jun/migration.sql b/prisma/migrations/20250616155255_16_jun/migration.sql new file mode 100644 index 00000000..fff7b159 --- /dev/null +++ b/prisma/migrations/20250616155255_16_jun/migration.sql @@ -0,0 +1,193 @@ +/* + Warnings: + + - You are about to drop the column `nomor` on the `DaftarInformasiPublik` table. All the data in the column will be lost. + - You are about to drop the column `image` on the `GalleryFoto` table. All the data in the column will be lost. + - You are about to drop the column `video` on the `GalleryVideo` table. All the data in the column will be lost. + - You are about to drop the column `videosId` on the `GalleryVideo` table. All the data in the column will be lost. + - The primary key for the `IndeksKepuasanMasyarakat` table will be changed. If it partially fails, the table could be left without primary key constraint. + - You are about to drop the column `profilPerbekelId` on the `ProfileDesa` table. All the data in the column will be lost. + - You are about to drop the column `imageUrl` on the `ProfilePPID` table. All the data in the column will be lost. + - You are about to drop the `Images` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `Videos` table. If the table is not empty, all the data it contains will be lost. + - Added the required column `deskripsi` to the `GalleryFoto` table without a default value. This is not possible if the table is not empty. + - Added the required column `deskripsi` to the `GalleryVideo` table without a default value. This is not possible if the table is not empty. + - Added the required column `linkVideo` to the `GalleryVideo` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropForeignKey +ALTER TABLE "GalleryFoto" DROP CONSTRAINT "GalleryFoto_imagesId_fkey"; + +-- DropForeignKey +ALTER TABLE "GalleryVideo" DROP CONSTRAINT "GalleryVideo_videosId_fkey"; + +-- DropForeignKey +ALTER TABLE "ProfileDesa" DROP CONSTRAINT "ProfileDesa_profilPerbekelId_fkey"; + +-- DropIndex +DROP INDEX "GalleryVideo_videosId_key"; + +-- AlterTable +ALTER TABLE "DaftarInformasiPublik" DROP COLUMN "nomor"; + +-- AlterTable +ALTER TABLE "GalleryFoto" DROP COLUMN "image", +ADD COLUMN "deskripsi" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "GalleryVideo" DROP COLUMN "video", +DROP COLUMN "videosId", +ADD COLUMN "deskripsi" TEXT NOT NULL, +ADD COLUMN "linkVideo" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "IndeksKepuasanMasyarakat" DROP CONSTRAINT "IndeksKepuasanMasyarakat_pkey", +ALTER COLUMN "id" DROP DEFAULT, +ALTER COLUMN "id" SET DATA TYPE TEXT, +ADD CONSTRAINT "IndeksKepuasanMasyarakat_pkey" PRIMARY KEY ("id"); +DROP SEQUENCE "IndeksKepuasanMasyarakat_id_seq"; + +-- AlterTable +ALTER TABLE "ProfileDesa" DROP COLUMN "profilPerbekelId"; + +-- AlterTable +ALTER TABLE "ProfilePPID" DROP COLUMN "imageUrl", +ADD COLUMN "imageId" TEXT; + +-- DropTable +DROP TABLE "Images"; + +-- DropTable +DROP TABLE "Videos"; + +-- CreateTable +CREATE TABLE "StrukturPPID" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "imageId" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "isActive" BOOLEAN NOT NULL DEFAULT true, + + CONSTRAINT "StrukturPPID_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ProfileDesaImage" ( + "id" TEXT NOT NULL, + "label" TEXT NOT NULL, + "profileDesaId" TEXT NOT NULL, + "imageId" TEXT NOT NULL, + + CONSTRAINT "ProfileDesaImage_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "PelayananSuratKeterangan" ( + "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 "PelayananSuratKeterangan_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "PelayananTelunjukSaktiDesa" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "deskripsi" TEXT NOT NULL, + "link" 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 "PelayananTelunjukSaktiDesa_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "PelayananPerizinanBerusaha" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "deskripsi" TEXT NOT NULL, + "link" 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 "PelayananPerizinanBerusaha_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "PelayananPendudukNonPermanen" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "deskripsi" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "isActive" BOOLEAN NOT NULL DEFAULT true, + + CONSTRAINT "PelayananPendudukNonPermanen_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Penghargaan" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "juara" 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 "Penghargaan_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Posyandu" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "nomor" 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 "Posyandu_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "StrukturPPID" ADD CONSTRAINT "StrukturPPID_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ProfilePPID" ADD CONSTRAINT "ProfilePPID_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ProfileDesaImage" ADD CONSTRAINT "ProfileDesaImage_profileDesaId_fkey" FOREIGN KEY ("profileDesaId") REFERENCES "ProfileDesa"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ProfileDesaImage" ADD CONSTRAINT "ProfileDesaImage_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "GalleryFoto" ADD CONSTRAINT "GalleryFoto_imagesId_fkey" FOREIGN KEY ("imagesId") REFERENCES "FileStorage"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "PelayananSuratKeterangan" ADD CONSTRAINT "PelayananSuratKeterangan_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Penghargaan" ADD CONSTRAINT "Penghargaan_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Posyandu" ADD CONSTRAINT "Posyandu_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20250617083234_17jun/migration.sql b/prisma/migrations/20250617083234_17jun/migration.sql new file mode 100644 index 00000000..2ff042ca --- /dev/null +++ b/prisma/migrations/20250617083234_17jun/migration.sql @@ -0,0 +1,78 @@ +/* + Warnings: + + - You are about to drop the column `profileDesaId` on the `ProfileDesaImage` table. All the data in the column will be lost. + - You are about to drop the `ProfileDesa` table. If the table is not empty, all the data it contains will be lost. + - Added the required column `maskotDesaId` to the `ProfileDesaImage` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropForeignKey +ALTER TABLE "ProfileDesaImage" DROP CONSTRAINT "ProfileDesaImage_profileDesaId_fkey"; + +-- AlterTable +ALTER TABLE "ProfilPerbekel" ADD COLUMN "imageId" TEXT; + +-- AlterTable +ALTER TABLE "ProfileDesaImage" DROP COLUMN "profileDesaId", +ADD COLUMN "maskotDesaId" TEXT NOT NULL; + +-- DropTable +DROP TABLE "ProfileDesa"; + +-- CreateTable +CREATE TABLE "SejarahDesa" ( + "id" TEXT NOT NULL, + "judul" TEXT NOT NULL, + "deskripsi" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "isActive" BOOLEAN NOT NULL DEFAULT true, + + CONSTRAINT "SejarahDesa_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "VisiMisiDesa" ( + "id" TEXT NOT NULL, + "visi" TEXT NOT NULL, + "misi" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "isActive" BOOLEAN NOT NULL DEFAULT true, + + CONSTRAINT "VisiMisiDesa_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "LambangDesa" ( + "id" TEXT NOT NULL, + "judul" TEXT NOT NULL, + "deskripsi" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "isActive" BOOLEAN NOT NULL DEFAULT true, + + CONSTRAINT "LambangDesa_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "MaskotDesa" ( + "id" TEXT NOT NULL, + "judul" TEXT NOT NULL, + "deskripsi" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "isActive" BOOLEAN NOT NULL DEFAULT true, + + CONSTRAINT "MaskotDesa_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "ProfileDesaImage" ADD CONSTRAINT "ProfileDesaImage_maskotDesaId_fkey" FOREIGN KEY ("maskotDesaId") REFERENCES "MaskotDesa"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ProfilPerbekel" ADD CONSTRAINT "ProfilPerbekel_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 374834dd..187b4e07 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -63,14 +63,13 @@ model FileStorage { Berita Berita[] PotensiDesa PotensiDesa[] Posyandu Posyandu[] - ProfilePPID ProfilePPID[] StrukturPPID StrukturPPID[] - GalleryFoto GalleryFoto[] - PelayananSuratKeterangan PelayananSuratKeterangan[] - Penghargaan Penghargaan[] + ProfileDesaImage ProfileDesaImage[] + ProfilePPID ProfilePPID[] + ProfilPerbekel ProfilPerbekel[] } //========================================= MENU PPID ========================================= // @@ -248,32 +247,68 @@ model GrafikBerdasarkanUmur { // ========================================= MENU DESA ========================================= // // ========================================= PROFILE DESA ========================================= // -model ProfileDesa { - id String @id @default(cuid()) - sejarah String @db.Text - visi String @db.Text - misi String @db.Text - lambang String @db.Text - maskot String @db.Text - ProfilPerbekel ProfilPerbekel? @relation(fields: [profilPerbekelId], references: [id]) - profilPerbekelId String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - deletedAt DateTime @default(now()) - isActive Boolean @default(true) +model SejarahDesa { + id String @id @default(cuid()) + judul String @db.Text + deskripsi String @db.Text + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} + +model VisiMisiDesa { + id String @id @default(cuid()) + visi String @db.Text + misi String @db.Text + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} + +model LambangDesa { + id String @id @default(cuid()) + judul String + deskripsi String @db.Text + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} + +model MaskotDesa { + id String @id @default(cuid()) + judul String + deskripsi String @db.Text + images ProfileDesaImage[] + 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]) + 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 - ProfileDesa ProfileDesa[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - deletedAt DateTime @default(now()) - isActive Boolean @default(true) + 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) } // ========================================= BERITA ========================================= // @@ -412,16 +447,16 @@ model PelayananPendudukNonPermanen { // ========================================= PENGHARGAAN ========================================= // model Penghargaan { - id String @id @default(cuid()) + id String @id @default(cuid()) name String juara String - deskripsi String @db.Text + deskripsi 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) } // ========================================= MENU KESEHATAN ========================================= // diff --git a/prisma/seed.ts b/prisma/seed.ts index 82a63aec..b6b9067b 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -12,6 +12,11 @@ import visiMisiPPID from "./data/ppid/visi-misi-ppid/visimisiPPID.json"; import strukturPPID from "./data/ppid/struktur-ppid/strukturPPID.json"; import pelayananPerizinanBerusaha from "./data/desa/layanan/pelayananPerizinanBerusaha.json"; import pelayananPendudukNonPermanen from "./data/desa/layanan/pelayanaPendudukNonPermanen.json"; +import sejarahDesa from "./data/desa/profile/sejarah_desa.json"; +import visiMisiDesa from "./data/desa/profile/visi_misi_desa.json"; +import lambangDesa from "./data/desa/profile/lambang_desa.json"; +import maskotDesa from "./data/desa/profile/maskot_desa.json"; +import profilPerbekel from "./data/desa/profile/profil_perbekel.json"; (async () => { for (const l of layanan) { @@ -30,6 +35,104 @@ import pelayananPendudukNonPermanen from "./data/desa/layanan/pelayanaPendudukNo console.log("layanan success ..."); + for (const l of sejarahDesa) { + await prisma.sejarahDesa.upsert({ + where: { + id: l.id, + }, + update: { + judul: l.judul, + deskripsi: l.deskripsi, + }, + create: { + id: l.id, + judul: l.judul, + deskripsi: l.deskripsi, + }, + }); + } + + console.log("sejarah desa success ..."); + + for (const l of maskotDesa) { + await prisma.maskotDesa.upsert({ + where: { + id: l.id, + }, + update: { + judul: l.judul, + deskripsi: l.deskripsi, + }, + create: { + id: l.id, + judul: l.judul, + deskripsi: l.deskripsi, + }, + }); + } + + console.log("maskot desa success ..."); + + for (const l of lambangDesa) { + await prisma.lambangDesa.upsert({ + where: { + id: l.id, + }, + update: { + judul: l.judul, + deskripsi: l.deskripsi, + }, + create: { + id: l.id, + judul: l.judul, + deskripsi: l.deskripsi, + }, + }); + } + + console.log("lambang desa success ..."); + + for (const c of profilPerbekel) { + await prisma.profilPerbekel.upsert({ + where: { id: c.id }, + update: { + biodata: c.biodata, + pengalaman: c.pengalaman, + pengalamanOrganisasi: c.pengalamanOrganisasi, + programUnggulan: c.programUnggulan, + // imageId tidak di-update + }, + create: { + id: c.id, + biodata: c.biodata, + pengalaman: c.pengalaman, + pengalamanOrganisasi: c.pengalamanOrganisasi, + programUnggulan: c.programUnggulan, + // imageId tidak di-create + }, + }); + } + console.log("✅ profilePerbekel seeded without imageId (editable later via UI)"); + + for (const l of visiMisiDesa) { + await prisma.visiMisiDesa.upsert({ + where: { + id: l.id, + }, + update: { + visi: l.visi, + misi: l.misi, + }, + create: { + id: l.id, + visi: l.visi, + misi: l.misi, + }, + }); + } + + console.log("visi misi desa success ..."); + for (const l of pelayananPerizinanBerusaha) { await prisma.pelayananPerizinanBerusaha.upsert({ where: { @@ -67,8 +170,7 @@ import pelayananPendudukNonPermanen from "./data/desa/layanan/pelayanaPendudukNo }, }); } - - console.log("pelayanan perizinan berusaha success ..."); + console.log("pelayanan penduduk non permanen success ..."); for (const s of strukturPPID) { await prisma.strukturPPID.upsert({ diff --git a/public/darmasaba-icon.png b/public/darmasaba-icon.png new file mode 100644 index 00000000..0fad186e Binary files /dev/null and b/public/darmasaba-icon.png differ diff --git a/src/app/admin/(dashboard)/_state/desa/profile.ts b/src/app/admin/(dashboard)/_state/desa/profile.ts index 744b426d..321ab96d 100644 --- a/src/app/admin/(dashboard)/_state/desa/profile.ts +++ b/src/app/admin/(dashboard)/_state/desa/profile.ts @@ -1,239 +1,816 @@ -import ApiFetch from "@/lib/api-fetch"; -import { Prisma } from "@prisma/client"; import { toast } from "react-toastify"; import { proxy } from "valtio"; import { z } from "zod"; +import { Prisma } from "@prisma/client"; -/* Sejarah */ -const templateFormSejarahForm = z.object({ - sejarah: z.string().min(3, "Sejarah minimal 3 karakter"), -}) +// ========================================= SEJARAH DESA ========================================= // +const sejarahDesaForm = z.object({ + judul: z.string().min(3, "Judul minimal 3 karakter"), + deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"), +}); -type SejarahForm = Prisma.ProfileDesaGetPayload<{ - select: { - id: true; - sejarah: true; - } -}> +const sejarahDesaDefaultForm = { + judul: "", + deskripsi: "", +}; -const Sejarah = proxy({ - findById: { - data: null as SejarahForm | null, - loading: false, - initialize() { - Sejarah.findById.data = { - id: "", - sejarah: "", - } as SejarahForm; - }, - async load(id: string) { - try { - Sejarah.findById.loading = true; - const res = await ApiFetch.api.desa.profile["find-by-id"].get({ - query: { id }, - }); - if (res.status === 200) { - Sejarah.findById.data = { - id: id, - sejarah: res.data?.data?.sejarah ?? "" - }; - } else { - toast.error("Gagal mengambil data sejarah"); - } - } catch (error) { - console.error((error as Error).message); - toast.error("Terjadi kesalahan saat mengambil data sejarah"); - } finally { - Sejarah.findById.loading = false; - } +type SejarahDesaForm = Prisma.SejarahDesaGetPayload<{ + select: { + id: true; + judul: true; + deskripsi: true; + }; +}>; + +const sejarahDesa = proxy({ + findUnique: { + data: null as SejarahDesaForm | null, + loading: false, + error: null as string | null, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + this.loading = true; + this.error = null; + + try { + const response = await fetch(`/api/desa/profile/sejarah-desa/${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; + } else { + throw new Error( + result.message || "Gagal mengambil data sejarah desa" + ); + } + } catch (error) { + const msg = (error as Error).message; + this.error = msg; + console.error("Load sejarah desa error:", msg); + toast.error("Terjadi kesalahan saat mengambil data sejarah desa"); + return null; + } finally { + this.loading = false; + } }, - update: { - loading: false, - async save(data: SejarahForm) { - const cek = templateFormSejarahForm.safeParse(data); - if (!cek.success) { - const errors = cek.error.issues - .map((issue) => `${issue.path.join(".")}: ${issue.message}`) - .join(", "); - toast.error(`Form tidak valid: ${errors}`); - return; - } - try { - Sejarah.update.loading = true; - const res = await ApiFetch.api.desa.profile.sejarah["update"].post(data); - if (res.status === 200) { - toast.success("Berhasil update sejarah"); - await Sejarah.findById.load(data.id); - } else { - toast.error("Gagal update sejarah"); - } - } catch (error) { - console.error((error as Error).message); - toast.error("Terjadi kesalahan saat update sejarah"); - } finally { - Sejarah.update.loading = false; - } - } - } -}) -/* Visi Misi Desa */ -const templateFormVisiForm = z.object({ - visi: z.string().min(3, "Visi minimal 3 karakter"), - misi: z.string().min(3, "Misi minimal 3 karakter") -}) - -type VisiMisiDesaForm = Prisma.ProfileDesaGetPayload<{ - select: { - id: true; - visi: true; - misi: true; - } -}> - -const VisiMisiDesa = proxy({ - findById: { - data: null as VisiMisiDesaForm | null, - loading: false, - initialize() { - VisiMisiDesa.findById.data = { - id: "", - visi: "", - misi: "" - } as VisiMisiDesaForm; - }, - async load(id: string) { - try { - VisiMisiDesa.findById.loading = true; - const res = await ApiFetch.api.desa.profile["find-by-id"].get({ - query: { id }, - }); - if (res.status === 200) { - VisiMisiDesa.findById.data = { - id: id, - visi: res.data?.data?.visi ?? "", - misi: res.data?.data?.misi ?? "" - }; - } else { - toast.error("Gagal mengambil data visi misi"); - } - } catch (error) { - console.error((error as Error).message); - toast.error("Terjadi kesalahan saat mengambil data visi misi"); - } finally { - VisiMisiDesa.findById.loading = false; - } - } + reset() { + this.data = null; + this.error = null; + this.loading = false; }, - update: { - loading: false, - async save(data: VisiMisiDesaForm) { - const cek = templateFormVisiForm.safeParse(data); - if (!cek.success) { - const errors = cek.error.issues - .map((issue) => `${issue.path.join(".")}: ${issue.message}`) - .join(", "); - toast.error(`Form tidak valid: ${errors}`); - return; - } - try { - VisiMisiDesa.update.loading = true; - const res = await ApiFetch.api.desa.profile.visimisiDesa["update"].post(data); - if (res.status === 200) { - toast.success("Berhasil update visi misi"); - await VisiMisiDesa.findById.load(data.id); - } else { - toast.error("Gagal update visi"); - } - } catch (error) { - console.error((error as Error).message); - toast.error("Terjadi kesalahan saat update visi misi"); - } finally { - VisiMisiDesa.update.loading = false; - } - } - } -}) -/* Lambang Desa */ -const templateFormLambangDesaForm = z.object({ - lambang: z.string().min(3, "Lambang minimal 3 karakter"), -}) + }, + update: { + id: "", + form: { ...sejarahDesaDefaultForm }, + loading: false, + error: null as string | null, + isReadOnly: false, -type LambangDesaForm = Prisma.ProfileDesaGetPayload<{ - select: { - id: true; - lambang: true; - } -}> - -const LambangDesa = proxy({ - findById: { - data: null as LambangDesaForm | null, - loading: false, - initialize() { - LambangDesa.findById.data = { - id: "", - lambang: "", - } as LambangDesaForm; - }, - async load(id: string) { - try { - LambangDesa.findById.loading = true; - const res = await ApiFetch.api.desa.profile["find-by-id"].get({ - query: { id }, - }); - if (res.status === 200) { - LambangDesa.findById.data = { - id: id, - lambang: res.data?.data?.lambang ?? "" - }; - } else { - toast.error("Gagal mengambil data lambang desa"); - } - } catch (error) { - console.error((error as Error).message); - toast.error("Terjadi kesalahan saat mengambil data lambang desa"); - } finally { - LambangDesa.findById.loading = false; - } - } + initialize(sejarahData: SejarahDesaForm) { + this.id = sejarahData.id; + this.isReadOnly = false; + this.form = { + judul: sejarahData.judul || "", + deskripsi: sejarahData.deskripsi || "", + }; }, - update: { - loading: false, - async save(data: LambangDesaForm) { - const cek = templateFormLambangDesaForm.safeParse(data); - if (!cek.success) { - const errors = cek.error.issues - .map((issue) => `${issue.path.join(".")}: ${issue.message}`) - .join(", "); - toast.error(`Form tidak valid: ${errors}`); - return; + + updateField(field: keyof typeof sejarahDesaDefaultForm, value: string) { + this.form[field] = value; + }, + + 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; + } + + 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}`); } - try { - LambangDesa.update.loading = true; - const res = await ApiFetch.api.desa.profile.lambangDesa["update"].post(data); - if (res.status === 200) { - toast.success("Berhasil update lambang desa"); - await LambangDesa.findById.load(data.id); - } else { - toast.error("Gagal update lambang desa"); - } - } catch (error) { - console.error((error as Error).message); - toast.error("Terjadi kesalahan saat update lambang desa"); - } finally { - LambangDesa.update.loading = false; + + 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 ========================================= // +const visiMisiDesaForm = z.object({ + visi: z.string().min(3, "Visi minimal 3 karakter"), + misi: z.string().min(3, "Misi minimal 3 karakter"), +}); + +const visiMisiDesaDefaultForm = { + visi: "", + misi: "", +}; + +type VisiMisiDesaForm = Prisma.VisiMisiDesaGetPayload<{ + select: { + id: true; + visi: true; + misi: true; + }; +}>; + +const visiMisiDesa = proxy({ + findUnique: { + data: null as VisiMisiDesaForm | null, + loading: false, + error: null as string | null, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + this.loading = true; + this.error = null; + + 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; + } else { + throw new Error( + result.message || "Gagal mengambil data visi misi desa" + ); + } + } catch (error) { + const msg = (error as Error).message; + this.error = msg; + console.error("Load visi misi desa error:", msg); + toast.error("Terjadi kesalahan saat mengambil data visi misi desa"); + return null; + } finally { + this.loading = false; + } + }, + + reset() { + this.data = null; + this.error = null; + this.loading = false; + }, + }, + update: { + id: "", + form: { ...visiMisiDesaDefaultForm }, + loading: false, + error: null as string | null, + isReadOnly: false, + + initialize(visiMisiData: VisiMisiDesaForm) { + this.id = visiMisiData.id; + this.isReadOnly = false; + this.form = { + visi: visiMisiData.visi || "", + misi: visiMisiData.misi || "", + }; + }, + + updateField(field: keyof typeof visiMisiDesaDefaultForm, value: string) { + this.form[field] = value; + }, + + 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; + } + } +}); + +// ========================================= LAMBANG DESA ========================================= // +const lambangDesaForm = z.object({ + judul: z.string().min(3, "Judul minimal 3 karakter"), + deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"), +}); + +const lambangDesaDefaultForm = { + judul: "", + deskripsi: "", +}; + +type LambangDesaForm = Prisma.LambangDesaGetPayload<{ + select: { + id: true; + judul: true; + deskripsi: true; + }; +}>; + +const lambangDesa = proxy({ + findUnique: { + data: null as LambangDesaForm | null, + loading: false, + error: null as string | null, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + this.loading = true; + this.error = null; + + try { + const response = await fetch(`/api/desa/profile/lambang-desa/${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; + } else { + throw new Error( + result.message || "Gagal mengambil data lambang desa" + ); + } + } catch (error) { + const msg = (error as Error).message; + this.error = msg; + console.error("Load lambang desa error:", msg); + toast.error("Terjadi kesalahan saat mengambil data lambang desa"); + return null; + } finally { + this.loading = false; + } + }, + + reset() { + this.data = null; + this.error = null; + this.loading = false; + }, + }, + update: { + id: "", + form: { ...lambangDesaDefaultForm }, + loading: false, + error: null as string | null, + isReadOnly: false, + + initialize(lambangDesaData: LambangDesaForm) { + this.id = lambangDesaData.id; + this.isReadOnly = false; + this.form = { + judul: lambangDesaData.judul || "", + deskripsi: lambangDesaData.deskripsi || "", + }; + }, + + updateField(field: keyof typeof lambangDesaDefaultForm, value: string) { + this.form[field] = value; + }, + + 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; + } + + 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; + } + } +}); + +// ========================================= MASKOT DESA ========================================= // +const maskotForm = z.object({ + judul: z.string().min(3, "Judul minimal 3 karakter"), + deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"), + images: z + .array( + z.object({ + label: z.string().min(1, "Label wajib"), + imageId: z.string().min(1, "Image ID wajib"), + }) + ) + .min(1, "Minimal 1 gambar harus diisi"), +}); + +const maskotDefaultForm = { + judul: "", + deskripsi: "", + images: [] as { label: string; imageId: string }[], +}; + +type FormData = typeof maskotDefaultForm; + +type MaskotDesaForm = Prisma.MaskotDesaGetPayload<{ + include: { + images: { + include: { + image: { + select: { + id: true; + name: true; + path: true; + link: true; + }; + }; + }; + }; + }; +}>; + +const maskotDesa = proxy({ + findUnique: { + data: null as MaskotDesaForm | null, + loading: false, + error: null as string | null, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + this.loading = true; + this.error = null; + + try { + const response = await fetch(`/api/desa/profile/maskot-desa/${id}`); + const result = await response.json(); + + if (response.ok && result.success) { + this.data = result.data; + return result.data; + } else { + throw new Error(result.message || "Gagal mengambil data profile"); + } + } catch (error) { + const msg = (error as Error).message; + this.error = msg; + console.error("Load profile error:", msg); + toast.error("Terjadi kesalahan saat mengambil data profile"); + return null; + } finally { + this.loading = false; + } + }, + + reset() { + this.data = null; + this.error = null; + this.loading = false; + }, + }, + + update: { + id: "", + form: { ...maskotDefaultForm }, + loading: false, + error: null as string | null, + isReadOnly: false, + + initialize(profileData: MaskotDesaForm) { + this.id = profileData.id; + this.isReadOnly = false; + this.form = { + judul: profileData.judul || "", + deskripsi: profileData.deskripsi || "", + images: (profileData.images || []).map((img) => ({ + label: img.label, + imageId: img.image.id, + })), + }; + }, + + updateField(field: K, value: FormData[K]) { + this.form[field] = value; + }, + + addImage() { + this.form.images.push({ label: "", imageId: "" }); + }, + + removeImage(index: number) { + this.form.images.splice(index, 1); + }, + + async submit() { + const validation = maskotForm.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/maskot-desa/${this.id}`, + { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(this.form), + } + ); + + const result = await response.json(); + + if (response.ok && result.success) { + toast.success("Berhasil update profile"); + await maskotDesa.findUnique.load(this.id); + return true; + } else { + throw new Error(result.message || "Gagal update profile"); + } + } catch (error) { + const msg = (error as Error).message; + this.error = msg; + toast.error("Terjadi kesalahan saat update profile"); + return false; + } finally { + this.loading = false; + } + }, + + reset() { + this.id = ""; + this.form = { ...maskotDefaultForm }; + this.error = null; + this.loading = false; + this.isReadOnly = false; + }, + }, + + async loadForEdit(id: string) { + const data = await this.findUnique.load(id); + if (data) { + this.update.initialize(data); } + return data; + }, + + reset() { + this.findUnique.reset(); + this.update.reset(); + }, +}); + +// ========================================= PROFIL PERBEKEL ========================================= // +const profilPerbekelForm = z.object({ + biodata: z.string().min(3, "Biodata minimal 3 karakter"), + pengalaman: z.string().min(3, "Pengalaman minimal 3 karakter"), + pengalamanOrganisasi: z + .string() + .min(3, "Pengalaman Organisasi minimal 3 karakter"), + programUnggulan: z.string().min(3, "Program Unggulan minimal 3 karakter"), + imageId: z.string().min(1, "Gambar wajib dipilih"), +}); + +const profilPerbekelDefaultForm = { + biodata: "", + pengalaman: "", + pengalamanOrganisasi: "", + programUnggulan: "", + imageId: "", +}; + +type ProfilPerbekelForm = Prisma.ProfilPerbekelGetPayload<{ + select: { + id: true; + biodata: true; + pengalaman: true; + pengalamanOrganisasi: true; + programUnggulan: true; + imageId: true; + image?: { + select: { + link: true; + }; + }; + }; +}>; + +const profilPerbekel = proxy({ + findUnique: { + data: null as ProfilPerbekelForm | null, + loading: false, + error: null as string | null, + + async load(id: string) { + if (!id) { + toast.warn("ID tidak valid"); + return null; + } + + this.loading = true; + this.error = null; + + try { + const response = await fetch(`/api/desa/profile/profil-perbekel/${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; + } else { + throw new Error( + result.message || "Gagal mengambil data profil perbekel" + ); + } + } catch (error) { + const msg = (error as Error).message; + this.error = msg; + toast.error("Terjadi kesalahan saat mengambil data profil perbekel"); + return null; + } finally { + this.loading = false; + } + }, + + reset() { + this.data = null; + this.error = null; + this.loading = false; + }, + }, + + edit: { + id: "", + form: { ...profilPerbekelDefaultForm }, + loading: false, + error: null as string | null, + isReadOnly: false, + + initialize(profilData: ProfilPerbekelForm) { + this.id = profilData.id; + this.isReadOnly = false; + this.form = { + biodata: profilData.biodata || "", + pengalaman: profilData.pengalaman || "", + pengalamanOrganisasi: profilData.pengalamanOrganisasi || "", + programUnggulan: profilData.programUnggulan || "", + imageId: profilData.imageId || "", + }; + }, + + updateField(field: keyof typeof profilPerbekelDefaultForm, value: string) { + this.form[field] = value; + }, + + async submit() { + const validation = profilPerbekelForm.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/profil-perbekel/${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 profil perbekel"); + await profilPerbekel.findUnique.load(this.id); + return true; + } else { + throw new Error(result.message || "Gagal update profil perbekel"); + } + } catch (error) { + const msg = (error as Error).message; + this.error = msg; + toast.error("Terjadi kesalahan saat update profil perbekel"); + return false; + } finally { + this.loading = false; + } + }, + reset() { + this.id = ""; + this.form = { ...profilPerbekelDefaultForm }; + this.error = null; + this.loading = false; + this.isReadOnly = false; + }, + }, + + async loadForEdit(id: string) { + const profileData = await this.findUnique.load(id); + if (profileData) { + this.edit.initialize(profileData); + } + return profileData; + }, + + reset() { + this.findUnique.reset(); + this.edit.reset(); + }, }); const stateProfileDesa = { - Sejarah, - VisiMisiDesa, - LambangDesa, + maskotDesa, + profilPerbekel, + visiMisiDesa, + sejarahDesa, }; - export default stateProfileDesa; diff --git a/src/app/admin/(dashboard)/desa/gallery/lib/youtube-utils.ts b/src/app/admin/(dashboard)/desa/gallery/lib/youtube-utils.ts index 7e3cf64b..26fa1175 100644 --- a/src/app/admin/(dashboard)/desa/gallery/lib/youtube-utils.ts +++ b/src/app/admin/(dashboard)/desa/gallery/lib/youtube-utils.ts @@ -1,18 +1,31 @@ -export function convertYoutubeUrlToEmbed(url: string): string | null { - const watchRegex = /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/; - const shortRegex = /(?:https?:\/\/)?youtu\.be\/([^?]+)/; +export function convertYoutubeUrlToEmbed(url: string) { + const videoIdMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/); + return videoIdMatch ? `https://www.youtube.com/embed/${videoIdMatch[1]}` : null; +} + + + + + + + + + +// (url: string): string | null { +// const watchRegex = /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^&]+)/; +// const shortRegex = /(?:https?:\/\/)?youtu\.be\/([^?]+)/; - const matchWatch = url.match(watchRegex); - const matchShort = url.match(shortRegex); +// const matchWatch = url.match(watchRegex); +// const matchShort = url.match(shortRegex); - if (matchWatch) { - return `https://www.youtube.com/embed/${matchWatch[1]}`; - } +// if (matchWatch) { +// return `https://www.youtube.com/embed/${matchWatch[1]}`; +// } - if (matchShort) { - return `https://www.youtube.com/embed/${matchShort[1]}`; - } +// if (matchShort) { +// return `https://www.youtube.com/embed/${matchShort[1]}`; +// } - return null; - } +// return null; +// } \ No newline at end of file diff --git a/src/app/admin/(dashboard)/desa/gallery/video/[id]/edit/page.tsx b/src/app/admin/(dashboard)/desa/gallery/video/[id]/edit/page.tsx index 4c47df85..25550868 100644 --- a/src/app/admin/(dashboard)/desa/gallery/video/[id]/edit/page.tsx +++ b/src/app/admin/(dashboard)/desa/gallery/video/[id]/edit/page.tsx @@ -11,18 +11,16 @@ import { toast } from 'react-toastify'; import { useProxy } from 'valtio/utils'; import { convertYoutubeUrlToEmbed } from '../../../lib/youtube-utils'; - - function EditVideo() { - const router = useRouter(); - const [embedLink, setEmbedLink] = useState(""); + const router = useRouter(); const videoState = useProxy(stateGallery.video) const params = useParams() + const [formData, setFormData] = useState({ - name: videoState.findUnique.data?.name || '', - deskripsi: videoState.findUnique.data?.deskripsi || '', - linkVideo: videoState.findUnique.data?.linkVideo || '', - }) + name: '', + deskripsi: '', + linkVideo: '', + }); useEffect(() => { const loadVideo = async () => { @@ -36,8 +34,6 @@ function EditVideo() { deskripsi: data.deskripsi || '', linkVideo: data.linkVideo || '', }); - const embed = convertYoutubeUrlToEmbed(data.linkVideo); - setEmbedLink(embed || ""); } } catch (error) { console.error('Error loading video:', error); @@ -47,7 +43,15 @@ function EditVideo() { loadVideo(); }, [params?.id]); + const embedLink = convertYoutubeUrlToEmbed(formData.linkVideo); + const handleSubmit = async () => { + const converted = convertYoutubeUrlToEmbed(formData.linkVideo); + if (!converted) { + toast.error("Link YouTube tidak valid. Pastikan formatnya benar."); + return; + } + try { videoState.update.form = { ...videoState.update.form, @@ -55,11 +59,6 @@ function EditVideo() { deskripsi: formData.deskripsi, linkVideo: formData.linkVideo, }; - const converted = convertYoutubeUrlToEmbed(formData.linkVideo); - if (!converted) { - toast.error("Link YouTube tidak valid. Pastikan formatnya benar."); - return; - } await videoState.update.update(); toast.success('Video berhasil diperbarui!'); router.push('/admin/desa/gallery/video'); @@ -80,29 +79,23 @@ function EditVideo() { Edit Video + Judul Video} placeholder='Masukkan judul video' value={formData.name} onChange={(val) => { - setFormData({ - ...formData, - name: val.target.value, - }) + setFormData({ ...formData, name: val.target.value }); }} /> + { - setFormData({ - ...formData, - linkVideo: e.currentTarget.value, - }) - const embed = convertYoutubeUrlToEmbed(e.currentTarget.value); - setEmbedLink(embed || ""); + setFormData({ ...formData, linkVideo: e.currentTarget.value }); }} required /> @@ -118,18 +111,17 @@ function EditVideo() { > )} + Deskripsi Video { - setFormData({ - ...formData, - deskripsi: val, - }) + setFormData({ ...formData, deskripsi: val }); }} /> + diff --git a/src/app/admin/(dashboard)/desa/gallery/video/create/page.tsx b/src/app/admin/(dashboard)/desa/gallery/video/create/page.tsx index 4ce09d19..bf48f34d 100644 --- a/src/app/admin/(dashboard)/desa/gallery/video/create/page.tsx +++ b/src/app/admin/(dashboard)/desa/gallery/video/create/page.tsx @@ -16,7 +16,7 @@ function CreateVideo() { const videoState = useProxy(stateGallery.video) const router = useRouter(); const [link, setLink] = useState(""); - const [embedLink, setEmbedLink] = useState(""); + const embedLink = convertYoutubeUrlToEmbed(link); const resetForm = () => { videoState.create.form = { @@ -26,15 +26,17 @@ function CreateVideo() { }; }; const handleSubmit = async () => { - const converted = convertYoutubeUrlToEmbed(videoState.create.form.linkVideo); - if (!converted) { + if (!embedLink) { toast.error("Link YouTube tidak valid. Pastikan formatnya benar."); return; } + + videoState.create.form.linkVideo = embedLink; // pastikan diset di sini juga (jaga-jaga) await videoState.create.create(); resetForm(); - router.push("/admin/desa/gallery/video") + router.push("/admin/desa/gallery/video"); }; + return ( @@ -63,8 +65,6 @@ function CreateVideo() { value={link} onChange={(e) => { setLink(e.currentTarget.value); - const embed = convertYoutubeUrlToEmbed(e.currentTarget.value); - setEmbedLink(embed || ""); }} required /> diff --git a/src/app/admin/(dashboard)/desa/profile/_lib/layoutTabsDetail.tsx b/src/app/admin/(dashboard)/desa/profile/_lib/layoutTabsDetail.tsx new file mode 100644 index 00000000..1cd94190 --- /dev/null +++ b/src/app/admin/(dashboard)/desa/profile/_lib/layoutTabsDetail.tsx @@ -0,0 +1,62 @@ +/* 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 LayoutTabsDetail({ children }: { children: React.ReactNode }) { + const router = useRouter() + const pathname = usePathname() + const tabs = [ + { + label: "Profile Desa", + value: "profiledesa", + href: "/admin/desa/profile/profile-desa" + }, + { + label: "Profile Perbekel", + value: "profileperbekel", + href: "/admin/desa/profile/profile-perbekel" + } + ]; + const curentTab = tabs.find(tab => tab.href === pathname) + const [activeTab, setActiveTab] = useState(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 ( + + Profile Desa + + + {tabs.map((e, i) => ( + {e.label} + ))} + + {tabs.map((e, i) => ( + + {/* Konten dummy, bisa diganti tergantung routing */} + <> + + ))} + + {children} + + ); +} + +export default LayoutTabsDetail; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/desa/profile/_lib/layoutTabsEdit.tsx b/src/app/admin/(dashboard)/desa/profile/_lib/layoutTabsEdit.tsx new file mode 100644 index 00000000..75201d3d --- /dev/null +++ b/src/app/admin/(dashboard)/desa/profile/_lib/layoutTabsEdit.tsx @@ -0,0 +1,71 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +'use client' +import colors from '@/con/colors'; +import { Stack, Tabs, TabsList, TabsPanel, TabsTab } from '@mantine/core'; +import { usePathname, useRouter } from 'next/navigation'; +import React, { useEffect, useState } from 'react'; + +function LayoutTabsEdit({ children }: { children: React.ReactNode }) { + const router = useRouter() + const pathname = usePathname() + const tabs = [ + { + label: "Sejarah Desa", + value: "sejarahdesa", + href: "/admin/desa/profile/edit/sejarah_desa" + }, + { + label: "Visi Misi Desa", + value: "visimisidesa", + href: "/admin/desa/profile/edit/visi_misi_desa" + }, + { + label: "Lambang Desa", + value: "lambangdesa", + href: "/admin/desa/profile/edit/lambang_desa" + }, + { + label: "Maskot Desa", + value: "maskotdesa", + href: "/admin/desa/profile/edit/maskot_desa" + }, + ]; + const curentTab = tabs.find(tab => tab.href === pathname) + const [activeTab, setActiveTab] = useState(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 ( + + + + {tabs.map((e, i) => ( + {e.label} + ))} + + {tabs.map((e, i) => ( + + {/* Konten dummy, bisa diganti tergantung routing */} + <> + + ))} + + {children} + + ); +} + +export default LayoutTabsEdit; \ No newline at end of file diff --git a/src/app/admin/(dashboard)/desa/profile/ui/lambang_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/edit/lambang_desa/page.tsx similarity index 100% rename from src/app/admin/(dashboard)/desa/profile/ui/lambang_desa/page.tsx rename to src/app/admin/(dashboard)/desa/profile/edit/lambang_desa/page.tsx diff --git a/src/app/admin/(dashboard)/desa/profile/edit/layout.tsx b/src/app/admin/(dashboard)/desa/profile/edit/layout.tsx new file mode 100644 index 00000000..3e19b208 --- /dev/null +++ b/src/app/admin/(dashboard)/desa/profile/edit/layout.tsx @@ -0,0 +1,13 @@ +'use client' + +import LayoutTabsEdit from "../_lib/layoutTabsEdit" + +function Layout({children}: {children: React.ReactNode}) { + return ( + + {children} + + ); +} + +export default Layout; diff --git a/src/app/admin/(dashboard)/desa/profile/ui/maskot_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/edit/maskot_desa/page.tsx similarity index 100% rename from src/app/admin/(dashboard)/desa/profile/ui/maskot_desa/page.tsx rename to src/app/admin/(dashboard)/desa/profile/edit/maskot_desa/page.tsx diff --git a/src/app/admin/(dashboard)/desa/profile/ui/profile_perbekel/page.tsx b/src/app/admin/(dashboard)/desa/profile/edit/profile_perbekel/page.tsx similarity index 100% rename from src/app/admin/(dashboard)/desa/profile/ui/profile_perbekel/page.tsx rename to src/app/admin/(dashboard)/desa/profile/edit/profile_perbekel/page.tsx diff --git a/src/app/admin/(dashboard)/desa/profile/edit/sejarah_desa/create.tsx b/src/app/admin/(dashboard)/desa/profile/edit/sejarah_desa/create.tsx new file mode 100644 index 00000000..632194fe --- /dev/null +++ b/src/app/admin/(dashboard)/desa/profile/edit/sejarah_desa/create.tsx @@ -0,0 +1,34 @@ +'use client' +import colors from '@/con/colors'; +import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core'; + + + +function SejarahDesa() { + + + return ( + + + + + + Sejarah Desa + Deskripsi Sejarah Desa + + + + + + + + + ); +} + +export default SejarahDesa; diff --git a/src/app/admin/(dashboard)/desa/profile/edit/sejarah_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/edit/sejarah_desa/page.tsx new file mode 100644 index 00000000..ae48be5a --- /dev/null +++ b/src/app/admin/(dashboard)/desa/profile/edit/sejarah_desa/page.tsx @@ -0,0 +1,25 @@ +import colors from '@/con/colors'; +import { Box, Paper, Stack } from '@mantine/core'; + +function Page() { + return ( + + + + + + {/* + + + + + */} + + + + + + ); +} + +export default Page; diff --git a/src/app/admin/(dashboard)/desa/profile/ui/visi_misi_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/edit/visi_misi_desa/page.tsx similarity index 100% rename from src/app/admin/(dashboard)/desa/profile/ui/visi_misi_desa/page.tsx rename to src/app/admin/(dashboard)/desa/profile/edit/visi_misi_desa/page.tsx diff --git a/src/app/admin/(dashboard)/desa/profile/layout.tsx b/src/app/admin/(dashboard)/desa/profile/layout.tsx new file mode 100644 index 00000000..f82687f5 --- /dev/null +++ b/src/app/admin/(dashboard)/desa/profile/layout.tsx @@ -0,0 +1,11 @@ +'use client' + +import LayoutTabsDetail from "./_lib/layoutTabsDetail" + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ) +} \ No newline at end of file diff --git a/src/app/admin/(dashboard)/desa/profile/page.tsx b/src/app/admin/(dashboard)/desa/profile/page.tsx deleted file mode 100644 index 8f85b2fa..00000000 --- a/src/app/admin/(dashboard)/desa/profile/page.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import colors from '@/con/colors'; -import { Stack, Title, Tabs, TabsList, TabsTab, TabsPanel } from '@mantine/core'; -import React from 'react'; -import SejarahDesa from './ui/sejarah_desa/page'; -import VisiMisiDesa from './ui/visi_misi_desa/page'; -import LambangDesa from './ui/lambang_desa/page'; -import MaskotDesa from './ui/maskot_desa/page'; -import ProfilePerbekel from './ui/profile_perbekel/page'; - -function Page() { - return ( - - Profile Desa - - - - Sejarah Desa - - - Visi Misi Desa - - - Lambang Desa - - - Maskot Desa - - - Profile Perbekel - - - - - - - - - - - - - - - - - - - - - ); -} - -export default Page; diff --git a/src/app/admin/(dashboard)/desa/profile/profile-desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-desa/page.tsx new file mode 100644 index 00000000..b7574e55 --- /dev/null +++ b/src/app/admin/(dashboard)/desa/profile/profile-desa/page.tsx @@ -0,0 +1,134 @@ +'use client' +import colors from '@/con/colors'; +import { Paper, Stack, Grid, GridCol, Title, Button, Box, Text, Center, Image, SimpleGrid } from '@mantine/core'; +import { IconEdit } from '@tabler/icons-react'; +import { useRouter } from 'next/navigation'; +import React from 'react'; + +function Page() { + const router = useRouter() + return ( + + + + + Preview Profile Desa + + + + + + {/* Sejarah Desa */} + + + + +
+ +
+ Sejarah Desa +
+ + + Test + + +
+
+
+ {/* Visi Misi Desa */} + + + + +
+ +
+
+ + Visi Desa + + Test + + + + Misi Desa + + Test + + +
+
+
+ {/* Lambang Desa */} + + + + +
+ +
+ Lambang Desa +
+ + + Test + + +
+
+
+ {/* Maskot Desa */} + + + + +
+ +
+ Maskot Desa +
+ + + Test + + + +
+ + + + Pohon Pudak + + +
+
+ + + + Bunga Pudak + + +
+
+ + + Test + + +
+
+
+
+
+ ); +} + +export default Page; diff --git a/src/app/admin/(dashboard)/desa/profile/profile-perbekel/page.tsx b/src/app/admin/(dashboard)/desa/profile/profile-perbekel/page.tsx new file mode 100644 index 00000000..11324dea --- /dev/null +++ b/src/app/admin/(dashboard)/desa/profile/profile-perbekel/page.tsx @@ -0,0 +1,12 @@ +import colors from '@/con/colors'; +import { Paper, Text } from '@mantine/core'; + +function Page() { + return ( + + Test + + ); +} + +export default Page; diff --git a/src/app/admin/(dashboard)/desa/profile/ui/sejarah_desa/listPage.tsx b/src/app/admin/(dashboard)/desa/profile/ui/sejarah_desa/listPage.tsx deleted file mode 100644 index 35a8dbda..00000000 --- a/src/app/admin/(dashboard)/desa/profile/ui/sejarah_desa/listPage.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import colors from '@/con/colors'; -import { Box, Paper, Stack, Title } from '@mantine/core'; -import React from 'react'; - -function ListPage() { - return ( - - - - List Sejarah Desa - - - - ); -} - -export default ListPage; diff --git a/src/app/admin/(dashboard)/desa/profile/ui/sejarah_desa/page.tsx b/src/app/admin/(dashboard)/desa/profile/ui/sejarah_desa/page.tsx deleted file mode 100644 index ea95455e..00000000 --- a/src/app/admin/(dashboard)/desa/profile/ui/sejarah_desa/page.tsx +++ /dev/null @@ -1,65 +0,0 @@ -'use client' -import stateProfileDesa from '@/app/admin/(dashboard)/_state/desa/profile'; -import colors from '@/con/colors'; -import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, Title } from '@mantine/core'; -import { useProxy } from 'valtio/utils'; -import DesaEditorText from '../../../_com/desaEditorText'; -import ListPage from './listPage'; -import { useShallowEffect } from '@mantine/hooks'; - - -function SejarahDesa() { - const stateSejarah = useProxy(stateProfileDesa.Sejarah) - - useShallowEffect(() => { - if (!stateSejarah.findById.data) { - stateSejarah.findById.initialize() - } - }, []) - - const submit = () => { - if (stateSejarah.findById.data?.id && stateSejarah.findById.data.sejarah) { - stateSejarah.update.save({ - id: stateSejarah.findById.data.id, - sejarah: stateSejarah.findById.data.sejarah - }) - } - } - - return ( - - - - - - Sejarah Desa - Deskripsi Sejarah Desa - { - if (stateSejarah.findById.data) { - stateSejarah.findById.data.sejarah = val - } - }} - initialContent={stateSejarah.findById.data?.sejarah ?? ""} - /> - - - - - - - - - - ); -} - -export default SejarahDesa; diff --git a/src/app/admin/(dashboard)/ppid/profile-ppid/page.tsx b/src/app/admin/(dashboard)/ppid/profile-ppid/page.tsx index 59632351..dfc5b927 100644 --- a/src/app/admin/(dashboard)/ppid/profile-ppid/page.tsx +++ b/src/app/admin/(dashboard)/ppid/profile-ppid/page.tsx @@ -45,7 +45,7 @@ function Page() {
- +
@@ -62,7 +62,7 @@ function Page() {
Foto Profil PPID { diff --git a/src/app/admin/_com/list_PageAdmin.tsx b/src/app/admin/_com/list_PageAdmin.tsx index 5624c16d..d5d9606c 100644 --- a/src/app/admin/_com/list_PageAdmin.tsx +++ b/src/app/admin/_com/list_PageAdmin.tsx @@ -108,7 +108,7 @@ export const navBar = [ { id: "Desa_1", name: "Profile", - path: "/admin/desa/profile" + path: "/admin/desa/profile/profile-desa" }, { id: "Desa_2", diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/find-by-id.ts new file mode 100644 index 00000000..ad95798b --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/find-by-id.ts @@ -0,0 +1,49 @@ +import prisma from "@/lib/prisma"; + +export default async function profilePerbekelFindById(request: Request) { + const url = new URL(request.url); + const pathSegments = url.pathname.split('/'); + const id = pathSegments[pathSegments.length - 1]; + + if (!id) { + return Response.json({ + success: false, + message: "ID tidak boleh kosong", + }, { status: 400 }); + } + + try { + if (typeof id !== 'string') { + return Response.json({ + success: false, + message: "ID tidak valid", + }, { status: 400 }); + } + + const data = await prisma.profilPerbekel.findUnique({ + where: { id }, + include: { + image: true, + } + }); + + if (!data) { + return Response.json({ + success: false, + message: "Data tidak ditemukan", + }, { status: 404 }); + } + + return Response.json({ + success: true, + message: "Data berhasil ditemukan", + data, + }, { status: 200 }); + } catch (error) { + console.error("Error fetching profile Perbekel:", error); + return Response.json({ + success: false, + message: "Terjadi kesalahan saat mengambil data profile Perbekel", + }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/index.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/index.ts new file mode 100644 index 00000000..89f93490 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/index.ts @@ -0,0 +1,32 @@ +import Elysia, { t } from "elysia"; +import profilePerbekelFindById from "./find-by-id"; +import profilePerbekelUpdate from "./update"; + +const ProfilPerbekel = new Elysia({ + prefix: "/profileperbekel", + tags: ["Desa/Profile"], +}) + .get("/:id", async (context) => { + const response = await profilePerbekelFindById( + new Request(context.request) + ); + return response; + }) + .put( + "/:id", + async (context) => { + const response = await profilePerbekelUpdate(context); + return response; + }, + { + body: t.Object({ + biodata: t.String(), + pengalaman: t.String(), + pengalamanOrganisasi: t.String(), + programUnggulan: t.String(), + imageId: t.String(), + }), + } + ); + +export default ProfilPerbekel; diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/update.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/update.ts index 7b81bfda..00bec4ed 100644 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/update.ts +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profilePerbekel/update.ts @@ -1,33 +1,118 @@ import prisma from "@/lib/prisma"; import { Prisma } from "@prisma/client"; import { Context } from "elysia"; +import path from "path"; +import fs from "fs/promises"; -type FormCreate = Prisma.ProfilPerbekelGetPayload<{ - select: { - id: true; - biodata: true; - pengalaman: true; - pengalamanOrganisasi: true; - programUnggulan: true; - } -}> +type FormUpdate = Prisma.ProfilPerbekelGetPayload<{ + select: { + id: true; + biodata: true; + pengalaman: true; + pengalamanOrganisasi: true; + programUnggulan: true; + imageId: true; + }; +}>; export default async function profilePerbekelUpdate(context: Context) { - const body = context.body as FormCreate; + try { + const id = context.params?.id as string; + const body = (await context.body) as Omit; - await prisma.profilPerbekel.update({ - where: { - id: body.id - }, + const { biodata, pengalaman, pengalamanOrganisasi, programUnggulan, imageId } = body; + + if (!id) { + return new Response( + JSON.stringify({ + success: false, + message: "ID tidak boleh kosong", + }), + { + status: 400, + headers: { + "Content-Type": "application/json", + }, + } + ); + } + + const exisitng = await prisma.profilPerbekel.findUnique({ + where: { + id, + }, + include: { + image: true, + }, + }); + + if (!exisitng) { + return new Response( + JSON.stringify({ + success: false, + message: "Data tidak ditemukan", + }), + { + status: 404, + headers: { + "Content-Type": "application/json", + }, + } + ); + } + + if (exisitng.imageId !== imageId) { + const oldImage = exisitng.image; + if (oldImage) { + try { + const filePath = path.join(oldImage.path, oldImage.name); + await fs.unlink(filePath); + await prisma.fileStorage.delete({ + where: { id: oldImage.id }, + }); + } catch (error) { + console.error("Gagal hapus gambar lama:", error); + } + } + } + + const updated = await prisma.profilPerbekel.update({ + where: {id}, data: { - biodata: body.biodata, - pengalaman: body.pengalaman, - pengalamanOrganisasi: body.pengalamanOrganisasi, - programUnggulan: body.programUnggulan, + biodata, + pengalaman, + pengalamanOrganisasi, + programUnggulan, + imageId, } }) - return { - success: true, - message: "Profile Perbekel Berhasil Diupdate", - } -} \ No newline at end of file + return new Response( + JSON.stringify({ + success: true, + message: "Data berhasil diperbarui", + data: updated, + }), + { + status: 200, + headers: { + "Content-Type": "application/json", + }, + } + ); + + } catch (error) { + console.error("Error updating profile Perbekel:", error); + return new Response( + JSON.stringify({ + success: false, + message: "Terjadi kesalahan saat mengupdate profile Perbekel", + }), + { + status: 500, + headers: { + "Content-Type": "application/json", + }, + } + ); + } +} diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/find-by-id.ts deleted file mode 100644 index 6443567e..00000000 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/find-by-id.ts +++ /dev/null @@ -1,33 +0,0 @@ -import prisma from "@/lib/prisma"; -import { Context } from "elysia"; - -export default async function profileDesaFindById(context: Context) { - try { - const id = context?.params?.slugs?.[0]; - - // If no ID provided, get the first profile - if (!id) { - const data = await prisma.profileDesa.findFirst(); - return { - success: true, - data, - }; - } - - const data = await prisma.profileDesa.findUniqueOrThrow({ - where: { id }, - }); - - return { - success: true, - data, - }; - } catch (error) { - console.error("Error fetching profileDesa:", error); - - return { - success: false, - message: error instanceof Error ? error.message : "Unknown error", - }; - } -} diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/index.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/index.ts index 26e2e225..2145310a 100644 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/index.ts +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/index.ts @@ -1,51 +1,16 @@ -import Elysia, { t } from "elysia"; -import lambangDesaUpdate from "./lambangDesa/update"; -import maskotDesaUpdate from "./maskotDesa/update"; -import profilePerbekelUpdate from "../profilePerbekel/update"; -import sejarahDesaUpdate from "./sejarah/update"; -import visimisiDesaUpdate from "./visimisiDesa/update"; -import profileDesaFindById from "./find-by-id"; +import SejarahDesa from "./sejarah"; +import VisiMisiDesa from "./visi-misi"; +import LambangDesa from "./lambang-desa"; +import MaskotDesa from "./maskot-desa"; +import Elysia from "elysia"; const ProfileDesa = new Elysia({ - prefix: "/profile", - tags: ["Desa/Profile"] -}) -.get("/find-by-id", profileDesaFindById) -.post("/profilePerbekel/update", profilePerbekelUpdate, { - body: t.Object({ - id: t.String(), - biodata: t.String(), - pengalaman: t.String(), - pengalamanOrganisasi: t.String(), - programUnggulan: t.String(), - }) -}) -.post("/visimisiDesa/update", visimisiDesaUpdate, { - body: t.Object({ - id: t.String(), - visi: t.String(), - misi: t.String(), - }) -}) -.post("/sejarah/update", sejarahDesaUpdate, { - body: t.Object({ - id: t.String(), - sejarah: t.String(), - }) -}) -.post("/lambangDesa/update", lambangDesaUpdate, { - body: t.Object({ - id: t.String(), - lambang: t.String(), - }) -}) -.post("/maskotDesa/update", maskotDesaUpdate, { - body: t.Object({ - id: t.String(), - maskot: t.String(), - }) + prefix: "/profile", + tags: ["Desa/Profile"], }) + .use(SejarahDesa) + .use(VisiMisiDesa) + .use(LambangDesa) + .use(MaskotDesa); - - -export default ProfileDesa +export default ProfileDesa; diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/find-by-id.ts new file mode 100644 index 00000000..3e5d68f9 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/find-by-id.ts @@ -0,0 +1,60 @@ +import prisma from "@/lib/prisma"; + +export default async function lambangDesaFindById(request: Request) { + const url = new URL(request.url); + const pathSegments = url.pathname.split("/"); + const id = pathSegments[pathSegments.length - 1]; + + if (!id) { + return Response.json( + { + success: false, + message: "ID tidak boleh kosong", + }, + { status: 400 } + ); + } + + try { + if (typeof id !== "string") { + return Response.json( + { + success: false, + message: "ID tidak valid", + }, + { status: 400 } + ); + } + + const data = await prisma.lambangDesa.findUnique({ + where: { id }, + }); + + if (!data) { + return Response.json( + { + success: false, + message: "Data tidak ditemukan", + }, + { status: 404 } + ); + } + + return Response.json( + { + success: true, + data, + }, + { status: 200 } + ); + } catch (error) { + console.error("Gagal mengambil data lambang desa:", error); + return Response.json( + { + success: false, + message: "Terjadi kesalahan saat mengambil data", + }, + { status: 500 } + ); + } +} diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/index.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/index.ts new file mode 100644 index 00000000..dd959c73 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/index.ts @@ -0,0 +1,28 @@ +import Elysia, { t } from "elysia"; + +import lambangDesaFindById from "./find-by-id"; +import lambangDesaUpdate from "./update"; + +const LambangDesa = new Elysia({ + prefix: "/lambang", + tags: ["Desa/Profile"], +}) + .get("/:id", async (context) => { + const response = await lambangDesaFindById(new Request(context.request)); + return response; + }) + .put( + "/:id", + async (context) => { + const response = await lambangDesaUpdate(context); + return response; + }, + { + body: t.Object({ + judul: t.String(), + deskripsi: t.String(), + }), + } + ); + +export default LambangDesa; diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/update.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/update.ts new file mode 100644 index 00000000..6c2b353f --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambang-desa/update.ts @@ -0,0 +1,50 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function lambangDesaUpdate(context: Context) { + try { + const id = context.params?.id as string; + const body = await context.body as { + judul: string; + deskripsi: string; + }; + + if (!id) { + return new Response(JSON.stringify({ + success: false, + message: "ID tidak boleh kosong", + }), { status: 400 }); + } + + const existing = await prisma.lambangDesa.findUnique({ + where: { id }, + }); + + if (!existing) { + return new Response(JSON.stringify({ + success: false, + message: "Data tidak ditemukan", + }), { status: 404 }); + } + + const updated = await prisma.lambangDesa.update({ + where: { id }, + data: { + judul: body.judul, + deskripsi: body.deskripsi, + }, + }); + + return new Response(JSON.stringify({ + success: true, + message: "Berhasil memperbarui data", + data: updated, + }), { status: 200 }); + } catch (error) { + console.error("Update error:", error); + return new Response(JSON.stringify({ + success: false, + message: "Gagal memperbarui data: " + (error instanceof Error ? error.message : 'Unknown error'), + }), { status: 500 }); + } +} diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambangDesa/update.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambangDesa/update.ts deleted file mode 100644 index 34f1c992..00000000 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/lambangDesa/update.ts +++ /dev/null @@ -1,28 +0,0 @@ -import prisma from "@/lib/prisma"; -import { Prisma } from "@prisma/client"; -import { Context } from "elysia"; - -type FormCreate = Prisma.ProfileDesaGetPayload<{ - select: { - id: true; - lambang: true; - } -}> - -export default async function lambangDesaUpdate(context: Context) { - const body = context.body as FormCreate; - - await prisma.profileDesa.update({ - where: { - id: body.id - }, - data: { - lambang: body.lambang, - } - }) - - return { - success: true, - message: "Profile Desa Berhasil Diupdate", - } -} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/find-by-id.ts new file mode 100644 index 00000000..e16ddd7d --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/find-by-id.ts @@ -0,0 +1,53 @@ +import prisma from "@/lib/prisma"; + +export default async function maskotDesaFindById(request: Request){ + const url = new URL(request.url); + const pathSegments = url.pathname.split('/'); + const id = pathSegments[pathSegments.length - 1]; + + if (!id) { + return Response.json({ + success: false, + message: "ID tidak boleh kosong", + }, {status: 400}) + } + + try { + if (typeof id !== 'string') { + return Response.json({ + success: false, + message: "ID tidak valid", + }, {status: 400}) + } + + const data = await prisma.maskotDesa.findUnique({ + where: { id }, + include: { + images: { + include: { + image: true, + } + } + } + }) + + if(!data) { + return Response.json({ + success: false, + message: "Data tidak ditemukan", + }, {status: 404}) + } + + return Response.json({ + success: true, + message: "Berhasil mengambil data berdasarkan ID", + data, + }, {status: 200}) + } catch (error) { + console.error("Find by ID error:", error); + return Response.json({ + success: false, + message: "Gagal mengambil data: " + (error instanceof Error ? error.message : 'Unknown error'), + }, {status: 500}) + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/index.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/index.ts new file mode 100644 index 00000000..878d9f8c --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/index.ts @@ -0,0 +1,31 @@ +import maskotDesaUpdate from "./update"; +import maskotDesaFindById from "./find-by-id"; +import Elysia, { t } from "elysia"; + +const MaskotDesa = new Elysia({ + prefix: "/maskot", + tags: ["Desa/Profile"], +}) +.get("/:id", async (context) => { + const response = await maskotDesaFindById(new Request(context.request)); + return response; + }) + .put( + "/:id", + async (context) => { + const response = await maskotDesaUpdate(context); + return response; + }, + { + body: t.Object({ + maskot: t.String(), + images: t.Array( + t.Object({ + imageId: t.String(), + label: t.String(), + }) + ), + }), + } + ) +export default MaskotDesa; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/update.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/update.ts new file mode 100644 index 00000000..161a3a93 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskot-desa/update.ts @@ -0,0 +1,78 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function maskotDesaUpdate(context: Context) { + try { + const id = context.params?.id as string; + const body = await context.body as { + judul: string; + deskripsi: string; + images: { label: string; imageId: string }[]; + }; + + if (!id) { + return new Response(JSON.stringify({ + success: false, + message: "ID tidak boleh kosong", + }), { status: 400 }); + } + + const existing = await prisma.maskotDesa.findUnique({ + where: { id }, + include: { images: { include: { image: true } } } + }); + + if (!existing) { + return new Response(JSON.stringify({ + success: false, + message: "Data tidak ditemukan", + }), { status: 404 }); + } + + // Hapus semua gambar lama (dan file-nya jika perlu) + for (const old of existing.images) { + try { + await prisma.fileStorage.delete({ where: { id: old.imageId } }); + // opsional: hapus file dari disk juga kalau kamu simpan file fisik + // await fs.unlink(path.join(old.image.path, old.image.name)); + } catch (error) { + console.warn("Gagal hapus gambar lama:", error); + } + } + + // Update profile & re-create images + const updated = await prisma.maskotDesa.update({ + where: { id }, + data: { + judul: body.judul, + deskripsi: body.deskripsi, + images: { + deleteMany: {}, + create: body.images.map((img) => ({ + label: img.label, + imageId: img.imageId + })) + } + }, + include: { + images: { + include: { + image: true + } + } + } + }); + + return new Response(JSON.stringify({ + success: true, + message: "Data berhasil diperbarui", + data: updated, + }), { status: 200 }); + } catch (error) { + console.error("Gagal update MaskotDesa:", error); + return new Response(JSON.stringify({ + success: false, + message: "Terjadi kesalahan saat update", + }), { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskotDesa/update.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskotDesa/update.ts deleted file mode 100644 index a94ff33c..00000000 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/maskotDesa/update.ts +++ /dev/null @@ -1,29 +0,0 @@ -import prisma from "@/lib/prisma"; -import { Prisma } from "@prisma/client"; -import { Context } from "elysia"; - -type FormCreate = Prisma.ProfileDesaGetPayload<{ - select: { - id: true; - maskot: true; - } -}> - -export default async function maskotDesaUpdate(context: Context) { - const body = context.body as FormCreate; - - await prisma.profileDesa.update({ - where: { - id: body.id - }, - data: { - maskot: body.maskot, - } - }) - - return { - success: true, - message: "Profile Desa Berhasil Diupdate", - } -} - \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/find-by-id.ts new file mode 100644 index 00000000..cc8b5890 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/find-by-id.ts @@ -0,0 +1,45 @@ +import prisma from "@/lib/prisma"; + +export default async function sejarahDesaFindById(request: Request) { + const url = new URL(request.url); + const pathSegments = url.pathname.split('/'); + const id = pathSegments[pathSegments.length - 1]; + + if (!id) { + return Response.json({ + success: false, + message: "ID tidak boleh kosong", + }, {status: 400}) + } + + try { + if (typeof id !== 'string') { + return Response.json({ + success: false, + message: "ID tidak valid", + }, {status: 400}) + } + + const data = await prisma.sejarahDesa.findUnique({ + where: { id }, + }) + + if (!data) { + return Response.json({ + success: false, + message: "Data tidak ditemukan", + }, {status: 404}) + } + + return Response.json({ + success: true, + data, + }, {status: 200}) + } catch (error) { + console.error("Gagal mengambil data sejarah desa:", error) + return Response.json({ + success: false, + message: "Terjadi kesalahan saat mengambil data", + }, {status: 500}) + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/index.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/index.ts new file mode 100644 index 00000000..bb972b6d --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/index.ts @@ -0,0 +1,27 @@ +import Elysia, { t } from "elysia"; +import sejarahDesaFindById from "./find-by-id"; +import sejarahDesaUpdate from "./update"; + +const SejarahDesa = new Elysia({ + prefix: "/sejarah", + tags: ["Desa/Profile"], +}) + .get("/:id", async (context) => { + const response = await sejarahDesaFindById(new Request(context.request)); + return response; + }) + .put( + "/:id", + async (context) => { + const response = await sejarahDesaUpdate(context); + return response; + }, + { + body: t.Object({ + judul: t.String(), + deskripsi: t.String(), + }), + } + ); + +export default SejarahDesa; diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/update.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/update.ts index 316ebc6f..32958feb 100644 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/update.ts +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/update.ts @@ -1,29 +1,50 @@ import prisma from "@/lib/prisma"; -import { Prisma } from "@prisma/client"; import { Context } from "elysia"; -type FormCreate = Prisma.ProfileDesaGetPayload<{ - select: { - id: true; - sejarah: true; - } -}> - export default async function sejarahDesaUpdate(context: Context) { - const body = context.body as FormCreate; + try { + const id = context.params?.id as string; + const body = await context.body as { + judul: string; + deskripsi: string; + }; - await prisma.profileDesa.update({ - where: { - id: body.id - }, - data: { - sejarah: body.sejarah, + if (!id) { + return new Response(JSON.stringify({ + success: false, + message: "ID tidak boleh kosong", + }), { status: 400 }); } - }) - return { - success: true, - message: "Profile Desa Berhasil Diupdate", + const existing = await prisma.sejarahDesa.findUnique({ + where: { id }, + }); + + if (!existing) { + return new Response(JSON.stringify({ + success: false, + message: "Data tidak ditemukan", + }), { status: 404 }); + } + + const updated = await prisma.sejarahDesa.update({ + where: { id }, + data: { + judul: body.judul, + deskripsi: body.deskripsi, + }, + }); + + return new Response(JSON.stringify({ + success: true, + message: "Berhasil memperbarui data", + data: updated, + }), { status: 200 }); + } catch (error) { + console.error("Update error:", error); + return new Response(JSON.stringify({ + success: false, + message: "Gagal memperbarui data: " + (error instanceof Error ? error.message : 'Unknown error'), + }), { status: 500 }); } -} - \ No newline at end of file +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/find-by-id.ts new file mode 100644 index 00000000..ff0f4c00 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/find-by-id.ts @@ -0,0 +1,46 @@ +import prisma from "@/lib/prisma"; + +export default async function visiMisiDesaFindById(request: Request) { + const url = new URL(request.url); + const pathSegments = url.pathname.split('/'); + const id = pathSegments[pathSegments.length - 1]; + + if (!id) { + return Response.json({ + success: false, + message: "ID tidak boleh kosong", + }, {status: 400}) + } + + try { + if (typeof id !== 'string') { + return Response.json({ + success: false, + message: "ID tidak valid", + }, {status: 400}) + } + + const data = await prisma.visiMisiDesa.findUnique({ + where: { id }, + }) + + if (!data) { + return Response.json({ + success: false, + message: "Data tidak ditemukan", + }, {status: 404}) + } + + return Response.json({ + success: true, + message: "Data ditemukan", + data: data, + }, {status: 200}) + } catch (error) { + console.error("Find by ID error:", error); + return Response.json({ + success: false, + message: "Gagal menemukan data: " + (error instanceof Error ? error.message : 'Unknown error'), + }, {status: 500}) + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/index.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/index.ts new file mode 100644 index 00000000..0f6b0c8e --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/index.ts @@ -0,0 +1,27 @@ +import Elysia, { t } from "elysia"; +import visiMisiDesaUpdate from "./update"; +import visiMisiDesaFindById from "./find-by-id"; + +const VisiMisiDesa = new Elysia({ + prefix: "/visi-misi", + tags: ["Desa/Profile"], +}) + .get("/:id", async (context) => { + const response = await visiMisiDesaFindById(new Request(context.request)); + return response; + }) + .put( + "/:id", + async (context) => { + const response = await visiMisiDesaUpdate(context); + return response; + }, + { + body: t.Object({ + visi: t.String(), + misi: t.String(), + }), + } + ); + +export default VisiMisiDesa; diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/update.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/update.ts new file mode 100644 index 00000000..442aff8e --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visi-misi/update.ts @@ -0,0 +1,50 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +export default async function visiMisiDesaUpdate(context: Context) { + try { + const id = context.params?.id as string; + const body = await context.body as { + visi: string; + misi: string; + }; + + if (!id) { + return new Response(JSON.stringify({ + success: false, + message: "ID tidak boleh kosong", + }), { status: 400 }); + } + + const existing = await prisma.visiMisiDesa.findUnique({ + where: { id }, + }); + + if (!existing) { + return new Response(JSON.stringify({ + success: false, + message: "Data tidak ditemukan", + }), { status: 404 }); + } + + const updated = await prisma.visiMisiDesa.update({ + where: { id }, + data: { + visi: body.visi, + misi: body.misi, + }, + }); + + return new Response(JSON.stringify({ + success: true, + message: "Berhasil memperbarui data", + data: updated, + }), { status: 200 }); + } catch (error) { + console.error("Update error:", error); + return new Response(JSON.stringify({ + success: false, + message: "Gagal memperbarui data: " + (error instanceof Error ? error.message : 'Unknown error'), + }), { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visimisiDesa/update.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visimisiDesa/update.ts deleted file mode 100644 index a7dedf4d..00000000 --- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/visimisiDesa/update.ts +++ /dev/null @@ -1,29 +0,0 @@ -import prisma from "@/lib/prisma"; -import { Prisma } from "@prisma/client"; -import { Context } from "elysia"; - -type FormCreate = Prisma.ProfileDesaGetPayload<{ - select: { - id: true; - visi: true; - misi: true; - } -}> -export default async function visimisiDesaUpdate(context: Context) { - const body = context.body as FormCreate; - - await prisma.profileDesa.update({ - where: { - id: body.id - }, - data: { - visi: body.visi, - misi: body.misi, - } - }) - - return { - success: true, - message: "Profile Desa Berhasil Diupdate", - } -}