Compare commits
4 Commits
nico/21-ma
...
nico/25-ma
| Author | SHA1 | Date | |
|---|---|---|---|
| 02738104b5 | |||
| 92de697ae0 | |||
| cf6a5422ec | |||
| ee9368e911 |
@@ -6,6 +6,6 @@
|
|||||||
"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": "/assets/images/ppid/profile-ppid/perbekel.png"
|
"imageUrl": "/uploads/seeded-images/profile-ppid/perbekel.png"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -242,17 +242,18 @@ model Berita {
|
|||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
judul String
|
judul String
|
||||||
deskripsi String
|
deskripsi String
|
||||||
image String
|
image FileStorage @relation(fields: [imageId], references: [id])
|
||||||
|
imageId String
|
||||||
content String @db.Text
|
content String @db.Text
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime @default(now())
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
KatagoryBerita KatagoryBerita? @relation(fields: [katagoryBeritaId], references: [id])
|
kategoriBerita KategoriBerita? @relation(fields: [kategoriBeritaId], references: [id])
|
||||||
katagoryBeritaId String?
|
kategoriBeritaId String?
|
||||||
}
|
}
|
||||||
|
|
||||||
model KatagoryBerita {
|
model KategoriBerita {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String @unique
|
name String @unique
|
||||||
beritas Berita[]
|
beritas Berita[]
|
||||||
@@ -593,7 +594,8 @@ model FileStorage {
|
|||||||
mimeType String
|
mimeType String
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime? // nullable agar bisa menandakan belum dihapus
|
deletedAt DateTime?
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
link String
|
link String
|
||||||
|
Berita Berita[]
|
||||||
}
|
}
|
||||||
|
|||||||
532
prisma/seed.ts
@@ -1,277 +1,277 @@
|
|||||||
import prisma from '@/lib/prisma'
|
import prisma from "@/lib/prisma";
|
||||||
import categoryPengumuman from './data/category-pengumuman.json'
|
import categoryPengumuman from "./data/category-pengumuman.json";
|
||||||
import katagoryBerita from './data/katagory-berita.json'
|
import kategoriBerita from "./data/kategori-berita.json";
|
||||||
import caraMemperolehInformasi from './data/list-caraMemperolehInformasi.json'
|
import caraMemperolehInformasi from "./data/list-caraMemperolehInformasi.json";
|
||||||
import caraMemperolehSalinanInformasi from './data/list-caraMemperolehSalinanInformasi.json'
|
import caraMemperolehSalinanInformasi from "./data/list-caraMemperolehSalinanInformasi.json";
|
||||||
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 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 profileDesa from "./data/desa/profile/profile_desa.json";
|
||||||
import profilePerbekel from './data/desa/profile/profil_perbekel.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 path from "path";
|
||||||
import fs from 'fs'
|
import fs from "fs";
|
||||||
import { mkdir, writeFile } from 'fs/promises'
|
import { mkdir, writeFile } from "fs/promises";
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from "uuid";
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
for (const l of layanan) {
|
for (const l of layanan) {
|
||||||
await prisma.layanan.upsert({
|
await prisma.layanan.upsert({
|
||||||
where: {
|
where: {
|
||||||
name: l.name
|
name: l.name,
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
name: l.name
|
name: l.name,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
name: l.name
|
name: l.name,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("layanan success ...");
|
||||||
|
|
||||||
|
for (const p of potensi) {
|
||||||
|
await prisma.potensi.upsert({
|
||||||
|
where: {
|
||||||
|
name: p.name,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: p.name,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
name: p.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("potensi success ...");
|
||||||
|
|
||||||
|
for (const k of kategoriBerita) {
|
||||||
|
await prisma.kategoriBerita.upsert({
|
||||||
|
where: {
|
||||||
|
name: k.name,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: k.name,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
name: k.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("kategori berita success ...");
|
||||||
|
|
||||||
|
for (const c of categoryPengumuman) {
|
||||||
|
await prisma.categoryPengumuman.upsert({
|
||||||
|
where: {
|
||||||
|
name: c.name,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: c.name,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
name: c.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("category pengumuman success ...");
|
||||||
|
|
||||||
|
for (const j of jenisInformasiDiminta) {
|
||||||
|
await prisma.jenisInformasiDiminta.upsert({
|
||||||
|
where: {
|
||||||
|
name: j.name,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: j.name,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
name: j.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("jenis informasi diminta success ...");
|
||||||
|
|
||||||
|
for (const c of caraMemperolehInformasi) {
|
||||||
|
await prisma.caraMemperolehInformasi.upsert({
|
||||||
|
where: {
|
||||||
|
name: c.name,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: c.name,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
name: c.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("cara memperoleh informasi success ...");
|
||||||
|
|
||||||
|
for (const c of caraMemperolehSalinanInformasi) {
|
||||||
|
await prisma.caraMemperolehSalinanInformasi.upsert({
|
||||||
|
where: {
|
||||||
|
name: c.name,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: c.name,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
name: c.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("cara memperoleh salinan informasi success ...");
|
||||||
|
|
||||||
|
const seedProfilePPID = async () => {
|
||||||
|
const targetDir = path.resolve("public", "uploads", "seeded-images", "profile-ppid")
|
||||||
|
|
||||||
|
// Buat folder hanya jika belum ada
|
||||||
|
if (!fs.existsSync(targetDir)) {
|
||||||
|
await mkdir(targetDir, { recursive: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("layanan success ...")
|
for (const c of profilePPID) {
|
||||||
|
let finalImageUrl = c.imageUrl
|
||||||
for (const p of potensi) {
|
|
||||||
await prisma.potensi.upsert({
|
if (c.imageUrl.startsWith("/uploads/seeded-images/")) {
|
||||||
where: {
|
const filename = path.basename(c.imageUrl)
|
||||||
name: p.name
|
const seedImagePath = path.resolve("prisma", "seed-images", filename)
|
||||||
},
|
|
||||||
update: {
|
const targetFilename = `${uuid()}_${filename}`
|
||||||
name: p.name
|
const targetPath = path.join(targetDir, targetFilename)
|
||||||
},
|
|
||||||
create: {
|
const buffer = fs.readFileSync(seedImagePath)
|
||||||
name: p.name
|
await writeFile(targetPath, buffer)
|
||||||
}
|
|
||||||
})
|
finalImageUrl = `/uploads/seeded-images/profile-ppid/${targetFilename}`
|
||||||
}
|
|
||||||
|
|
||||||
console.log("potensi success ...")
|
|
||||||
|
|
||||||
for (const k of katagoryBerita) {
|
|
||||||
await prisma.katagoryBerita.upsert({
|
|
||||||
where: {
|
|
||||||
name: k.name
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
name: k.name
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
name: k.name
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("katagory berita success ...")
|
|
||||||
|
|
||||||
for (const c of categoryPengumuman) {
|
|
||||||
await prisma.categoryPengumuman.upsert({
|
|
||||||
where: {
|
|
||||||
name: c.name
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
name: c.name
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
name: c.name
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("category pengumuman success ...")
|
|
||||||
|
|
||||||
for (const j of jenisInformasiDiminta) {
|
|
||||||
await prisma.jenisInformasiDiminta.upsert({
|
|
||||||
where: {
|
|
||||||
name: j.name
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
name: j.name
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
name: j.name
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
console.log("jenis informasi diminta success ...")
|
|
||||||
|
|
||||||
for (const c of caraMemperolehInformasi) {
|
|
||||||
await prisma.caraMemperolehInformasi.upsert({
|
|
||||||
where: {
|
|
||||||
name: c.name
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
name: c.name
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
name: c.name
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
console.log("cara memperoleh informasi success ...")
|
|
||||||
|
|
||||||
for (const c of caraMemperolehSalinanInformasi) {
|
|
||||||
await prisma.caraMemperolehSalinanInformasi.upsert({
|
|
||||||
where: {
|
|
||||||
name: c.name
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
name: c.name
|
|
||||||
},
|
|
||||||
create: {
|
|
||||||
name: c.name
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
console.log("cara memperoleh salinan informasi success ...")
|
|
||||||
|
|
||||||
const seedProfilePPID = async () => {
|
|
||||||
const targetDir = path.resolve("public", "assets", "images", "ppid", "profile-ppid")
|
|
||||||
await mkdir(targetDir, { recursive: true })
|
|
||||||
|
|
||||||
for (const c of profilePPID) {
|
|
||||||
let finalImageUrl = c.imageUrl
|
|
||||||
|
|
||||||
// kalau imageUrl diawali dengan "/assets/images", artinya kita harus copy file dari seed-images
|
|
||||||
if (c.imageUrl.startsWith("/assets/images/")) {
|
|
||||||
const filename = path.basename(c.imageUrl) // misal "perbekel.png"
|
|
||||||
const seedImagePath = path.resolve("prisma", "seed-images", filename)
|
|
||||||
|
|
||||||
// Buat nama baru yang unik agar tidak bentrok
|
|
||||||
const targetFilename = `${uuid()}_${filename}`
|
|
||||||
const targetPath = path.join(targetDir, targetFilename)
|
|
||||||
|
|
||||||
// Salin file dari prisma/seed-images ke public/
|
|
||||||
const buffer = fs.readFileSync(seedImagePath)
|
|
||||||
await writeFile(targetPath, buffer)
|
|
||||||
|
|
||||||
finalImageUrl = `/assets/images/ppid/profile-ppid/${targetFilename}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upsert ke database
|
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await seedProfilePPID()
|
await prisma.profilePPID.upsert({
|
||||||
|
where: { id: c.id },
|
||||||
|
update: {
|
||||||
for (const v of visiMisiPPID) {
|
name: c.name,
|
||||||
await prisma.visiMisiPPID.upsert({
|
biodata: c.biodata,
|
||||||
where: {
|
riwayat: c.riwayat,
|
||||||
id: v.id,
|
pengalaman: c.pengalaman,
|
||||||
},
|
unggulan: c.unggulan,
|
||||||
update: {
|
imageUrl: finalImageUrl,
|
||||||
misi: v.misi,
|
},
|
||||||
visi: v.visi
|
create: {
|
||||||
},
|
id: c.id,
|
||||||
create: {
|
name: c.name,
|
||||||
id: v.id,
|
biodata: c.biodata,
|
||||||
misi: v.misi,
|
riwayat: c.riwayat,
|
||||||
visi: v.visi
|
pengalaman: c.pengalaman,
|
||||||
}
|
unggulan: c.unggulan,
|
||||||
})
|
imageUrl: finalImageUrl,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
console.log("visi misi PPID success ...")
|
|
||||||
|
console.log("✅ profilePPID seeded from JSON with image copying")
|
||||||
|
}
|
||||||
|
|
||||||
|
await seedProfilePPID()
|
||||||
|
|
||||||
for (const v of dasarHukumPPID) {
|
for (const v of visiMisiPPID) {
|
||||||
await prisma.dasarHukumPPID.upsert({
|
await prisma.visiMisiPPID.upsert({
|
||||||
where: {
|
where: {
|
||||||
id: v.id,
|
id: v.id,
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
judul: v.judul,
|
misi: v.misi,
|
||||||
content: v.content
|
visi: v.visi,
|
||||||
},
|
},
|
||||||
create: {
|
create: {
|
||||||
id: v.id,
|
id: v.id,
|
||||||
judul: v.judul,
|
misi: v.misi,
|
||||||
content: v.content
|
visi: v.visi,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
console.log("dasar hukum PPID success ...")
|
console.log("visi misi PPID success ...");
|
||||||
|
|
||||||
for (const v of profileDesa) {
|
for (const v of dasarHukumPPID) {
|
||||||
await prisma.profileDesa.upsert({
|
await prisma.dasarHukumPPID.upsert({
|
||||||
where: {
|
where: {
|
||||||
id: v.id,
|
id: v.id,
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
sejarah: v.sejarah,
|
judul: v.judul,
|
||||||
visi: v.visi,
|
content: v.content,
|
||||||
misi: v.misi,
|
},
|
||||||
lambang: v.lambang,
|
create: {
|
||||||
maskot: v.maskot,
|
id: v.id,
|
||||||
profilPerbekelId: v.profilPerbekelId
|
judul: v.judul,
|
||||||
},
|
content: v.content,
|
||||||
create: {
|
},
|
||||||
id: v.id,
|
});
|
||||||
sejarah: v.sejarah,
|
}
|
||||||
visi: v.visi,
|
console.log("dasar hukum PPID success ...");
|
||||||
misi: v.misi,
|
|
||||||
lambang: v.lambang,
|
|
||||||
maskot: v.maskot,
|
|
||||||
profilPerbekelId: v.profilPerbekelId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
console.log("profile desa success ...")
|
|
||||||
|
|
||||||
for (const v of profilePerbekel) {
|
for (const v of profileDesa) {
|
||||||
await prisma.profilPerbekel.upsert({
|
await prisma.profileDesa.upsert({
|
||||||
where: {
|
where: {
|
||||||
id: v.id,
|
id: v.id,
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
biodata: v.biodata,
|
sejarah: v.sejarah,
|
||||||
pengalaman: v.pengalaman,
|
visi: v.visi,
|
||||||
pengalamanOrganisasi: v.pengalamanOrganisasi,
|
misi: v.misi,
|
||||||
programUnggulan: v.programUnggulan
|
lambang: v.lambang,
|
||||||
},
|
maskot: v.maskot,
|
||||||
create: {
|
profilPerbekelId: v.profilPerbekelId,
|
||||||
id: v.id,
|
},
|
||||||
biodata: v.biodata,
|
create: {
|
||||||
pengalaman: v.pengalaman,
|
id: v.id,
|
||||||
pengalamanOrganisasi: v.pengalamanOrganisasi,
|
sejarah: v.sejarah,
|
||||||
programUnggulan: v.programUnggulan
|
visi: v.visi,
|
||||||
}
|
misi: v.misi,
|
||||||
})
|
lambang: v.lambang,
|
||||||
}
|
maskot: v.maskot,
|
||||||
console.log("profile perbekel success ...")
|
profilPerbekelId: v.profilPerbekelId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("profile desa success ...");
|
||||||
|
|
||||||
})().then(() => prisma.$disconnect()).catch((e) => {
|
for (const v of profilePerbekel) {
|
||||||
console.error(e)
|
await prisma.profilPerbekel.upsert({
|
||||||
prisma.$disconnect()
|
where: {
|
||||||
|
id: v.id,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
biodata: v.biodata,
|
||||||
|
pengalaman: v.pengalaman,
|
||||||
|
pengalamanOrganisasi: v.pengalamanOrganisasi,
|
||||||
|
programUnggulan: v.programUnggulan,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: v.id,
|
||||||
|
biodata: v.biodata,
|
||||||
|
pengalaman: v.pengalaman,
|
||||||
|
pengalamanOrganisasi: v.pengalamanOrganisasi,
|
||||||
|
programUnggulan: v.programUnggulan,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log("profile perbekel success ...");
|
||||||
|
})()
|
||||||
|
.then(() => prisma.$disconnect())
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e);
|
||||||
|
prisma.$disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("exit", () => {
|
||||||
|
prisma.$disconnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on('exit', () => {
|
process.on("SIGINT", () => {
|
||||||
prisma.$disconnect()
|
prisma.$disconnect();
|
||||||
})
|
process.exit(0);
|
||||||
|
});
|
||||||
process.on('SIGINT', () => {
|
|
||||||
prisma.$disconnect()
|
|
||||||
process.exit(0)
|
|
||||||
})
|
|
||||||
|
|||||||
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 254 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 275 KiB |
BIN
public/uploads/profile-ppid/1_1747885424609_budaya-1.jpg
Normal file
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 169 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 |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 275 KiB |
|
After Width: | Height: | Size: 275 KiB |
BIN
public/uploads/seeded-images/profile-ppid/perbekel.png
Normal file
|
After Width: | Height: | Size: 275 KiB |
35
src/app/admin/(dashboard)/_com/modalKonfirmasiHapus.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// components/modal/ModalKonfirmasiHapus.tsx
|
||||||
|
import { Modal, Text, Button, Flex } from "@mantine/core"
|
||||||
|
|
||||||
|
interface ModalKonfirmasiHapusProps {
|
||||||
|
opened: boolean
|
||||||
|
loading?: boolean
|
||||||
|
onClose: () => void
|
||||||
|
onConfirm: () => void
|
||||||
|
text: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ModalKonfirmasiHapus({
|
||||||
|
opened,
|
||||||
|
loading = false,
|
||||||
|
onClose,
|
||||||
|
onConfirm,
|
||||||
|
text,
|
||||||
|
}: ModalKonfirmasiHapusProps) {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
opened={opened}
|
||||||
|
onClose={onClose}
|
||||||
|
title="Konfirmasi Hapus"
|
||||||
|
centered
|
||||||
|
>
|
||||||
|
<Text mb="md">{text}</Text>
|
||||||
|
<Flex justify="flex-end" gap="sm">
|
||||||
|
<Button variant="default" onClick={onClose}>Batal</Button>
|
||||||
|
<Button color="red" onClick={onConfirm} loading={loading}>
|
||||||
|
Yakin Hapus
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,23 +1,34 @@
|
|||||||
/* 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";
|
||||||
|
|
||||||
|
// 1. Schema validasi dengan Zod
|
||||||
const templateForm = z.object({
|
const templateForm = 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"),
|
||||||
image: z.string().url().min(3, "Image minimal 3 karakter"),
|
|
||||||
content: z.string().min(3, "Content minimal 3 karakter"),
|
content: z.string().min(3, "Content minimal 3 karakter"),
|
||||||
katagoryBeritaId: z.string().nonempty(),
|
kategoriBeritaId: z.string().nonempty(),
|
||||||
|
imageId: z.string().nonempty(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 2. Default value form berita (hindari uncontrolled input)
|
||||||
|
const defaultForm = {
|
||||||
|
judul: "",
|
||||||
|
deskripsi: "",
|
||||||
|
imageId: "",
|
||||||
|
content: "",
|
||||||
|
kategoriBeritaId: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3. Kategori proxy
|
||||||
const category = proxy({
|
const category = proxy({
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| null
|
| null
|
||||||
| Prisma.KatagoryBeritaGetPayload<{ omit: { isActive: true } }>[],
|
| Prisma.KategoriBeritaGetPayload<{ omit: { isActive: true } }>[],
|
||||||
async load() {
|
async load() {
|
||||||
const res = await ApiFetch.api.desa.berita.category["find-many"].get();
|
const res = await ApiFetch.api.desa.berita.category["find-many"].get();
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
@@ -27,23 +38,12 @@ const category = proxy({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
type BeritaForm = Prisma.BeritaGetPayload<{
|
// 4. Berita proxy
|
||||||
select: {
|
|
||||||
judul: true;
|
|
||||||
deskripsi: true;
|
|
||||||
image: true;
|
|
||||||
content: true;
|
|
||||||
katagoryBeritaId: true;
|
|
||||||
};
|
|
||||||
}>;
|
|
||||||
|
|
||||||
const berita = proxy({
|
const berita = proxy({
|
||||||
create: {
|
create: {
|
||||||
form: {} as BeritaForm,
|
form: { ...defaultForm }, // ✅ ini kunci fix-nya
|
||||||
loading: false,
|
loading: false,
|
||||||
async create() {
|
async create() {
|
||||||
berita.create.form.image =
|
|
||||||
"https://www.shutterstock.com/image-vector/lower-news-live-streaming-breaking-600nw-2535984111.jpg";
|
|
||||||
const cek = templateForm.safeParse(berita.create.form);
|
const cek = templateForm.safeParse(berita.create.form);
|
||||||
if (!cek.success) {
|
if (!cek.success) {
|
||||||
const err = `[${cek.error.issues
|
const err = `[${cek.error.issues
|
||||||
@@ -51,6 +51,7 @@ const berita = proxy({
|
|||||||
.join("\n")}] required`;
|
.join("\n")}] required`;
|
||||||
return toast.error(err);
|
return toast.error(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
berita.create.loading = true;
|
berita.create.loading = true;
|
||||||
const res = await ApiFetch.api.desa.berita["create"].post(
|
const res = await ApiFetch.api.desa.berita["create"].post(
|
||||||
@@ -58,7 +59,7 @@ const berita = proxy({
|
|||||||
);
|
);
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
berita.findMany.load();
|
berita.findMany.load();
|
||||||
return toast.success("succes create");
|
return toast.success("success create");
|
||||||
}
|
}
|
||||||
|
|
||||||
return toast.error("failed create");
|
return toast.error("failed create");
|
||||||
@@ -68,23 +69,65 @@ const berita = proxy({
|
|||||||
berita.create.loading = false;
|
berita.create.loading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
resetForm() {
|
||||||
|
berita.create.form = { ...defaultForm };
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
findMany: {
|
findMany: {
|
||||||
data: null as
|
data: null as
|
||||||
| Prisma.BeritaGetPayload<{ omit: { isActive: true } }>[]
|
| Prisma.BeritaGetPayload<{
|
||||||
|
include: {
|
||||||
|
image: true;
|
||||||
|
kategoriBerita: true;
|
||||||
|
};
|
||||||
|
}>[]
|
||||||
| null,
|
| null,
|
||||||
async load() {
|
async load() {
|
||||||
const res = await ApiFetch.api.desa.berita["find-many"].get();
|
const res = await ApiFetch.api.desa.berita["find-many"].get();
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
berita.findMany.data = (res.data?.data as any) ?? [];
|
berita.findMany.data = (res.data?.data ) ?? [];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
delete: {
|
||||||
|
loading: false,
|
||||||
|
async byId(id: string) {
|
||||||
|
if (!id) return toast.warn("ID tidak valid");
|
||||||
|
|
||||||
|
try {
|
||||||
|
berita.delete.loading = true;
|
||||||
|
|
||||||
|
const response = await fetch(`/api/desa/berita/delete/${id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && result?.success) {
|
||||||
|
toast.success(result.message || "Berita berhasil dihapus");
|
||||||
|
await berita.findMany.load(); // refresh list
|
||||||
|
} else {
|
||||||
|
toast.error(result?.message || "Gagal menghapus berita");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Gagal delete:", error);
|
||||||
|
toast.error("Terjadi kesalahan saat menghapus berita");
|
||||||
|
} finally {
|
||||||
|
berita.delete.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 5. State global
|
||||||
const stateDashboardBerita = proxy({
|
const stateDashboardBerita = proxy({
|
||||||
category,
|
category,
|
||||||
berita,
|
berita,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default stateDashboardBerita;
|
export default stateDashboardBerita;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
'use client'
|
'use client'
|
||||||
import { RichTextEditor, Link } from '@mantine/tiptap';
|
import { RichTextEditor, Link } from '@mantine/tiptap';
|
||||||
import { useEditor } from '@tiptap/react';
|
import { useEditor } from '@tiptap/react';
|
||||||
@@ -8,28 +9,39 @@ import TextAlign from '@tiptap/extension-text-align';
|
|||||||
import Superscript from '@tiptap/extension-superscript';
|
import Superscript from '@tiptap/extension-superscript';
|
||||||
import SubScript from '@tiptap/extension-subscript';
|
import SubScript from '@tiptap/extension-subscript';
|
||||||
import { Button, Stack } from '@mantine/core';
|
import { Button, Stack } from '@mantine/core';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
const content =
|
// const content =
|
||||||
'<h2 style="text-align: center;">Welcome to Mantine rich text editor</h2><p><code>RichTextEditor</code> component focuses on usability and is designed to be as simple as possible to bring a familiar editing experience to regular users. <code>RichTextEditor</code> is based on <a href="https://tiptap.dev/" rel="noopener noreferrer" target="_blank">Tiptap.dev</a> and supports all of its features:</p><ul><li>General text formatting: <strong>bold</strong>, <em>italic</em>, <u>underline</u>, <s>strike-through</s> </li><li>Headings (h1-h6)</li><li>Sub and super scripts (<sup><sup /></sup> and <sub><sub /></sub> tags)</li><li>Ordered and bullet lists</li><li>Text align </li><li>And all <a href="https://tiptap.dev/extensions" target="_blank" rel="noopener noreferrer">other extensions</a></li></ul>';
|
// '<h2 style="text-align: center;">Welcome to Mantine rich text editor</h2><p><code>RichTextEditor</code> component focuses on usability and is designed to be as simple as possible to bring a familiar editing experience to regular users. <code>RichTextEditor</code> is based on <a href="https://tiptap.dev/" rel="noopener noreferrer" target="_blank">Tiptap.dev</a> and supports all of its features:</p><ul><li>General text formatting: <strong>bold</strong>, <em>italic</em>, <u>underline</u>, <s>strike-through</s> </li><li>Headings (h1-h6)</li><li>Sub and super scripts (<sup><sup /></sup> and <sub><sub /></sub> tags)</li><li>Ordered and bullet lists</li><li>Text align </li><li>And all <a href="https://tiptap.dev/extensions" target="_blank" rel="noopener noreferrer">other extensions</a></li></ul>';
|
||||||
|
|
||||||
export function BeritaEditor({ onSubmit }: { onSubmit: (val: string) => void }) {
|
export function BeritaEditor({
|
||||||
const editor = useEditor({
|
onEditorReady,
|
||||||
extensions: [
|
showSubmit = true,
|
||||||
StarterKit,
|
onSubmit,
|
||||||
Underline,
|
}: {
|
||||||
Link,
|
onEditorReady?: (editor: any | null) => void;
|
||||||
Superscript,
|
onSubmit?: (val: string) => void;
|
||||||
SubScript,
|
showSubmit?: boolean;
|
||||||
Highlight,
|
}) {
|
||||||
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
const editor = useEditor({
|
||||||
],
|
extensions: [
|
||||||
content,
|
StarterKit,
|
||||||
immediatelyRender: false
|
Underline,
|
||||||
});
|
Link,
|
||||||
|
Superscript,
|
||||||
if (!editor) {
|
SubScript,
|
||||||
return null;
|
Highlight,
|
||||||
}
|
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
||||||
|
],
|
||||||
|
content: '',
|
||||||
|
immediatelyRender: false
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onEditorReady?.(editor);
|
||||||
|
}, [editor, onEditorReady] );
|
||||||
|
|
||||||
|
if (!editor) return null;
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -82,10 +94,12 @@ export function BeritaEditor({ onSubmit }: { onSubmit: (val: string) => void })
|
|||||||
|
|
||||||
<RichTextEditor.Content />
|
<RichTextEditor.Content />
|
||||||
</RichTextEditor>
|
</RichTextEditor>
|
||||||
<Button onClick={() => {
|
{showSubmit && (
|
||||||
if (!editor) return
|
<Button onClick={() => {
|
||||||
onSubmit(editor?.getHTML())
|
if (!editor) return
|
||||||
}}>Submit</Button>
|
onSubmit?.(editor?.getHTML())
|
||||||
|
}}>Submit</Button>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,101 +1,358 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
'use client'
|
'use client'
|
||||||
import { Box, Center, Group, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from '@mantine/core';
|
import colors from '@/con/colors';
|
||||||
|
import ApiFetch from '@/lib/api-fetch';
|
||||||
|
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 { Prisma } from '@prisma/client';
|
||||||
import { IconImageInPicture } from '@tabler/icons-react';
|
import { IconEdit, IconImageInPicture, IconX } from '@tabler/icons-react';
|
||||||
|
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';
|
import { BeritaEditor } from './_com/BeritaEditor';
|
||||||
import colors from '@/con/colors';
|
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Title order={3}>Berita</Title>
|
<Title order={3}>Berita</Title>
|
||||||
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
<BeritaCreate />
|
||||||
<BeritaCreate />
|
<BeritaList />
|
||||||
<BeritaList />
|
|
||||||
</SimpleGrid>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function BeritaCreate() {
|
function BeritaCreate() {
|
||||||
const beritaState = useProxy(stateDashboardBerita)
|
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 (
|
return (
|
||||||
<Box py={10}>
|
<Box py={10}>
|
||||||
<Paper bg={colors['white-1']} p={'md'}>
|
<Paper bg={colors["white-1"]} p={"md"} w={{ base: "100%", md: "50%" }}>
|
||||||
<Stack gap={"xs"}>
|
<Stack gap={"xs"}>
|
||||||
<SelectCategory onChange={(val) => {
|
<TextInput
|
||||||
beritaState.berita.create.form.katagoryBeritaId = val.id
|
value={beritaState.berita.create.form.judul}
|
||||||
}} />
|
onChange={(val) => {
|
||||||
<TextInput onChange={(val) => {
|
beritaState.berita.create.form.judul = val.target.value;
|
||||||
beritaState.berita.create.form.judul = val.target.value
|
}}
|
||||||
}} label={"Judul"} placeholder='masukkan judul' />
|
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
|
||||||
<TextInput onChange={(val) => {
|
placeholder="masukkan judul"
|
||||||
beritaState.berita.create.form.deskripsi = val.target.value
|
/>
|
||||||
}} label={"Deskripsi"} placeholder='masukkan deskripsi' />
|
<SelectCategory
|
||||||
<Center w={200} h={200} bg={"gray"}>
|
onChange={(val) => {
|
||||||
<IconImageInPicture />
|
beritaState.berita.create.form.kategoriBeritaId = val.id;
|
||||||
</Center>
|
}}
|
||||||
<BeritaEditor onSubmit={(val) => {
|
/>
|
||||||
|
<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"
|
||||||
|
/>
|
||||||
|
|
||||||
beritaState.berita.create.form.content = val
|
<FileInput
|
||||||
beritaState.berita.create.create()
|
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>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Box>
|
</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()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (!beritaState.berita.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) {
|
||||||
|
beritaState.berita.delete.byId(selectedId)
|
||||||
|
setModalHapus(false)
|
||||||
|
setSelectedId(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!beritaState.berita.findMany.data) {
|
||||||
|
return (
|
||||||
|
<Stack py={10}>
|
||||||
|
{Array.from({ length: 10 }).map((_, k) => (
|
||||||
|
<Skeleton key={k} h={40} />
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<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>
|
<Text fz={"xl"} fw={"bold"}>List Berita</Text>
|
||||||
{beritaState.berita.findMany.data?.map((item) => (
|
<SimpleGrid cols={{ base: 1, md: 4 }}>
|
||||||
<Text key={item.id}>{item.judul}</Text>
|
{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={() => {
|
||||||
|
setSelectedId(item.id)
|
||||||
|
setModalHapus(true)
|
||||||
|
}}
|
||||||
|
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: 150, md: 150, lg: 150 }} src={item.image?.link} alt="gambar" />
|
||||||
|
</Box>
|
||||||
|
</Paper>
|
||||||
|
))}
|
||||||
|
</SimpleGrid>
|
||||||
</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.KatagoryBeritaGetPayload<{
|
|
||||||
select: {
|
|
||||||
name: true,
|
|
||||||
id: true
|
|
||||||
}
|
|
||||||
}>) => void
|
|
||||||
}) {
|
|
||||||
const beritaState = useProxy(stateDashboardBerita)
|
|
||||||
useShallowEffect(() => {
|
|
||||||
beritaState.category.findMany.load()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
if (!beritaState.category.findMany.data) return <Skeleton h={40} />
|
function SelectCategory({
|
||||||
return <Group>
|
onChange,
|
||||||
<Select placeholder='pilih katagori' label={<Text fz={"sm"} fw={"bold"}>Pilih Kategori</Text>} data={beritaState.category.findMany.data.map((item) => ({
|
}: {
|
||||||
value: item.id,
|
onChange: (value: Prisma.KategoriBeritaGetPayload<{
|
||||||
label: item.name
|
select: {
|
||||||
}))} onChange={(v) => {
|
name: true;
|
||||||
const data = beritaState.category.findMany.data?.find((item) => item.id === v)
|
id: true;
|
||||||
if (!data) return
|
};
|
||||||
onChange(data)
|
}>) => void;
|
||||||
}} />
|
}) {
|
||||||
</Group>
|
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"
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// function SelectCategory({ onChange }: {
|
||||||
|
// onChange: (value: Prisma.KategoriBeritaGetPayload<{
|
||||||
|
// select: {
|
||||||
|
// name: true,
|
||||||
|
// id: true
|
||||||
|
// }
|
||||||
|
// }>) => void
|
||||||
|
// }) {
|
||||||
|
// const beritaState = useProxy(stateDashboardBerita)
|
||||||
|
// useShallowEffect(() => {
|
||||||
|
// beritaState.category.findMany.load()
|
||||||
|
// }, [])
|
||||||
|
|
||||||
|
// if (!beritaState.category.findMany.data) return <Skeleton h={40} />
|
||||||
|
// return <Group>
|
||||||
|
// <Select placeholder='pilih kategori' label={<Text fz={"sm"} fw={"bold"}>Pilih Kategori</Text>} data={beritaState.category.findMany.data.map((item) => ({
|
||||||
|
// value: item.id,
|
||||||
|
// label: item.name
|
||||||
|
// }))} onChange={(v) => {
|
||||||
|
// const data = beritaState.category.findMany.data?.find((item) => item.id === v)
|
||||||
|
// if (!data) return
|
||||||
|
// onChange(data)
|
||||||
|
// }} />
|
||||||
|
// </Group>
|
||||||
|
// }
|
||||||
|
|
||||||
export default Page;
|
export default Page;
|
||||||
|
|||||||
@@ -1,10 +1,46 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Box>
|
||||||
demografi-pekerjaan
|
<Stack gap={"xs"}>
|
||||||
</div>
|
<Box>
|
||||||
|
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={"md"}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Demografi Pekerjaan</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Jumlah Pekerja Laki - Laki</Text>}
|
||||||
|
placeholder="Masukkan jumlah pekerja laki - laki"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Jumlah Pekerja Perempuan</Text>}
|
||||||
|
placeholder="Masukkan jumlah pekerja perempuan"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Nama Pekerjaan</Text>}
|
||||||
|
placeholder="Masukkan nama pekerjaan"
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Paper bg={colors['white-1']} p={"md"}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Grafik Demografi Pekerjaan</Title>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,42 @@
|
|||||||
|
import { Box, Button, Group, Paper, Stack, TextInput, Title } from '@mantine/core';
|
||||||
|
import colors from '@/con/colors';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Box>
|
||||||
jumlah-penduduk-miskin-2024-2025
|
<Stack gap={"xs"}>
|
||||||
</div>
|
<Box>
|
||||||
|
<Paper p={"md"} bg={colors['white-1']} w={{ base: '100%', md: '50%' }}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Jumlah Penduduk Miskin 2024-2025</Title>
|
||||||
|
<TextInput
|
||||||
|
label="Tahun"
|
||||||
|
placeholder="masukkan tahun"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label="Jumlah Penduduk Miskin"
|
||||||
|
placeholder="masukkan jumlah penduduk miskin"
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Paper p={"md"} bg={colors['white-1']} >
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Grafik Jumlah Penduduk Miskin 2024-2025</Title>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,31 @@
|
|||||||
import React from 'react';
|
import colors from "@/con/colors";
|
||||||
|
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from "@mantine/core";
|
||||||
|
import PengangguranBerdasarkanUsia from "./ui/pengangguranBerdasarkanUsia/page";
|
||||||
|
import PengangguranBerdasarkanPendidikan from "./ui/pengangguranBerdasarkanPendidikan/page";
|
||||||
|
|
||||||
function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Box>
|
||||||
jumlah-penduduk-usia-kerja-yang-menganggur
|
<Stack gap={"xs"}>
|
||||||
</div>
|
<Title order={3}>Jumlah Penduduk Usia Kerja yang Menganggur</Title>
|
||||||
);
|
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Pengangguran Bredasarkan Usia"}>
|
||||||
}
|
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
||||||
|
<TabsTab value={"Pengangguran Bredasarkan Usia"}>
|
||||||
|
Pengangguran Bredasarkan Usia
|
||||||
|
</TabsTab>
|
||||||
|
<TabsTab value={"Pengangguran Bredasarkan Pendidikan"}>
|
||||||
|
Pengangguran Bredasarkan Pendidikan
|
||||||
|
</TabsTab>
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
export default Page;
|
<TabsPanel value={"Pengangguran Bredasarkan Usia"}>
|
||||||
|
<PengangguranBerdasarkanUsia />
|
||||||
|
</TabsPanel>
|
||||||
|
<TabsPanel value={"Pengangguran Bredasarkan Pendidikan"}>
|
||||||
|
<PengangguranBerdasarkanPendidikan/>
|
||||||
|
</TabsPanel>
|
||||||
|
</Tabs>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
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;
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
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;
|
||||||
@@ -1,11 +1,37 @@
|
|||||||
import React from 'react';
|
import colors from '@/con/colors';
|
||||||
|
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||||
|
import DataPengangguran from './ui/dataPengangguran/page';
|
||||||
|
import GrafikDataPengangguran from './ui/grafikDataPengangguran/page';
|
||||||
|
import DetailDataPengangguran from './ui/detailDataPengangguran/page';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack>
|
||||||
jumlah-pengangguran-2024-2025
|
<Title order={3}>Jumlah Pengangguran 2024-2025</Title>
|
||||||
</div>
|
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Data Pengangguran Desa"}>
|
||||||
);
|
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
||||||
|
<TabsTab value="Data Pengangguran Desa">
|
||||||
|
Data Pengangguran Desa
|
||||||
|
</TabsTab>
|
||||||
|
<TabsTab value="Grafik Data Pengangguran Desa">
|
||||||
|
Grafik Data Pengangguran Desa
|
||||||
|
</TabsTab>
|
||||||
|
<TabsTab value="Detail Data Pengangguran Desa">
|
||||||
|
Detail Data Pengangguran Desa
|
||||||
|
</TabsTab>
|
||||||
|
</TabsList>
|
||||||
|
<TabsPanel value="Data Pengangguran Desa">
|
||||||
|
<DataPengangguran/>
|
||||||
|
</TabsPanel>
|
||||||
|
<TabsPanel value="Grafik Data Pengangguran Desa">
|
||||||
|
<GrafikDataPengangguran/>
|
||||||
|
</TabsPanel>
|
||||||
|
<TabsPanel value="Detail Data Pengangguran Desa">
|
||||||
|
<DetailDataPengangguran/>
|
||||||
|
</TabsPanel>
|
||||||
|
</Tabs>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
export default Page;
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
|
||||||
|
function DataPengangguran() {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box py={15}>
|
||||||
|
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||||
|
<Box>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Data Pengangguran</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Total Pengangguran</Text>}
|
||||||
|
placeholder="masukkan total pengangguran"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Pengangguran Terdidik</Text>}
|
||||||
|
placeholder="masukkan pengangguran terdidik"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Usia Produktif</Text>}
|
||||||
|
placeholder="masukkan usia produktif"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Sedang Mencari Kerja</Text>}
|
||||||
|
placeholder="masukkan jumlah sedang mencari kerja"
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>List Data Pengangguran</Title>
|
||||||
|
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</SimpleGrid>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DataPengangguran;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Paper, Stack, Title } from '@mantine/core';
|
||||||
|
|
||||||
|
function DetailDataPengangguran() {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box py={15}>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Detail Data Pengangguran</Title>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DetailDataPengangguran;
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
|
||||||
|
function GrafikDataPengangguran() {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Box py={15}>
|
||||||
|
<Box>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Data Pengangguran</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Total Pengangguran</Text>}
|
||||||
|
placeholder="masukkan total pengangguran"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Pengangguran Terdidik</Text>}
|
||||||
|
placeholder="masukkan pengangguran terdidik"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Usia Produktif</Text>}
|
||||||
|
placeholder="masukkan usia produktif"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Sedang Mencari Kerja</Text>}
|
||||||
|
placeholder="masukkan jumlah sedang mencari kerja"
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button bg={colors['blue-button']}>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<Box py={10}>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>List Data Pengangguran</Title>
|
||||||
|
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GrafikDataPengangguran;
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
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,25 @@
|
|||||||
|
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,10 +1,31 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, SimpleGrid, Stack, Title } from '@mantine/core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import CreateLowongan from './create/createLowongan';
|
||||||
|
import ListDataLowongan from './listData/page';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack gap={"xs"}>
|
||||||
lowongan-kerja-lokal
|
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||||
</div>
|
<Box>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
||||||
|
<Title order={3}>Lowongan Kerja Lokal</Title>
|
||||||
|
<CreateLowongan/>
|
||||||
|
<Group>
|
||||||
|
<Button mt={10} bg={colors['blue-button']}>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
||||||
|
<ListDataLowongan/>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
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,10 +1,30 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Title } from '@mantine/core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import CreatePasarDesa from './create/createPasarDesa';
|
||||||
|
import ListDataUsaha from './listData/page';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack gap={'xs'}>
|
||||||
pasar-desa
|
<Box>
|
||||||
</div>
|
<Paper w={{ base: '100%', md: '50%' }} bg={colors['white-1']} p={'md'} radius={10}>
|
||||||
|
<Stack gap={'xs'}>
|
||||||
|
<Title order={3}>Pasar Desa</Title>
|
||||||
|
<CreatePasarDesa />
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
mt={10}
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<ListDataUsaha />
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,30 @@
|
|||||||
import React from 'react';
|
import colors from '@/con/colors';
|
||||||
|
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||||
|
import ProgramKemiskinan from './ui/program/page';
|
||||||
|
import StatistikKemiskinan from './ui/statistik/page';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack>
|
||||||
program-kemiskinan
|
<Title order={3}>Program Kemiskinan</Title>
|
||||||
</div>
|
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Program Kemiskinan"}>
|
||||||
|
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
||||||
|
<TabsTab value="Program Kemiskinan">
|
||||||
|
Program Kemiskinan
|
||||||
|
</TabsTab>
|
||||||
|
<TabsTab value="Statistik Kemiskinan">
|
||||||
|
Statistik Kemiskinan
|
||||||
|
</TabsTab>
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
<TabsPanel value="Program Kemiskinan">
|
||||||
|
<ProgramKemiskinan />
|
||||||
|
</TabsPanel>
|
||||||
|
<TabsPanel value="Statistik Kemiskinan">
|
||||||
|
<StatistikKemiskinan />
|
||||||
|
</TabsPanel>
|
||||||
|
</Tabs>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function ProgramKemiskinan() {
|
||||||
|
return (
|
||||||
|
<Box py={15}>
|
||||||
|
<SimpleGrid cols={{base: 1, md: 2}}>
|
||||||
|
<Box>
|
||||||
|
<Paper p={"md"} bg={colors['white-1']}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Program Kemiskinan</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Nama Program</Text>}
|
||||||
|
placeholder="Masukkan nama program"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Deskripsi Program</Text>}
|
||||||
|
placeholder="Masukkan deskripsi program"
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Paper p={"md"} bg={colors['white-1']}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>List Data Program Kemiskinan</Title>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</SimpleGrid>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProgramKemiskinan;
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function StatistikKemiskinan() {
|
||||||
|
return (
|
||||||
|
<Box py={15}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} p={"md"} bg={colors['white-1']}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Statistik Kemiskinan</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Tahun</Text>}
|
||||||
|
placeholder="Masukkan tahun"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Jumlah Penduduk Miskin</Text>}
|
||||||
|
placeholder="Masukkan jumlah penduduk miskin"
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Paper p={"md"} bg={colors['white-1']}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Statistik Kemiskinan</Title>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StatistikKemiskinan;
|
||||||
@@ -1,10 +1,31 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import DataSektorUnggulan from './ui/data_sektor_unggulan/page';
|
||||||
|
import GrafikDataSektor from './ui/grafik_data_sektor/page';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack>
|
||||||
sektor-unggulan-desa
|
<Title order={3}>Sektor Unggulan Desa</Title>
|
||||||
</div>
|
<Tabs color={colors['blue-button']} variant='pills' defaultValue={"Data Sektor Unggulan Desa"}>
|
||||||
|
<TabsList p={"xs"} bg={"#BBC8E7FF"}>
|
||||||
|
<TabsTab value="Data Sektor Unggulan Desa">
|
||||||
|
Data Sektor Unggulan Desa
|
||||||
|
</TabsTab>
|
||||||
|
<TabsTab value="Grafik Data Sektor Unggulan Desa">
|
||||||
|
Grafik Data Sektor Unggulan Desa
|
||||||
|
</TabsTab>
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
<TabsPanel value="Data Sektor Unggulan Desa">
|
||||||
|
<DataSektorUnggulan />
|
||||||
|
</TabsPanel>
|
||||||
|
<TabsPanel value="Grafik Data Sektor Unggulan Desa">
|
||||||
|
<GrafikDataSektor />
|
||||||
|
</TabsPanel>
|
||||||
|
</Tabs>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function DataSektorUnggulan() {
|
||||||
|
return (
|
||||||
|
<Box py={15}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||||
|
<Box>
|
||||||
|
<Paper p={"md"} bg={colors['white-1']}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Data Sektor Unggulan</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Nama Sektor Unggulan</Text>}
|
||||||
|
placeholder="Masukkan nama sektor unggulan"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Deskripsi Sektor Unggulan</Text>}
|
||||||
|
placeholder="Masukkan deskripsi sektor unggulan"
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Paper p={"md"} bg={colors['white-1']}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Data Sektor Unggulan</Title>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DataSektorUnggulan;
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function GrafikDataSektor() {
|
||||||
|
return (
|
||||||
|
<Box py={15}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Box>
|
||||||
|
<Paper w={{base: '100%', md: '50%'}} p={"md"} bg={colors['white-1']}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Grafik Data Sektor Unggulan</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Nama Sektor Unggulan</Text>}
|
||||||
|
placeholder="Masukkan nama sektor unggulan"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Jumlah Sektor Unggulan</Text>}
|
||||||
|
placeholder="Masukkan jumlah sektor unggulan"
|
||||||
|
/>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Paper p={"md"} bg={colors['white-1']}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Grafik Sektor Unggulan</Title>
|
||||||
|
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GrafikDataSektor;
|
||||||
@@ -1,10 +1,49 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, Stack, Text, TextInput, Title } from '@mantine/core';
|
||||||
|
import { IconImageInPicture } from '@tabler/icons-react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Box>
|
||||||
desa-digital-smart-village
|
<Stack gap={"xs"}>
|
||||||
</div>
|
<Box>
|
||||||
|
<Paper w={{ base: '100%', md: '50%' }} p={"md"} bg={colors['white-1']}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Create Data Desa Digital Smart Village</Title>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Nama Desa Digital Smart Village</Text>}
|
||||||
|
placeholder="Masukkan nama desa digital smart village"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Deskripsi Desa Digital Smart Village</Text>}
|
||||||
|
placeholder="Masukkan deskripsi desa digital smart village"
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"sm"} fw={"bold"}>
|
||||||
|
Upload Gambar Desa Digital Smart Village
|
||||||
|
</Text>
|
||||||
|
<IconImageInPicture size={24} />
|
||||||
|
</Box>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<Box>
|
||||||
|
<Paper p={"md"} bg={colors['white-1']}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>Create Data Desa Digital Smart Village</Title>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Stack, Text, TextInput } from '@mantine/core';
|
||||||
|
import { IconImageInPicture } from '@tabler/icons-react';
|
||||||
|
|
||||||
|
function CreateKontakDarurat() {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Judul Kontak Darurat</Text>}
|
||||||
|
placeholder="masukkan judul kontak darurat"
|
||||||
|
/>
|
||||||
|
<IconImageInPicture size={24} color={colors['blue-button']} />
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Nama Kontak Darurat</Text>}
|
||||||
|
placeholder="masukkan nama kontak darurat"
|
||||||
|
/>
|
||||||
|
<IconImageInPicture size={24} color={colors['blue-button']} />
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Nomor Kontak Darurat</Text>}
|
||||||
|
placeholder="masukkan nomor kontak darurat"
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateKontakDarurat;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Paper, Stack, Text, Title } from '@mantine/core';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function ListDataKontakDarurat() {
|
||||||
|
return (
|
||||||
|
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>List Data Kontak Darurat</Title>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Judul Kontak Darurat</Text>
|
||||||
|
<Text> </Text>
|
||||||
|
<Text fw={"bold"}>Nama Kontak Darurat</Text>
|
||||||
|
<Text> </Text>
|
||||||
|
<Text fw={"bold"}>Nomor Kontak Darurat</Text>
|
||||||
|
<Text> </Text>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ListDataKontakDarurat;
|
||||||
@@ -1,10 +1,30 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, SimpleGrid, Stack, Title } from '@mantine/core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import CreateKontakDarurat from './create/createKontak';
|
||||||
|
import ListDataKontakDarurat from './listData/page';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack gap={"xs"}>
|
||||||
kontak-darurat
|
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||||
</div>
|
<Box>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
||||||
|
<Title order={3}>Kontak Darurat</Title>
|
||||||
|
<CreateKontakDarurat/>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
mt={10}
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<ListDataKontakDarurat/>
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { Box, Stack, Text, TextInput } from '@mantine/core';
|
||||||
|
import React from 'react';
|
||||||
|
import { KeamananEditor } from '../../_com/keamananEditor';
|
||||||
|
|
||||||
|
|
||||||
|
function CreateLaporanPublik() {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Judul Laporan Publik</Text>}
|
||||||
|
placeholder="masukkan judul laporan publik"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Tanggal Laporan Publik</Text>}
|
||||||
|
placeholder="masukkan tanggal laporan publik"
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"sm"} fw={"bold"}>Deskripsi Laporan Publik</Text>
|
||||||
|
<KeamananEditor showSubmit={false} />
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateLaporanPublik;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box, Paper, Stack, Text, Title } from '@mantine/core';
|
||||||
|
import colors from '@/con/colors';
|
||||||
|
|
||||||
|
function ListDataLaporanPublik() {
|
||||||
|
return (
|
||||||
|
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>List Data Laporan Publik</Title>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Judul Laporan Publik</Text>
|
||||||
|
<Text></Text>
|
||||||
|
<Text fw={"bold"}>Tanggal Laporan Publik</Text>
|
||||||
|
<Text></Text>
|
||||||
|
<Text fw={"bold"}>Deskripsi Laporan Publik</Text>
|
||||||
|
<Text></Text>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ListDataLaporanPublik;
|
||||||
@@ -1,11 +1,28 @@
|
|||||||
import React from 'react';
|
import colors from "@/con/colors";
|
||||||
|
import { Box, Button, Group, Paper, SimpleGrid, Stack, Title } from "@mantine/core";
|
||||||
|
import CreateLaporanPublik from "./create/createLaporan";
|
||||||
|
import ListDataLaporanPublik from "./listData/page";
|
||||||
|
|
||||||
function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack gap={"xs"}>
|
||||||
laporan-publik
|
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||||
</div>
|
<Box>
|
||||||
);
|
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
||||||
}
|
<Title order={3}>Laporan Publik</Title>
|
||||||
|
<CreateLaporanPublik/>
|
||||||
export default Page;
|
<Group>
|
||||||
|
<Button
|
||||||
|
mt={10}
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<ListDataLaporanPublik/>
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Stack, Text, TextInput } from '@mantine/core';
|
||||||
|
import { IconImageInPicture } from '@tabler/icons-react';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function CreatePencegahan() {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Judul Pencegahan Kriminalitas</Text>}
|
||||||
|
placeholder="masukkan judul pencegahan kriminalitas"
|
||||||
|
/>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Deskripsi Pencegahan Kriminalitas</Text>}
|
||||||
|
placeholder="masukkan deskripsi pencegahan kriminalitas"
|
||||||
|
/>
|
||||||
|
<Text fw={"bold"} fz={"sm"} >Gambar Pencegahan Kriminalitas</Text>
|
||||||
|
<IconImageInPicture size={24} color={colors['blue-button']} />
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreatePencegahan;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Paper, Stack, Text, Title } from '@mantine/core';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function ListDataPencegahan() {
|
||||||
|
return (
|
||||||
|
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>List Data Pencegahan Kriminalitas</Title>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Judul Pencegahan Kriminalitas</Text>
|
||||||
|
<Text> </Text>
|
||||||
|
<Text fw={"bold"}>Deskripsi Pencegahan Kriminalitas</Text>
|
||||||
|
<Text> </Text>
|
||||||
|
<Text fw={"bold"}>Gambar Pencegahan Kriminalitas</Text>
|
||||||
|
<Text> </Text>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ListDataPencegahan;
|
||||||
@@ -1,10 +1,31 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Stack, SimpleGrid, Box, Paper, Title, Group, Button } from '@mantine/core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import CreatePencegahan from './create/createPencegahan';
|
||||||
|
import ListDataPencegahan from './listData/page';
|
||||||
|
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack gap={"xs"}>
|
||||||
pencegahan-kriminalitas
|
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||||
</div>
|
<Box>
|
||||||
|
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
||||||
|
<Title order={3}>Kontak Darurat</Title>
|
||||||
|
<CreatePencegahan/>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
mt={10}
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<ListDataPencegahan/>
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Stack, Text, TextInput } from '@mantine/core';
|
||||||
|
import { IconImageInPicture } from '@tabler/icons-react';
|
||||||
|
import React from 'react';
|
||||||
|
import { KeamananEditor } from '../../_com/keamananEditor';
|
||||||
|
|
||||||
|
function CreateTipsKeamanan() {
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<TextInput
|
||||||
|
label={<Text fz={"sm"} fw={"bold"}>Judul Tips Keamanan</Text>}
|
||||||
|
placeholder="masukkan judul tips keamanan"
|
||||||
|
/>
|
||||||
|
<Box>
|
||||||
|
<Text fz={"sm"} fw={"bold"}>Upload Gambar</Text>
|
||||||
|
<IconImageInPicture size={24} color={colors['blue-button']} />
|
||||||
|
</Box>
|
||||||
|
<KeamananEditor
|
||||||
|
showSubmit={false}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateTipsKeamanan;
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
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 ListDataTipsKeamanan() {
|
||||||
|
return (
|
||||||
|
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
||||||
|
<Stack gap={"xs"}>
|
||||||
|
<Title order={3}>List Data Tips Keamanan</Title>
|
||||||
|
<Box>
|
||||||
|
<Text fw={"bold"}>Judul Tips Keamanan</Text>
|
||||||
|
<Text></Text>
|
||||||
|
<Text fw={"bold"}>Gambar Tips Keamanan</Text>
|
||||||
|
<IconImageInPicture size={24} color={colors['blue-button']} />
|
||||||
|
<Text fw={"bold"}>Deskripsi Tips Keamanan</Text>
|
||||||
|
<Text></Text>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ListDataTipsKeamanan;
|
||||||
@@ -1,11 +1,31 @@
|
|||||||
|
import colors from '@/con/colors';
|
||||||
|
import { Box, Button, Group, Paper, SimpleGrid, Stack, Title } from '@mantine/core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import CreateTipsKeamanan from './create/createTips';
|
||||||
|
import ListDataTipsKeamanan from './listData/page';
|
||||||
|
|
||||||
function Page() {
|
function Page() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<Stack gap={"xs"}>
|
||||||
tips-keamanan
|
<SimpleGrid cols={{ base: 1, md: 2 }}>
|
||||||
</div>
|
<Box>
|
||||||
);
|
<Paper bg={colors['white-1']} p={'md'} radius={10}>
|
||||||
|
<Title order={3}>Tips Keamanan</Title>
|
||||||
|
<CreateTipsKeamanan/>
|
||||||
|
<Group>
|
||||||
|
<Button
|
||||||
|
mt={10}
|
||||||
|
bg={colors['blue-button']}
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
<ListDataTipsKeamanan/>
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
export default Page;
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ function GrafikHasilKepuasan() {
|
|||||||
<Stack>
|
<Stack>
|
||||||
<Title order={3}>Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik</Title>
|
<Title order={3}>Grafik Hasil Kepuasan Masyarakat Terhadap Pelayanan Publik</Title>
|
||||||
<TextInput
|
<TextInput
|
||||||
w={{ base: '100%', md: '50%' }}
|
|
||||||
label="Label"
|
label="Label"
|
||||||
placeholder="masukkan label"
|
placeholder="masukkan label"
|
||||||
value={grafikHasilKepuasan.create.form.label}
|
value={grafikHasilKepuasan.create.form.label}
|
||||||
@@ -45,7 +44,6 @@ function GrafikHasilKepuasan() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<TextInput
|
<TextInput
|
||||||
w={{ base: '100%', md: '50%' }}
|
|
||||||
label="Jumlah Kepuasan"
|
label="Jumlah Kepuasan"
|
||||||
type="number"
|
type="number"
|
||||||
placeholder="masukkan jumlah kepuasan"
|
placeholder="masukkan jumlah kepuasan"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
async function katagoryBeritaFindMany() {
|
async function kategoriBeritaFindMany() {
|
||||||
const data = await prisma.katagoryBerita.findMany();
|
const data = await prisma.kategoriBerita.findMany();
|
||||||
return { data };
|
return { data };
|
||||||
}
|
}
|
||||||
|
|
||||||
export default katagoryBeritaFindMany
|
export default kategoriBeritaFindMany
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ type FormCreate = Prisma.BeritaGetPayload<{
|
|||||||
select: {
|
select: {
|
||||||
judul: true;
|
judul: true;
|
||||||
deskripsi: true;
|
deskripsi: true;
|
||||||
image: true;
|
|
||||||
content: true;
|
content: true;
|
||||||
katagoryBeritaId: true;
|
kategoriBeritaId: true;
|
||||||
|
imageId: true;
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
async function beritaCreate(context: Context) {
|
async function beritaCreate(context: Context) {
|
||||||
const body = context.body as FormCreate;
|
const body = context.body as FormCreate;
|
||||||
|
console.log(body)
|
||||||
|
|
||||||
// console.log(body)
|
// console.log(body)
|
||||||
|
|
||||||
@@ -20,9 +21,9 @@ async function beritaCreate(context: Context) {
|
|||||||
data: {
|
data: {
|
||||||
content: body.content,
|
content: body.content,
|
||||||
deskripsi: body.deskripsi,
|
deskripsi: body.deskripsi,
|
||||||
image: body.image,
|
imageId: body.imageId,
|
||||||
judul: body.judul,
|
judul: body.judul,
|
||||||
katagoryBeritaId: body.katagoryBeritaId,
|
kategoriBeritaId: body.kategoriBeritaId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
55
src/app/api/[[...slugs]]/_lib/desa/berita/del.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const beritaDelete = async (context: Context) => {
|
||||||
|
const id = context.params?.id as string;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
body: "ID tidak diberikan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const berita = await prisma.berita.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: {
|
||||||
|
image: true,
|
||||||
|
kategoriBerita: true, // pastikan relasi image sudah ada di prisma schema
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!berita) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
body: "Berita tidak ditemukan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hapus file gambar dari filesystem jika ada
|
||||||
|
if (berita.image) {
|
||||||
|
try {
|
||||||
|
const filePath = path.join(berita.image.path, berita.image.name);
|
||||||
|
await fs.unlink(filePath);
|
||||||
|
await prisma.fileStorage.delete({
|
||||||
|
where: { id: berita.image.id },
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal hapus file image:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hapus berita dari DB
|
||||||
|
await prisma.berita.delete({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Berita dan file terkait berhasil dihapus",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default beritaDelete;
|
||||||
@@ -1,8 +1,27 @@
|
|||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
export default async function beritaFindMany() {
|
async function beritaFindMany() {
|
||||||
const res = await prisma.berita.findMany();
|
try {
|
||||||
return {
|
const data = await prisma.berita.findMany({
|
||||||
data: res,
|
where: { isActive: true },
|
||||||
};
|
include: {
|
||||||
|
image: true,
|
||||||
|
kategoriBerita: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Success fetch berita",
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Find many error:", e);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
message: "Failed fetch berita",
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default beritaFindMany;
|
||||||
|
|||||||
@@ -1,18 +1,30 @@
|
|||||||
import Elysia, { t } from "elysia";
|
import Elysia, { t } from "elysia";
|
||||||
import katagoryBeritaFindMany from "./category";
|
import kategoriBeritaFindMany from "./category";
|
||||||
import beritaFindMany from "./find-many";
|
import beritaFindMany from "./find-many";
|
||||||
import beritaCreate from "./create";
|
import beritaCreate from "./create";
|
||||||
|
import beritaDelete from "./del";
|
||||||
|
import beritaUpdate from "./updt";
|
||||||
|
|
||||||
const Berita = new Elysia({ prefix: "/berita", tags: ["Desa/Berita"] })
|
const Berita = new Elysia({ prefix: "/berita", tags: ["Desa/Berita"] })
|
||||||
.get("/category/find-many", katagoryBeritaFindMany)
|
.get("/category/find-many", kategoriBeritaFindMany)
|
||||||
.get("/find-many", beritaFindMany)
|
.get("/find-many", beritaFindMany)
|
||||||
.post("/create", beritaCreate, {
|
.post("/create", beritaCreate, {
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
judul: t.String(),
|
judul: t.String(),
|
||||||
deskripsi: t.String(),
|
deskripsi: t.String(),
|
||||||
image: t.String(),
|
imageId: t.String(),
|
||||||
content: t.String(),
|
content: t.String(),
|
||||||
katagoryBeritaId: t.Union([t.String(), t.Null()]),
|
kategoriBeritaId: t.Union([t.String(), t.Null()]),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.delete("/delete/:id", beritaDelete)
|
||||||
|
.put("/update/:id", beritaUpdate, {
|
||||||
|
body: t.Object({
|
||||||
|
judul: t.String(),
|
||||||
|
deskripsi: t.String(),
|
||||||
|
imageId: t.String(),
|
||||||
|
content: t.String(),
|
||||||
|
kategoriBeritaId: t.Union([t.String(), t.Null()]),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
153
src/app/api/[[...slugs]]/_lib/desa/berita/updt.ts
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
type FormUpdate = Prisma.BeritaGetPayload<{
|
||||||
|
select: {
|
||||||
|
id: true;
|
||||||
|
judul: true;
|
||||||
|
deskripsi: true;
|
||||||
|
content: true;
|
||||||
|
kategoriBeritaId: true;
|
||||||
|
imageId: true;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
|
||||||
|
// async function beritaUpdate(context: Context) {
|
||||||
|
// const body = (await context.body) as FormUpdate;
|
||||||
|
|
||||||
|
// const {
|
||||||
|
// id,
|
||||||
|
// judul,
|
||||||
|
// deskripsi,
|
||||||
|
// content,
|
||||||
|
// kategoriBeritaId,
|
||||||
|
// imageId,
|
||||||
|
// } = body;
|
||||||
|
|
||||||
|
// if (!id) {
|
||||||
|
// return {
|
||||||
|
// status: 400,
|
||||||
|
// body: "ID tidak boleh kosong",
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const existing = await prisma.berita.findUnique({
|
||||||
|
// where: { id },
|
||||||
|
// include: {
|
||||||
|
// image: true,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (!existing) {
|
||||||
|
// return {
|
||||||
|
// status: 404,
|
||||||
|
// body: "Berita tidak ditemukan",
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Cek jika imageId diubah
|
||||||
|
// if (existing.imageId && existing.imageId !== imageId) {
|
||||||
|
// const oldImage = existing.image;
|
||||||
|
// if (oldImage) {
|
||||||
|
// try {
|
||||||
|
// const filePath = path.join(oldImage.path, oldImage.name);
|
||||||
|
// await fs.unlink(filePath); // hapus file dari filesystem
|
||||||
|
// await prisma.fileStorage.delete({
|
||||||
|
// where: { id: oldImage.id }, // hapus record dari DB
|
||||||
|
// });
|
||||||
|
// } catch (err) {
|
||||||
|
// console.error("Gagal hapus gambar lama:", err);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const updated = await prisma.berita.update({
|
||||||
|
// where: { id },
|
||||||
|
// data: {
|
||||||
|
// judul,
|
||||||
|
// deskripsi,
|
||||||
|
// content,
|
||||||
|
// kategoriBeritaId,
|
||||||
|
// imageId,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// success: true,
|
||||||
|
// message: "Berita berhasil diupdate (gambar lama juga dihapus jika ada)",
|
||||||
|
// data: updated,
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export default beritaUpdate;
|
||||||
|
|
||||||
|
|
||||||
|
async function beritaUpdate(context: Context) {
|
||||||
|
const id = context.params.id as string; // ambil dari URL
|
||||||
|
const body = (await context.body) as Omit<FormUpdate, "id">;
|
||||||
|
|
||||||
|
const {
|
||||||
|
judul,
|
||||||
|
deskripsi,
|
||||||
|
content,
|
||||||
|
kategoriBeritaId,
|
||||||
|
imageId,
|
||||||
|
} = body;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
body: "ID tidak boleh kosong",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const existing = await prisma.berita.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: {
|
||||||
|
image: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existing) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
body: "Berita tidak ditemukan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existing.imageId && existing.imageId !== imageId) {
|
||||||
|
const oldImage = existing.image;
|
||||||
|
if (oldImage) {
|
||||||
|
try {
|
||||||
|
const filePath = path.join(oldImage.path, oldImage.name);
|
||||||
|
await fs.unlink(filePath);
|
||||||
|
await prisma.fileStorage.delete({
|
||||||
|
where: { id: oldImage.id },
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal hapus gambar lama:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await prisma.berita.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
judul,
|
||||||
|
deskripsi,
|
||||||
|
content,
|
||||||
|
kategoriBeritaId,
|
||||||
|
imageId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
message: "Berita berhasil diupdate",
|
||||||
|
data: updated,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export default beritaUpdate;
|
||||||
@@ -2,6 +2,7 @@ import Elysia from "elysia";
|
|||||||
import Berita from "./berita";
|
import Berita from "./berita";
|
||||||
import Pengumuman from "./pengumuman";
|
import Pengumuman from "./pengumuman";
|
||||||
import ProfileDesa from "./profile/profile_desa";
|
import ProfileDesa from "./profile/profile_desa";
|
||||||
|
|
||||||
|
|
||||||
const Desa = new Elysia({ prefix: "/api/desa", tags: ["Desa"] })
|
const Desa = new Elysia({ prefix: "/api/desa", tags: ["Desa"] })
|
||||||
.use(Berita)
|
.use(Berita)
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ const fileStorageCreate = async (context: Context) => {
|
|||||||
const data = await prisma.fileStorage.create({
|
const data = await prisma.fileStorage.create({
|
||||||
data: {
|
data: {
|
||||||
name: newName,
|
name: newName,
|
||||||
|
realName: file.name, // Add the original file name as realName
|
||||||
path: rootPath,
|
path: rootPath,
|
||||||
mimeType: file.type,
|
mimeType: file.type,
|
||||||
link: `/api/fileStorage/findUnique/${newName}`,
|
link: `/api/fileStorage/findUnique/${newName}`,
|
||||||
@@ -60,4 +61,4 @@ const fileStorageCreate = async (context: Context) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default fileStorageCreate;
|
export default fileStorageCreate;
|
||||||
60
src/app/api/[[...slugs]]/_lib/fileStorage/_lib/del.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { Context } from "elysia";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const UPLOAD_DIR = process.env.WIBU_UPLOAD_DIR;
|
||||||
|
|
||||||
|
const fileStorageDelete = async (context: Context) => {
|
||||||
|
const { params } = context;
|
||||||
|
|
||||||
|
const id = params?.id as string;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
return {
|
||||||
|
status: 400,
|
||||||
|
body: "ID file tidak ditemukan",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!UPLOAD_DIR) {
|
||||||
|
return {
|
||||||
|
status: 500,
|
||||||
|
body: "UPLOAD_DIR belum dikonfigurasi",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cek file dari database
|
||||||
|
const file = await prisma.fileStorage.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!file) {
|
||||||
|
return {
|
||||||
|
status: 404,
|
||||||
|
body: "File tidak ditemukan di database",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = path.join(file.path, file.name);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Hapus file dari filesystem
|
||||||
|
await fs.unlink(filePath);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Gagal hapus file:", err);
|
||||||
|
// Tetap lanjutkan hapus dari database meskipun file fisik tidak ditemukan
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hapus dari database
|
||||||
|
await prisma.fileStorage.delete({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: "File berhasil dihapus",
|
||||||
|
deletedId: id,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default fileStorageDelete;
|
||||||
@@ -2,6 +2,7 @@ import Elysia, { t } from "elysia";
|
|||||||
import fileStorageCreate from "./_lib/create";
|
import fileStorageCreate from "./_lib/create";
|
||||||
import fileStorageFindUnique from "./_lib/findUniq";
|
import fileStorageFindUnique from "./_lib/findUniq";
|
||||||
import { fileStorageFindMany } from "./_lib/findMany";
|
import { fileStorageFindMany } from "./_lib/findMany";
|
||||||
|
import fileStorageDelete from "./_lib/del";
|
||||||
|
|
||||||
const FileStorage = new Elysia({
|
const FileStorage = new Elysia({
|
||||||
prefix: "/api/fileStorage",
|
prefix: "/api/fileStorage",
|
||||||
@@ -14,6 +15,7 @@ const FileStorage = new Elysia({
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.get("/findUnique/:name", fileStorageFindUnique)
|
.get("/findUnique/:name", fileStorageFindUnique)
|
||||||
.get("/findMany", fileStorageFindMany);
|
.get("/findMany", fileStorageFindMany)
|
||||||
|
.delete("/delete/:id", fileStorageDelete);
|
||||||
|
|
||||||
export default FileStorage;
|
export default FileStorage;
|
||||||
|
|||||||
@@ -18,20 +18,14 @@ import uplImg from "./_lib/upl-img";
|
|||||||
import { uplImgSingle } from "./_lib/upl-img-single";
|
import { uplImgSingle } from "./_lib/upl-img-single";
|
||||||
import FileStorage from "./_lib/fileStorage";
|
import FileStorage from "./_lib/fileStorage";
|
||||||
|
|
||||||
|
|
||||||
const ROOT = process.cwd();
|
const ROOT = process.cwd();
|
||||||
|
|
||||||
if (!process.env.WIBU_UPLOAD_DIR)
|
if (!process.env.WIBU_UPLOAD_DIR)
|
||||||
throw new Error("WIBU_UPLOAD_DIR is not defined");
|
throw new Error("WIBU_UPLOAD_DIR is not defined");
|
||||||
|
|
||||||
const UPLOAD_DIR = path.join(ROOT, process.env.WIBU_UPLOAD_DIR);
|
const UPLOAD_DIR = path.join(ROOT, process.env.WIBU_UPLOAD_DIR);
|
||||||
const UPLOAD_DIR_IMAGE = path.join(
|
const UPLOAD_DIR_IMAGE = path.join(UPLOAD_DIR, "image");
|
||||||
UPLOAD_DIR,
|
|
||||||
"public",
|
|
||||||
"assets",
|
|
||||||
"images",
|
|
||||||
"ppid",
|
|
||||||
"profile-ppid"
|
|
||||||
);
|
|
||||||
|
|
||||||
// create uploads dir
|
// create uploads dir
|
||||||
fs.mkdir(UPLOAD_DIR, {
|
fs.mkdir(UPLOAD_DIR, {
|
||||||
@@ -73,8 +67,8 @@ const ApiServer = new Elysia()
|
|||||||
.use(swagger({ path: "/api/docs" }))
|
.use(swagger({ path: "/api/docs" }))
|
||||||
.use(
|
.use(
|
||||||
staticPlugin({
|
staticPlugin({
|
||||||
prefix: "/", // biar bisa akses dari root URL
|
assets: process.env.WIBU_UPLOAD_DIR || "./uploads",
|
||||||
assets: "./public",
|
prefix: "/uploads",
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.use(cors(corsConfig))
|
.use(cors(corsConfig))
|
||||||
@@ -93,116 +87,116 @@ const ApiServer = new Elysia()
|
|||||||
})
|
})
|
||||||
.group("/api", (app) =>
|
.group("/api", (app) =>
|
||||||
app
|
app
|
||||||
.get("/layanan", layanan)
|
.get("/layanan", layanan)
|
||||||
.get("/potensi", getPotensi)
|
.get("/potensi", getPotensi)
|
||||||
.get(
|
.get(
|
||||||
"/img/:name",
|
"/img/:name",
|
||||||
({ params, query }) => {
|
({ params, query }) => {
|
||||||
return img({
|
return img({
|
||||||
name: params.name,
|
name: params.name,
|
||||||
UPLOAD_DIR_IMAGE,
|
UPLOAD_DIR_IMAGE,
|
||||||
ROOT,
|
ROOT,
|
||||||
size: query.size,
|
size: query.size,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
params: t.Object({
|
params: t.Object({
|
||||||
name: t.String(),
|
name: t.String(),
|
||||||
}),
|
}),
|
||||||
query: t.Optional(
|
query: t.Optional(
|
||||||
t.Object({
|
t.Object({
|
||||||
size: t.Optional(t.Number()),
|
size: t.Optional(t.Number()),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.delete(
|
.delete(
|
||||||
"/img/:name",
|
"/img/:name",
|
||||||
({ params }) => {
|
({ params }) => {
|
||||||
return imgDel({
|
return imgDel({
|
||||||
name: params.name,
|
name: params.name,
|
||||||
UPLOAD_DIR_IMAGE,
|
UPLOAD_DIR_IMAGE,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
params: t.Object({
|
params: t.Object({
|
||||||
name: t.String(),
|
name: t.String(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.get(
|
.get(
|
||||||
"/imgs",
|
"/imgs",
|
||||||
({ query }) => {
|
({ query }) => {
|
||||||
return imgs({
|
return imgs({
|
||||||
search: query.search,
|
search: query.search,
|
||||||
page: query.page,
|
page: query.page,
|
||||||
count: query.count,
|
count: query.count,
|
||||||
UPLOAD_DIR_IMAGE,
|
UPLOAD_DIR_IMAGE,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
query: t.Optional(
|
query: t.Optional(
|
||||||
t.Object({
|
t.Object({
|
||||||
page: t.Number({ default: 1 }),
|
page: t.Number({ default: 1 }),
|
||||||
count: t.Number({ default: 10 }),
|
count: t.Number({ default: 10 }),
|
||||||
search: t.String({ default: "" }),
|
search: t.String({ default: "" }),
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
"/upl-img",
|
"/upl-img",
|
||||||
({ body }) => {
|
({ body }) => {
|
||||||
console.log(body.title);
|
console.log(body.title);
|
||||||
return uplImg({ files: body.files, UPLOAD_DIR_IMAGE });
|
return uplImg({ files: body.files, UPLOAD_DIR_IMAGE });
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
title: t.String(),
|
title: t.String(),
|
||||||
files: t.Files({ multiple: true }),
|
files: t.Files({ multiple: true }),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
"/upl-img-single",
|
"/upl-img-single",
|
||||||
({ body }) => {
|
({ body }) => {
|
||||||
return uplImgSingle({
|
return uplImgSingle({
|
||||||
fileName: body.name,
|
fileName: body.name,
|
||||||
file: body.file,
|
file: body.file,
|
||||||
UPLOAD_DIR_IMAGE,
|
UPLOAD_DIR_IMAGE,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
name: t.String(),
|
name: t.String(),
|
||||||
file: t.File(),
|
file: t.File(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
"/upl-csv-single",
|
"/upl-csv-single",
|
||||||
({ body }) => {
|
({ body }) => {
|
||||||
return uplCsvSingle({ fileName: body.name, file: body.file });
|
return uplCsvSingle({ fileName: body.name, file: body.file });
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
name: t.String(),
|
name: t.String(),
|
||||||
file: t.File(),
|
file: t.File(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.post(
|
.post(
|
||||||
"/upl-csv",
|
"/upl-csv",
|
||||||
({ body }) => {
|
({ body }) => {
|
||||||
return uplCsv({ files: body.files });
|
return uplCsv({ files: body.files });
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: t.Object({
|
body: t.Object({
|
||||||
files: t.Files(),
|
files: t.Files(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const GET = ApiServer.handle;
|
export const GET = ApiServer.handle;
|
||||||
export const POST = ApiServer.handle;
|
export const POST = ApiServer.handle;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Box, Button, Combobox, Flex, Group, Image, InputBase, InputPlaceholder, Paper, SimpleGrid, Stack, Text, TextInput, useCombobox } from '@mantine/core';
|
import { Box, Combobox, Flex, Image, InputBase, InputPlaceholder, Paper, SimpleGrid, Stack, Text, TextInput, useCombobox } from '@mantine/core';
|
||||||
import { IconArrowDown, IconMapPinFilled, IconSearch, IconShoppingCartFilled, IconStarFilled } from '@tabler/icons-react';
|
import { IconMapPinFilled, IconSearch, IconShoppingCartFilled, IconStarFilled } from '@tabler/icons-react';
|
||||||
|
import { motion } from 'motion/react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
import { motion } from 'motion/react';
|
|
||||||
|
|
||||||
const groceries = [
|
const groceries = [
|
||||||
'Makanan',
|
'Makanan',
|
||||||
@@ -170,9 +170,6 @@ function Page() {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
<Group justify='center'>
|
|
||||||
<Button bg={colors['blue-button']} rightSection={<IconArrowDown size={20} color={colors['white-1']} />} fz={'md'}>Lihat Produk Lainnya</Button>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -64,4 +64,4 @@ function Page() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Page;
|
export default Page;
|
||||||