Compare commits
22 Commits
nico/26-ma
...
nico/12-ju
| Author | SHA1 | Date | |
|---|---|---|---|
| 1462e1d256 | |||
| a1c2821153 | |||
| 9f66b037f9 | |||
| 9e725e2eea | |||
| e4b7418ed3 | |||
| 6d312b7a28 | |||
| 41ae92774d | |||
| 46748205fd | |||
| 5e74447056 | |||
| c9d0ea2a97 | |||
| 7d58513e33 | |||
| f56c5b3532 | |||
| 06622c49e8 | |||
| a1e7fddbed | |||
| 423ad0e2ba | |||
| 084435500f | |||
| 5037009c40 | |||
| 8f2b9665a9 | |||
| 77f99a7c8f | |||
| d88f168258 | |||
| f9bd2cea11 | |||
| 5734e5d9a7 |
@@ -24,6 +24,7 @@
|
|||||||
"@mantine/core": "^7.17.4",
|
"@mantine/core": "^7.17.4",
|
||||||
"@mantine/dates": "^7.17.4",
|
"@mantine/dates": "^7.17.4",
|
||||||
"@mantine/dropzone": "^7.17.0",
|
"@mantine/dropzone": "^7.17.0",
|
||||||
|
"@mantine/form": "^8.1.0",
|
||||||
"@mantine/hooks": "^7.17.4",
|
"@mantine/hooks": "^7.17.4",
|
||||||
"@mantine/tiptap": "^7.17.4",
|
"@mantine/tiptap": "^7.17.4",
|
||||||
"@paljs/types": "^8.1.0",
|
"@paljs/types": "^8.1.0",
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
"biodata": "<p>I.B Surya Prabhawa Manuaba, S.H., M.H., adalah Perbekel Darmasaba periode 2021-2027, seorang advokat, pendiri Mantra Legal Consultants & Advocates, serta aktif di bidang musik dan akademis. Dia menempuh pendidikan hukum di Universitas Udayana dan Universitas Mahasaraswati Denpasar, serta memiliki pengalaman luas di berbagai organisasi dan kepemimpinan.</p>",
|
"biodata": "<p>I.B Surya Prabhawa Manuaba, S.H., M.H., adalah Perbekel Darmasaba periode 2021-2027, seorang advokat, pendiri Mantra Legal Consultants & Advocates, serta aktif di bidang musik dan akademis. Dia menempuh pendidikan hukum di Universitas Udayana dan Universitas Mahasaraswati Denpasar, serta memiliki pengalaman luas di berbagai organisasi dan kepemimpinan.</p>",
|
||||||
"riwayat": "<ul> <li>2021 - 2027: Perbekel Desa Darmasaba</li> <li>2015 - Sekarang: Founder & Managing Director Mantra Legal Consultants & Advocates</li> <li>2020 - Sekarang: Founder Ugawa Record Music Studio</li> <li>2010 - 2016: Dosen Fakultas Hukum Universitas Mahasaraswati Denpasar</li> </ul>",
|
"riwayat": "<ul> <li>2021 - 2027: Perbekel Desa Darmasaba</li> <li>2015 - Sekarang: Founder & Managing Director Mantra Legal Consultants & Advocates</li> <li>2020 - Sekarang: Founder Ugawa Record Music Studio</li> <li>2010 - 2016: Dosen Fakultas Hukum Universitas Mahasaraswati Denpasar</li> </ul>",
|
||||||
"pengalaman": "<ul> <li>1996 – 1997: Ketua OSIS SMP Negeri 1 Abiansemal</li><li>1999 – 2000: Ketua OSIS SMA Negeri 1 Mengwi</li> <li>2008 – 2009: Ketua BEM Universitas Mahasaraswati Denpasar</li> <li>2008 – 2010: Ketua Sekaa Taruna Sila Dharma, Banjar Tengah, Desa Adat Tegal, Darmasaba</li> <li>2020 – Sekarang: Pengurus Young Lawyer Committee Peradi Denpasar</li> <li>2021 – Sekarang: Dewan Kehormatan Himpunan Pengusaha Muda Indonesia (HIPMI) Badung</li> <li>2023 – 2028: Komite Tetap Advokasi – Bidang Hukum dan Regulasi Kamar Dagang dan Industri Badung</li> </ul>",
|
"pengalaman": "<ul> <li>1996 – 1997: Ketua OSIS SMP Negeri 1 Abiansemal</li><li>1999 – 2000: Ketua OSIS SMA Negeri 1 Mengwi</li> <li>2008 – 2009: Ketua BEM Universitas Mahasaraswati Denpasar</li> <li>2008 – 2010: Ketua Sekaa Taruna Sila Dharma, Banjar Tengah, Desa Adat Tegal, Darmasaba</li> <li>2020 – Sekarang: Pengurus Young Lawyer Committee Peradi Denpasar</li> <li>2021 – Sekarang: Dewan Kehormatan Himpunan Pengusaha Muda Indonesia (HIPMI) Badung</li> <li>2023 – 2028: Komite Tetap Advokasi – Bidang Hukum dan Regulasi Kamar Dagang dan Industri Badung</li> </ul>",
|
||||||
"unggulan": "<h3>Pemberdayaan Ekonomi dan UMKM</h3> <ul> <li>Pelatihan dan pendampingan UMKM lokal</li> <li>Program bantuan modal usaha bagi pelaku usaha kecil</li><li>Digitalisasi UMKM untuk meningkatkan pemasaran produk lokal</li></ul>",
|
"unggulan": "<h3>Pemberdayaan Ekonomi dan UMKM</h3> <ul> <li>Pelatihan dan pendampingan UMKM lokal</li> <li>Program bantuan modal usaha bagi pelaku usaha kecil</li><li>Digitalisasi UMKM untuk meningkatkan pemasaran produk lokal</li></ul>"
|
||||||
"imageUrl": "/uploads/seeded-images/profile-ppid/perbekel.png"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
6
prisma/data/ppid/struktur-ppid/strukturPPID.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id" : "1",
|
||||||
|
"name" : "Struktur PPID"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,582 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `image` on the `Berita` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `katagoryBeritaId` on the `Berita` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `name` on the `FasilitasPendukung` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `fasilitasKesehatanId` on the `InformasiUmum` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the `KatagoryBerita` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
- Added the required column `imageId` to the `Berita` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `jadwal` to the `DokterdanTenagaMedis` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `specialist` to the `DokterdanTenagaMedis` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `content` to the `FasilitasPendukung` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "Berita" DROP CONSTRAINT "Berita_katagoryBeritaId_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "InformasiUmum" DROP CONSTRAINT "InformasiUmum_fasilitasKesehatanId_fkey";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Berita" DROP COLUMN "image",
|
||||||
|
DROP COLUMN "katagoryBeritaId",
|
||||||
|
ADD COLUMN "imageId" TEXT NOT NULL,
|
||||||
|
ADD COLUMN "kategoriBeritaId" TEXT;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "DataKematian_Kelahiran" ADD COLUMN "deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ADD COLUMN "isActive" BOOLEAN NOT NULL DEFAULT true;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "DokterdanTenagaMedis" ADD COLUMN "jadwal" TEXT NOT NULL,
|
||||||
|
ADD COLUMN "specialist" TEXT NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "FasilitasPendukung" DROP COLUMN "name",
|
||||||
|
ADD COLUMN "content" TEXT NOT NULL;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "InformasiUmum" DROP COLUMN "fasilitasKesehatanId";
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "KatagoryBerita";
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "FileStorage" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"realName" TEXT NOT NULL,
|
||||||
|
"path" TEXT NOT NULL,
|
||||||
|
"mimeType" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3),
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"link" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "FileStorage_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "VisiMisiPPID" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"visi" TEXT NOT NULL,
|
||||||
|
"misi" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "VisiMisiPPID_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "DasarHukumPPID" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"judul" TEXT NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "DasarHukumPPID_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ProfilePPID" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"biodata" TEXT NOT NULL,
|
||||||
|
"riwayat" TEXT NOT NULL,
|
||||||
|
"pengalaman" TEXT NOT NULL,
|
||||||
|
"unggulan" TEXT NOT NULL,
|
||||||
|
"imageUrl" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "ProfilePPID_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "DaftarInformasiPublik" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"nomor" SERIAL NOT NULL,
|
||||||
|
"jenisInformasi" TEXT NOT NULL,
|
||||||
|
"deskripsi" TEXT NOT NULL,
|
||||||
|
"tanggal" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "DaftarInformasiPublik_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "PermohonanInformasiPublik" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"nomor" SERIAL NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"nik" TEXT NOT NULL,
|
||||||
|
"notelp" TEXT NOT NULL,
|
||||||
|
"alamat" TEXT NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"jenisInformasiDimintaId" TEXT,
|
||||||
|
"caraMemperolehInformasiId" TEXT,
|
||||||
|
"caraMemperolehSalinanInformasiId" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "PermohonanInformasiPublik_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "JenisInformasiDiminta" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "JenisInformasiDiminta_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "CaraMemperolehInformasi" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "CaraMemperolehInformasi_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "CaraMemperolehSalinanInformasi" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "CaraMemperolehSalinanInformasi_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "FormulirPermohonanKeberatan" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"notelp" TEXT NOT NULL,
|
||||||
|
"alasan" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "FormulirPermohonanKeberatan_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "IndeksKepuasanMasyarakat" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"label" TEXT NOT NULL,
|
||||||
|
"kepuasan" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "IndeksKepuasanMasyarakat_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "GrafikBerdasarkanJenisKelamin" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"perempuan" TEXT NOT NULL,
|
||||||
|
"laki" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "GrafikBerdasarkanJenisKelamin_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "GrafikBerdasarkanResponden" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"sangatbaik" TEXT NOT NULL,
|
||||||
|
"baik" TEXT NOT NULL,
|
||||||
|
"kurangbaik" TEXT NOT NULL,
|
||||||
|
"tidakbaik" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "GrafikBerdasarkanResponden_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "GrafikBerdasarkanUmur" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"remaja" TEXT NOT NULL,
|
||||||
|
"dewasa" TEXT NOT NULL,
|
||||||
|
"orangtua" TEXT NOT NULL,
|
||||||
|
"lansia" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "GrafikBerdasarkanUmur_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ProfileDesa" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"sejarah" TEXT NOT NULL,
|
||||||
|
"visi" TEXT NOT NULL,
|
||||||
|
"misi" TEXT NOT NULL,
|
||||||
|
"lambang" TEXT NOT NULL,
|
||||||
|
"maskot" TEXT NOT NULL,
|
||||||
|
"profilPerbekelId" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "ProfileDesa_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ProfilPerbekel" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"biodata" TEXT NOT NULL,
|
||||||
|
"pengalaman" TEXT NOT NULL,
|
||||||
|
"pengalamanOrganisasi" TEXT NOT NULL,
|
||||||
|
"programUnggulan" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "ProfilPerbekel_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "KategoriBerita" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "KategoriBerita_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "PotensiDesa" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"deskripsi" TEXT NOT NULL,
|
||||||
|
"kategori" TEXT NOT NULL,
|
||||||
|
"imageId" TEXT NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "PotensiDesa_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "TarifDanLayanan" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"layanan" TEXT NOT NULL,
|
||||||
|
"tarif" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "TarifDanLayanan_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "JadwalKegiatan" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "JadwalKegiatan_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "InformasiJadwalKegiatan" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"tanggal" TEXT NOT NULL,
|
||||||
|
"waktu" TEXT NOT NULL,
|
||||||
|
"lokasi" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "InformasiJadwalKegiatan_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "DeskripsiJadwalKegiatan" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"deskripsi" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "DeskripsiJadwalKegiatan_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "LayananJadwalKegiatan" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "LayananJadwalKegiatan_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "SyaratKetentuanJadwalKegiatan" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "SyaratKetentuanJadwalKegiatan_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "DokumenJadwalKegiatan" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "DokumenJadwalKegiatan_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "PendaftaranJadwalKegiatan" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"tanggal" TEXT NOT NULL,
|
||||||
|
"namaOrangtua" TEXT NOT NULL,
|
||||||
|
"nomor" TEXT NOT NULL,
|
||||||
|
"alamat" TEXT NOT NULL,
|
||||||
|
"catatan" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "PendaftaranJadwalKegiatan_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "GrafikKepuasan" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"label" TEXT NOT NULL,
|
||||||
|
"jumlah" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "GrafikKepuasan_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "ArtikelKesehatan" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"title" TEXT NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "ArtikelKesehatan_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Introduction" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "Introduction_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Symptom" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"title" TEXT NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "Symptom_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Prevention" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"title" TEXT NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "Prevention_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "FirstAid" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"title" TEXT NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "FirstAid_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "MythVsFact" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"title" TEXT NOT NULL,
|
||||||
|
"mitos" TEXT NOT NULL,
|
||||||
|
"fakta" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "MythVsFact_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "DoctorSign" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"content" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"deletedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
|
||||||
|
CONSTRAINT "DoctorSign_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "_FasilitasKesehatanToInformasiUmum" (
|
||||||
|
"A" TEXT NOT NULL,
|
||||||
|
"B" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "_FasilitasKesehatanToInformasiUmum_AB_pkey" PRIMARY KEY ("A","B")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "_FasilitasKesehatanToTarifDanLayanan" (
|
||||||
|
"A" TEXT NOT NULL,
|
||||||
|
"B" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "_FasilitasKesehatanToTarifDanLayanan_AB_pkey" PRIMARY KEY ("A","B")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "FileStorage_name_key" ON "FileStorage"("name");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "JenisInformasiDiminta_name_key" ON "JenisInformasiDiminta"("name");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "CaraMemperolehInformasi_name_key" ON "CaraMemperolehInformasi"("name");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "CaraMemperolehSalinanInformasi_name_key" ON "CaraMemperolehSalinanInformasi"("name");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "KategoriBerita_name_key" ON "KategoriBerita"("name");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "_FasilitasKesehatanToInformasiUmum_B_index" ON "_FasilitasKesehatanToInformasiUmum"("B");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "_FasilitasKesehatanToTarifDanLayanan_B_index" ON "_FasilitasKesehatanToTarifDanLayanan"("B");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "PermohonanInformasiPublik" ADD CONSTRAINT "PermohonanInformasiPublik_jenisInformasiDimintaId_fkey" FOREIGN KEY ("jenisInformasiDimintaId") REFERENCES "JenisInformasiDiminta"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "PermohonanInformasiPublik" ADD CONSTRAINT "PermohonanInformasiPublik_caraMemperolehInformasiId_fkey" FOREIGN KEY ("caraMemperolehInformasiId") REFERENCES "CaraMemperolehInformasi"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "PermohonanInformasiPublik" ADD CONSTRAINT "PermohonanInformasiPublik_caraMemperolehSalinanInformasiId_fkey" FOREIGN KEY ("caraMemperolehSalinanInformasiId") REFERENCES "CaraMemperolehSalinanInformasi"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "ProfileDesa" ADD CONSTRAINT "ProfileDesa_profilPerbekelId_fkey" FOREIGN KEY ("profilPerbekelId") REFERENCES "ProfilPerbekel"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Berita" ADD CONSTRAINT "Berita_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "Berita" ADD CONSTRAINT "Berita_kategoriBeritaId_fkey" FOREIGN KEY ("kategoriBeritaId") REFERENCES "KategoriBerita"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "PotensiDesa" ADD CONSTRAINT "PotensiDesa_imageId_fkey" FOREIGN KEY ("imageId") REFERENCES "FileStorage"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "_FasilitasKesehatanToInformasiUmum" ADD CONSTRAINT "_FasilitasKesehatanToInformasiUmum_A_fkey" FOREIGN KEY ("A") REFERENCES "FasilitasKesehatan"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "_FasilitasKesehatanToInformasiUmum" ADD CONSTRAINT "_FasilitasKesehatanToInformasiUmum_B_fkey" FOREIGN KEY ("B") REFERENCES "InformasiUmum"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "_FasilitasKesehatanToTarifDanLayanan" ADD CONSTRAINT "_FasilitasKesehatanToTarifDanLayanan_A_fkey" FOREIGN KEY ("A") REFERENCES "FasilitasKesehatan"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "_FasilitasKesehatanToTarifDanLayanan" ADD CONSTRAINT "_FasilitasKesehatanToTarifDanLayanan_B_fkey" FOREIGN KEY ("B") REFERENCES "TarifDanLayanan"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
@@ -47,7 +47,39 @@ model AppMenuChild {
|
|||||||
appMenuId String?
|
appMenuId String?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================= FILE STORAGE ========================================= //
|
||||||
|
|
||||||
|
model FileStorage {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String @unique
|
||||||
|
realName String
|
||||||
|
path String
|
||||||
|
mimeType String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime?
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
link String
|
||||||
|
Berita Berita[]
|
||||||
|
PotensiDesa PotensiDesa[]
|
||||||
|
Posyandu Posyandu[]
|
||||||
|
ProfilePPID ProfilePPID[]
|
||||||
|
StrukturPPID StrukturPPID[]
|
||||||
|
}
|
||||||
|
|
||||||
//========================================= MENU PPID ========================================= //
|
//========================================= MENU PPID ========================================= //
|
||||||
|
|
||||||
|
//========================================= STRUKTUR PPID ========================================= //
|
||||||
|
model StrukturPPID {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name 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)
|
||||||
|
}
|
||||||
// ========================================= VISI MISI PPID ========================================= //
|
// ========================================= VISI MISI PPID ========================================= //
|
||||||
model VisiMisiPPID {
|
model VisiMisiPPID {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
@@ -72,23 +104,23 @@ model DasarHukumPPID {
|
|||||||
|
|
||||||
// ========================================= PROFILE PPID ========================================= //
|
// ========================================= PROFILE PPID ========================================= //
|
||||||
model ProfilePPID {
|
model ProfilePPID {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String @db.Text
|
name String @db.Text
|
||||||
biodata String @db.Text
|
biodata String @db.Text
|
||||||
riwayat String @db.Text
|
riwayat String @db.Text
|
||||||
pengalaman String @db.Text
|
pengalaman String @db.Text
|
||||||
unggulan String @db.Text
|
unggulan String @db.Text
|
||||||
imageUrl String?
|
image FileStorage? @relation(fields: [imageId], references: [id])
|
||||||
createdAt DateTime @default(now())
|
imageId String?
|
||||||
updatedAt DateTime @updatedAt
|
createdAt DateTime @default(now())
|
||||||
deletedAt DateTime @default(now())
|
updatedAt DateTime @updatedAt
|
||||||
isActive Boolean @default(true)
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================= DAFTAR INFORMASI PUBLIK ========================================= //
|
// ========================================= DAFTAR INFORMASI PUBLIK ========================================= //
|
||||||
model DaftarInformasiPublik {
|
model DaftarInformasiPublik {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
nomor Int @default(autoincrement())
|
|
||||||
jenisInformasi String
|
jenisInformasi String
|
||||||
deskripsi String
|
deskripsi String
|
||||||
tanggal String
|
tanggal String
|
||||||
@@ -164,7 +196,7 @@ model FormulirPermohonanKeberatan {
|
|||||||
|
|
||||||
// ========================================= IKM ========================================= //
|
// ========================================= IKM ========================================= //
|
||||||
model IndeksKepuasanMasyarakat {
|
model IndeksKepuasanMasyarakat {
|
||||||
id Int @id @default(autoincrement())
|
id String @id @default(cuid())
|
||||||
label String
|
label String
|
||||||
kepuasan String
|
kepuasan String
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
@@ -263,6 +295,21 @@ model KategoriBerita {
|
|||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================= POTENSI DESA ========================================= //
|
||||||
|
model PotensiDesa {
|
||||||
|
id String @id @default(cuid())
|
||||||
|
name String
|
||||||
|
deskripsi String
|
||||||
|
kategori String
|
||||||
|
image FileStorage @relation(fields: [imageId], references: [id])
|
||||||
|
imageId String
|
||||||
|
content String @db.Text
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
deletedAt DateTime @default(now())
|
||||||
|
isActive Boolean @default(true)
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================= PENGUMUMAN ========================================= //
|
// ========================================= PENGUMUMAN ========================================= //
|
||||||
model Pengumuman {
|
model Pengumuman {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
@@ -584,18 +631,16 @@ model DoctorSign {
|
|||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// === BARU
|
// ========================================= POSYANDU ========================================= //
|
||||||
|
model Posyandu {
|
||||||
model FileStorage {
|
id String @id @default(cuid())
|
||||||
id String @id @default(cuid())
|
name String
|
||||||
name String @unique
|
nomor String
|
||||||
realName String
|
deskripsi String
|
||||||
path String
|
image FileStorage @relation(fields: [imageId], references: [id])
|
||||||
mimeType String
|
imageId String
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime?
|
deletedAt DateTime @default(now())
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
link String
|
|
||||||
Berita Berita[]
|
|
||||||
}
|
}
|
||||||
|
|||||||
144
prisma/seed.ts
@@ -6,15 +6,10 @@ import caraMemperolehSalinanInformasi from "./data/list-caraMemperolehSalinanInf
|
|||||||
import jenisInformasiDiminta from "./data/list-jenisInfromasi.json";
|
import jenisInformasiDiminta from "./data/list-jenisInfromasi.json";
|
||||||
import layanan from "./data/list-layanan.json";
|
import layanan from "./data/list-layanan.json";
|
||||||
import potensi from "./data/list-potensi.json";
|
import potensi from "./data/list-potensi.json";
|
||||||
import visiMisiPPID from "./data/ppid/visi-misi-ppid/visimisiPPID.json";
|
|
||||||
import dasarHukumPPID from "./data/ppid/dasar-hukum-ppid/dasarhukumPPID.json";
|
import dasarHukumPPID from "./data/ppid/dasar-hukum-ppid/dasarhukumPPID.json";
|
||||||
import profileDesa from "./data/desa/profile/profile_desa.json";
|
|
||||||
import profilePerbekel from "./data/desa/profile/profil_perbekel.json";
|
|
||||||
import profilePPID from "./data/ppid/profile-ppid/profilePPid.json";
|
import profilePPID from "./data/ppid/profile-ppid/profilePPid.json";
|
||||||
import path from "path";
|
import visiMisiPPID from "./data/ppid/visi-misi-ppid/visimisiPPID.json";
|
||||||
import fs from "fs";
|
import strukturPPID from "./data/ppid/struktur-ppid/strukturPPID.json";
|
||||||
import { mkdir, writeFile } from "fs/promises";
|
|
||||||
import { v4 as uuid } from "uuid";
|
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
for (const l of layanan) {
|
for (const l of layanan) {
|
||||||
@@ -33,6 +28,22 @@ import { v4 as uuid } from "uuid";
|
|||||||
|
|
||||||
console.log("layanan success ...");
|
console.log("layanan success ...");
|
||||||
|
|
||||||
|
for (const s of strukturPPID) {
|
||||||
|
await prisma.strukturPPID.upsert({
|
||||||
|
where: {
|
||||||
|
id: s.id,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: s.name,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: s.id,
|
||||||
|
name: s.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("struktur ppid success ...");
|
||||||
|
|
||||||
for (const p of potensi) {
|
for (const p of potensi) {
|
||||||
await prisma.potensi.upsert({
|
await prisma.potensi.upsert({
|
||||||
where: {
|
where: {
|
||||||
@@ -126,56 +137,29 @@ import { v4 as uuid } from "uuid";
|
|||||||
}
|
}
|
||||||
console.log("cara memperoleh salinan informasi success ...");
|
console.log("cara memperoleh salinan informasi success ...");
|
||||||
|
|
||||||
const seedProfilePPID = async () => {
|
for (const c of profilePPID) {
|
||||||
const targetDir = path.resolve("public", "uploads", "seeded-images", "profile-ppid")
|
await prisma.profilePPID.upsert({
|
||||||
|
where: { id: c.id },
|
||||||
// Buat folder hanya jika belum ada
|
update: {
|
||||||
if (!fs.existsSync(targetDir)) {
|
name: c.name,
|
||||||
await mkdir(targetDir, { recursive: true })
|
biodata: c.biodata,
|
||||||
}
|
riwayat: c.riwayat,
|
||||||
|
pengalaman: c.pengalaman,
|
||||||
for (const c of profilePPID) {
|
unggulan: c.unggulan,
|
||||||
let finalImageUrl = c.imageUrl
|
// imageId tidak di-update
|
||||||
|
},
|
||||||
if (c.imageUrl.startsWith("/uploads/seeded-images/")) {
|
create: {
|
||||||
const filename = path.basename(c.imageUrl)
|
id: c.id,
|
||||||
const seedImagePath = path.resolve("prisma", "seed-images", filename)
|
name: c.name,
|
||||||
|
biodata: c.biodata,
|
||||||
const targetFilename = `${uuid()}_${filename}`
|
riwayat: c.riwayat,
|
||||||
const targetPath = path.join(targetDir, targetFilename)
|
pengalaman: c.pengalaman,
|
||||||
|
unggulan: c.unggulan,
|
||||||
const buffer = fs.readFileSync(seedImagePath)
|
// imageId tidak di-create
|
||||||
await writeFile(targetPath, buffer)
|
},
|
||||||
|
});
|
||||||
finalImageUrl = `/uploads/seeded-images/profile-ppid/${targetFilename}`
|
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.profilePPID.upsert({
|
|
||||||
where: { id: c.id },
|
|
||||||
update: {
|
|
||||||
name: c.name,
|
|
||||||
biodata: c.biodata,
|
|
||||||
riwayat: c.riwayat,
|
|
||||||
pengalaman: c.pengalaman,
|
|
||||||
unggulan: c.unggulan,
|
|
||||||
imageUrl: finalImageUrl,
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
id: c.id,
|
|
||||||
name: c.name,
|
|
||||||
biodata: c.biodata,
|
|
||||||
riwayat: c.riwayat,
|
|
||||||
pengalaman: c.pengalaman,
|
|
||||||
unggulan: c.unggulan,
|
|
||||||
imageUrl: finalImageUrl,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("✅ profilePPID seeded from JSON with image copying")
|
|
||||||
}
|
}
|
||||||
|
console.log("✅ profilePPID seeded without imageId (editable later via UI)");
|
||||||
await seedProfilePPID()
|
|
||||||
|
|
||||||
for (const v of visiMisiPPID) {
|
for (const v of visiMisiPPID) {
|
||||||
await prisma.visiMisiPPID.upsert({
|
await prisma.visiMisiPPID.upsert({
|
||||||
@@ -212,54 +196,6 @@ import { v4 as uuid } from "uuid";
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log("dasar hukum PPID success ...");
|
console.log("dasar hukum PPID success ...");
|
||||||
|
|
||||||
for (const v of profileDesa) {
|
|
||||||
await prisma.profileDesa.upsert({
|
|
||||||
where: {
|
|
||||||
id: v.id,
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
sejarah: v.sejarah,
|
|
||||||
visi: v.visi,
|
|
||||||
misi: v.misi,
|
|
||||||
lambang: v.lambang,
|
|
||||||
maskot: v.maskot,
|
|
||||||
profilPerbekelId: v.profilPerbekelId,
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
id: v.id,
|
|
||||||
sejarah: v.sejarah,
|
|
||||||
visi: v.visi,
|
|
||||||
misi: v.misi,
|
|
||||||
lambang: v.lambang,
|
|
||||||
maskot: v.maskot,
|
|
||||||
profilPerbekelId: v.profilPerbekelId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
console.log("profile desa success ...");
|
|
||||||
|
|
||||||
for (const v of profilePerbekel) {
|
|
||||||
await prisma.profilPerbekel.upsert({
|
|
||||||
where: {
|
|
||||||
id: v.id,
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
biodata: v.biodata,
|
|
||||||
pengalaman: v.pengalaman,
|
|
||||||
pengalamanOrganisasi: v.pengalamanOrganisasi,
|
|
||||||
programUnggulan: v.programUnggulan,
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
id: v.id,
|
|
||||||
biodata: v.biodata,
|
|
||||||
pengalaman: v.pengalaman,
|
|
||||||
pengalamanOrganisasi: v.pengalamanOrganisasi,
|
|
||||||
programUnggulan: v.programUnggulan,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
console.log("profile perbekel success ...");
|
|
||||||
})()
|
})()
|
||||||
.then(() => prisma.$disconnect())
|
.then(() => prisma.$disconnect())
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 275 KiB After Width: | Height: | Size: 275 KiB |
BIN
public/struktur_ppid.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 275 KiB |
85
src/app/admin/(dashboard)/_com/createEditor.tsx
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
// TestEditor.tsx
|
||||||
|
import { RichTextEditor, Link } from '@mantine/tiptap';
|
||||||
|
import { useEditor } from '@tiptap/react';
|
||||||
|
import Highlight from '@tiptap/extension-highlight';
|
||||||
|
import StarterKit from '@tiptap/starter-kit';
|
||||||
|
import Underline from '@tiptap/extension-underline';
|
||||||
|
import TextAlign from '@tiptap/extension-text-align';
|
||||||
|
import Superscript from '@tiptap/extension-superscript';
|
||||||
|
import SubScript from '@tiptap/extension-subscript';
|
||||||
|
|
||||||
|
type CreateEditorProps = {
|
||||||
|
value: string;
|
||||||
|
onChange: (content: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function CreateEditor({ value, onChange }: CreateEditorProps) {
|
||||||
|
const editor = useEditor({
|
||||||
|
extensions: [
|
||||||
|
StarterKit,
|
||||||
|
Underline,
|
||||||
|
Link,
|
||||||
|
Superscript,
|
||||||
|
SubScript,
|
||||||
|
Highlight,
|
||||||
|
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
||||||
|
],
|
||||||
|
content: value,
|
||||||
|
onUpdate: () => {
|
||||||
|
if (editor) {
|
||||||
|
onChange(editor.getHTML());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RichTextEditor editor={editor}>
|
||||||
|
<RichTextEditor.Toolbar sticky stickyOffset="var(--docs-header-height)">
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.Bold />
|
||||||
|
<RichTextEditor.Italic />
|
||||||
|
<RichTextEditor.Underline />
|
||||||
|
<RichTextEditor.Strikethrough />
|
||||||
|
<RichTextEditor.ClearFormatting />
|
||||||
|
<RichTextEditor.Highlight />
|
||||||
|
<RichTextEditor.Code />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.H1 />
|
||||||
|
<RichTextEditor.H2 />
|
||||||
|
<RichTextEditor.H3 />
|
||||||
|
<RichTextEditor.H4 />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.Blockquote />
|
||||||
|
<RichTextEditor.Hr />
|
||||||
|
<RichTextEditor.BulletList />
|
||||||
|
<RichTextEditor.OrderedList />
|
||||||
|
<RichTextEditor.Subscript />
|
||||||
|
<RichTextEditor.Superscript />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.Link />
|
||||||
|
<RichTextEditor.Unlink />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.AlignLeft />
|
||||||
|
<RichTextEditor.AlignCenter />
|
||||||
|
<RichTextEditor.AlignJustify />
|
||||||
|
<RichTextEditor.AlignRight />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.Undo />
|
||||||
|
<RichTextEditor.Redo />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
</RichTextEditor.Toolbar>
|
||||||
|
|
||||||
|
<RichTextEditor.Content />
|
||||||
|
</RichTextEditor>
|
||||||
|
);
|
||||||
|
}
|
||||||
102
src/app/admin/(dashboard)/_com/editEditor.tsx
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
'use client'
|
||||||
|
import { RichTextEditor, Link } from '@mantine/tiptap';
|
||||||
|
import { useEditor } from '@tiptap/react';
|
||||||
|
import Highlight from '@tiptap/extension-highlight';
|
||||||
|
import StarterKit from '@tiptap/starter-kit';
|
||||||
|
import Underline from '@tiptap/extension-underline';
|
||||||
|
import TextAlign from '@tiptap/extension-text-align';
|
||||||
|
import Superscript from '@tiptap/extension-superscript';
|
||||||
|
import SubScript from '@tiptap/extension-subscript';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
type EditEditorProps = {
|
||||||
|
value: string;
|
||||||
|
onChange: (content: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function EditEditor({ value, onChange }: EditEditorProps) {
|
||||||
|
const editor = useEditor({
|
||||||
|
extensions: [
|
||||||
|
StarterKit,
|
||||||
|
Underline,
|
||||||
|
Link,
|
||||||
|
Superscript,
|
||||||
|
SubScript,
|
||||||
|
Highlight,
|
||||||
|
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
||||||
|
],
|
||||||
|
content: value,
|
||||||
|
// Hapus `immediatelyRender` dan `onMount`
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sinkronisasi konten saat `value` berubah
|
||||||
|
useEffect(() => {
|
||||||
|
if (editor && value !== editor.getHTML()) {
|
||||||
|
editor.commands.setContent(value);
|
||||||
|
}
|
||||||
|
}, [value, editor]);
|
||||||
|
|
||||||
|
// Sinkronisasi konten ke parent saat diubah
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editor) return;
|
||||||
|
|
||||||
|
const updateHandler = () => onChange(editor.getHTML());
|
||||||
|
editor.on('update', updateHandler);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
editor.off('update', updateHandler);
|
||||||
|
};
|
||||||
|
}, [editor, onChange]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RichTextEditor editor={editor}>
|
||||||
|
<RichTextEditor.Toolbar sticky stickyOffset="var(--docs-header-height)">
|
||||||
|
{/* Toolbar seperti sebelumnya */}
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.Bold />
|
||||||
|
<RichTextEditor.Italic />
|
||||||
|
<RichTextEditor.Underline />
|
||||||
|
<RichTextEditor.Strikethrough />
|
||||||
|
<RichTextEditor.ClearFormatting />
|
||||||
|
<RichTextEditor.Highlight />
|
||||||
|
<RichTextEditor.Code />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.H1 />
|
||||||
|
<RichTextEditor.H2 />
|
||||||
|
<RichTextEditor.H3 />
|
||||||
|
<RichTextEditor.H4 />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.Blockquote />
|
||||||
|
<RichTextEditor.Hr />
|
||||||
|
<RichTextEditor.BulletList />
|
||||||
|
<RichTextEditor.OrderedList />
|
||||||
|
<RichTextEditor.Subscript />
|
||||||
|
<RichTextEditor.Superscript />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.Link />
|
||||||
|
<RichTextEditor.Unlink />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.AlignLeft />
|
||||||
|
<RichTextEditor.AlignCenter />
|
||||||
|
<RichTextEditor.AlignJustify />
|
||||||
|
<RichTextEditor.AlignRight />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
|
||||||
|
<RichTextEditor.ControlsGroup>
|
||||||
|
<RichTextEditor.Undo />
|
||||||
|
<RichTextEditor.Redo />
|
||||||
|
</RichTextEditor.ControlsGroup>
|
||||||
|
</RichTextEditor.Toolbar>
|
||||||
|
|
||||||
|
<RichTextEditor.Content />
|
||||||
|
</RichTextEditor>
|
||||||
|
);
|
||||||
|
}
|
||||||
27
src/app/admin/(dashboard)/_com/header.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Grid, GridCol, Paper, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconSearch } from '@tabler/icons-react'; // Sesuaikan jika kamu pakai icon lain
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
|
||||||
|
|
||||||
|
const HeaderSearch = ({ title = "", placeholder = "pencarian", searchIcon = <IconSearch size={20} /> }: { title: string, placeholder?: string, searchIcon?: React.ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<Grid>
|
||||||
|
<GridCol span={{ base: 12, md: 9 }}>
|
||||||
|
<Title order={3}>{title}</Title>
|
||||||
|
</GridCol>
|
||||||
|
<GridCol span={{ base: 12, md: 3 }}>
|
||||||
|
<Paper radius={"lg"} bg={colors['white-1']}>
|
||||||
|
<TextInput
|
||||||
|
radius="lg"
|
||||||
|
placeholder={placeholder}
|
||||||
|
leftSection={searchIcon}
|
||||||
|
w="100%"
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HeaderSearch;
|
||||||
30
src/app/admin/(dashboard)/_com/judulList.tsx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Grid, GridCol, Button, Text } from '@mantine/core';
|
||||||
|
import { IconCircleDashedPlus } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const JudulList = ({ title = "", href = "#" }) => {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const handleNavigate = () => {
|
||||||
|
router.push(href);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid align="center" mb={10}>
|
||||||
|
<GridCol span={{ base: 12, md: 11 }}>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>{title}</Text>
|
||||||
|
</GridCol>
|
||||||
|
<GridCol span={{ base: 12, md: 1 }} ta="right">
|
||||||
|
<Button onClick={handleNavigate} bg={colors['blue-button']}>
|
||||||
|
<IconCircleDashedPlus size={25} />
|
||||||
|
</Button>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default JudulList;
|
||||||
41
src/app/admin/(dashboard)/_com/jusulListTab.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Grid, GridCol, Button, Text, Paper, TextInput } from '@mantine/core';
|
||||||
|
import { IconCircleDashedPlus, IconSearch } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const JudulListTab = ({ title = "", href = "#", placeholder = "pencarian", searchIcon = <IconSearch size={20} /> }) => {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const handleNavigate = () => {
|
||||||
|
router.push(href);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid mb={10}>
|
||||||
|
<GridCol span={{ base: 12, md: 8 }}>
|
||||||
|
<Text fz={{base: "md", md: "xl"}} fw={"bold"}>{title}</Text>
|
||||||
|
</GridCol>
|
||||||
|
<GridCol span={{ base: 9, md: 3}} ta="right">
|
||||||
|
<Paper radius={"lg"} bg={colors['white-1']}>
|
||||||
|
<TextInput
|
||||||
|
radius="lg"
|
||||||
|
placeholder={placeholder}
|
||||||
|
leftSection={searchIcon}
|
||||||
|
w="100%"
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
|
</GridCol>
|
||||||
|
<GridCol span={{ base: 3, md: 1}} ta="right">
|
||||||
|
<Button onClick={handleNavigate} bg={colors['blue-button']}>
|
||||||
|
<IconCircleDashedPlus size={25} />
|
||||||
|
</Button>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default JudulListTab;
|
||||||
@@ -21,7 +21,7 @@ export function ModalKonfirmasiHapus({
|
|||||||
<Modal
|
<Modal
|
||||||
opened={opened}
|
opened={opened}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
title="Konfirmasi Hapus"
|
title={<Text fw={"bold"} fz={"xl"}>Konfirmasi Hapus</Text>}
|
||||||
centered
|
centered
|
||||||
>
|
>
|
||||||
<Text mb="md">{text}</Text>
|
<Text mb="md">{text}</Text>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
@@ -57,10 +56,10 @@ const berita = proxy({
|
|||||||
);
|
);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
berita.findMany.load();
|
berita.findMany.load();
|
||||||
return toast.success("success create");
|
return toast.success("Berita berhasil disimpan!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return toast.error("failed create");
|
return toast.error("Gagal menyimpan berita");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log((error as Error).message);
|
console.log((error as Error).message);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -88,6 +87,30 @@ const berita = proxy({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as
|
||||||
|
| Prisma.BeritaGetPayload<{
|
||||||
|
include: {
|
||||||
|
image: true;
|
||||||
|
kategoriBerita: true;
|
||||||
|
};
|
||||||
|
}> | null,
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/desa/berita/${id}`);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
berita.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error('Failed to fetch berita:', res.statusText);
|
||||||
|
berita.findUnique.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching berita:', error);
|
||||||
|
berita.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
delete: {
|
delete: {
|
||||||
loading: false,
|
loading: false,
|
||||||
async byId(id: string) {
|
async byId(id: string) {
|
||||||
|
|||||||
@@ -1,83 +1,246 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ApiFetch from "@/lib/api-fetch"
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { Prisma } from "@prisma/client"
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify"
|
import { toast } from "react-toastify";
|
||||||
import { proxy } from "valtio"
|
import { proxy } from "valtio";
|
||||||
import { z } from "zod"
|
import { z } from "zod";
|
||||||
|
|
||||||
const templateFormPengumuman = z.object({
|
const templateFormPengumuman = z.object({
|
||||||
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
judul: z.string().min(3, "Judul minimal 3 karakter"),
|
||||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||||
content: z.string().min(3, "Content minimal 3 karakter"),
|
content: z.string().min(3, "Content minimal 3 karakter"),
|
||||||
categoryPengumumanId: z.string().nonempty(),
|
categoryPengumumanId: z.string().nonempty(),
|
||||||
})
|
});
|
||||||
|
|
||||||
const category = proxy ({
|
const category = proxy({
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| null
|
| null
|
||||||
| Prisma.CategoryPengumumanGetPayload<{ omit: { isActive: true } }>[],
|
| Prisma.CategoryPengumumanGetPayload<{ omit: { isActive: true } }>[],
|
||||||
async load() {
|
async load() {
|
||||||
const res = await ApiFetch.api.desa.pengumuman.category["find-many"].get();
|
const res = await ApiFetch.api.desa.pengumuman.category[
|
||||||
if (res.status === 200) {
|
"find-many"
|
||||||
category.findMany.data = res.data?.data as any ?? [];
|
].get();
|
||||||
}
|
if (res.status === 200) {
|
||||||
}
|
category.findMany.data = (res.data?.data as any) ?? [];
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
type PengumumanForm = Prisma.PengumumanGetPayload<{
|
type PengumumanForm = Prisma.PengumumanGetPayload<{
|
||||||
select: {
|
select: {
|
||||||
judul: true;
|
judul: true;
|
||||||
deskripsi: true;
|
deskripsi: true;
|
||||||
content: true;
|
content: true;
|
||||||
categoryPengumumanId: true;
|
categoryPengumumanId: true;
|
||||||
}
|
};
|
||||||
}>
|
}>;
|
||||||
const pengumuman = proxy({
|
const pengumuman = proxy({
|
||||||
create: {
|
create: {
|
||||||
form: {} as PengumumanForm,
|
form: {} as PengumumanForm,
|
||||||
loading: false,
|
loading: false,
|
||||||
async create() {
|
async create() {
|
||||||
const cek = templateFormPengumuman.safeParse(pengumuman.create.form);
|
const cek = templateFormPengumuman.safeParse(pengumuman.create.form);
|
||||||
if (!cek.success) {
|
if (!cek.success) {
|
||||||
const err = `[${cek.error.issues
|
const err = `[${cek.error.issues
|
||||||
.map((v) => `${v.path.join(".")}`)
|
.map((v) => `${v.path.join(".")}`)
|
||||||
.join("\n")}] required`;
|
.join("\n")}] required`;
|
||||||
return toast.error(err);
|
return toast.error(err);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
pengumuman.create.loading = true;
|
pengumuman.create.loading = true;
|
||||||
const res = await ApiFetch.api.desa.pengumuman["create"].post(pengumuman.create.form)
|
const res = await ApiFetch.api.desa.pengumuman["create"].post(
|
||||||
|
pengumuman.create.form
|
||||||
|
);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
pengumuman.findMany.load();
|
pengumuman.findMany.load();
|
||||||
return toast.success("success create");
|
return toast.success("success create");
|
||||||
}
|
}
|
||||||
console.log(res)
|
console.log(res);
|
||||||
return toast.error("failed create");
|
return toast.error("failed create");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log((error as Error).message);
|
console.log((error as Error).message);
|
||||||
} finally{
|
} finally {
|
||||||
pengumuman.create.loading = false;
|
pengumuman.create.loading = false;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.PengumumanGetPayload<{omit: {isActive: true}}>[]
|
| Prisma.PengumumanGetPayload<{
|
||||||
| null,
|
include: {
|
||||||
async load () {
|
CategoryPengumuman: true;
|
||||||
const res = await ApiFetch.api.desa.pengumuman["find-many"].get();
|
|
||||||
console.log(res)
|
|
||||||
if (res.status === 200) {
|
|
||||||
pengumuman.findMany.data = res.data?.data ?? [];
|
|
||||||
}
|
}
|
||||||
}
|
}>[]
|
||||||
}
|
| null,
|
||||||
})
|
async load() {
|
||||||
|
const res = await ApiFetch.api.desa.pengumuman["find-many"].get();
|
||||||
|
console.log(res);
|
||||||
|
if (res.status === 200) {
|
||||||
|
pengumuman.findMany.data = res.data?.data ?? [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// findUnique: {
|
||||||
|
// data: null as
|
||||||
|
// | Prisma.PengumumanGetPayload<{
|
||||||
|
// include: {
|
||||||
|
// CategoryPengumuman: true;
|
||||||
|
// }
|
||||||
|
// }>
|
||||||
|
// | null,
|
||||||
|
// async load(id: string) {
|
||||||
|
// try {
|
||||||
|
// const res = await fetch(`/api/desa/pengumuman/${id}`);
|
||||||
|
// if (res.ok) {
|
||||||
|
// const data = await res.json();
|
||||||
|
// pengumuman.findUnique.data = data.data ?? null;
|
||||||
|
// } else {
|
||||||
|
// console.error('Failed to fetch pengumuman:', res.statusText);
|
||||||
|
// pengumuman.findUnique.data = null;
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('Error fetching pengumuman:', error);
|
||||||
|
// pengumuman.findUnique.data = null;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
|
||||||
|
try {
|
||||||
|
pengumuman.delete.loading = true;
|
||||||
|
|
||||||
|
const response = await fetch(`/api/desa/pengumuman/delete/${id}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result?.success) {
|
||||||
|
toast.success(result.message || "Pengumuman berhasil dihapus");
|
||||||
|
await pengumuman.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(result?.message || "Gagal menghapus pengumuman");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus pengumuman");
|
||||||
|
} finally {
|
||||||
|
pengumuman.delete.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
id: "",
|
||||||
|
form: {} as PengumumanForm,
|
||||||
|
loading: false,
|
||||||
|
|
||||||
|
async load(id: string) {
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/desa/pengumuman/${id}`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result?.success) {
|
||||||
|
const data = result.data;
|
||||||
|
this.id = data.id;
|
||||||
|
this.form = {
|
||||||
|
judul: data.judul,
|
||||||
|
deskripsi: data.deskripsi,
|
||||||
|
content: data.content,
|
||||||
|
categoryPengumumanId: data.categoryPengumumanId || "",
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
throw new Error(result?.message || "Gagal mengambil data pengumuman");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error((error as Error).message);
|
||||||
|
toast.error("Terjadi kesalahan saat mengambil data pengumuman");
|
||||||
|
} finally {
|
||||||
|
pengumuman.update.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
const cek = templateFormPengumuman.safeParse(pengumuman.update.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
toast.error(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
pengumuman.update.loading = true;
|
||||||
|
|
||||||
|
const response = await fetch(`/api/desa/pengumuman/${this.id}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
judul: this.form.judul,
|
||||||
|
deskripsi: this.form.deskripsi,
|
||||||
|
content: this.form.content,
|
||||||
|
categoryPengumumanId: this.form.categoryPengumumanId,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
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 pengumuman");
|
||||||
|
await pengumuman.findMany.load(); // refresh list
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || "Gagal update pengumuman");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating pengumuman:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Terjadi kesalahan saat update pengumuman"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
pengumuman.update.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const stateDesaPengumuman = proxy({
|
const stateDesaPengumuman = proxy({
|
||||||
category,
|
category,
|
||||||
pengumuman
|
pengumuman,
|
||||||
})
|
});
|
||||||
export default stateDesaPengumuman
|
export default stateDesaPengumuman;
|
||||||
|
|||||||
223
src/app/admin/(dashboard)/_state/desa/potensi.ts
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import { proxy } from "valtio";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const templateForm = z.object({
|
||||||
|
name: z.string().min(1).max(50),
|
||||||
|
deskripsi: z.string().min(1).max(50),
|
||||||
|
kategori: z.string().min(1).max(50),
|
||||||
|
imageId: z.string().min(1).max(50),
|
||||||
|
content: z.string().min(1).max(5000),
|
||||||
|
})
|
||||||
|
|
||||||
|
const defaultForm = {
|
||||||
|
name: "",
|
||||||
|
deskripsi: "",
|
||||||
|
kategori: "",
|
||||||
|
imageId: "",
|
||||||
|
content: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
const potensiDesaState = proxy({
|
||||||
|
create: {
|
||||||
|
form: { ...defaultForm },
|
||||||
|
loading: false,
|
||||||
|
async create() {
|
||||||
|
const cek = templateForm.safeParse(potensiDesaState.create.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
return toast.error(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
potensiDesaState.create.loading = true;
|
||||||
|
const res = await ApiFetch.api.desa.potensi["create"].post(potensiDesaState.create.form);
|
||||||
|
if (res.status === 200) {
|
||||||
|
potensiDesaState.findMany.load();
|
||||||
|
return toast.success("Potensi berhasil disimpan!");
|
||||||
|
}
|
||||||
|
return toast.error("Gagal menyimpan potensi");
|
||||||
|
} catch (error) {
|
||||||
|
console.log((error as Error).message);
|
||||||
|
} finally {
|
||||||
|
potensiDesaState.create.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
findMany: {
|
||||||
|
data: null as
|
||||||
|
| Prisma.PotensiDesaGetPayload<{
|
||||||
|
include: {
|
||||||
|
image: true;
|
||||||
|
}
|
||||||
|
}>[]
|
||||||
|
| null,
|
||||||
|
async load() {
|
||||||
|
const res = await ApiFetch.api.desa.potensi["find-many"].get();
|
||||||
|
if (res.status === 200) {
|
||||||
|
potensiDesaState.findMany.data = res.data?.data ?? [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as
|
||||||
|
| Prisma.PotensiDesaGetPayload<{
|
||||||
|
include: {
|
||||||
|
image: true;
|
||||||
|
}
|
||||||
|
}> | null,
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/desa/potensi/${id}`);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
potensiDesaState.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error('Failed to fetch potensi:', res.statusText);
|
||||||
|
potensiDesaState.findUnique.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching potensi:', error);
|
||||||
|
potensiDesaState.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
|
||||||
|
try {
|
||||||
|
potensiDesaState.delete.loading = true;
|
||||||
|
|
||||||
|
const response = await fetch(`/api/desa/potensi/del/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result?.success) {
|
||||||
|
toast.success(result.message || "Potensi berhasil dihapus");
|
||||||
|
await potensiDesaState.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(result?.message || "Gagal menghapus potensi");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus potensi");
|
||||||
|
} finally {
|
||||||
|
potensiDesaState.delete.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
id: "",
|
||||||
|
form: { ...defaultForm },
|
||||||
|
loading: false,
|
||||||
|
|
||||||
|
async load(id: string) {
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/desa/potensi/${id}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result?.success) {
|
||||||
|
const data = result.data;
|
||||||
|
this.id = data.id;
|
||||||
|
this.form = {
|
||||||
|
name: data.name,
|
||||||
|
deskripsi: data.deskripsi,
|
||||||
|
kategori: data.kategori,
|
||||||
|
imageId: data.imageId || "",
|
||||||
|
content: data.content,
|
||||||
|
};
|
||||||
|
return data; // Return the loaded data
|
||||||
|
} else {
|
||||||
|
throw new Error(result?.message || "Gagal memuat data");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading potensi:", error);
|
||||||
|
toast.error(error instanceof Error ? error.message : "Gagal memuat data");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
const cek = templateForm.safeParse(potensiDesaState.edit.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
toast.error(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
potensiDesaState.edit.loading = true;
|
||||||
|
|
||||||
|
const response = await fetch(`/api/desa/potensi/${this.id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
name: this.form.name,
|
||||||
|
deskripsi: this.form.deskripsi,
|
||||||
|
kategori: this.form.kategori,
|
||||||
|
imageId: this.form.imageId,
|
||||||
|
content: this.form.content,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
toast.success("Berhasil update potensi");
|
||||||
|
await potensiDesaState.findMany.load(); // refresh list
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || "Gagal update potensi");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating potensi:", error);
|
||||||
|
toast.error(error instanceof Error ? error.message : "Terjadi kesalahan saat update potensi");
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
potensiDesaState.edit.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
potensiDesaState.edit.id = "";
|
||||||
|
potensiDesaState.edit.form = { ...defaultForm };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default potensiDesaState
|
||||||
|
|
||||||
|
|
||||||
@@ -5,61 +5,239 @@ import { proxy } from "valtio";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const templateDaftarInformasi = z.object({
|
const templateDaftarInformasi = z.object({
|
||||||
jenisInformasi: z.string().min(3, "Jenis Informasi minimal 3 karakter"),
|
jenisInformasi: z.string().min(3, "Jenis Informasi minimal 3 karakter"),
|
||||||
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
|
||||||
tanggal: z.string().min(3, "Tanggal minimal 3 karakter"),
|
tanggal: z.string().min(3, "Tanggal minimal 3 karakter"),
|
||||||
})
|
});
|
||||||
|
|
||||||
|
const defaultForm = {
|
||||||
|
jenisInformasi: "",
|
||||||
|
deskripsi: "",
|
||||||
|
tanggal: "",
|
||||||
|
};
|
||||||
|
|
||||||
type DaftarInformasi = Prisma.DaftarInformasiPublikGetPayload<{
|
type DaftarInformasi = Prisma.DaftarInformasiPublikGetPayload<{
|
||||||
select: {
|
select: {
|
||||||
jenisInformasi: true;
|
jenisInformasi: true;
|
||||||
deskripsi: true;
|
deskripsi: true;
|
||||||
tanggal: true;
|
tanggal: true;
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
const daftarInformasi = proxy({
|
const daftarInformasiPublik = proxy({
|
||||||
create: {
|
create: {
|
||||||
form: {} as DaftarInformasi,
|
form: {} as DaftarInformasi,
|
||||||
loading: false,
|
loading: false,
|
||||||
async create() {
|
async create() {
|
||||||
const cek = templateDaftarInformasi.safeParse(daftarInformasi.create.form);
|
const cek = templateDaftarInformasi.safeParse(
|
||||||
if (!cek.success) {
|
daftarInformasiPublik.create.form
|
||||||
const err = `[${cek.error.issues
|
);
|
||||||
.map((v) => `${v.path.join(".")}`)
|
if (!cek.success) {
|
||||||
.join("\n")}] required`;
|
const err = `[${cek.error.issues
|
||||||
return toast.error(err);
|
.map((v) => `${v.path.join(".")}`)
|
||||||
}
|
.join("\n")}] required`;
|
||||||
try {
|
return toast.error(err);
|
||||||
daftarInformasi.create.loading = true;
|
}
|
||||||
const res = await ApiFetch.api.ppid.daftarinformasipublik["create"].post(daftarInformasi.create.form);
|
try {
|
||||||
if (res.status === 200) {
|
daftarInformasiPublik.create.loading = true;
|
||||||
daftarInformasi.findMany.load();
|
const res = await ApiFetch.api.ppid.daftarinformasipublik[
|
||||||
return toast.success("success create");
|
"create"
|
||||||
}
|
].post(daftarInformasiPublik.create.form);
|
||||||
return toast.error("failed create");
|
if (res.status === 200) {
|
||||||
} catch (error) {
|
daftarInformasiPublik.findMany.load();
|
||||||
console.log((error as Error).message);
|
return toast.success("success create");
|
||||||
} finally {
|
|
||||||
daftarInformasi.create.loading = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
findMany: {
|
|
||||||
data: null as
|
|
||||||
| Prisma.DaftarInformasiPublikGetPayload<{ omit: { isActive: true } }>[]
|
|
||||||
| null,
|
|
||||||
async load() {
|
|
||||||
const res = await ApiFetch.api.ppid.daftarinformasipublik["find-many"].get();
|
|
||||||
if (res.status === 200) {
|
|
||||||
daftarInformasi.findMany.data = res.data?.data ?? [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
return toast.error("failed create");
|
||||||
});
|
} catch (error) {
|
||||||
|
console.log((error as Error).message);
|
||||||
|
} finally {
|
||||||
|
daftarInformasiPublik.create.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findMany: {
|
||||||
|
data: null as
|
||||||
|
| Prisma.DaftarInformasiPublikGetPayload<{ omit: { isActive: true } }>[]
|
||||||
|
| null,
|
||||||
|
async load() {
|
||||||
|
const res = await ApiFetch.api.ppid.daftarinformasipublik[
|
||||||
|
"find-many"
|
||||||
|
].get();
|
||||||
|
if (res.status === 200) {
|
||||||
|
daftarInformasiPublik.findMany.data = res.data?.data ?? [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as Prisma.DaftarInformasiPublikGetPayload<{
|
||||||
|
omit: { isActive: true };
|
||||||
|
}> | null,
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/ppid/daftarinformasipublik/${id}`);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
daftarInformasiPublik.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"Failed to fetch daftar informasi publik:",
|
||||||
|
res.statusText
|
||||||
|
);
|
||||||
|
daftarInformasiPublik.findUnique.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching daftar informasi publik:", error);
|
||||||
|
daftarInformasiPublik.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
|
||||||
const stateDaftarInformasiPublik = proxy({
|
try {
|
||||||
daftarInformasi
|
daftarInformasiPublik.delete.loading = true;
|
||||||
})
|
|
||||||
|
|
||||||
export default stateDaftarInformasiPublik;
|
const response = await fetch(
|
||||||
|
`/api/ppid/daftarinformasipublik/del/${id}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result?.success) {
|
||||||
|
toast.success(
|
||||||
|
result.message || "Daftar Informasi Publik berhasil dihapus"
|
||||||
|
);
|
||||||
|
await daftarInformasiPublik.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(
|
||||||
|
result?.message || "Gagal menghapus daftar informasi publik"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus daftar informasi publik");
|
||||||
|
} finally {
|
||||||
|
daftarInformasiPublik.delete.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
edit: {
|
||||||
|
id: "",
|
||||||
|
form: { ...defaultForm },
|
||||||
|
loading: false,
|
||||||
|
|
||||||
|
async load(id: string) {
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/ppid/daftarinformasipublik/${id}`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result?.success) {
|
||||||
|
const data = result.data;
|
||||||
|
this.id = data.id;
|
||||||
|
this.form = {
|
||||||
|
jenisInformasi: data.jenisInformasi,
|
||||||
|
deskripsi: data.deskripsi,
|
||||||
|
tanggal: data.tanggal,
|
||||||
|
};
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
throw new Error(result?.message || "Gagal mengambil data");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading daftar informasi publik:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error ? error.message : "Gagal memuat data"
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async update() {
|
||||||
|
const cek = templateDaftarInformasi.safeParse(
|
||||||
|
daftarInformasiPublik.edit.form
|
||||||
|
);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
return toast.error(err);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
daftarInformasiPublik.edit.loading = true;
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/ppid/daftarinformasipublik/${this.id}`,
|
||||||
|
{
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
jenisInformasi: this.form.jenisInformasi,
|
||||||
|
deskripsi: this.form.deskripsi,
|
||||||
|
tanggal: this.form.tanggal,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
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 daftar informasi publik");
|
||||||
|
await daftarInformasiPublik.findMany.load(); // refresh list
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
result.message || "Gagal update daftar informasi publik"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating daftar informasi publik:", error);
|
||||||
|
toast.error(
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Terjadi kesalahan saat update daftar informasi publik"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
daftarInformasiPublik.edit.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
daftarInformasiPublik.edit.id = "";
|
||||||
|
daftarInformasiPublik.edit.form = { ...defaultForm };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default daftarInformasiPublik;
|
||||||
|
|||||||
@@ -11,12 +11,13 @@ const templateGrafikJenisKelamin = z.object({
|
|||||||
|
|
||||||
type GrafikJenisKelamin = Prisma.GrafikBerdasarkanJenisKelaminGetPayload<{
|
type GrafikJenisKelamin = Prisma.GrafikBerdasarkanJenisKelaminGetPayload<{
|
||||||
select: {
|
select: {
|
||||||
|
id: true;
|
||||||
laki: true;
|
laki: true;
|
||||||
perempuan: true;
|
perempuan: true;
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
const defaultForm: GrafikJenisKelamin = {
|
const defaultForm: Omit<GrafikJenisKelamin, 'id'> & { id?: string } = {
|
||||||
laki: "",
|
laki: "",
|
||||||
perempuan: "",
|
perempuan: "",
|
||||||
};
|
};
|
||||||
@@ -41,9 +42,16 @@ const grafikBerdasarkanJenisKelamin = proxy({
|
|||||||
"create"
|
"create"
|
||||||
].post(grafikBerdasarkanJenisKelamin.create.form);
|
].post(grafikBerdasarkanJenisKelamin.create.form);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
grafikBerdasarkanJenisKelamin.create.form = defaultForm;
|
const id = res.data?.data?.id;
|
||||||
grafikBerdasarkanJenisKelamin.findMany.load();
|
if (id) {
|
||||||
return toast.success("success create");
|
toast.success("Success create");
|
||||||
|
grafikBerdasarkanJenisKelamin.create.form = {
|
||||||
|
laki: "",
|
||||||
|
perempuan: "",
|
||||||
|
};
|
||||||
|
grafikBerdasarkanJenisKelamin.findMany.load();
|
||||||
|
return id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return toast.error("failed create");
|
return toast.error("failed create");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -69,9 +77,103 @@ const grafikBerdasarkanJenisKelamin = proxy({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as Prisma.GrafikBerdasarkanJenisKelaminGetPayload<{
|
||||||
|
omit: { isActive: true };
|
||||||
|
}> | null,
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/ppid/grafikberdasarkanjeniskelamin/${id}`
|
||||||
|
);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
grafikBerdasarkanJenisKelamin.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to fetch data", res.status, res.statusText);
|
||||||
|
grafikBerdasarkanJenisKelamin.findUnique.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading grafik berdasarkan jenis kelamin:", error);
|
||||||
|
grafikBerdasarkanJenisKelamin.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
id: "",
|
||||||
|
form: {...defaultForm},
|
||||||
|
loading: false,
|
||||||
|
async byId() {
|
||||||
|
// Method implementation if needed
|
||||||
|
},
|
||||||
|
async submit() {
|
||||||
|
const id = this.id;
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const cek = templateGrafikJenisKelamin.safeParse(this.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`;
|
||||||
|
toast.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`/api/ppid/grafikberdasarkanjeniskelamin/${id}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(this.form),
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
if (!response.ok || !result?.success) {
|
||||||
|
throw new Error(result?.message || "Gagal update data");
|
||||||
|
}
|
||||||
|
toast.success("Berhasil update data!");
|
||||||
|
await grafikBerdasarkanJenisKelamin.findMany.load();
|
||||||
|
return result.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error update data:", error);
|
||||||
|
toast.error("Gagal update data grafik berdasarkan jenis kelamin");
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
|
||||||
|
try {
|
||||||
|
grafikBerdasarkanJenisKelamin.delete.loading = true;
|
||||||
|
|
||||||
|
const response = await fetch(`/api/ppid/grafikberdasarkanjeniskelamin/del/${id}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result?.success) {
|
||||||
|
toast.success(result.message || "Grafik berdasarkan jenis kelamin berhasil dihapus");
|
||||||
|
await grafikBerdasarkanJenisKelamin.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(result?.message || "Gagal menghapus grafik berdasarkan jenis kelamin");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus grafik berdasarkan jenis kelamin");
|
||||||
|
} finally {
|
||||||
|
grafikBerdasarkanJenisKelamin.delete.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const stateGrafikBerdasarkanJenisKelamin = proxy({
|
export default grafikBerdasarkanJenisKelamin;
|
||||||
grafikBerdasarkanJenisKelamin,
|
|
||||||
});
|
|
||||||
export default stateGrafikBerdasarkanJenisKelamin;
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const templateGrafikResponden = z.object({
|
|||||||
|
|
||||||
type GrafikResponden = Prisma.GrafikBerdasarkanRespondenGetPayload<{
|
type GrafikResponden = Prisma.GrafikBerdasarkanRespondenGetPayload<{
|
||||||
select: {
|
select: {
|
||||||
|
id: true;
|
||||||
sangatbaik: true;
|
sangatbaik: true;
|
||||||
baik: true;
|
baik: true;
|
||||||
kurangbaik: true;
|
kurangbaik: true;
|
||||||
@@ -20,7 +21,7 @@ type GrafikResponden = Prisma.GrafikBerdasarkanRespondenGetPayload<{
|
|||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
const defaultForm: GrafikResponden = {
|
const defaultForm: Omit<GrafikResponden, 'id'> & { id?: string } = {
|
||||||
sangatbaik: "",
|
sangatbaik: "",
|
||||||
baik: "",
|
baik: "",
|
||||||
kurangbaik: "",
|
kurangbaik: "",
|
||||||
@@ -47,9 +48,15 @@ const grafikBerdasarkanResponden = proxy({
|
|||||||
"create"
|
"create"
|
||||||
].post(grafikBerdasarkanResponden.create.form);
|
].post(grafikBerdasarkanResponden.create.form);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
grafikBerdasarkanResponden.create.form = defaultForm;
|
const id = res.data?.data?.id;
|
||||||
grafikBerdasarkanResponden.findMany.load();
|
if (id) {
|
||||||
return toast.success("success create");
|
toast.success("Success create");
|
||||||
|
grafikBerdasarkanResponden.create.form = {
|
||||||
|
...defaultForm
|
||||||
|
};
|
||||||
|
grafikBerdasarkanResponden.findMany.load();
|
||||||
|
return id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return toast.error("failed create");
|
return toast.error("failed create");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -75,10 +82,112 @@ const grafikBerdasarkanResponden = proxy({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as Prisma.GrafikBerdasarkanRespondenGetPayload<{
|
||||||
|
omit: { isActive: true };
|
||||||
|
}> | null,
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/ppid/grafikberdasarkanresponden/${id}`
|
||||||
|
);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
grafikBerdasarkanResponden.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to fetch data", res.status, res.statusText);
|
||||||
|
grafikBerdasarkanResponden.findUnique.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading grafik berdasarkan responden:", error);
|
||||||
|
grafikBerdasarkanResponden.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
id: "",
|
||||||
|
form: {...defaultForm},
|
||||||
|
loading: false,
|
||||||
|
async byId() {
|
||||||
|
|
||||||
|
},
|
||||||
|
async submit() {
|
||||||
|
const id = this.id;
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cek = templateGrafikResponden.safeParse(this.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
toast.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/ppid/grafikberdasarkanresponden/${id}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(this.form),
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok || !result?.success) {
|
||||||
|
throw new Error(result?.message || "Gagal update data");
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.success("Berhasil update data!");
|
||||||
|
|
||||||
|
await grafikBerdasarkanResponden.findMany.load();
|
||||||
|
|
||||||
|
return result.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error update data:", error);
|
||||||
|
toast.error("Gagal update data grafik berdasarkan responden");
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
|
||||||
|
try {
|
||||||
|
grafikBerdasarkanResponden.delete.loading = true;
|
||||||
|
|
||||||
|
const response = await fetch(`/api/ppid/grafikberdasarkanresponden/del/${id}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result?.success) {
|
||||||
|
toast.success(result.message || "Grafik berdasarkan responden berhasil dihapus");
|
||||||
|
await grafikBerdasarkanResponden.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(result?.message || "Gagal menghapus grafik berdasarkan responden");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus grafik berdasarkan responden");
|
||||||
|
} finally {
|
||||||
|
grafikBerdasarkanResponden.delete.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const stateGrafikResponden = proxy({
|
export default grafikBerdasarkanResponden;
|
||||||
grafikBerdasarkanResponden,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default stateGrafikResponden;
|
|
||||||
@@ -5,80 +5,182 @@ import { proxy } from "valtio";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const templateGrafikUmur = z.object({
|
const templateGrafikUmur = z.object({
|
||||||
remaja: z.string().min(2, "Data remaja harus diisi"),
|
remaja: z.string().min(2, "Data remaja harus diisi"),
|
||||||
dewasa: z.string().min(2, "Data dewasa harus diisi"),
|
dewasa: z.string().min(2, "Data dewasa harus diisi"),
|
||||||
orangtua: z.string().min(2, "Data orangtua harus diisi"),
|
orangtua: z.string().min(2, "Data orangtua harus diisi"),
|
||||||
lansia: z.string().min(2, "Data lansia harus diisi"),
|
lansia: z.string().min(2, "Data lansia harus diisi"),
|
||||||
});
|
});
|
||||||
|
|
||||||
type GrafikUmur = Prisma.GrafikBerdasarkanUmurGetPayload<{
|
type GrafikUmur = Prisma.GrafikBerdasarkanUmurGetPayload<{
|
||||||
select: {
|
select: {
|
||||||
remaja: true;
|
id: true;
|
||||||
dewasa: true;
|
remaja: true;
|
||||||
orangtua: true;
|
dewasa: true;
|
||||||
lansia: true;
|
orangtua: true;
|
||||||
};
|
lansia: true;
|
||||||
|
};
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
const defaultForm: GrafikUmur = {
|
const defaultForm: Omit<GrafikUmur, "id"> & { id?: string } = {
|
||||||
remaja: "",
|
remaja: "",
|
||||||
dewasa: "",
|
dewasa: "",
|
||||||
orangtua: "",
|
orangtua: "",
|
||||||
lansia: "",
|
lansia: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const grafikBerdasarkanUmur = proxy({
|
const grafikBerdasarkanUmur = proxy({
|
||||||
create: {
|
create: {
|
||||||
form: defaultForm,
|
form: defaultForm,
|
||||||
loading: false,
|
loading: false,
|
||||||
async create() {
|
async create() {
|
||||||
const cek = templateGrafikUmur.safeParse(
|
const cek = templateGrafikUmur.safeParse(
|
||||||
grafikBerdasarkanUmur.create.form
|
grafikBerdasarkanUmur.create.form
|
||||||
);
|
);
|
||||||
if (!cek.success) {
|
if (!cek.success) {
|
||||||
const err = `[${cek.error.issues
|
const err = `[${cek.error.issues
|
||||||
.map((v) => `${v.path.join(".")}`)
|
.map((v) => `${v.path.join(".")}`)
|
||||||
.join("\n")}] required`;
|
.join("\n")}] required`;
|
||||||
return toast.error(err);
|
return toast.error(err);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
grafikBerdasarkanUmur.create.loading = true;
|
grafikBerdasarkanUmur.create.loading = true;
|
||||||
const res = await ApiFetch.api.ppid.grafikberdasarkanumur[
|
const res = await ApiFetch.api.ppid.grafikberdasarkanumur[
|
||||||
"create"
|
"create"
|
||||||
].post(grafikBerdasarkanUmur.create.form);
|
].post(grafikBerdasarkanUmur.create.form);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
grafikBerdasarkanUmur.create.form = defaultForm;
|
const id = res.data?.data?.id;
|
||||||
grafikBerdasarkanUmur.findMany.load();
|
if (id) {
|
||||||
return toast.success("success create");
|
toast.success("Success create");
|
||||||
}
|
grafikBerdasarkanUmur.create.form = {
|
||||||
return toast.error("failed create");
|
remaja: "",
|
||||||
} catch (error) {
|
dewasa: "",
|
||||||
console.log((error as Error).message);
|
orangtua: "",
|
||||||
} finally {
|
lansia: "",
|
||||||
grafikBerdasarkanUmur.create.loading = false;
|
};
|
||||||
}
|
grafikBerdasarkanUmur.findMany.load();
|
||||||
},
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toast.error("failed create");
|
||||||
|
} catch (error) {
|
||||||
|
console.log((error as Error).message);
|
||||||
|
} finally {
|
||||||
|
grafikBerdasarkanUmur.create.loading = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
findMany: {
|
},
|
||||||
data: null as
|
findMany: {
|
||||||
| Prisma.GrafikBerdasarkanUmurGetPayload<{
|
data: null as
|
||||||
omit: { isActive: true };
|
| Prisma.GrafikBerdasarkanUmurGetPayload<{
|
||||||
|
omit: { isActive: true };
|
||||||
}>[]
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
loading: false,
|
loading: false,
|
||||||
async load() {
|
async load() {
|
||||||
const res = await ApiFetch.api.ppid.grafikberdasarkanumur[
|
const res = await ApiFetch.api.ppid.grafikberdasarkanumur[
|
||||||
"find-many"
|
"find-many"
|
||||||
].get();
|
].get();
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
grafikBerdasarkanUmur.findMany.data = res.data?.data ?? [];
|
grafikBerdasarkanUmur.findMany.data = res.data?.data ?? [];
|
||||||
}
|
}
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as Prisma.GrafikBerdasarkanUmurGetPayload<{
|
||||||
|
omit: { isActive: true };
|
||||||
|
}> | null,
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`/api/ppid/grafikberdasarkanumur/${id}`);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
grafikBerdasarkanUmur.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to fetch data", res.status, res.statusText);
|
||||||
|
grafikBerdasarkanUmur.findUnique.data = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading grafik berdasarkan umur:", error);
|
||||||
|
grafikBerdasarkanUmur.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
id: "",
|
||||||
|
form: { ...defaultForm },
|
||||||
|
loading: false,
|
||||||
|
async byId() {
|
||||||
|
// Method implementation if needed
|
||||||
|
},
|
||||||
|
async submit() {
|
||||||
|
const id = this.id;
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const cek = templateGrafikUmur.safeParse(this.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues
|
||||||
|
.map((v) => `${v.path.join(".")}`)
|
||||||
|
.join("\n")}] required`;
|
||||||
|
toast.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/ppid/grafikberdasarkanumur/${id}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(this.form),
|
||||||
|
});
|
||||||
|
const result = await response.json();
|
||||||
|
if (!response.ok || !result?.success) {
|
||||||
|
throw new Error(result?.message || "Gagal update data");
|
||||||
|
}
|
||||||
|
toast.success("Berhasil update data!");
|
||||||
|
await grafikBerdasarkanUmur.findMany.load();
|
||||||
|
return result.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error update data:", error);
|
||||||
|
toast.error("Gagal update data grafik berdasarkan umur");
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
|
||||||
const stateGrafikBerdasarkanUmur = proxy({
|
try {
|
||||||
grafikBerdasarkanUmur,
|
grafikBerdasarkanUmur.delete.loading = true;
|
||||||
})
|
|
||||||
|
|
||||||
export default stateGrafikBerdasarkanUmur;
|
const response = await fetch(`/api/ppid/grafikberdasarkanumur/del/${id}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result?.success) {
|
||||||
|
toast.success(result.message || "Grafik berdasarkan umur berhasil dihapus");
|
||||||
|
await grafikBerdasarkanUmur.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(result?.message || "Gagal menghapus grafik berdasarkan umur");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus grafik berdasarkan umur");
|
||||||
|
} finally {
|
||||||
|
grafikBerdasarkanUmur.delete.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default grafikBerdasarkanUmur;
|
||||||
@@ -11,12 +11,13 @@ const templateGrafikHasilKepuasanMasyarakat = z.object({
|
|||||||
|
|
||||||
type GrafikHasilKepuasanMasyarakat = Prisma.IndeksKepuasanMasyarakatGetPayload<{
|
type GrafikHasilKepuasanMasyarakat = Prisma.IndeksKepuasanMasyarakatGetPayload<{
|
||||||
select: {
|
select: {
|
||||||
|
id: true;
|
||||||
label: true;
|
label: true;
|
||||||
kepuasan: true;
|
kepuasan: true;
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
const defaultForm: GrafikHasilKepuasanMasyarakat = {
|
const defaultForm: Omit<GrafikHasilKepuasanMasyarakat, 'id'> & { id?: string } = {
|
||||||
label: "",
|
label: "",
|
||||||
kepuasan: "",
|
kepuasan: "",
|
||||||
};
|
};
|
||||||
@@ -41,13 +42,18 @@ const grafikHasilKepuasanMasyarakat = proxy({
|
|||||||
grafikHasilKepuasanMasyarakat.create.form
|
grafikHasilKepuasanMasyarakat.create.form
|
||||||
);
|
);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
grafikHasilKepuasanMasyarakat.create.form = {
|
const id = res.data?.data?.id;
|
||||||
label: "",
|
if (id) {
|
||||||
kepuasan: ""
|
toast.success("Success create");
|
||||||
};
|
grafikHasilKepuasanMasyarakat.create.form = {
|
||||||
grafikHasilKepuasanMasyarakat.findMany.load();
|
label: "",
|
||||||
return toast.success("success create");
|
kepuasan: "",
|
||||||
|
};
|
||||||
|
grafikHasilKepuasanMasyarakat.findMany.load();
|
||||||
|
return id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return toast.error("failed create");
|
return toast.error("failed create");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log((error as Error).message);
|
console.log((error as Error).message);
|
||||||
@@ -58,19 +64,127 @@ const grafikHasilKepuasanMasyarakat = proxy({
|
|||||||
},
|
},
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.IndeksKepuasanMasyarakatGetPayload<{ omit: { isActive: true } }>[]
|
| Prisma.IndeksKepuasanMasyarakatGetPayload<{
|
||||||
| null,
|
omit: { isActive: true };
|
||||||
|
}>[]
|
||||||
|
| null,
|
||||||
async load() {
|
async load() {
|
||||||
const res = await ApiFetch.api.ppid.grafikhasilkepuasamanmasyarakat["find-many"].get();
|
const res = await ApiFetch.api.ppid.grafikhasilkepuasamanmasyarakat[
|
||||||
if (res.status === 200) {
|
"find-many"
|
||||||
grafikHasilKepuasanMasyarakat.findMany.data = res.data?.data ?? [];
|
].get();
|
||||||
|
if (res.status === 200) {
|
||||||
|
grafikHasilKepuasanMasyarakat.findMany.data = res.data?.data ?? [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
findUnique: {
|
||||||
|
data: null as Prisma.IndeksKepuasanMasyarakatGetPayload<{
|
||||||
|
omit: { isActive: true };
|
||||||
|
}> | null,
|
||||||
|
|
||||||
|
async load(id: string) {
|
||||||
|
try {
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/ppid/grafikhasilkepuasamanmasyarakat/${id}`
|
||||||
|
);
|
||||||
|
if (res.ok) {
|
||||||
|
const data = await res.json();
|
||||||
|
grafikHasilKepuasanMasyarakat.findUnique.data = data.data ?? null;
|
||||||
|
} else {
|
||||||
|
console.error("Failed to fetch data", res.status, res.statusText);
|
||||||
|
grafikHasilKepuasanMasyarakat.findUnique.data = null;
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading grafik hasil kepuasan masyarakat:", error);
|
||||||
|
grafikHasilKepuasanMasyarakat.findUnique.data = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
id: "",
|
||||||
|
form: { ...defaultForm },
|
||||||
|
loading: false,
|
||||||
|
async byId() {
|
||||||
|
// Method implementation if needed
|
||||||
|
},
|
||||||
|
async submit() {
|
||||||
|
const id = this.id;
|
||||||
|
if (!id) {
|
||||||
|
toast.warn("ID tidak valid");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Validasi pakai Zod
|
||||||
|
const cek = templateGrafikHasilKepuasanMasyarakat.safeParse(this.form);
|
||||||
|
if (!cek.success) {
|
||||||
|
const err = `[${cek.error.issues.map((v) => `${v.path.join(".")}`).join("\n")}] required`;
|
||||||
|
toast.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/ppid/grafikhasilkepuasamanmasyarakat/${id}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(this.form),
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (!response.ok || !result?.success) {
|
||||||
|
throw new Error(result?.message || "Gagal update data");
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.success("Berhasil update data!");
|
||||||
|
|
||||||
|
// ✅ Optional: refresh list kalau kamu langsung ke halaman list
|
||||||
|
await grafikHasilKepuasanMasyarakat.findMany.load();
|
||||||
|
|
||||||
|
return result.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error update data:", error);
|
||||||
|
toast.error("Gagal update data grafik hasil kepuasan masyarakat");
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
|
||||||
|
try {
|
||||||
|
grafikHasilKepuasanMasyarakat.delete.loading = true;
|
||||||
|
|
||||||
|
const response = await fetch(`/api/ppid/grafikhasilkepuasamanmasyarakat/del/${id}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result?.success) {
|
||||||
|
toast.success(result.message || "Grafik hasil kepuasan masyarakat berhasil dihapus");
|
||||||
|
await grafikHasilKepuasanMasyarakat.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(result?.message || "Gagal menghapus grafik hasil kepuasan masyarakat");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus grafik hasil kepuasan masyarakat");
|
||||||
|
} finally {
|
||||||
|
grafikHasilKepuasanMasyarakat.delete.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const stateGrafikHasilKepuasanMasyarakat = proxy({
|
export default grafikHasilKepuasanMasyarakat;
|
||||||
grafikHasilKepuasanMasyarakat,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default stateGrafikHasilKepuasanMasyarakat;
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import ApiFetch from "@/lib/api-fetch";
|
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { proxy } from "valtio";
|
import { proxy } from "valtio";
|
||||||
@@ -13,11 +12,18 @@ const templateForm = z.object({
|
|||||||
riwayat: z.string().min(3, "Riwayat minimal 3 karakter"),
|
riwayat: z.string().min(3, "Riwayat minimal 3 karakter"),
|
||||||
pengalaman: z.string().min(3, "Pengalaman minimal 3 karakter"),
|
pengalaman: z.string().min(3, "Pengalaman minimal 3 karakter"),
|
||||||
unggulan: z.string().min(3, "Unggulan minimal 3 karakter"),
|
unggulan: z.string().min(3, "Unggulan minimal 3 karakter"),
|
||||||
|
imageId: z.string().min(1, "Gambar wajib dipilih"),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
const defaultForm = {
|
||||||
* Tipe data ProfilePPID yang digunakan dalam form dan API, berdasarkan Prisma schema.
|
name: "",
|
||||||
*/
|
biodata: "",
|
||||||
|
riwayat: "",
|
||||||
|
pengalaman: "",
|
||||||
|
unggulan: "",
|
||||||
|
imageId: "",
|
||||||
|
};
|
||||||
|
|
||||||
type ProfilePPIDForm = Prisma.ProfilePPIDGetPayload<{
|
type ProfilePPIDForm = Prisma.ProfilePPIDGetPayload<{
|
||||||
select: {
|
select: {
|
||||||
id: true;
|
id: true;
|
||||||
@@ -26,147 +32,170 @@ type ProfilePPIDForm = Prisma.ProfilePPIDGetPayload<{
|
|||||||
riwayat: true;
|
riwayat: true;
|
||||||
pengalaman: true;
|
pengalaman: true;
|
||||||
unggulan: true;
|
unggulan: true;
|
||||||
imageUrl: true;
|
imageId: true;
|
||||||
|
image?: {
|
||||||
|
select: {
|
||||||
|
link: true;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* State utama ProfilePPID yang mencakup fitur:
|
* Improved State Management - Consolidated and more robust
|
||||||
* - Ambil data berdasarkan ID
|
|
||||||
* - Update data
|
|
||||||
* - Upload gambar
|
|
||||||
*/
|
*/
|
||||||
const stateProfilePPID = proxy({
|
const stateProfilePPID = proxy({
|
||||||
/**
|
// Consolidated data management
|
||||||
* Bagian untuk ambil data berdasarkan ID
|
profile: {
|
||||||
*/
|
|
||||||
findById: {
|
|
||||||
data: null as ProfilePPIDForm | null,
|
data: null as ProfilePPIDForm | null,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
error: null as string | null,
|
||||||
|
|
||||||
/**
|
// Single method to load profile data
|
||||||
* Inisialisasi data kosong ke dalam state.
|
|
||||||
*/
|
|
||||||
initialize() {
|
|
||||||
stateProfilePPID.findById.data = {
|
|
||||||
id: '',
|
|
||||||
name: '',
|
|
||||||
biodata: '',
|
|
||||||
riwayat: '',
|
|
||||||
pengalaman: '',
|
|
||||||
unggulan: '',
|
|
||||||
imageUrl: ''
|
|
||||||
} as ProfilePPIDForm;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mengambil data profil berdasarkan ID.
|
|
||||||
* @param {string} id - ID dari profile yang ingin diambil.
|
|
||||||
*/
|
|
||||||
async load(id: string) {
|
async load(id: string) {
|
||||||
try {
|
if (!id) {
|
||||||
stateProfilePPID.findById.loading = true;
|
toast.warn("ID tidak valid");
|
||||||
const res = await ApiFetch.api.ppid.profileppid["find-by-id"].get({
|
return null;
|
||||||
query: { id },
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (res.status === 200) {
|
this.loading = true;
|
||||||
stateProfilePPID.findById.data = res.data?.data ?? null;
|
this.error = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/ppid/profileppid/${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 {
|
} else {
|
||||||
toast.error("Gagal mengambil data profile");
|
throw new Error(result.message || "Gagal mengambil data profile");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error((error as Error).message);
|
const errorMessage = (error as Error).message;
|
||||||
|
this.error = errorMessage;
|
||||||
|
console.error("Load profile error:", errorMessage);
|
||||||
toast.error("Terjadi kesalahan saat mengambil data profile");
|
toast.error("Terjadi kesalahan saat mengambil data profile");
|
||||||
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
stateProfilePPID.findById.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Reset profile data
|
||||||
|
reset() {
|
||||||
|
this.data = null;
|
||||||
|
this.error = null;
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
// Edit form management
|
||||||
* Bagian untuk update data profile
|
editForm: {
|
||||||
*/
|
id: "",
|
||||||
update: {
|
form: { ...defaultForm },
|
||||||
loading: false,
|
loading: false,
|
||||||
|
error: null as string | null,
|
||||||
|
isReadOnly: false, // Flag untuk data yang tidak bisa diedit
|
||||||
|
|
||||||
/**
|
// Initialize form with profile data
|
||||||
* Melakukan validasi dan menyimpan perubahan data profile ke server.
|
initialize(profileData: ProfilePPIDForm) {
|
||||||
* @param {ProfilePPIDForm} data - Data profil yang akan disimpan.
|
this.id = profileData.id;
|
||||||
*/
|
this.isReadOnly = false; // Semua data bisa diedit
|
||||||
async save(data: ProfilePPIDForm) {
|
this.form = {
|
||||||
const cek = templateForm.safeParse(data);
|
name: profileData.name || "",
|
||||||
|
biodata: profileData.biodata || "",
|
||||||
|
riwayat: profileData.riwayat || "",
|
||||||
|
pengalaman: profileData.pengalaman || "",
|
||||||
|
unggulan: profileData.unggulan || "",
|
||||||
|
imageId: profileData.imageId || "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
if (!cek.success) {
|
// Update form field
|
||||||
const errors = cek.error.issues
|
updateField(field: keyof typeof defaultForm, value: string) {
|
||||||
|
this.form[field] = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Submit form
|
||||||
|
async submit() {
|
||||||
|
// Validate form
|
||||||
|
const validation = templateForm.safeParse(this.form);
|
||||||
|
|
||||||
|
if (!validation.success) {
|
||||||
|
const errors = validation.error.issues
|
||||||
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
||||||
.join(", ");
|
.join(", ");
|
||||||
toast.error(`Form tidak valid: ${errors}`);
|
toast.error(`Form tidak valid: ${errors}`);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
this.loading = true;
|
||||||
stateProfilePPID.update.loading = true;
|
this.error = null;
|
||||||
const res = await ApiFetch.api.ppid.profileppid["update"].post(data);
|
|
||||||
|
|
||||||
if (res.status === 200) {
|
try {
|
||||||
|
const response = await fetch(`/api/ppid/profileppid/${this.id}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(this.form),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorData = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(errorData.message || `HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
toast.success("Berhasil update profile");
|
toast.success("Berhasil update profile");
|
||||||
await stateProfilePPID.findById.load(data.id);
|
// Refresh profile data
|
||||||
|
await stateProfilePPID.profile.load(this.id);
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
toast.error("Gagal update profile");
|
throw new Error(result.message || "Gagal update profile");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error((error as Error).message);
|
const errorMessage = (error as Error).message;
|
||||||
|
this.error = errorMessage;
|
||||||
|
console.error("Update profile error:", errorMessage);
|
||||||
toast.error("Terjadi kesalahan saat update profile");
|
toast.error("Terjadi kesalahan saat update profile");
|
||||||
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
stateProfilePPID.update.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Reset form
|
||||||
|
reset() {
|
||||||
|
this.id = "";
|
||||||
|
this.form = { ...defaultForm };
|
||||||
|
this.error = null;
|
||||||
|
this.loading = false;
|
||||||
|
this.isReadOnly = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
// Helper methods
|
||||||
* Bagian untuk upload gambar profil
|
async loadForEdit(id: string) {
|
||||||
*/
|
const profileData = await this.profile.load(id);
|
||||||
uploadImage: {
|
if (profileData) {
|
||||||
loading: false,
|
this.editForm.initialize(profileData);
|
||||||
|
}
|
||||||
/**
|
return profileData;
|
||||||
* Mengunggah gambar profil berdasarkan ID.
|
|
||||||
* @param {File} file - File gambar yang akan diunggah.
|
|
||||||
* @param {string} id - ID dari profil yang akan diperbarui gambarnya.
|
|
||||||
*/
|
|
||||||
async save(file: File, id: string) {
|
|
||||||
if (!file || !id) {
|
|
||||||
toast.error("File atau ID harus disertakan");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
stateProfilePPID.uploadImage.loading = true;
|
|
||||||
|
|
||||||
const form = new FormData();
|
|
||||||
form.append("file", file);
|
|
||||||
form.append("id", id);
|
|
||||||
|
|
||||||
const res = await ApiFetch.api.ppid.profileppid["edit-img"].post(form);
|
|
||||||
|
|
||||||
if (res.status === 200) {
|
|
||||||
toast.success("Berhasil mengunggah gambar");
|
|
||||||
await stateProfilePPID.findById.load(id);
|
|
||||||
} else {
|
|
||||||
toast.error("Gagal mengunggah gambar");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error((error as Error).message);
|
|
||||||
toast.error("Terjadi kesalahan saat mengunggah gambar");
|
|
||||||
} finally {
|
|
||||||
stateProfilePPID.uploadImage.loading = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.profile.reset();
|
||||||
|
this.editForm.reset();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
export default stateProfilePPID;
|
||||||
* Ekspor state utama ProfilePPID untuk digunakan di komponen lain.
|
|
||||||
*/
|
|
||||||
export default stateProfilePPID;
|
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import { proxy } from "valtio";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const templateForm = z.object({
|
||||||
|
name: z.string().min(3, "Nama minimal 3 karakter"),
|
||||||
|
imageId: z.string().min(1, "Gambar wajib dipilih"),
|
||||||
|
})
|
||||||
|
|
||||||
|
const defaultForm = {
|
||||||
|
name: "",
|
||||||
|
imageId: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
type StrukturPPIDForm = Prisma.StrukturPPIDGetPayload<{
|
||||||
|
select: {
|
||||||
|
id: true;
|
||||||
|
name: true;
|
||||||
|
imageId: true;
|
||||||
|
image?: {
|
||||||
|
select: {
|
||||||
|
link: true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const stateStrukturPPID = proxy({
|
||||||
|
struktur: {
|
||||||
|
data: null as StrukturPPIDForm | 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/ppid/strukturppid/${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 struktur")
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = (error as Error).message;
|
||||||
|
this.error = errorMessage;
|
||||||
|
console.error("Load struktur error:", errorMessage);
|
||||||
|
toast.error("Terjadi kesalahan saat mengambil data struktur");
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.data = null;
|
||||||
|
this.error = null;
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
editStruktur: {
|
||||||
|
id: "",
|
||||||
|
form: { ...defaultForm },
|
||||||
|
loading: false,
|
||||||
|
error: null as string | null,
|
||||||
|
isReadOnly: false,
|
||||||
|
|
||||||
|
initialize(strukturData: StrukturPPIDForm) {
|
||||||
|
this.id = strukturData.id;
|
||||||
|
this.isReadOnly = false;
|
||||||
|
this.form = {
|
||||||
|
name: strukturData.name || "",
|
||||||
|
imageId: strukturData.imageId || "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
updateField(field: keyof typeof defaultForm, value: string) {
|
||||||
|
this.form[field] = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
async submit() {
|
||||||
|
const validation = templateForm.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/ppid/strukturppid/${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 struktur");
|
||||||
|
await stateStrukturPPID.struktur.load(this.id);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || "Gagal update struktur");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = (error as Error).message;
|
||||||
|
this.error = errorMessage;
|
||||||
|
console.error("Update struktur error:", errorMessage);
|
||||||
|
toast.error("Terjadi kesalahan saat update struktur");
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.id = "";
|
||||||
|
this.form = { ...defaultForm };
|
||||||
|
this.error = null;
|
||||||
|
this.loading = false;
|
||||||
|
this.isReadOnly = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async loadForEdit(id: string) {
|
||||||
|
const strukturData = await this.struktur.load(id);
|
||||||
|
if (strukturData) {
|
||||||
|
this.editStruktur.initialize(strukturData);
|
||||||
|
}
|
||||||
|
return strukturData;
|
||||||
|
},
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.struktur.reset();
|
||||||
|
this.editStruktur.reset();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default stateStrukturPPID;
|
||||||
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -12,30 +11,30 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { IconImageInPicture } from "@tabler/icons-react";
|
import { IconArrowBack, IconImageInPicture } from "@tabler/icons-react";
|
||||||
|
import { useParams, useRouter } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useRouter, useParams } from "next/navigation";
|
|
||||||
import { useProxy } from "valtio/utils";
|
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
import { useProxy } from "valtio/utils";
|
||||||
|
|
||||||
|
|
||||||
|
import EditEditor from "@/app/admin/(dashboard)/_com/editEditor";
|
||||||
|
import colors from "@/con/colors";
|
||||||
import ApiFetch from "@/lib/api-fetch";
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
import { FileInput } from "@mantine/core";
|
import { FileInput } from "@mantine/core";
|
||||||
import stateDashboardBerita from "../../../../_state/desa/berita";
|
|
||||||
import { Prisma } from "@prisma/client";
|
|
||||||
import { useShallowEffect } from "@mantine/hooks";
|
import { useShallowEffect } from "@mantine/hooks";
|
||||||
import { BeritaEditor } from "../../_com/BeritaEditor";
|
import { Prisma } from "@prisma/client";
|
||||||
|
import stateDashboardBerita from "../../../../_state/desa/berita";
|
||||||
|
|
||||||
function BeritaEdit() {
|
function EditBerita() {
|
||||||
const beritaState = useProxy(stateDashboardBerita);
|
const beritaState = useProxy(stateDashboardBerita);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
|
|
||||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||||
const [file, setFile] = useState<File | null>(null);
|
const [file, setFile] = useState<File | null>(null);
|
||||||
const [editorInstance, setEditorInstance] = useState<any>(null);
|
|
||||||
const [isEditorReady, setIsEditorReady] = useState(false);
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
judul: beritaState.berita.edit.form.judul || '',
|
judul: beritaState.berita.edit.form.judul || '',
|
||||||
deskripsi: beritaState.berita.edit.form.deskripsi || '',
|
deskripsi: beritaState.berita.edit.form.deskripsi || '',
|
||||||
@@ -49,7 +48,7 @@ function BeritaEdit() {
|
|||||||
const loadBerita = async () => {
|
const loadBerita = async () => {
|
||||||
const id = params?.id as string;
|
const id = params?.id as string;
|
||||||
if (!id) return;
|
if (!id) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await stateDashboardBerita.berita.edit.load(id); // akses langsung, bukan dari proxy
|
const data = await stateDashboardBerita.berita.edit.load(id); // akses langsung, bukan dari proxy
|
||||||
if (data) {
|
if (data) {
|
||||||
@@ -60,7 +59,7 @@ function BeritaEdit() {
|
|||||||
content: data.content || '',
|
content: data.content || '',
|
||||||
imageId: data.imageId || '',
|
imageId: data.imageId || '',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (data?.image?.link) {
|
if (data?.image?.link) {
|
||||||
setPreviewImage(data.image.link);
|
setPreviewImage(data.image.link);
|
||||||
}
|
}
|
||||||
@@ -70,57 +69,28 @@ function BeritaEdit() {
|
|||||||
toast.error("Gagal memuat data berita");
|
toast.error("Gagal memuat data berita");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
loadBerita();
|
loadBerita();
|
||||||
}, [params?.id]); // ✅ hapus beritaState dari dependency
|
}, [params?.id]); // ✅ hapus beritaState dari dependency
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Handle editor ready
|
|
||||||
const handleEditorReady = (editor: any) => {
|
|
||||||
setEditorInstance(editor);
|
|
||||||
setIsEditorReady(true);
|
|
||||||
|
|
||||||
// Set initial content if exists
|
|
||||||
if (formData.content) {
|
|
||||||
editor.commands.setContent(formData.content);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
if (!isEditorReady || !editorInstance) {
|
|
||||||
return toast.error("Editor belum siap");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const htmlContent = editorInstance.getHTML();
|
|
||||||
if (!htmlContent || htmlContent === "<p></p>") {
|
|
||||||
return toast.warn("Konten tidak boleh kosong");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update form data with editor content
|
|
||||||
const updatedFormData = {
|
|
||||||
...formData,
|
|
||||||
content: htmlContent
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update global state with form data
|
// Update global state with form data
|
||||||
beritaState.berita.edit.form = {
|
beritaState.berita.edit.form = {
|
||||||
judul: updatedFormData.judul,
|
...beritaState.berita.edit.form,
|
||||||
deskripsi: updatedFormData.deskripsi,
|
judul: formData.judul,
|
||||||
content: updatedFormData.content,
|
deskripsi: formData.deskripsi,
|
||||||
kategoriBeritaId: updatedFormData.kategoriBeritaId || '',
|
content: formData.content,
|
||||||
imageId: beritaState.berita.edit.form.imageId // Keep existing imageId if not changed
|
kategoriBeritaId: formData.kategoriBeritaId || '',
|
||||||
|
imageId: formData.imageId // Keep existing imageId if not changed
|
||||||
};
|
};
|
||||||
|
|
||||||
// Jika ada file baru, upload
|
// Jika ada file baru, upload
|
||||||
if (file) {
|
if (file) {
|
||||||
const res = await ApiFetch.api.fileStorage.create.post({
|
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
|
||||||
file,
|
|
||||||
name: file.name,
|
|
||||||
});
|
|
||||||
|
|
||||||
const uploaded = res.data?.data;
|
const uploaded = res.data?.data;
|
||||||
|
|
||||||
if (!uploaded?.id) {
|
if (!uploaded?.id) {
|
||||||
return toast.error("Gagal upload gambar");
|
return toast.error("Gagal upload gambar");
|
||||||
}
|
}
|
||||||
@@ -129,7 +99,6 @@ function BeritaEdit() {
|
|||||||
beritaState.berita.edit.form.imageId = uploaded.id;
|
beritaState.berita.edit.form.imageId = uploaded.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
await beritaState.berita.edit.update();
|
await beritaState.berita.edit.update();
|
||||||
toast.success("Berita berhasil diperbarui!");
|
toast.success("Berita berhasil diperbarui!");
|
||||||
router.push("/admin/desa/berita");
|
router.push("/admin/desa/berita");
|
||||||
@@ -140,12 +109,18 @@ function BeritaEdit() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors["blue-button"]} size={30} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
<Paper bg={"white"} p={"md"} w={{ base: "100%", md: "50%" }}>
|
<Paper bg={"white"} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||||
<Stack gap={"xs"}>
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Edit Berita</Title>
|
||||||
<TextInput
|
<TextInput
|
||||||
value={formData.judul}
|
value={formData.judul}
|
||||||
onChange={(e) => setFormData({...formData, judul: e.target.value})}
|
onChange={(e) => setFormData({ ...formData, judul: e.target.value })}
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
||||||
placeholder="masukkan judul"
|
placeholder="masukkan judul"
|
||||||
/>
|
/>
|
||||||
@@ -162,7 +137,7 @@ function BeritaEdit() {
|
|||||||
|
|
||||||
<TextInput
|
<TextInput
|
||||||
value={formData.deskripsi}
|
value={formData.deskripsi}
|
||||||
onChange={(e) => setFormData({...formData, deskripsi: e.target.value})}
|
onChange={(e) => setFormData({ ...formData, deskripsi: e.target.value })}
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
|
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
|
||||||
placeholder="masukkan deskripsi"
|
placeholder="masukkan deskripsi"
|
||||||
/>
|
/>
|
||||||
@@ -190,15 +165,16 @@ function BeritaEdit() {
|
|||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||||
<BeritaEditor
|
<EditEditor
|
||||||
initialContent={formData.content}
|
value={formData.content}
|
||||||
onEditorReady={handleEditorReady}
|
onChange={(htmlContent) => {
|
||||||
showSubmit={false}
|
setFormData((prev) => ({ ...prev, content: htmlContent }));
|
||||||
onUpdate={(content) => setFormData({...formData, content})}
|
beritaState.berita.edit.form.content = htmlContent;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Button onClick={handleSubmit}>Simpan Perubahan</Button>
|
<Button onClick={handleSubmit}>Edit Berita</Button>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -233,9 +209,9 @@ function SelectCategory({
|
|||||||
return <Skeleton height={38} />;
|
return <Skeleton height={38} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const selectedValue = value || defaultValue;
|
const selectedValue = value || defaultValue;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
|
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
|
||||||
@@ -262,4 +238,4 @@ function SelectCategory({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BeritaEdit;
|
export default EditBerita;
|
||||||
120
src/app/admin/(dashboard)/desa/berita/[id]/page.tsx
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
'use client'
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
|
||||||
|
import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||||
|
import { useParams, useRouter } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
|
import stateDashboardBerita from '../../../_state/desa/berita';
|
||||||
|
|
||||||
|
function DetailBerita() {
|
||||||
|
const beritaState = useProxy(stateDashboardBerita)
|
||||||
|
const [modalHapus, setModalHapus] = useState(false)
|
||||||
|
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||||
|
const params = useParams()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
beritaState.berita.findUnique.load(params?.id as string)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
|
const handleHapus = () => {
|
||||||
|
if (selectedId) {
|
||||||
|
beritaState.berita.delete.byId(selectedId)
|
||||||
|
setModalHapus(false)
|
||||||
|
setSelectedId(null)
|
||||||
|
router.push("/admin/desa/berita")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!beritaState.berita.findUnique.data) {
|
||||||
|
return (
|
||||||
|
<Stack py={10}>
|
||||||
|
{Array.from({ length: 10 }).map((_, k) => (
|
||||||
|
<Skeleton key={k} h={40} />
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper bg={colors['white-1']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Berita</Text>
|
||||||
|
{beritaState.berita.findUnique.data ? (
|
||||||
|
<Paper key={beritaState.berita.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Kategori</Text>
|
||||||
|
<Text fz={"lg"}>{beritaState.berita.findUnique.data?.kategoriBerita?.name}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Judul</Text>
|
||||||
|
<Text fz={"lg"}>{beritaState.berita.findUnique.data?.judul}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
|
||||||
|
<Text fz={"lg"} >{beritaState.berita.findUnique.data?.deskripsi}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Gambar</Text>
|
||||||
|
<Image w={{ base: 150, md: 150, lg: 150 }} src={beritaState.berita.findUnique.data?.image?.link} alt="gambar" />
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Konten</Text>
|
||||||
|
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: beritaState.berita.findUnique.data?.content }} />
|
||||||
|
</Box>
|
||||||
|
<Flex gap={"xs"} mt={10}>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (beritaState.berita.findUnique.data) {
|
||||||
|
setSelectedId(beritaState.berita.findUnique.data.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={beritaState.berita.delete.loading || !beritaState.berita.findUnique.data}
|
||||||
|
color={"red"}
|
||||||
|
>
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (beritaState.berita.findUnique.data) {
|
||||||
|
router.push(`/admin/desa/berita/${beritaState.berita.findUnique.data.id}/edit`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={!beritaState.berita.findUnique.data}
|
||||||
|
color={"green"}
|
||||||
|
>
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
) : null}
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Konfirmasi Hapus */}
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text='Apakah anda yakin ingin menghapus berita ini?'
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailBerita;
|
||||||
168
src/app/admin/(dashboard)/desa/berita/create/page.tsx
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import ApiFetch from '@/lib/api-fetch';
|
||||||
|
import { Box, Button, Center, FileInput, Image, Paper, Select, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { Prisma } from '@prisma/client';
|
||||||
|
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import CreateEditor from '../../../_com/createEditor';
|
||||||
|
import stateDashboardBerita from '../../../_state/desa/berita';
|
||||||
|
|
||||||
|
export default function CreateBerita() {
|
||||||
|
const beritaState = useProxy(stateDashboardBerita);
|
||||||
|
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||||
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
// Reset state di valtio
|
||||||
|
beritaState.berita.create.form = {
|
||||||
|
judul: "",
|
||||||
|
deskripsi: "",
|
||||||
|
kategoriBeritaId: "",
|
||||||
|
imageId: "",
|
||||||
|
content: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reset state lokal
|
||||||
|
setPreviewImage(null);
|
||||||
|
setFile(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!file) {
|
||||||
|
return toast.warn("Pilih file gambar terlebih dahulu");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload gambar dulu
|
||||||
|
const res = await ApiFetch.api.fileStorage.create.post({
|
||||||
|
file,
|
||||||
|
name: file.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
const uploaded = res.data?.data;
|
||||||
|
if (!uploaded?.id) {
|
||||||
|
return toast.error("Gagal upload gambar");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simpan ID gambar ke form
|
||||||
|
beritaState.berita.create.form.imageId = uploaded.id;
|
||||||
|
|
||||||
|
// Submit data berita
|
||||||
|
await beritaState.berita.create.create();
|
||||||
|
|
||||||
|
// Reset form setelah submit
|
||||||
|
resetForm();
|
||||||
|
router.push("/admin/desa/berita")
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Create Berita</Title>
|
||||||
|
<TextInput
|
||||||
|
value={beritaState.berita.create.form.judul}
|
||||||
|
onChange={(val) => {
|
||||||
|
beritaState.berita.create.form.judul = val.target.value;
|
||||||
|
}}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
||||||
|
placeholder="masukkan judul"
|
||||||
|
/>
|
||||||
|
<SelectCategory
|
||||||
|
onChange={(val) => {
|
||||||
|
beritaState.berita.create.form.kategoriBeritaId = val.id;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
value={beritaState.berita.create.form.deskripsi}
|
||||||
|
onChange={(val) => {
|
||||||
|
beritaState.berita.create.form.deskripsi = val.target.value;
|
||||||
|
}}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
|
||||||
|
placeholder="masukkan deskripsi"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FileInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar</Text>}
|
||||||
|
value={file}
|
||||||
|
onChange={async (e) => {
|
||||||
|
if (!e) return;
|
||||||
|
setFile(e);
|
||||||
|
const base64 = await e.arrayBuffer().then((buf) =>
|
||||||
|
"data:image/png;base64," + Buffer.from(buf).toString("base64")
|
||||||
|
);
|
||||||
|
setPreviewImage(base64);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{previewImage ? (
|
||||||
|
<Image alt="" src={previewImage} w={200} h={200} />
|
||||||
|
) : (
|
||||||
|
<Center w={200} h={200} bg={"gray"}>
|
||||||
|
<IconImageInPicture />
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
<Box>
|
||||||
|
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||||
|
<CreateEditor
|
||||||
|
value={beritaState.berita.create.form.content}
|
||||||
|
onChange={(htmlContent) => {
|
||||||
|
beritaState.berita.create.form.content = htmlContent;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Button bg={colors['blue-button']} onClick={handleSubmit}>Simpan Berita</Button>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
function SelectCategory({
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
onChange: (value: Prisma.KategoriBeritaGetPayload<{
|
||||||
|
select: {
|
||||||
|
name: true;
|
||||||
|
id: true;
|
||||||
|
};
|
||||||
|
}>) => void;
|
||||||
|
}) {
|
||||||
|
const categoryState = useProxy(stateDashboardBerita.category);
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
categoryState.findMany.load();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!categoryState.findMany.data) {
|
||||||
|
return <Skeleton height={38} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
|
||||||
|
placeholder="Pilih kategori"
|
||||||
|
data={categoryState.findMany.data.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}))}
|
||||||
|
onChange={(val) => {
|
||||||
|
const selected = categoryState.findMany.data?.find((item) => item.id === val);
|
||||||
|
if (selected) {
|
||||||
|
onChange(selected);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
searchable
|
||||||
|
nothingFoundMessage="Tidak ditemukan"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,214 +1,37 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import ApiFetch from '@/lib/api-fetch';
|
import { Box, Button, Grid, GridCol, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, TextInput, Title } from '@mantine/core';
|
||||||
import { ActionIcon, Box, Button, Center, FileInput, Flex, Image, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
|
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
import { Prisma } from '@prisma/client';
|
import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||||
import { IconEdit, IconImageInPicture, IconX } from '@tabler/icons-react';
|
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus';
|
|
||||||
import stateDashboardBerita from '../../_state/desa/berita';
|
import stateDashboardBerita from '../../_state/desa/berita';
|
||||||
import { BeritaEditor } from './_com/BeritaEditor';
|
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Title order={3}>Berita</Title>
|
<Grid>
|
||||||
<BeritaCreate />
|
<GridCol span={{ base: 12, md: 9 }}>
|
||||||
|
<Title order={3}>Berita</Title>
|
||||||
|
</GridCol>
|
||||||
|
<GridCol span={{ base: 12, md: 3 }}>
|
||||||
|
<Paper radius={"lg"} bg={colors['white-1']}>
|
||||||
|
<TextInput
|
||||||
|
radius={"lg"}
|
||||||
|
placeholder='pencarian'
|
||||||
|
leftSection={<IconSearch size={20} />}
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
<BeritaList />
|
<BeritaList />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function BeritaCreate() {
|
|
||||||
const beritaState = useProxy(stateDashboardBerita);
|
|
||||||
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
|
||||||
const [file, setFile] = useState<File | null>(null);
|
|
||||||
const [editorInstance, setEditorInstance] = useState<any>(null);
|
|
||||||
|
|
||||||
const resetForm = () => {
|
|
||||||
// Reset state di valtio
|
|
||||||
beritaState.berita.create.form = {
|
|
||||||
judul: "",
|
|
||||||
deskripsi: "",
|
|
||||||
kategoriBeritaId: "",
|
|
||||||
imageId: "",
|
|
||||||
content: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reset state lokal
|
|
||||||
setPreviewImage(null);
|
|
||||||
setFile(null);
|
|
||||||
if (editorInstance) {
|
|
||||||
editorInstance.commands.setContent(""); // Kosongkan editor
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
|
||||||
if (!file) {
|
|
||||||
return toast.warn("Pilih file gambar terlebih dahulu");
|
|
||||||
}
|
|
||||||
if (!editorInstance) return toast.error("Editor belum siap");
|
|
||||||
|
|
||||||
const htmlContent = editorInstance.getHTML();
|
|
||||||
if (!htmlContent || htmlContent === "<p></p>") return toast.warn("Konten tidak boleh kosong");
|
|
||||||
|
|
||||||
beritaState.berita.create.form.content = htmlContent;
|
|
||||||
|
|
||||||
// Upload gambar dulu
|
|
||||||
const res = await ApiFetch.api.fileStorage.create.post({
|
|
||||||
file,
|
|
||||||
name: file.name,
|
|
||||||
});
|
|
||||||
|
|
||||||
const uploaded = res.data?.data;
|
|
||||||
if (!uploaded?.id) {
|
|
||||||
return toast.error("Gagal upload gambar");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simpan ID gambar ke form
|
|
||||||
beritaState.berita.create.form.imageId = uploaded.id;
|
|
||||||
|
|
||||||
// Submit data berita
|
|
||||||
await beritaState.berita.create.create();
|
|
||||||
|
|
||||||
toast.success("Berita berhasil disimpan!");
|
|
||||||
|
|
||||||
// Reset form setelah submit
|
|
||||||
resetForm();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box py={10}>
|
|
||||||
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<TextInput
|
|
||||||
value={beritaState.berita.create.form.judul}
|
|
||||||
onChange={(val) => {
|
|
||||||
beritaState.berita.create.form.judul = val.target.value;
|
|
||||||
}}
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
|
||||||
placeholder="masukkan judul"
|
|
||||||
/>
|
|
||||||
<SelectCategory
|
|
||||||
onChange={(val) => {
|
|
||||||
beritaState.berita.create.form.kategoriBeritaId = val.id;
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
value={beritaState.berita.create.form.deskripsi}
|
|
||||||
onChange={(val) => {
|
|
||||||
beritaState.berita.create.form.deskripsi = val.target.value;
|
|
||||||
}}
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
|
|
||||||
placeholder="masukkan deskripsi"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FileInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar</Text>}
|
|
||||||
value={file}
|
|
||||||
onChange={async (e) => {
|
|
||||||
if (!e) return;
|
|
||||||
setFile(e);
|
|
||||||
const base64 = await e.arrayBuffer().then((buf) =>
|
|
||||||
"data:image/png;base64," + Buffer.from(buf).toString("base64")
|
|
||||||
);
|
|
||||||
setPreviewImage(base64);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{previewImage ? (
|
|
||||||
<Image alt="" src={previewImage} w={200} h={200} />
|
|
||||||
) : (
|
|
||||||
<Center w={200} h={200} bg={"gray"}>
|
|
||||||
<IconImageInPicture />
|
|
||||||
</Center>
|
|
||||||
)}
|
|
||||||
<Box>
|
|
||||||
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
|
||||||
<BeritaEditor
|
|
||||||
showSubmit={false}
|
|
||||||
onEditorReady={(ed) => setEditorInstance(ed)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Button onClick={handleSubmit}>Simpan Berita</Button>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// function BeritaList() {
|
|
||||||
// const beritaState = useProxy(stateDashboardBerita)
|
|
||||||
// useShallowEffect(() => {
|
|
||||||
// beritaState.berita.findMany.load()
|
|
||||||
// }, [])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// const router = useRouter()
|
|
||||||
|
|
||||||
// if (!beritaState.berita.findMany.data) return <Stack py={10}>
|
|
||||||
// {Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
|
|
||||||
// </Stack>
|
|
||||||
// return (
|
|
||||||
// <Box py={10}>
|
|
||||||
// <Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
// <Stack>
|
|
||||||
// <Text fz={"xl"} fw={"bold"}>List Berita</Text>
|
|
||||||
// <SimpleGrid cols={{ base: 1, md: 4 }}>
|
|
||||||
// {beritaState.berita.findMany.data?.map((item) => (
|
|
||||||
// <Paper key={item.id} bg={colors['BG-trans']} p={'md'}>
|
|
||||||
// <Box >
|
|
||||||
// <Flex justify="flex-end" mt={10}>
|
|
||||||
// <ActionIcon
|
|
||||||
// onClick={() => beritaState.berita.delete.byId(item.id)}
|
|
||||||
// disabled={beritaState.berita.delete.loading}
|
|
||||||
// color={colors['blue-button']} variant='transparent'>
|
|
||||||
// <IconX size={20} />
|
|
||||||
// </ActionIcon>
|
|
||||||
// <ActionIcon onClick={() => {
|
|
||||||
// router.push("/desa/berita/edit");
|
|
||||||
// }} color={colors['blue-button']} variant='transparent'>
|
|
||||||
// <IconEdit size={20} />
|
|
||||||
// </ActionIcon>
|
|
||||||
// </Flex>
|
|
||||||
// <Text fw={"bold"} fz={"sm"}>
|
|
||||||
// Kategori
|
|
||||||
// </Text>
|
|
||||||
// <Text>{item.kategoriBerita?.name}</Text>
|
|
||||||
// <Text fw={"bold"} fz={"sm"}>
|
|
||||||
// Judul
|
|
||||||
// </Text>
|
|
||||||
// <Text>{item.judul}</Text>
|
|
||||||
// <Text lineClamp={1} fw={"bold"} fz={"sm"}>
|
|
||||||
// Deskripsi
|
|
||||||
// </Text>
|
|
||||||
// <Text size='sm' lineClamp={2}>{item.deskripsi}</Text>
|
|
||||||
// <Text fw={"bold"} fz={"sm"}>
|
|
||||||
// Gambar
|
|
||||||
// </Text>
|
|
||||||
// <Image w={{ base: 100, md: 150 }} src={item.image?.link} alt="gambar" />
|
|
||||||
// </Box>
|
|
||||||
// </Paper>
|
|
||||||
// ))}
|
|
||||||
// </SimpleGrid>
|
|
||||||
// </Stack>
|
|
||||||
// </Paper>
|
|
||||||
// </Box>
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
|
|
||||||
function BeritaList() {
|
function BeritaList() {
|
||||||
const beritaState = useProxy(stateDashboardBerita)
|
const beritaState = useProxy(stateDashboardBerita)
|
||||||
const [modalHapus, setModalHapus] = useState(false)
|
|
||||||
const [selectedId, setSelectedId] = useState<string | null>(null)
|
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
beritaState.berita.findMany.load()
|
beritaState.berita.findMany.load()
|
||||||
@@ -216,20 +39,10 @@ function BeritaList() {
|
|||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const handleHapus = () => {
|
|
||||||
if (selectedId) {
|
|
||||||
beritaState.berita.delete.byId(selectedId)
|
|
||||||
setModalHapus(false)
|
|
||||||
setSelectedId(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!beritaState.berita.findMany.data) {
|
if (!beritaState.berita.findMany.data) {
|
||||||
return (
|
return (
|
||||||
<Stack py={10}>
|
<Stack py={10}>
|
||||||
{Array.from({ length: 10 }).map((_, k) => (
|
<Skeleton h={500} />
|
||||||
<Skeleton key={k} h={40} />
|
|
||||||
))}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -238,94 +51,54 @@ function BeritaList() {
|
|||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
<Stack>
|
<Stack>
|
||||||
<Text fz={"xl"} fw={"bold"}>List Berita</Text>
|
<Grid>
|
||||||
<SimpleGrid cols={{ base: 1, md: 4 }}>
|
<GridCol span={{ base: 12, md: 11 }}>
|
||||||
{beritaState.berita.findMany.data?.map((item) => (
|
<Text fz={"xl"} fw={"bold"}>List Berita</Text>
|
||||||
<Paper key={item.id} bg={colors['BG-trans']} p={'md'}>
|
</GridCol>
|
||||||
<Box>
|
<GridCol span={{ base: 12, md: 1 }}>
|
||||||
<Flex justify="flex-end" mt={10}>
|
<Button onClick={() => router.push("/admin/desa/berita/create")} bg={colors['blue-button']}>
|
||||||
<ActionIcon
|
<IconCircleDashedPlus size={25} />
|
||||||
onClick={() => {
|
</Button>
|
||||||
setSelectedId(item.id)
|
</GridCol>
|
||||||
setModalHapus(true)
|
</Grid>
|
||||||
}}
|
<Box style={{ overflowX: "auto" }}>
|
||||||
disabled={beritaState.berita.delete.loading}
|
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
||||||
color={colors['blue-button']} variant='transparent'
|
<TableThead>
|
||||||
>
|
<TableTr>
|
||||||
<IconX size={20} />
|
<TableTh w={250}>Judul</TableTh>
|
||||||
</ActionIcon>
|
<TableTh w={250}>Kategori</TableTh>
|
||||||
<ActionIcon
|
<TableTh w={250}>Image</TableTh>
|
||||||
onClick={() => router.push(`/admin/desa/berita/edit/${item.id}`)}
|
<TableTh w={200}>Detail</TableTh>
|
||||||
color={colors['blue-button']} variant='transparent'
|
|
||||||
>
|
</TableTr>
|
||||||
<IconEdit size={20} />
|
</TableThead>
|
||||||
</ActionIcon>
|
<TableTbody >
|
||||||
</Flex>
|
{beritaState.berita.findMany.data?.map((item) => (
|
||||||
<Text fw={"bold"} fz={"sm"}>Kategori</Text>
|
<TableTr key={item.id}>
|
||||||
<Text>{item.kategoriBerita?.name}</Text>
|
<TableTd >
|
||||||
<Text fw={"bold"} fz={"sm"}>Judul</Text>
|
<Box w={100}>
|
||||||
<Text>{item.judul}</Text>
|
<Text truncate="end" fz={"sm"}>{item.judul}</Text>
|
||||||
<Text lineClamp={1} fw={"bold"} fz={"sm"}>Deskripsi</Text>
|
</Box>
|
||||||
<Text size='sm' lineClamp={2}>{item.deskripsi}</Text>
|
</TableTd>
|
||||||
<Text fw={"bold"} fz={"sm"}>Gambar</Text>
|
<TableTd >{item.kategoriBerita?.name}</TableTd>
|
||||||
<Image w={{ base: 150, md: 150, lg: 150 }} src={item.image?.link} alt="gambar" />
|
<TableTd>
|
||||||
</Box>
|
<Image w={100} src={item.image?.link} alt="gambar" />
|
||||||
</Paper>
|
</TableTd>
|
||||||
))}
|
<TableTd>
|
||||||
</SimpleGrid>
|
<Button bg={"green"} onClick={() => router.push(`/admin/desa/berita/${item.id}`)}>
|
||||||
|
<IconDeviceImacCog size={25} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
|
||||||
|
</TableTr>
|
||||||
|
))}
|
||||||
|
</TableTbody>
|
||||||
|
</Table> </Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{/* Modal Konfirmasi Hapus */}
|
|
||||||
<ModalKonfirmasiHapus
|
|
||||||
opened={modalHapus}
|
|
||||||
onClose={() => setModalHapus(false)}
|
|
||||||
onConfirm={handleHapus}
|
|
||||||
text='Apakah anda yakin ingin menghapus berita ini?'
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function SelectCategory({
|
|
||||||
onChange,
|
|
||||||
}: {
|
|
||||||
onChange: (value: Prisma.KategoriBeritaGetPayload<{
|
|
||||||
select: {
|
|
||||||
name: true;
|
|
||||||
id: true;
|
|
||||||
};
|
|
||||||
}>) => void;
|
|
||||||
}) {
|
|
||||||
const categoryState = useProxy(stateDashboardBerita.category);
|
|
||||||
|
|
||||||
useShallowEffect(() => {
|
|
||||||
categoryState.findMany.load();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (!categoryState.findMany.data) {
|
|
||||||
return <Skeleton height={38} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Select
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
|
|
||||||
placeholder="Pilih kategori"
|
|
||||||
data={categoryState.findMany.data.map((item) => ({
|
|
||||||
label: item.name,
|
|
||||||
value: item.id,
|
|
||||||
}))}
|
|
||||||
onChange={(val) => {
|
|
||||||
const selected = categoryState.findMany.data?.find((item) => item.id === val);
|
|
||||||
if (selected) {
|
|
||||||
onChange(selected);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
searchable
|
|
||||||
nothingFoundMessage="Tidak ditemukan"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Page;
|
export default Page;
|
||||||
|
|||||||
46
src/app/admin/(dashboard)/desa/gallery/foto/create/page.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
'use client'
|
||||||
|
import { KeamananEditor } from '@/app/admin/(dashboard)/keamanan/_com/keamananEditor';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function CreateFoto() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Create Foto</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Judul Foto</Text>}
|
||||||
|
placeholder='Masukkan judul foto'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Tanggal Foto</Text>}
|
||||||
|
placeholder='Masukkan tanggal foto'
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"sm"}>Deskripsi Foto</Text>
|
||||||
|
<KeamananEditor
|
||||||
|
showSubmit={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateFoto;
|
||||||
62
src/app/admin/(dashboard)/desa/gallery/foto/detail/page.tsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Flex, Paper, Stack, Text } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
|
|
||||||
|
function DetailFoto() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Foto</Text>
|
||||||
|
|
||||||
|
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Judul Foto</Text>
|
||||||
|
<Text>Foto 1</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Tanggal Foto</Text>
|
||||||
|
<Text>2022-01-01</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Deskripsi Foto</Text>
|
||||||
|
<Text>Deskripsi Foto 1</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Flex gap={"xs"}>
|
||||||
|
<Button color="red">
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => router.push('/admin/desa/gallery/foto/edit')} color="green">
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Hapus
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text="Apakah anda yakin ingin menghapus potensi ini?"
|
||||||
|
/> */}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailFoto;
|
||||||
|
|
||||||
46
src/app/admin/(dashboard)/desa/gallery/foto/edit/page.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
'use client'
|
||||||
|
import { KeamananEditor } from '@/app/admin/(dashboard)/keamanan/_com/keamananEditor';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function EditFoto() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Edit Foto</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Judul Foto</Text>}
|
||||||
|
placeholder='Masukkan judul foto'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Tanggal Foto</Text>}
|
||||||
|
placeholder='Masukkan tanggal foto'
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"sm"}>Deskripsi Foto</Text>
|
||||||
|
<KeamananEditor
|
||||||
|
showSubmit={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditFoto;
|
||||||
46
src/app/admin/(dashboard)/desa/gallery/foto/page.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||||
|
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import JudulListTab from '../../../_com/jusulListTab';
|
||||||
|
|
||||||
|
function Foto() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<JudulListTab
|
||||||
|
title='List Foto'
|
||||||
|
href='/admin/desa/gallery/foto/create'
|
||||||
|
placeholder='pencarian'
|
||||||
|
searchIcon={<IconSearch size={16} />}
|
||||||
|
/>
|
||||||
|
<Table striped withTableBorder withRowBorders>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh>Judul Foto</TableTh>
|
||||||
|
<TableTh>Tanggal Foto</TableTh>
|
||||||
|
<TableTh>Deskripsi Foto</TableTh>
|
||||||
|
<TableTh>Detail</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
<TableTr>
|
||||||
|
<TableTd>Foto 1</TableTd>
|
||||||
|
<TableTd>2022-01-01</TableTd>
|
||||||
|
<TableTd>Deskripsi Foto 1</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button onClick={() => router.push('/admin/desa/gallery/foto/detail')}>
|
||||||
|
<IconDeviceImac size={20} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Foto;
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||||
import { IconPhoto, IconVideo } from '@tabler/icons-react';
|
import { IconPhoto, IconVideo } from '@tabler/icons-react';
|
||||||
import Foto from './ui/foto/page';
|
import Foto from './foto/page';
|
||||||
import Video from './ui/video/page';
|
import Video from './video/page';
|
||||||
|
|
||||||
function Gallery() {
|
function Gallery() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Paper, Stack, Title } from '@mantine/core';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function ListFoto() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>List Foto</Title>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ListFoto;
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Button, Center, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
|
|
||||||
import { IconUpload } from '@tabler/icons-react';
|
|
||||||
import { DesaEditor } from '../../../_com/desaEditor';
|
|
||||||
import ListFoto from './listPage';
|
|
||||||
|
|
||||||
function Foto() {
|
|
||||||
return (
|
|
||||||
<Box py={10}>
|
|
||||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>Foto</Title>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Tanggal Foto</Text>}
|
|
||||||
placeholder="2022-01-01"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Judul Foto</Text>}
|
|
||||||
placeholder="Judul Foto"
|
|
||||||
/>
|
|
||||||
<Text fz={"sm"} fw={"bold"}>Upload Foto</Text>
|
|
||||||
<Box bg={colors['BG-trans']} p={"md"}>
|
|
||||||
<Center>
|
|
||||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
|
||||||
</Center>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Text fz={"sm"} fw={"bold"}>Deskripsi Foto</Text>
|
|
||||||
<DesaEditor
|
|
||||||
showSubmit={false}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Group>
|
|
||||||
<Button
|
|
||||||
mt={10}
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<ListFoto/>
|
|
||||||
</SimpleGrid>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Foto;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Paper, Stack, Title } from '@mantine/core';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function ListVideo() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>List Video</Title>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ListVideo;
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Button, Center, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
|
|
||||||
import { IconUpload } from '@tabler/icons-react';
|
|
||||||
import { DesaEditor } from '../../../_com/desaEditor';
|
|
||||||
import ListVideo from './listPage';
|
|
||||||
|
|
||||||
function Video() {
|
|
||||||
return (
|
|
||||||
<Box py={10}>
|
|
||||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>Video</Title>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Tanggal Video</Text>}
|
|
||||||
placeholder="2022-01-01"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Judul Video</Text>}
|
|
||||||
placeholder="Judul Video"
|
|
||||||
/>
|
|
||||||
<Text fz={"sm"} fw={"bold"}>Upload Video</Text>
|
|
||||||
<Box bg={colors['BG-trans']} p={"md"}>
|
|
||||||
<Center>
|
|
||||||
<IconUpload size={52} color="var(--mantine-color-blue-6)" stroke={1.5} />
|
|
||||||
</Center>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Text fz={"sm"} fw={"bold"}>Deskripsi Video</Text>
|
|
||||||
<DesaEditor
|
|
||||||
showSubmit={false}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Group>
|
|
||||||
<Button
|
|
||||||
mt={10}
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<ListVideo/>
|
|
||||||
</SimpleGrid>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Video;
|
|
||||||
46
src/app/admin/(dashboard)/desa/gallery/video/create/page.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
'use client'
|
||||||
|
import { KeamananEditor } from '@/app/admin/(dashboard)/keamanan/_com/keamananEditor';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function CreateVideo() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Create Video</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Judul Video</Text>}
|
||||||
|
placeholder='Masukkan judul video'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Tanggal Video</Text>}
|
||||||
|
placeholder='Masukkan tanggal video'
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"sm"}>Deskripsi Video</Text>
|
||||||
|
<KeamananEditor
|
||||||
|
showSubmit={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateVideo;
|
||||||
62
src/app/admin/(dashboard)/desa/gallery/video/detail/page.tsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Flex, Paper, Stack, Text } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
|
|
||||||
|
function DetailVideo() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Video</Text>
|
||||||
|
|
||||||
|
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Judul Video</Text>
|
||||||
|
<Text>Video 1</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Tanggal Video</Text>
|
||||||
|
<Text>2022-01-01</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Deskripsi Video</Text>
|
||||||
|
<Text>Deskripsi Video 1</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Flex gap={"xs"}>
|
||||||
|
<Button color="red">
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => router.push('/admin/desa/gallery/video/edit')} color="green">
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Hapus
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text="Apakah anda yakin ingin menghapus potensi ini?"
|
||||||
|
/> */}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailVideo;
|
||||||
|
|
||||||
46
src/app/admin/(dashboard)/desa/gallery/video/edit/page.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
'use client'
|
||||||
|
import { KeamananEditor } from '@/app/admin/(dashboard)/keamanan/_com/keamananEditor';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function EditVideo() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Edit Video</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Judul Video</Text>}
|
||||||
|
placeholder='Masukkan judul video'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Tanggal Video</Text>}
|
||||||
|
placeholder='Masukkan tanggal video'
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"sm"}>Deskripsi Video</Text>
|
||||||
|
<KeamananEditor
|
||||||
|
showSubmit={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditVideo;
|
||||||
46
src/app/admin/(dashboard)/desa/gallery/video/page.tsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||||
|
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import JudulListTab from '../../../_com/jusulListTab';
|
||||||
|
|
||||||
|
function Video() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<JudulListTab
|
||||||
|
title='List Video'
|
||||||
|
href='/admin/desa/gallery/video/create'
|
||||||
|
placeholder='pencarian'
|
||||||
|
searchIcon={<IconSearch size={16} />}
|
||||||
|
/>
|
||||||
|
<Table striped withTableBorder withRowBorders>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh>Judul Video</TableTh>
|
||||||
|
<TableTh>Tanggal Video</TableTh>
|
||||||
|
<TableTh>Deskripsi Video</TableTh>
|
||||||
|
<TableTh>Detail</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
<TableTr>
|
||||||
|
<TableTd>Video 1</TableTd>
|
||||||
|
<TableTd>2022-01-01</TableTd>
|
||||||
|
<TableTd>Deskripsi Video 1</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button onClick={() => router.push('/admin/desa/gallery/video/detail')}>
|
||||||
|
<IconDeviceImac size={20} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Video;
|
||||||
53
src/app/admin/(dashboard)/desa/pengumuman/[id]/edit/page.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { KeamananEditor } from '@/app/admin/(dashboard)/keamanan/_com/keamananEditor';
|
||||||
|
|
||||||
|
function EditPengumuman() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Edit Pengumuman</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Judul</Text>}
|
||||||
|
placeholder='Masukkan judul'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Deskripsi Singkat</Text>}
|
||||||
|
placeholder='Masukkan deskripsi singkat'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Tanggal</Text>}
|
||||||
|
placeholder='Masukkan tanggal'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Waktu</Text>}
|
||||||
|
placeholder='Masukkan waktu'
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"sm"}>Deskripsi</Text>
|
||||||
|
<KeamananEditor
|
||||||
|
showSubmit={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditPengumuman;
|
||||||
110
src/app/admin/(dashboard)/desa/pengumuman/[id]/page.tsx
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { Box, Button, Paper } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
|
||||||
|
|
||||||
|
function DetailPengumuman() {
|
||||||
|
// const pengumumanState = useProxy(stateDesaPengumuman)
|
||||||
|
// const [modalHapus, setModalHapus] = useState(false)
|
||||||
|
// const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||||
|
// const params = useParams()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
// useShallowEffect(() => {
|
||||||
|
// pengumumanState.pengumuman.findUnique.load(params?.id as string)
|
||||||
|
// }, [])
|
||||||
|
|
||||||
|
|
||||||
|
// const handleHapus = () => {
|
||||||
|
// if (selectedId) {
|
||||||
|
// pengumumanState.pengumuman.delete.byId(selectedId)
|
||||||
|
// setModalHapus(false)
|
||||||
|
// setSelectedId(null)
|
||||||
|
// router.push("/admin/desa/pengumuman")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!pengumumanState.pengumuman.findUnique.data) {
|
||||||
|
// return (
|
||||||
|
// <Stack py={10}>
|
||||||
|
// <Skeleton h={400} />
|
||||||
|
// </Stack>
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper bg={colors['white-1']} w={{ base: "100%", md: "100%", lg: "50%" }} p={'md'}>
|
||||||
|
{/* <Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Pengumuman</Text>
|
||||||
|
{pengumumanState.pengumuman.findUnique.data ? (
|
||||||
|
<Paper key={pengumumanState.pengumuman.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Kategori</Text>
|
||||||
|
<Text fz={"lg"}>{pengumumanState.pengumuman.findUnique.data?.CategoryPengumuman?.name}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Judul</Text>
|
||||||
|
<Text fz={"lg"}>{pengumumanState.pengumuman.findUnique.data?.judul}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Deskripsi</Text>
|
||||||
|
<Text fz={"lg"}>{pengumumanState.pengumuman.findUnique.data?.deskripsi}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"lg"}>Konten</Text>
|
||||||
|
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: pengumumanState.pengumuman.findUnique.data?.content }} />
|
||||||
|
</Box>
|
||||||
|
<Flex gap={"xs"} mt={10}>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (pengumumanState.pengumuman.findUnique.data) {
|
||||||
|
setSelectedId(pengumumanState.pengumuman.findUnique.data.id);
|
||||||
|
setModalHapus(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={pengumumanState.pengumuman.delete.loading || !pengumumanState.pengumuman.findUnique.data}
|
||||||
|
color={"red"}
|
||||||
|
>
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (pengumumanState.pengumuman.findUnique.data) {
|
||||||
|
router.push(`/admin/desa/pengumuman/${pengumumanState.pengumuman.findUnique.data.id}/edit`);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={!pengumumanState.pengumuman.findUnique.data}
|
||||||
|
color={"green"}
|
||||||
|
>
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
) : null}
|
||||||
|
</Stack> */}
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Konfirmasi Hapus
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text='Apakah anda yakin ingin menghapus berita ini?'
|
||||||
|
/> */}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailPengumuman;
|
||||||
110
src/app/admin/(dashboard)/desa/pengumuman/create/page.tsx
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { Prisma } from '@prisma/client';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import CreateEditor from '../../../_com/createEditor';
|
||||||
|
import stateDesaPengumuman from '../../../_state/desa/pengumuman';
|
||||||
|
|
||||||
|
function CreatePengumuman() {
|
||||||
|
const pengumumanState = useProxy(stateDesaPengumuman)
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
await pengumumanState.pengumuman.create.create()
|
||||||
|
resetForm()
|
||||||
|
router.push("/admin/desa/pengumuman")
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
pengumumanState.pengumuman.create.form = {
|
||||||
|
judul: "",
|
||||||
|
deskripsi: "",
|
||||||
|
content: "",
|
||||||
|
categoryPengumumanId: "",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Create Pengumuman</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Judul</Text>}
|
||||||
|
placeholder='Masukkan judul'
|
||||||
|
onChange={(val) => {
|
||||||
|
pengumumanState.pengumuman.create.form.judul = val.target.value
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<SelectCategory
|
||||||
|
onChange={(val) => {
|
||||||
|
pengumumanState.pengumuman.create.form.categoryPengumumanId = val.id;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Deskripsi Singkat</Text>}
|
||||||
|
placeholder='Masukkan deskripsi singkat'
|
||||||
|
onChange={(val) => {
|
||||||
|
pengumumanState.pengumuman.create.form.deskripsi = val.target.value
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>
|
||||||
|
<CreateEditor
|
||||||
|
value={pengumumanState.pengumuman.create.form.content}
|
||||||
|
onChange={(htmlContent) => {
|
||||||
|
pengumumanState.pengumuman.create.form.content = htmlContent;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Group>
|
||||||
|
<Button onClick={handleSubmit} bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectCategory({
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
onChange: (value: Prisma.CategoryPengumumanGetPayload<{ select: { name: true; id: true; } }>) => void;
|
||||||
|
}) {
|
||||||
|
const categoryState = useProxy(stateDesaPengumuman.category);
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
categoryState.findMany.load();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Kategori</Text>}
|
||||||
|
placeholder='Pilih kategori'
|
||||||
|
data={categoryState.findMany.data?.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}))}
|
||||||
|
onChange={(val) => {
|
||||||
|
const selected = categoryState.findMany.data?.find((item) => item.id === val);
|
||||||
|
if (selected) {
|
||||||
|
onChange(selected);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
searchable
|
||||||
|
nothingFoundMessage="Tidak ditemukan"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreatePengumuman;
|
||||||
@@ -1,102 +1,111 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { Box, Group, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
|
|
||||||
import React from 'react';
|
|
||||||
import { useProxy } from 'valtio/utils';
|
|
||||||
import stateDesaPengumuman from '../../_state/desa/pengumuman';
|
|
||||||
import { useShallowEffect } from '@mantine/hooks';
|
|
||||||
import { Prisma } from '@prisma/client';
|
|
||||||
import { BeritaEditor } from '../berita/_com/BeritaEditor';
|
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Grid, GridCol, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { IconCircleDashedPlus, IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import HeaderSearch from '../../_com/header';
|
||||||
|
import stateDesaPengumuman from '../../_state/desa/pengumuman';
|
||||||
|
import { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Title order={3}>Pengumuman</Title>
|
<HeaderSearch
|
||||||
<SimpleGrid cols={{
|
title='Pengumuman'
|
||||||
base: 1, md: 2
|
placeholder='pencarian'
|
||||||
}}>
|
searchIcon={<IconSearch size={20} />}
|
||||||
<PengumumanCreate />
|
/>
|
||||||
<PengumumanList />
|
<PengumumanList />
|
||||||
</SimpleGrid>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function PengumumanCreate() {
|
|
||||||
const pengumumanState = useProxy(stateDesaPengumuman)
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box py={10}>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<SelectCategory onChange={(val) => {
|
|
||||||
pengumumanState.pengumuman.create.form.categoryPengumumanId = val.id
|
|
||||||
}} />
|
|
||||||
<TextInput onChange={(val) => {
|
|
||||||
pengumumanState.pengumuman.create.form.judul = val.target.value
|
|
||||||
}} label={<Text fz={"sm"} fw={"bold"}>Judul</Text>} placeholder='masukkan judul' />
|
|
||||||
<TextInput onChange={(val) => {
|
|
||||||
pengumumanState.pengumuman.create.form.deskripsi = val.target.value
|
|
||||||
}} label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>} placeholder='masukkan deskripsi' />
|
|
||||||
<BeritaEditor onSubmit={(val) => {
|
|
||||||
pengumumanState.pengumuman.create.form.content = val
|
|
||||||
pengumumanState.pengumuman.create.create()
|
|
||||||
}} />
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function PengumumanList() {
|
function PengumumanList() {
|
||||||
const pengumumanState = useProxy(stateDesaPengumuman)
|
const pengumumanState = useProxy(stateDesaPengumuman)
|
||||||
|
const [modalHapus, setModalHapus] = useState(false)
|
||||||
|
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
pengumumanState.pengumuman.findMany.load()
|
pengumumanState.pengumuman.findMany.load()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (!pengumumanState.pengumuman.findMany.data) return <Stack py={10}>
|
const router = useRouter()
|
||||||
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
|
|
||||||
</Stack>
|
const handleHapus = () => {
|
||||||
|
if (selectedId) {
|
||||||
|
pengumumanState.pengumuman.delete.byId(selectedId)
|
||||||
|
setModalHapus(false)
|
||||||
|
setSelectedId(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pengumumanState.pengumuman.findMany.data) {
|
||||||
|
return (
|
||||||
|
<Stack py={10}>
|
||||||
|
<Skeleton h={500} />
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
<Stack gap={"xs"}>
|
<Stack>
|
||||||
<Text fz={"xl"} fw={"bold"}>List Pengumuman</Text>
|
<Grid>
|
||||||
{pengumumanState.pengumuman.findMany.data?.map((item) => (
|
<GridCol span={{ base: 12, md: 11 }}>
|
||||||
<Text key={item.id}>{item.judul}</Text>
|
<Text fz={"xl"} fw={"bold"}>List Pengumuman</Text>
|
||||||
))}
|
</GridCol>
|
||||||
|
<GridCol span={{ base: 12, md: 1 }}>
|
||||||
|
<Button onClick={() => router.push("/admin/desa/pengumuman/create")} bg={colors['blue-button']}>
|
||||||
|
<IconCircleDashedPlus size={25} />
|
||||||
|
</Button>
|
||||||
|
</GridCol>
|
||||||
|
</Grid>
|
||||||
|
<Box style={{ overflowX: "auto" }}>
|
||||||
|
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh w={250}>Judul</TableTh>
|
||||||
|
<TableTh w={250}>Kategori</TableTh>
|
||||||
|
<TableTh w={200}>Detail</TableTh>
|
||||||
|
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody >
|
||||||
|
{pengumumanState.pengumuman.findMany.data?.map((item) => (
|
||||||
|
<TableTr key={item.id}>
|
||||||
|
<TableTd >
|
||||||
|
<Box w={100}>
|
||||||
|
<Text truncate="end" fz={"sm"}>{item.judul}</Text>
|
||||||
|
</Box>
|
||||||
|
</TableTd>
|
||||||
|
<TableTd >{item.CategoryPengumuman?.name}</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button bg={"green"} onClick={() => router.push(`/admin/desa/pengumuman/detail`)}>
|
||||||
|
<IconDeviceImacCog size={25} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
))}
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Konfirmasi Hapus */}
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text='Apakah anda yakin ingin menghapus berita ini?'
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectCategory({ onChange }: {
|
|
||||||
onChange: (value: Prisma.CategoryPengumumanGetPayload<{
|
|
||||||
select: {
|
|
||||||
name: true,
|
|
||||||
id: true
|
|
||||||
}
|
|
||||||
}>) => void
|
|
||||||
}) {
|
|
||||||
const pengumumanState = useProxy(stateDesaPengumuman)
|
|
||||||
useShallowEffect(() => {
|
|
||||||
pengumumanState.category.findMany.load()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (!pengumumanState.category.findMany.data) return <Skeleton h={40} />
|
|
||||||
return <Group>
|
|
||||||
{/* {JSON.stringify(pengumumanState.category.findMany.data)} */}
|
|
||||||
<Select placeholder='pilih kategori' label={<Text fz={"sm"} fw={"bold"}>Pilih Kategori</Text>} data={pengumumanState.category.findMany.data.map((item) => ({
|
|
||||||
value: item.id,
|
|
||||||
label: item.name
|
|
||||||
}))} onChange={(v) => {
|
|
||||||
const data = pengumumanState.category.findMany.data?.find((item) => item.id === v)
|
|
||||||
if (!data) return
|
|
||||||
onChange(data)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
}
|
|
||||||
export default Page;
|
export default Page;
|
||||||
|
|||||||
128
src/app/admin/(dashboard)/desa/potensi/create/page.tsx
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import ApiFetch from '@/lib/api-fetch';
|
||||||
|
import { Box, Button, Center, FileInput, Image, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import potensiDesaState from '../../../_state/desa/potensi';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import CreateEditor from '../../../_com/createEditor';
|
||||||
|
|
||||||
|
|
||||||
|
function CreatePotensi() {
|
||||||
|
const potensiState = useProxy(potensiDesaState);
|
||||||
|
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||||
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
if (!file) return toast.warn('Pilih file gambar terlebih dahulu');
|
||||||
|
|
||||||
|
const res = await ApiFetch.api.fileStorage.create.post({
|
||||||
|
file,
|
||||||
|
name: file.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
const uploaded = res.data?.data;
|
||||||
|
if (!uploaded?.id) {
|
||||||
|
return toast.error('Gagal upload gambar');
|
||||||
|
}
|
||||||
|
|
||||||
|
potensiState.create.form.imageId = uploaded.id;
|
||||||
|
|
||||||
|
await potensiState.create.create();
|
||||||
|
|
||||||
|
resetForm();
|
||||||
|
router.push('/admin/desa/potensi');
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
potensiState.create.form = {
|
||||||
|
name: '',
|
||||||
|
deskripsi: '',
|
||||||
|
kategori: '',
|
||||||
|
imageId: '',
|
||||||
|
content: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
setPreviewImage(null);
|
||||||
|
setFile(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper bg={colors['white-1']} p="md" w={{ base: '100%', md: '50%' }}>
|
||||||
|
<Stack gap="xs">
|
||||||
|
<Title order={3}>Create Potensi</Title>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
value={potensiState.create.form.name}
|
||||||
|
onChange={(val) => (potensiState.create.form.name = val.target.value)}
|
||||||
|
label={<Text fz="sm" fw="bold">Judul</Text>}
|
||||||
|
placeholder="masukkan judul"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
value={potensiState.create.form.deskripsi}
|
||||||
|
onChange={(val) => (potensiState.create.form.deskripsi = val.target.value)}
|
||||||
|
label={<Text fz="sm" fw="bold">Deskripsi</Text>}
|
||||||
|
placeholder="masukkan deskripsi"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
value={potensiState.create.form.kategori}
|
||||||
|
onChange={(val) => (potensiState.create.form.kategori = val.target.value)}
|
||||||
|
label={<Text fz="sm" fw="bold">Kategori</Text>}
|
||||||
|
placeholder="masukkan kategori"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FileInput
|
||||||
|
label={<Text fz="sm" fw="bold">Upload Gambar</Text>}
|
||||||
|
value={file}
|
||||||
|
onChange={async (e) => {
|
||||||
|
if (!e) return;
|
||||||
|
setFile(e);
|
||||||
|
const base64 = await e.arrayBuffer().then((buf) =>
|
||||||
|
'data:image/png;base64,' + Buffer.from(buf).toString('base64')
|
||||||
|
);
|
||||||
|
setPreviewImage(base64);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{previewImage ? (
|
||||||
|
<Image alt="" src={previewImage} w={200} h={200} />
|
||||||
|
) : (
|
||||||
|
<Center w={200} h={200} bg="gray">
|
||||||
|
<IconImageInPicture />
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fz="sm" fw="bold">Konten</Text>
|
||||||
|
<CreateEditor
|
||||||
|
value={potensiState.create.form.content}
|
||||||
|
onChange={(htmlContent) => {
|
||||||
|
potensiState.create.form.content = htmlContent;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Button bg={colors['blue-button']} onClick={handleSubmit}>
|
||||||
|
Simpan Potensi
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreatePotensi;
|
||||||
121
src/app/admin/(dashboard)/desa/potensi/detail/[id]/page.tsx
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Flex, Image, Paper, Skeleton, Stack, Text } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import React from 'react';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import potensiDesaState from '../../../../_state/desa/potensi';
|
||||||
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { useParams } from 'next/navigation';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { ModalKonfirmasiHapus } from '@/app/admin/(dashboard)/_com/modalKonfirmasiHapus';
|
||||||
|
|
||||||
|
export default function DetailPotensi() {
|
||||||
|
const router = useRouter()
|
||||||
|
const params = useParams()
|
||||||
|
const [modalHapus, setModalHapus] = useState(false)
|
||||||
|
const [selectedId, setSelectedId] = useState<string | null>(null)
|
||||||
|
const potensiState = useProxy(potensiDesaState)
|
||||||
|
|
||||||
|
useShallowEffect(() => {
|
||||||
|
potensiState.findUnique.load(params?.id as string)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const handleHapus = () => {
|
||||||
|
if (selectedId) {
|
||||||
|
potensiState.delete.byId(selectedId)
|
||||||
|
setModalHapus(false)
|
||||||
|
setSelectedId(null)
|
||||||
|
router.push("/admin/desa/potensi")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!potensiState.findUnique.data) {
|
||||||
|
return (
|
||||||
|
<Stack py={10}>
|
||||||
|
{Array.from({ length: 10 }).map((_, k) => (
|
||||||
|
<Skeleton key={k} h={40} />
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Potensi</Text>
|
||||||
|
{potensiState.findUnique.data ? (
|
||||||
|
<Paper key={potensiState.findUnique.data.id} bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"lg"} fw={"bold"}>Judul</Text>
|
||||||
|
<Text fz={"lg"}>{potensiState.findUnique.data.name}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"lg"} fw={"bold"}>Kategori</Text>
|
||||||
|
<Text fz={"lg"}>{potensiState.findUnique.data.kategori}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
|
||||||
|
<Text fz={"lg"}>{potensiState.findUnique.data.deskripsi}</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"lg"} fw={"bold"}>Gambar</Text>
|
||||||
|
<Image src={potensiState.findUnique.data.image?.link} alt="gambar" />
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"lg"} fw={"bold"}>Konten</Text>
|
||||||
|
<Text fz={"lg"} dangerouslySetInnerHTML={{ __html: potensiState.findUnique.data.content }} />
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Flex gap={"xs"}>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (potensiState.findUnique.data) {
|
||||||
|
setSelectedId(potensiState.findUnique.data.id)
|
||||||
|
setModalHapus(true)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={potensiState.delete.loading || !potensiState.findUnique.data}
|
||||||
|
color="red"
|
||||||
|
>
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (potensiState.findUnique.data) {
|
||||||
|
router.push(`/admin/desa/potensi/edit/${potensiState.findUnique.data.id}`)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
disabled={!potensiState.findUnique.data}
|
||||||
|
color="green"
|
||||||
|
>
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
) : null}
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Hapus */}
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text="Apakah anda yakin ingin menghapus potensi ini?"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
169
src/app/admin/(dashboard)/desa/potensi/edit/[id]/page.tsx
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import EditEditor from "@/app/admin/(dashboard)/_com/editEditor";
|
||||||
|
import potensiDesaState from "@/app/admin/(dashboard)/_state/desa/potensi";
|
||||||
|
import colors from "@/con/colors";
|
||||||
|
import ApiFetch from "@/lib/api-fetch";
|
||||||
|
import { Box, Button, Paper, Stack, Title, TextInput, FileInput, Center, Text, Image } from "@mantine/core";
|
||||||
|
import { IconArrowBack, IconImageInPicture } from "@tabler/icons-react";
|
||||||
|
import { useParams } from "next/navigation";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import { useProxy } from "valtio/utils";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function EditPotensi() {
|
||||||
|
const potensiState = useProxy(potensiDesaState)
|
||||||
|
const router = useRouter()
|
||||||
|
const params = useParams()
|
||||||
|
|
||||||
|
const [previewImage, setPreviewImage] = useState<string | null>(null);
|
||||||
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
name: '',
|
||||||
|
deskripsi: '',
|
||||||
|
kategori: '',
|
||||||
|
content: '',
|
||||||
|
imageId: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadPotensi = async () => {
|
||||||
|
const id = params?.id as string;
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await potensiDesaState.edit.load(id); // ambil data dari API
|
||||||
|
if (data) {
|
||||||
|
setFormData({
|
||||||
|
name: data.name || '',
|
||||||
|
deskripsi: data.deskripsi || '',
|
||||||
|
kategori: data.kategori || '',
|
||||||
|
content: data.content || '',
|
||||||
|
imageId: data.imageId || '',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data?.image?.link) {
|
||||||
|
setPreviewImage(data.image.link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error loading potensi:", error);
|
||||||
|
toast.error("Gagal memuat data potensi");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadPotensi();
|
||||||
|
}, [params?.id]);
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
// Sinkronkan semua data dari formData ke state global
|
||||||
|
potensiState.edit.form = {
|
||||||
|
...potensiState.edit.form,
|
||||||
|
name: formData.name,
|
||||||
|
deskripsi: formData.deskripsi,
|
||||||
|
kategori: formData.kategori,
|
||||||
|
content: formData.content,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
const res = await ApiFetch.api.fileStorage.create.post({ file, name: file.name });
|
||||||
|
const uploaded = res.data?.data;
|
||||||
|
|
||||||
|
if (!uploaded?.id) {
|
||||||
|
return toast.error("Gagal upload gambar");
|
||||||
|
}
|
||||||
|
|
||||||
|
potensiState.edit.form.imageId = uploaded.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
await potensiState.edit.update();
|
||||||
|
toast.success("Potensi berhasil diperbarui!");
|
||||||
|
router.push("/admin/desa/potensi");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating potensi:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat memperbarui potensi");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Edit Potensi</Title>
|
||||||
|
<TextInput
|
||||||
|
value={formData.name}
|
||||||
|
onChange={(e) => {
|
||||||
|
const val = e.target.value;
|
||||||
|
setFormData((prev) => ({ ...prev, name: val }));
|
||||||
|
potensiState.edit.form.name = val;
|
||||||
|
}}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
||||||
|
placeholder="masukkan judul"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
value={formData.deskripsi}
|
||||||
|
onChange={(e) => {
|
||||||
|
const val = e.target.value;
|
||||||
|
setFormData((prev) => ({ ...prev, deskripsi: val }));
|
||||||
|
potensiState.edit.form.deskripsi = val;
|
||||||
|
}}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
|
||||||
|
placeholder="masukkan deskripsi"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
value={formData.kategori}
|
||||||
|
onChange={(e) => {
|
||||||
|
const val = e.target.value;
|
||||||
|
setFormData((prev) => ({ ...prev, kategori: val }));
|
||||||
|
potensiState.edit.form.kategori = val;
|
||||||
|
}}
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
|
||||||
|
placeholder="masukkan kategori"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FileInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar</Text>}
|
||||||
|
value={file}
|
||||||
|
onChange={async (e) => {
|
||||||
|
if (!e) return;
|
||||||
|
setFile(e);
|
||||||
|
const base64 = await e.arrayBuffer().then((buf) =>
|
||||||
|
"data:image/png;base64," + Buffer.from(buf).toString("base64")
|
||||||
|
);
|
||||||
|
setPreviewImage(base64);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{previewImage ? (
|
||||||
|
<Image alt="" src={previewImage} w={200} h={200} />
|
||||||
|
) : (
|
||||||
|
<Center w={200} h={200} bg={"gray"}>
|
||||||
|
<IconImageInPicture />
|
||||||
|
</Center>
|
||||||
|
)}
|
||||||
|
<Box>
|
||||||
|
<Text fz={"sm"} fw={"bold"}>Konten</Text>
|
||||||
|
<EditEditor
|
||||||
|
value={formData.content}
|
||||||
|
onChange={(htmlContent) => {
|
||||||
|
setFormData((prev) => ({ ...prev, content: htmlContent }));
|
||||||
|
potensiState.edit.form.content = htmlContent;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Button bg={colors['blue-button']} onClick={handleSubmit}>Edit Potensi</Button>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditPotensi;
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Paper, Stack, Title } from '@mantine/core';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function ListPotensi() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack>
|
|
||||||
<Title order={3}>List Potensi Desa</Title>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ListPotensi;
|
|
||||||
@@ -1,40 +1,89 @@
|
|||||||
|
|
||||||
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Group, Paper, SimpleGrid, Stack, TextInput, Title } from '@mantine/core';
|
import { Box, Button, Image, Paper, Skeleton, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text } from '@mantine/core';
|
||||||
import ListPotensi from './listPage';
|
import { useShallowEffect } from '@mantine/hooks';
|
||||||
|
import { IconDeviceImacCog, IconSearch } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { useProxy } from 'valtio/utils';
|
||||||
|
import HeaderSearch from '../../_com/header';
|
||||||
|
import JudulList from '../../_com/judulList';
|
||||||
|
import potensiDesaState from '../../_state/desa/potensi';
|
||||||
|
|
||||||
|
|
||||||
function Potensi() {
|
function Potensi() {
|
||||||
return (
|
return (
|
||||||
<Box py={10}>
|
<Box>
|
||||||
<Stack gap={"xs"}>
|
<HeaderSearch
|
||||||
<Title order={3}>Potensi Desa</Title>
|
title='Potensi Desa'
|
||||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
placeholder='pencarian'
|
||||||
<Box>
|
searchIcon={<IconSearch size={20} />}
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
/>
|
||||||
<Stack>
|
<ListPotensi />
|
||||||
<TextInput
|
|
||||||
label="Nama Potensi"
|
|
||||||
placeholder='masukkan nama potensi'
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label="Kategori Potensi"
|
|
||||||
placeholder='masukkan kategori potensi'
|
|
||||||
/>
|
|
||||||
<Group>
|
|
||||||
<Button
|
|
||||||
mt={10}
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<ListPotensi />
|
|
||||||
</SimpleGrid>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ListPotensi() {
|
||||||
|
const potensiState = useProxy(potensiDesaState)
|
||||||
|
useShallowEffect(() => {
|
||||||
|
potensiState.findMany.load()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
|
||||||
|
if (!potensiState.findMany.data) {
|
||||||
|
return (
|
||||||
|
<Stack py={10}>
|
||||||
|
<Skeleton h={500} />
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<JudulList
|
||||||
|
title='List Potensi'
|
||||||
|
href='/admin/desa/potensi/create'
|
||||||
|
/>
|
||||||
|
<Box style={{ overflowX: "auto" }}>
|
||||||
|
<Table striped withRowBorders withTableBorder style={{ minWidth: '700px' }}>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh>Judul</TableTh>
|
||||||
|
<TableTh>Kategori</TableTh>
|
||||||
|
<TableTh>Image</TableTh>
|
||||||
|
<TableTh>Detail</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
{potensiState.findMany.data?.map((item) => (
|
||||||
|
<TableTr key={item.id}>
|
||||||
|
<TableTd>
|
||||||
|
<Box w={100}>
|
||||||
|
<Text truncate="end" fz={"sm"}>{item.name}</Text>
|
||||||
|
</Box></TableTd>
|
||||||
|
<TableTd>{item.kategori}</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Image w={100} src={item.image?.link} alt="image" />
|
||||||
|
</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button onClick={() => router.push(`/admin/desa/potensi/detail/${item.id}`)}>
|
||||||
|
<IconDeviceImacCog size={25} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
))}
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default Potensi;
|
export default Potensi;
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
function CreateDemografiPekerjaan() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Create Demografi Pekerjaan</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pekerjaan</Text>}
|
||||||
|
placeholder='Masukkan pekerjaan'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Jumlah Pekerja Laki - Laki</Text>}
|
||||||
|
placeholder='Masukkan jumlah pekerja laki - laki'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Jumlah Pekerja Perempuan</Text>}
|
||||||
|
placeholder='Masukkan jumlah pekerja perempuan'
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateDemografiPekerjaan;
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Flex, Paper, Stack, Text } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
|
|
||||||
|
function DetailDemografiPekerjaan() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Demografi Pekerjaan</Text>
|
||||||
|
|
||||||
|
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Pekerjaan</Text>
|
||||||
|
<Text>Karyawan</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Jumlah Pekerja Laki - Laki</Text>
|
||||||
|
<Text>200</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Jumlah Pekerja Perempuan</Text>
|
||||||
|
<Text>100</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Flex gap={"xs"}>
|
||||||
|
<Button color="red">
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/demografi-pekerjaan/edit')} color="green">
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Hapus
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text="Apakah anda yakin ingin menghapus potensi ini?"
|
||||||
|
/> */}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailDemografiPekerjaan;
|
||||||
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
function EditDemografiPekerjaan() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Edit Demografi Pekerjaan</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pekerjaan</Text>}
|
||||||
|
placeholder='Masukkan pekerjaan'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Jumlah Pekerja Laki - Laki</Text>}
|
||||||
|
placeholder='Masukkan jumlah pekerja laki - laki'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Jumlah Pekerja Perempuan</Text>}
|
||||||
|
placeholder='Masukkan jumlah pekerja perempuan'
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditDemografiPekerjaan;
|
||||||
@@ -1,47 +1,58 @@
|
|||||||
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||||
import React from 'react';
|
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||||
|
import HeaderSearch from '../../_com/header';
|
||||||
|
import JudulList from '../../_com/judulList';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
function Page() {
|
function DemografiPekerjaan() {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Stack gap={"xs"}>
|
<HeaderSearch
|
||||||
<Box>
|
title='Demografi Pekerjaan'
|
||||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={"md"}>
|
placeholder='pencarian'
|
||||||
<Stack gap={"xs"}>
|
searchIcon={<IconSearch size={20} />}
|
||||||
<Title order={3}>Demografi Pekerjaan</Title>
|
/>
|
||||||
<TextInput
|
<ListDemografiPekerjaan/>
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Jumlah Pekerja Laki - Laki</Text>}
|
|
||||||
placeholder="Masukkan jumlah pekerja laki - laki"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Jumlah Pekerja Perempuan</Text>}
|
|
||||||
placeholder="Masukkan jumlah pekerja perempuan"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Nama Pekerjaan</Text>}
|
|
||||||
placeholder="Masukkan nama pekerjaan"
|
|
||||||
/>
|
|
||||||
<Group>
|
|
||||||
<Button
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={"md"}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>Grafik Demografi Pekerjaan</Title>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
function ListDemografiPekerjaan() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<JudulList
|
||||||
|
title='List Demografi Pekerjaan'
|
||||||
|
href='/admin/ekonomi/demografi-pekerjaan/create'
|
||||||
|
/>
|
||||||
|
<Table striped withTableBorder withRowBorders>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh>Pekerjaan</TableTh>
|
||||||
|
<TableTh>Jumlah Pekerja Laki - Laki</TableTh>
|
||||||
|
<TableTh>Jumlah Pekerja Perempuan</TableTh>
|
||||||
|
<TableTh>Detail</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
<TableTr>
|
||||||
|
<TableTd>Karyawan</TableTd>
|
||||||
|
<TableTd>200</TableTd>
|
||||||
|
<TableTd>100</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/demografi-pekerjaan/detail')}>
|
||||||
|
<IconDeviceImac size={20} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DemografiPekerjaan;
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
function CreateJumlahPendudukMiskin() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Create Jumlah Penduduk Miskin</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Tahun</Text>}
|
||||||
|
placeholder='Masukkan tahun'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Jumlah Penduduk Miskin</Text>}
|
||||||
|
placeholder='Masukkan jumlah penduduk miskin'
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateJumlahPendudukMiskin;
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Flex, Paper, Stack, Text } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
|
|
||||||
|
function DetailJumlahPendudukMiskin() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Jumlah Penduduk Miskin</Text>
|
||||||
|
|
||||||
|
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Tahun</Text>
|
||||||
|
<Text>2024</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Jumlah Penduduk Miskin</Text>
|
||||||
|
<Text>100</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Flex gap={"xs"}>
|
||||||
|
<Button color="red">
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/jumlah-penduduk-miskin-2024-2025/edit')} color="green">
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Hapus
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text="Apakah anda yakin ingin menghapus potensi ini?"
|
||||||
|
/> */}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailJumlahPendudukMiskin;
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
function EditJumlahPendudukMiskin() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Edit Jumlah Penduduk Miskin</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Tahun</Text>}
|
||||||
|
placeholder='Masukkan tahun'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Jumlah Penduduk Miskin</Text>}
|
||||||
|
placeholder='Masukkan jumlah penduduk miskin'
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditJumlahPendudukMiskin;
|
||||||
@@ -1,43 +1,56 @@
|
|||||||
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import React from 'react';
|
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||||
|
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||||
|
import HeaderSearch from '../../_com/header';
|
||||||
|
import JudulList from '../../_com/judulList';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
function Page() {
|
function JumlahPendudukMiskin() {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Stack gap={"xs"}>
|
<HeaderSearch
|
||||||
<Box>
|
title='Jumlah Penduduk Miskin'
|
||||||
<Paper p={"md"} bg={colors['white-1']} w={{ base: '100%', md: '50%' }}>
|
placeholder='pencarian'
|
||||||
<Stack gap={"xs"}>
|
searchIcon={<IconSearch size={20} />}
|
||||||
<Title order={3}>Jumlah Penduduk Miskin 2024-2025</Title>
|
/>
|
||||||
<TextInput
|
<ListJumlahPendudukMiskin/>
|
||||||
label="Tahun"
|
|
||||||
placeholder="masukkan tahun"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label="Jumlah Penduduk Miskin"
|
|
||||||
placeholder="masukkan jumlah penduduk miskin"
|
|
||||||
/>
|
|
||||||
<Group>
|
|
||||||
<Button
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Paper p={"md"} bg={colors['white-1']} >
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>Grafik Jumlah Penduduk Miskin 2024-2025</Title>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
function ListJumlahPendudukMiskin() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<JudulList
|
||||||
|
title='List Jumlah Penduduk Miskin'
|
||||||
|
href='/admin/ekonomi/jumlah-penduduk-miskin-2024-2025/create'
|
||||||
|
/>
|
||||||
|
<Table striped withTableBorder withRowBorders>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh>Tahun</TableTh>
|
||||||
|
<TableTh>Jumlah Penduduk Miskin</TableTh>
|
||||||
|
<TableTh>Detail</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
<TableTr>
|
||||||
|
<TableTd>2024</TableTd>
|
||||||
|
<TableTd>100</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/jumlah-penduduk-miskin-2024-2025/detail')}>
|
||||||
|
<IconDeviceImac size={20} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default JumlahPendudukMiskin;
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import colors from "@/con/colors";
|
import colors from "@/con/colors";
|
||||||
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from "@mantine/core";
|
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from "@mantine/core";
|
||||||
import PengangguranBerdasarkanUsia from "./ui/pengangguranBerdasarkanUsia/page";
|
import PengangguranBerdasarkanUsia from "./pengangguran_berdasarkan_usia/page";
|
||||||
import PengangguranBerdasarkanPendidikan from "./ui/pengangguranBerdasarkanPendidikan/page";
|
import PengangguranBerdasarkanPendidikan from "./pengangguran_berdasarkan_pendidikan/page";
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Stack gap={"xs"}>
|
<Stack gap={"xs"}>
|
||||||
<Title order={3}>Jumlah Penduduk Usia Kerja yang Menganggur</Title>
|
<Title order={3}>Jumlah Penduduk Usia Kerja yang Menganggur</Title>
|
||||||
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Pengangguran Bredasarkan Usia"}>
|
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Pengangguran Berdasarkan Usia"}>
|
||||||
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
||||||
<TabsTab value={"Pengangguran Bredasarkan Usia"}>
|
<TabsTab value={"Pengangguran Berdasarkan Usia"}>
|
||||||
Pengangguran Bredasarkan Usia
|
Pengangguran Berdasarkan Usia
|
||||||
</TabsTab>
|
</TabsTab>
|
||||||
<TabsTab value={"Pengangguran Bredasarkan Pendidikan"}>
|
<TabsTab value={"Pengangguran Berdasarkan Pendidikan"}>
|
||||||
Pengangguran Bredasarkan Pendidikan
|
Pengangguran Berdasarkan Pendidikan
|
||||||
</TabsTab>
|
</TabsTab>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<TabsPanel value={"Pengangguran Bredasarkan Usia"}>
|
<TabsPanel value={"Pengangguran Berdasarkan Usia"}>
|
||||||
<PengangguranBerdasarkanUsia />
|
<PengangguranBerdasarkanUsia />
|
||||||
</TabsPanel>
|
</TabsPanel>
|
||||||
<TabsPanel value={"Pengangguran Bredasarkan Pendidikan"}>
|
<TabsPanel value={"Pengangguran Berdasarkan Pendidikan"}>
|
||||||
<PengangguranBerdasarkanPendidikan/>
|
<PengangguranBerdasarkanPendidikan/>
|
||||||
</TabsPanel>
|
</TabsPanel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
function CreatePengangguranBerdasarkanPendidikan() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Create Pengangguran Berdasarkan Pendidikan</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pendidikan SD</Text>}
|
||||||
|
placeholder='Masukkan pendidikan sd'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pendidikan SMP</Text>}
|
||||||
|
placeholder='Masukkan pendidikan smp'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pendidikan SMA</Text>}
|
||||||
|
placeholder='Masukkan pendidikan sma'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pendidikan S1</Text>}
|
||||||
|
placeholder='Masukkan pendidikan s1'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pendidikan S2</Text>}
|
||||||
|
placeholder='Masukkan pendidikan s2'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pendidikan S3</Text>}
|
||||||
|
placeholder='Masukkan pendidikan s3'
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreatePengangguranBerdasarkanPendidikan;
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Flex, Paper, Stack, Text } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
|
|
||||||
|
function DetailPengangguranBerdasarkanPendidikan() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Pengangguran Berdasarkan Pendidikan</Text>
|
||||||
|
|
||||||
|
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Pendidikan SD</Text>
|
||||||
|
<Text>50</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Pendidikan SMP</Text>
|
||||||
|
<Text>60</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Pendidikan SMA</Text>
|
||||||
|
<Text>80</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Pendidikan S1</Text>
|
||||||
|
<Text>40</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Pendidikan S2</Text>
|
||||||
|
<Text>20</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Pendidikan S3</Text>
|
||||||
|
<Text>10</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Flex gap={"xs"}>
|
||||||
|
<Button color="red">
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/edit')} color="green">
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Hapus
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text="Apakah anda yakin ingin menghapus potensi ini?"
|
||||||
|
/> */}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailPengangguranBerdasarkanPendidikan;
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
function EditPengangguranBerdasarkanPendidikan() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Edit Pengangguran Berdasarkan Pendidikan</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pendidikan SD</Text>}
|
||||||
|
placeholder='Masukkan pendidikan sd'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pendidikan SMP</Text>}
|
||||||
|
placeholder='Masukkan pendidikan smp'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pendidikan SMA</Text>}
|
||||||
|
placeholder='Masukkan pendidikan sma'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pendidikan S1</Text>}
|
||||||
|
placeholder='Masukkan pendidikan s1'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pendidikan S2</Text>}
|
||||||
|
placeholder='Masukkan pendidikan s2'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pendidikan S3</Text>}
|
||||||
|
placeholder='Masukkan pendidikan s3'
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditPengangguranBerdasarkanPendidikan;
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Title } from '@mantine/core';
|
||||||
|
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import JudulListTab from '../../../_com/jusulListTab';
|
||||||
|
|
||||||
|
function PengangguranBerdasarkanPendidikan() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<JudulListTab
|
||||||
|
title='Pengangguran Berdasarkan Pendidikan'
|
||||||
|
href='/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/create'
|
||||||
|
placeholder='pencarian'
|
||||||
|
searchIcon={<IconSearch size={20} />}
|
||||||
|
/>
|
||||||
|
<Title order={4}>List Pengangguran Berdasarkan Pendidikan</Title>
|
||||||
|
<Table striped withTableBorder withRowBorders>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh>Pendidikan SD</TableTh>
|
||||||
|
<TableTh>Pendidikan SMP</TableTh>
|
||||||
|
<TableTh>Pendidikan SMA</TableTh>
|
||||||
|
<TableTh>Detail</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
<TableTr>
|
||||||
|
<TableTd>80</TableTd>
|
||||||
|
<TableTd>40</TableTd>
|
||||||
|
<TableTd>20</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_pendidikan/detail')}>
|
||||||
|
<IconDeviceImac size={20} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PengangguranBerdasarkanPendidikan;
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
function CreatePengangguranBerdasarkanUsia() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Create Pengangguran Berdasarkan Usia</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Usia 18 - 25</Text>}
|
||||||
|
placeholder='Masukkan usia'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Usia 26 - 35</Text>}
|
||||||
|
placeholder='Masukkan usia'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Usia 36 - 45</Text>}
|
||||||
|
placeholder='Masukkan usia'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Usia 46 +</Text>}
|
||||||
|
placeholder='Masukkan usia'
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreatePengangguranBerdasarkanUsia;
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Flex, Paper, Stack, Text } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
|
|
||||||
|
function DetailPengangguranBerdasarkanUsia() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Pengangguran Berdasarkan Usia</Text>
|
||||||
|
|
||||||
|
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Usia 18 - 25</Text>
|
||||||
|
<Text>80</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Usia 26 - 35</Text>
|
||||||
|
<Text>40</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Usia 36 - 45</Text>
|
||||||
|
<Text>20</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Usia 46 +</Text>
|
||||||
|
<Text>10</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Total</Text>
|
||||||
|
<Text>150</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Flex gap={"xs"}>
|
||||||
|
<Button color="red">
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/edit')} color="green">
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Hapus
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text="Apakah anda yakin ingin menghapus potensi ini?"
|
||||||
|
/> */}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailPengangguranBerdasarkanUsia;
|
||||||
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function EditPengangguranBerdasarkanUsia() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Edit Pengangguran Berdasarkan Usia</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Usia 18 - 25</Text>}
|
||||||
|
placeholder='Masukkan usia'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Usia 26 - 35</Text>}
|
||||||
|
placeholder='Masukkan usia'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Usia 36 - 45</Text>}
|
||||||
|
placeholder='Masukkan usia'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Usia 46 +</Text>}
|
||||||
|
placeholder='Masukkan usia'
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditPengangguranBerdasarkanUsia;
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Paper, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Title } from '@mantine/core';
|
||||||
|
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import JudulListTab from '../../../_com/jusulListTab';
|
||||||
|
|
||||||
|
function PengangguranBerdasarkanUsia() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<JudulListTab
|
||||||
|
title='Pengangguran Berdasarkan Usia'
|
||||||
|
href='/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/create'
|
||||||
|
placeholder='pencarian'
|
||||||
|
searchIcon={<IconSearch size={20} />}
|
||||||
|
/>
|
||||||
|
<Title order={4}>List Pengangguran Berdasarkan Usia</Title>
|
||||||
|
<Table striped withTableBorder withRowBorders>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh>Usia 18 - 25</TableTh>
|
||||||
|
<TableTh>Usia 26 - 35</TableTh>
|
||||||
|
<TableTh>Usia 36 - 45</TableTh>
|
||||||
|
<TableTh>Detail</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
<TableTr>
|
||||||
|
<TableTd>80</TableTd>
|
||||||
|
<TableTd>40</TableTd>
|
||||||
|
<TableTd>20</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/jumlah-penduduk-usia-kerja-yang-menganggur/pengangguran_berdasarkan_usia/detail')}>
|
||||||
|
<IconDeviceImac size={20} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PengangguranBerdasarkanUsia;
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function PengangguranBerdasarkanPendidikan() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Box py={15}>
|
|
||||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'} radius={10}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>Pengangguran Berdasarkan Pendidikan</Title>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Pendidikan SD</Text>}
|
|
||||||
placeholder="masukkan jumlah penduduk Pendidikan SD yang menganggur"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Pendidikan SMP</Text>}
|
|
||||||
placeholder="masukkan jumlah penduduk Pendidikan SMP yang menganggur"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Pendidikan SMA / SMK</Text>}
|
|
||||||
placeholder="masukkan jumlah penduduk Pendidikan SMA / SMK yang menganggur"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Pendidikan D1-D3 / S1</Text>}
|
|
||||||
placeholder="masukkan jumlah penduduk Pendidikan D1-D3 yang menganggur"
|
|
||||||
/>
|
|
||||||
<Group>
|
|
||||||
<Button mt={10} bg={colors['blue-button']}>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
|
|
||||||
<Stack>
|
|
||||||
<Title pb={10} order={3}>Grafik Pengangguran Berdasarkan Pendidikan</Title>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PengangguranBerdasarkanPendidikan;
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function PengangguranBerdasarkanUsia() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Box py={15}>
|
|
||||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'} radius={10}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>Pengangguran Berdasarkan Usia</Title>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Usia 18 - 25 tahun</Text>}
|
|
||||||
placeholder="masukkan jumlah penduduk usia 18 - 25 tahun yang menganggur"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Usia 26 - 35 tahun</Text>}
|
|
||||||
placeholder="masukkan jumlah penduduk usia 26 - 35 tahun yang menganggur"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Usia 36 - 45 tahun</Text>}
|
|
||||||
placeholder="masukkan jumlah penduduk usia 36 - 45 tahun yang menganggur"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Usia 46+</Text>}
|
|
||||||
placeholder="masukkan jumlah penduduk usia 46+ yang menganggur"
|
|
||||||
/>
|
|
||||||
<Group>
|
|
||||||
<Button mt={10} bg={colors['blue-button']}>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} w={{ base: '100%', md: '50%' }} p={'md'}>
|
|
||||||
<Stack>
|
|
||||||
<Title pb={10} order={3}>Grafik Pengangguran Berdasarkan Usia</Title>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PengangguranBerdasarkanUsia;
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
function CreateJumlahPengangguran() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Create Jumlah Pengangguran</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pengangguran Terdidik</Text>}
|
||||||
|
placeholder='Masukkan pengangguran terdidik'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Usia Produktif</Text>}
|
||||||
|
placeholder='Masukkan usia produktif'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Sedang Mencari Kerja</Text>}
|
||||||
|
placeholder='Masukkan sedang mencari kerja'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pengangguran Tidak Terdidik</Text>}
|
||||||
|
placeholder='Masukkan pengangguran tidak terdidik'
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateJumlahPengangguran;
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Flex, Paper, Stack, Text } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
|
|
||||||
|
function DetailJumlahPengangguran() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Jumlah Pengangguran</Text>
|
||||||
|
|
||||||
|
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Pengangguran Terdidik</Text>
|
||||||
|
<Text>100</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Usia Produktif</Text>
|
||||||
|
<Text>200</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Sedang Mencari Kerja</Text>
|
||||||
|
<Text>300</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Pengangguran Tidak Terdidik</Text>
|
||||||
|
<Text>30</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Flex gap={"xs"}>
|
||||||
|
<Button color="red">
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/jumlah-pengangguran-2024-2025/edit')} color="green">
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Hapus
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text="Apakah anda yakin ingin menghapus potensi ini?"
|
||||||
|
/> */}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailJumlahPengangguran;
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
function EditJumlahPengangguran() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Edit Jumlah Pengangguran</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pengangguran Terdidik</Text>}
|
||||||
|
placeholder='Masukkan pengangguran terdidik'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Usia Produktif</Text>}
|
||||||
|
placeholder='Masukkan usia produktif'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Sedang Mencari Kerja</Text>}
|
||||||
|
placeholder='Masukkan sedang mencari kerja'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pengangguran Tidak Terdidik</Text>}
|
||||||
|
placeholder='Masukkan pengangguran tidak terdidik'
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditJumlahPengangguran;
|
||||||
@@ -1,37 +1,58 @@
|
|||||||
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||||
import DataPengangguran from './ui/dataPengangguran/page';
|
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||||
import GrafikDataPengangguran from './ui/grafikDataPengangguran/page';
|
import HeaderSearch from '../../_com/header';
|
||||||
import DetailDataPengangguran from './ui/detailDataPengangguran/page';
|
import JudulList from '../../_com/judulList';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
function Page() {
|
function JumlahPengangguran() {
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Box>
|
||||||
<Title order={3}>Jumlah Pengangguran 2024-2025</Title>
|
<HeaderSearch
|
||||||
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Data Pengangguran Desa"}>
|
title='Jumlah Pengangguran 2024-2025'
|
||||||
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
placeholder='pencarian'
|
||||||
<TabsTab value="Data Pengangguran Desa">
|
searchIcon={<IconSearch size={20} />}
|
||||||
Data Pengangguran Desa
|
/>
|
||||||
</TabsTab>
|
<ListJumlahPengangguran/>
|
||||||
<TabsTab value="Grafik Data Pengangguran Desa">
|
</Box>
|
||||||
Grafik Data Pengangguran Desa
|
);
|
||||||
</TabsTab>
|
|
||||||
<TabsTab value="Detail Data Pengangguran Desa">
|
|
||||||
Detail Data Pengangguran Desa
|
|
||||||
</TabsTab>
|
|
||||||
</TabsList>
|
|
||||||
<TabsPanel value="Data Pengangguran Desa">
|
|
||||||
<DataPengangguran/>
|
|
||||||
</TabsPanel>
|
|
||||||
<TabsPanel value="Grafik Data Pengangguran Desa">
|
|
||||||
<GrafikDataPengangguran/>
|
|
||||||
</TabsPanel>
|
|
||||||
<TabsPanel value="Detail Data Pengangguran Desa">
|
|
||||||
<DetailDataPengangguran/>
|
|
||||||
</TabsPanel>
|
|
||||||
</Tabs>
|
|
||||||
</Stack>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
function ListJumlahPengangguran() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<JudulList
|
||||||
|
title='List Jumlah Pengangguran 2024-2025'
|
||||||
|
href='/admin/ekonomi/jumlah-pengangguran-2024-2025/create'
|
||||||
|
/>
|
||||||
|
<Table striped withTableBorder withRowBorders>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh>Pengangguran Terdidik</TableTh>
|
||||||
|
<TableTh>Usia Produktif</TableTh>
|
||||||
|
<TableTh>Sedang Mencari Kerja</TableTh>
|
||||||
|
<TableTh>Detail</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
<TableTr>
|
||||||
|
<TableTd>100</TableTd>
|
||||||
|
<TableTd>200</TableTd>
|
||||||
|
<TableTd>300</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/jumlah-pengangguran-2024-2025/detail')}>
|
||||||
|
<IconDeviceImac size={20} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default JumlahPengangguran;
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
|
|
||||||
|
|
||||||
function DataPengangguran() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Box py={15}>
|
|
||||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>Data Pengangguran</Title>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Total Pengangguran</Text>}
|
|
||||||
placeholder="masukkan total pengangguran"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Pengangguran Terdidik</Text>}
|
|
||||||
placeholder="masukkan pengangguran terdidik"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Usia Produktif</Text>}
|
|
||||||
placeholder="masukkan usia produktif"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Sedang Mencari Kerja</Text>}
|
|
||||||
placeholder="masukkan jumlah sedang mencari kerja"
|
|
||||||
/>
|
|
||||||
<Group>
|
|
||||||
<Button bg={colors['blue-button']}>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>List Data Pengangguran</Title>
|
|
||||||
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
</SimpleGrid>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DataPengangguran;
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Paper, Stack, Title } from '@mantine/core';
|
|
||||||
|
|
||||||
function DetailDataPengangguran() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Box py={15}>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>Detail Data Pengangguran</Title>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default DetailDataPengangguran;
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
|
||||||
|
|
||||||
function GrafikDataPengangguran() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Box py={15}>
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>Data Pengangguran</Title>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Total Pengangguran</Text>}
|
|
||||||
placeholder="masukkan total pengangguran"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Pengangguran Terdidik</Text>}
|
|
||||||
placeholder="masukkan pengangguran terdidik"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Usia Produktif</Text>}
|
|
||||||
placeholder="masukkan usia produktif"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Sedang Mencari Kerja</Text>}
|
|
||||||
placeholder="masukkan jumlah sedang mencari kerja"
|
|
||||||
/>
|
|
||||||
<Group>
|
|
||||||
<Button bg={colors['blue-button']}>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<Box py={10}>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>List Data Pengangguran</Title>
|
|
||||||
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default GrafikDataPengangguran;
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { Box, Stack, Text, TextInput, Title } from '@mantine/core';
|
|
||||||
|
|
||||||
function CreateLowongan() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={4}>Lowongan Kerja Lokal</Title>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Bekerja Sebagai</Text>}
|
|
||||||
placeholder="masukkan bekerja sebagai"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Alamat Usaha</Text>}
|
|
||||||
placeholder="masukkan alamat usaha"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Waktu Kerja</Text>}
|
|
||||||
placeholder="masukkan waktu kerja"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Gaji selama 1 bulan</Text>}
|
|
||||||
placeholder="masukkan gaji selama 1 bulan"
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CreateLowongan;
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { KeamananEditor } from '../../../keamanan/_com/keamananEditor';
|
||||||
|
|
||||||
|
|
||||||
|
function CreateLowonganKerja() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Create Lowongan Kerja Lokal</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pekerjaan</Text>}
|
||||||
|
placeholder='Masukkan pekerjaan'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Nama Usaha</Text>}
|
||||||
|
placeholder='Masukkan nama usaha'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Alamat Usaha</Text>}
|
||||||
|
placeholder='Masukkan alamat usaha'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Nomor Telepon</Text>}
|
||||||
|
placeholder='Masukkan nomor telepon'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Gaji selama 1 bulan</Text>}
|
||||||
|
placeholder='Masukkan gaji'
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"sm"}>Deskripsi Lowongan Kerja</Text>
|
||||||
|
<KeamananEditor
|
||||||
|
showSubmit={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateLowonganKerja;
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Flex, Paper, Stack, Text } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconEdit, IconX } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
|
|
||||||
|
function DetailLowonganKerjaLokal() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Lowongan Kerja Lokal</Text>
|
||||||
|
|
||||||
|
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Bekerja Sebagai</Text>
|
||||||
|
<Text>Karyawan</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Nama Usaha</Text>
|
||||||
|
<Text>BIBD</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Alamat Usaha</Text>
|
||||||
|
<Text>Jalan In Aja</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Nomor Telepon</Text>
|
||||||
|
<Text>0896232831883</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Waktu Kerja</Text>
|
||||||
|
<Text>Full Time</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Gaji selama 1 bulan</Text>
|
||||||
|
<Text>Rp. 3.000.000</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Deskripsi Lowongan Kerja</Text>
|
||||||
|
<Text> Pekerjaan dengan gaji Rp. 3.000.000</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Flex gap={"xs"}>
|
||||||
|
<Button color="red">
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/lowongan-kerja-lokal/edit')} color="green">
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Hapus
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text="Apakah anda yakin ingin menghapus potensi ini?"
|
||||||
|
/> */}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailLowonganKerjaLokal;
|
||||||
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { KeamananEditor } from '../../../keamanan/_com/keamananEditor';
|
||||||
|
|
||||||
|
|
||||||
|
function EditLowonganKerja() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Edit Lowongan Kerja Lokal</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Pekerjaan</Text>}
|
||||||
|
placeholder='Masukkan pekerjaan'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Nama Usaha</Text>}
|
||||||
|
placeholder='Masukkan nama usaha'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Alamat Usaha</Text>}
|
||||||
|
placeholder='Masukkan alamat usaha'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Nomor Telepon</Text>}
|
||||||
|
placeholder='Masukkan nomor telepon'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Gaji selama 1 bulan</Text>}
|
||||||
|
placeholder='Masukkan gaji'
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"sm"}>Deskripsi Lowongan Kerja</Text>
|
||||||
|
<KeamananEditor
|
||||||
|
showSubmit={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditLowonganKerja;
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Paper, Stack, Title, Text } from '@mantine/core';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function ListDataLowongan() {
|
|
||||||
return (
|
|
||||||
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={3}>List Data Lowongan Kerja Lokal</Title>
|
|
||||||
<Box>
|
|
||||||
<Text fw={"bold"}>Bekerja Sebagai</Text>
|
|
||||||
<Text> </Text>
|
|
||||||
<Text fw={"bold"}>Alamat Usaha</Text>
|
|
||||||
<Text> </Text>
|
|
||||||
<Text fw={"bold"}>Waktu Kerja</Text>
|
|
||||||
<Text> </Text>
|
|
||||||
<Text fw={"bold"}>Gaji selama 1 bulan</Text>
|
|
||||||
<Text> </Text>
|
|
||||||
</Box>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ListDataLowongan;
|
|
||||||
@@ -1,32 +1,58 @@
|
|||||||
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Group, Paper, SimpleGrid, Stack, Title } from '@mantine/core';
|
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||||
import React from 'react';
|
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||||
import CreateLowongan from './create/createLowongan';
|
import HeaderSearch from '../../_com/header';
|
||||||
import ListDataLowongan from './listData/page';
|
import JudulList from '../../_com/judulList';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
function Page() {
|
function LowonganKerjaLokal() {
|
||||||
return (
|
return (
|
||||||
<Stack gap={"xs"}>
|
<Box>
|
||||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
<HeaderSearch
|
||||||
<Box>
|
title='Lowongan Kerja Lokal'
|
||||||
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
placeholder='pencarian'
|
||||||
<Title order={3}>Lowongan Kerja Lokal</Title>
|
searchIcon={<IconSearch size={20} />}
|
||||||
<CreateLowongan/>
|
/>
|
||||||
<Group>
|
<ListLowonganKerjaLokal/>
|
||||||
<Button mt={10} bg={colors['blue-button']}>
|
</Box>
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<Box>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
|
||||||
<ListDataLowongan/>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
</SimpleGrid>
|
|
||||||
</Stack>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
function ListLowonganKerjaLokal() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<JudulList
|
||||||
|
title='List Lowongan Kerja Lokal'
|
||||||
|
href='/admin/ekonomi/lowongan-kerja-lokal/create'
|
||||||
|
/>
|
||||||
|
<Table striped withTableBorder withRowBorders>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh>Bekerja Sebagai</TableTh>
|
||||||
|
<TableTh>Nama Usaha</TableTh>
|
||||||
|
<TableTh>Alamat Usaha</TableTh>
|
||||||
|
<TableTh>Detail</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
<TableTr>
|
||||||
|
<TableTd>Karyawan</TableTd>
|
||||||
|
<TableTd>BIBD</TableTd>
|
||||||
|
<TableTd>Jalan In Aja</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/lowongan-kerja-lokal/detail')}>
|
||||||
|
<IconDeviceImac size={20} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LowonganKerjaLokal;
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Stack, Text, TextInput, Title } from '@mantine/core';
|
|
||||||
import { IconImageInPicture } from '@tabler/icons-react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function CreatePasarDesa() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Title order={4}>Produk Pasar Desa</Title>
|
|
||||||
<Box>
|
|
||||||
<Text fz={"sm"} fw={"bold"}>Masukkan Foto Produk</Text>
|
|
||||||
<IconImageInPicture size={24} color={colors['blue-button']} />
|
|
||||||
</Box>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Nama Produk</Text>}
|
|
||||||
placeholder="masukkan nama produk"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Harga Produk</Text>}
|
|
||||||
placeholder="masukkan harga produk"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Rating Produk</Text>}
|
|
||||||
placeholder="masukkan rating produk"
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label={<Text fz={"sm"} fw={"bold"}>Alamat Usaha</Text>}
|
|
||||||
placeholder="masukkan alamat usaha"
|
|
||||||
/>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CreatePasarDesa;
|
|
||||||
56
src/app/admin/(dashboard)/ekonomi/pasar-desa/create/page.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { KeamananEditor } from '../../../keamanan/_com/keamananEditor';
|
||||||
|
|
||||||
|
function CreatePasarDesa() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Create Pasar Desa</Title>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"sm"}>Masukkan Image</Text>
|
||||||
|
<IconImageInPicture size={50} />
|
||||||
|
</Box>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Nama Produk</Text>}
|
||||||
|
placeholder='Masukkan nama produk'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Harga Produk</Text>}
|
||||||
|
placeholder='Masukkan harga produk'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Rating Produk</Text>}
|
||||||
|
placeholder='Masukkan rating produk'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Alamat Usaha</Text>}
|
||||||
|
placeholder='Masukkan alamat usaha'
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"sm"}>Deskripsi Produk</Text>
|
||||||
|
<KeamananEditor
|
||||||
|
showSubmit={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreatePasarDesa;
|
||||||
74
src/app/admin/(dashboard)/ekonomi/pasar-desa/detail/page.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Paper, Stack, Flex, Text, Image } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconX, IconEdit } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import React from 'react';
|
||||||
|
// import { ModalKonfirmasiHapus } from '../../../_com/modalKonfirmasiHapus';
|
||||||
|
|
||||||
|
function DetailPasarDesa() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button variant="subtle" onClick={() => router.back()}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25} />
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Paper w={{ base: "100%", md: "50%" }} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack>
|
||||||
|
<Text fz={"xl"} fw={"bold"}>Detail Pasar Desa</Text>
|
||||||
|
|
||||||
|
<Paper bg={colors['BG-trans']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"lg"} fw={"bold"}>Nama Produk</Text>
|
||||||
|
<Text fz={"lg"}>Test Judul</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"lg"} fw={"bold"}>Harga Produk</Text>
|
||||||
|
<Text fz={"lg"}>Rp. 20.000</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"lg"} fw={"bold"}>Rating Produk</Text>
|
||||||
|
<Text fz={"lg"}>5</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"lg"} fw={"bold"}>Alamat Usaha</Text>
|
||||||
|
<Text fz={"lg"}>Jalan In Aja</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"lg"} fw={"bold"}>Gambar</Text>
|
||||||
|
<Image src={"/"} alt="gambar" />
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"lg"} fw={"bold"}>Deskripsi</Text>
|
||||||
|
<Text fz={"lg"} >Test Konten</Text>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Flex gap={"xs"}>
|
||||||
|
<Button color="red">
|
||||||
|
<IconX size={20} />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/pasar-desa/edit')} color="green">
|
||||||
|
<IconEdit size={20} />
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Modal Hapus
|
||||||
|
<ModalKonfirmasiHapus
|
||||||
|
opened={modalHapus}
|
||||||
|
onClose={() => setModalHapus(false)}
|
||||||
|
onConfirm={handleHapus}
|
||||||
|
text="Apakah anda yakin ingin menghapus potensi ini?"
|
||||||
|
/> */}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailPasarDesa;
|
||||||
56
src/app/admin/(dashboard)/ekonomi/pasar-desa/edit/page.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack, IconImageInPicture } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { KeamananEditor } from '../../../keamanan/_com/keamananEditor';
|
||||||
|
|
||||||
|
function EditPasarDesa() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Edit Pasar Desa</Title>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"sm"}>Masukkan Image</Text>
|
||||||
|
<IconImageInPicture size={50} />
|
||||||
|
</Box>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Nama Produk</Text>}
|
||||||
|
placeholder='Masukkan nama produk'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Harga Produk</Text>}
|
||||||
|
placeholder='Masukkan harga produk'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Rating Produk</Text>}
|
||||||
|
placeholder='Masukkan rating produk'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Alamat Usaha</Text>}
|
||||||
|
placeholder='Masukkan alamat usaha'
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"} fz={"sm"}>Deskripsi Produk</Text>
|
||||||
|
<KeamananEditor
|
||||||
|
showSubmit={false}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditPasarDesa;
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
import colors from '@/con/colors';
|
|
||||||
import { Box, Paper, Stack, Text, Title } from '@mantine/core';
|
|
||||||
import { IconImageInPicture } from '@tabler/icons-react';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
function ListDataUsaha() {
|
|
||||||
return (
|
|
||||||
<Box>
|
|
||||||
<Stack gap={"xs"}>
|
|
||||||
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
|
||||||
<Box>
|
|
||||||
<Title mb={10} order={4}>List Produk Pasar Desa</Title>
|
|
||||||
<Box>
|
|
||||||
<Text fz={"sm"} fw={"bold"}>Foto Produk</Text>
|
|
||||||
<IconImageInPicture size={24} color={colors['blue-button']} />
|
|
||||||
</Box>
|
|
||||||
<Text fz={"sm"} fw={"bold"}>Nama Produk</Text>
|
|
||||||
<Text></Text>
|
|
||||||
<Text fz={"sm"} fw={"bold"}>Harga Produk</Text>
|
|
||||||
<Text></Text>
|
|
||||||
<Text fz={"sm"} fw={"bold"}>Rating Produk</Text>
|
|
||||||
<Text></Text>
|
|
||||||
<Text fz={"sm"} fw={"bold"}>Alamat Usaha</Text>
|
|
||||||
<Text></Text>
|
|
||||||
</Box>
|
|
||||||
</Paper>
|
|
||||||
</Stack>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ListDataUsaha;
|
|
||||||
@@ -1,31 +1,60 @@
|
|||||||
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Group, Paper, Stack, Title } from '@mantine/core';
|
import { Box, Button, Paper, Table, TableTbody, TableTd, TableTh, TableThead, TableTr } from '@mantine/core';
|
||||||
import React from 'react';
|
import { IconDeviceImac, IconSearch } from '@tabler/icons-react';
|
||||||
import CreatePasarDesa from './create/createPasarDesa';
|
import HeaderSearch from '../../_com/header';
|
||||||
import ListDataUsaha from './listData/page';
|
import JudulList from '../../_com/judulList';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
function Page() {
|
function PasarDesa() {
|
||||||
return (
|
return (
|
||||||
<Stack gap={'xs'}>
|
<Box>
|
||||||
<Box>
|
<HeaderSearch
|
||||||
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'} radius={10}>
|
title='Pasar Desa'
|
||||||
<Stack gap={'xs'}>
|
placeholder='pencarian'
|
||||||
<Title order={3}>Pasar Desa</Title>
|
searchIcon={<IconSearch size={20} />}
|
||||||
<CreatePasarDesa />
|
/>
|
||||||
<Group>
|
<ListPasarDesa/>
|
||||||
<Button
|
</Box>
|
||||||
mt={10}
|
|
||||||
bg={colors['blue-button']}
|
|
||||||
>
|
|
||||||
Submit
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
</Box>
|
|
||||||
<ListDataUsaha />
|
|
||||||
</Stack>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
function ListPasarDesa() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box py={10}>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<JudulList
|
||||||
|
title='List Pasar Desa'
|
||||||
|
href='/admin/ekonomi/pasar-desa/create'
|
||||||
|
/>
|
||||||
|
<Table striped withTableBorder withRowBorders>
|
||||||
|
<TableThead>
|
||||||
|
<TableTr>
|
||||||
|
<TableTh>Nama Produk</TableTh>
|
||||||
|
<TableTh>Harga Produk</TableTh>
|
||||||
|
<TableTh>Rating Produk</TableTh>
|
||||||
|
<TableTh>Alamat Usaha</TableTh>
|
||||||
|
<TableTh>Detail</TableTh>
|
||||||
|
</TableTr>
|
||||||
|
</TableThead>
|
||||||
|
<TableTbody>
|
||||||
|
<TableTr>
|
||||||
|
<TableTd>Produk 1</TableTd>
|
||||||
|
<TableTd>Harga Rp. 20.000</TableTd>
|
||||||
|
<TableTd>Rating 5</TableTd>
|
||||||
|
<TableTd>Jalan In Aja</TableTd>
|
||||||
|
<TableTd>
|
||||||
|
<Button onClick={() => router.push('/admin/ekonomi/pasar-desa/detail')}>
|
||||||
|
<IconDeviceImac size={20} />
|
||||||
|
</Button>
|
||||||
|
</TableTd>
|
||||||
|
</TableTr>
|
||||||
|
</TableTbody>
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PasarDesa;
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
'use client'
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconArrowBack } from '@tabler/icons-react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
function CreateProgramKemiskinan() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box mb={10}>
|
||||||
|
<Button onClick={() => router.back()} variant='subtle' color={'blue'}>
|
||||||
|
<IconArrowBack color={colors['blue-button']} size={25}/>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={4}>Create Program Kemiskinan</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Judul Program</Text>}
|
||||||
|
placeholder='Masukkan judul program'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Deskripsi Singkat</Text>}
|
||||||
|
placeholder='Masukkan deskripsi'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Jumlah Masyarakat Miskin</Text>}
|
||||||
|
placeholder='Masukkan jumlah masyarakat miskin'
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fw={"bold"} fz={"sm"}>Deskripsi</Text>}
|
||||||
|
placeholder='Masukkan deskripsi'
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>Submit</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateProgramKemiskinan;
|
||||||