Compare commits

...

6 Commits

Author SHA1 Message Date
02738104b5 Senin, 26 May 2025 :
Yang Sudah Di Kerjakan
* Tampilan UI Admin di menu ekonomi
* API Create, dan delete berita

Yang Akan Dikerjakan:
* API Di Menu Desa : Edit Berita
* Tampilan UI Admin Di Menu Inovasi
2025-05-26 14:26:10 +08:00
92de697ae0 Nico-25 Mei 2025:
Membuat create berita dan gambar
Membuat fungsi tombol di mana bisa menghapus konten sesuai idnya
2025-05-25 11:33:50 +08:00
cf6a5422ec Bagian Berita Di Admin Sudah Bisa Upload Di Bun Dev atau Bun Start 2025-05-23 16:30:46 +08:00
ee9368e911 UI Admin Keamanan 2025-05-23 11:33:59 +08:00
cab86eb02f Nico 21 Mei :
Yang udah dikerjakan:
Tampilan UI Menu DesaTampilan UI Menu Kesehatan

Yang Akan dikerjakan:
Tampilan UI Keamanan, dan Menu Selanjutnya
Memperbaiki eror di bagian inputan dimana saat nginput data outputnya ikut ke ganti seharusnya di submit dulu
2025-05-21 11:28:27 +08:00
d1e39ae7f9 Senin, 20 May 2025 : 2025-05-20 12:07:00 +08:00
118 changed files with 3310 additions and 741 deletions

View File

@@ -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>",
"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>",
"imageUrl": "/assets/images/ppid/profile-ppid/perbekel.png"
"imageUrl": "/uploads/seeded-images/profile-ppid/perbekel.png"
}
]

View File

@@ -242,17 +242,18 @@ model Berita {
id String @id @default(cuid())
judul String
deskripsi String
image String
image FileStorage @relation(fields: [imageId], references: [id])
imageId String
content String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime @default(now())
isActive Boolean @default(true)
KatagoryBerita KatagoryBerita? @relation(fields: [katagoryBeritaId], references: [id])
katagoryBeritaId String?
kategoriBerita KategoriBerita? @relation(fields: [kategoriBeritaId], references: [id])
kategoriBeritaId String?
}
model KatagoryBerita {
model KategoriBerita {
id String @id @default(cuid())
name String @unique
beritas Berita[]
@@ -593,7 +594,8 @@ model FileStorage {
mimeType String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime? // nullable agar bisa menandakan belum dihapus
deletedAt DateTime?
isActive Boolean @default(true)
link String
Berita Berita[]
}

View File

@@ -1,277 +1,277 @@
import prisma from '@/lib/prisma'
import categoryPengumuman from './data/category-pengumuman.json'
import katagoryBerita from './data/katagory-berita.json'
import caraMemperolehInformasi from './data/list-caraMemperolehInformasi.json'
import caraMemperolehSalinanInformasi from './data/list-caraMemperolehSalinanInformasi.json'
import jenisInformasiDiminta from './data/list-jenisInfromasi.json'
import layanan from './data/list-layanan.json'
import potensi from './data/list-potensi.json'
import visiMisiPPID from './data/ppid/visi-misi-ppid/visimisiPPID.json'
import dasarHukumPPID from './data/ppid/dasar-hukum-ppid/dasarhukumPPID.json'
import profileDesa from './data/desa/profile/profile_desa.json'
import profilePerbekel from './data/desa/profile/profil_perbekel.json'
import profilePPID from './data/ppid/profile-ppid/profilePPid.json'
import path from 'path'
import fs from 'fs'
import { mkdir, writeFile } from 'fs/promises'
import { v4 as uuid } from 'uuid'
import prisma from "@/lib/prisma";
import categoryPengumuman from "./data/category-pengumuman.json";
import kategoriBerita from "./data/kategori-berita.json";
import caraMemperolehInformasi from "./data/list-caraMemperolehInformasi.json";
import caraMemperolehSalinanInformasi from "./data/list-caraMemperolehSalinanInformasi.json";
import jenisInformasiDiminta from "./data/list-jenisInfromasi.json";
import layanan from "./data/list-layanan.json";
import potensi from "./data/list-potensi.json";
import visiMisiPPID from "./data/ppid/visi-misi-ppid/visimisiPPID.json";
import dasarHukumPPID from "./data/ppid/dasar-hukum-ppid/dasarhukumPPID.json";
import profileDesa from "./data/desa/profile/profile_desa.json";
import profilePerbekel from "./data/desa/profile/profil_perbekel.json";
import profilePPID from "./data/ppid/profile-ppid/profilePPid.json";
import path from "path";
import fs from "fs";
import { mkdir, writeFile } from "fs/promises";
import { v4 as uuid } from "uuid";
(async () => {
for (const l of layanan) {
await prisma.layanan.upsert({
where: {
name: l.name
},
update: {
name: l.name
},
create: {
name: l.name
}
})
for (const l of layanan) {
await prisma.layanan.upsert({
where: {
name: l.name,
},
update: {
name: l.name,
},
create: {
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 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 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")
for (const c of profilePPID) {
let finalImageUrl = c.imageUrl
if (c.imageUrl.startsWith("/uploads/seeded-images/")) {
const filename = path.basename(c.imageUrl)
const seedImagePath = path.resolve("prisma", "seed-images", filename)
const targetFilename = `${uuid()}_${filename}`
const targetPath = path.join(targetDir, targetFilename)
const buffer = fs.readFileSync(seedImagePath)
await writeFile(targetPath, buffer)
finalImageUrl = `/uploads/seeded-images/profile-ppid/${targetFilename}`
}
await seedProfilePPID()
for (const v of visiMisiPPID) {
await prisma.visiMisiPPID.upsert({
where: {
id: v.id,
},
update: {
misi: v.misi,
visi: v.visi
},
create: {
id: v.id,
misi: v.misi,
visi: v.visi
}
})
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("visi misi PPID success ...")
console.log("✅ profilePPID seeded from JSON with image copying")
}
await seedProfilePPID()
for (const v of dasarHukumPPID) {
await prisma.dasarHukumPPID.upsert({
where: {
id: v.id,
},
update: {
judul: v.judul,
content: v.content
},
create: {
id: v.id,
judul: v.judul,
content: v.content
}
})
}
console.log("dasar hukum PPID success ...")
for (const v of visiMisiPPID) {
await prisma.visiMisiPPID.upsert({
where: {
id: v.id,
},
update: {
misi: v.misi,
visi: v.visi,
},
create: {
id: v.id,
misi: v.misi,
visi: v.visi,
},
});
}
console.log("visi misi PPID success ...");
for (const v of profileDesa) {
await prisma.profileDesa.upsert({
where: {
id: v.id,
},
update: {
sejarah: v.sejarah,
visi: v.visi,
misi: v.misi,
lambang: v.lambang,
maskot: v.maskot,
profilPerbekelId: v.profilPerbekelId
},
create: {
id: v.id,
sejarah: v.sejarah,
visi: v.visi,
misi: v.misi,
lambang: v.lambang,
maskot: v.maskot,
profilPerbekelId: v.profilPerbekelId
}
})
}
console.log("profile desa success ...")
for (const v of dasarHukumPPID) {
await prisma.dasarHukumPPID.upsert({
where: {
id: v.id,
},
update: {
judul: v.judul,
content: v.content,
},
create: {
id: v.id,
judul: v.judul,
content: v.content,
},
});
}
console.log("dasar hukum PPID success ...");
for (const v of profilePerbekel) {
await prisma.profilPerbekel.upsert({
where: {
id: v.id,
},
update: {
biodata: v.biodata,
pengalaman: v.pengalaman,
pengalamanOrganisasi: v.pengalamanOrganisasi,
programUnggulan: v.programUnggulan
},
create: {
id: v.id,
biodata: v.biodata,
pengalaman: v.pengalaman,
pengalamanOrganisasi: v.pengalamanOrganisasi,
programUnggulan: v.programUnggulan
}
})
}
console.log("profile perbekel success ...")
for (const v of profileDesa) {
await prisma.profileDesa.upsert({
where: {
id: v.id,
},
update: {
sejarah: v.sejarah,
visi: v.visi,
misi: v.misi,
lambang: v.lambang,
maskot: v.maskot,
profilPerbekelId: v.profilPerbekelId,
},
create: {
id: v.id,
sejarah: v.sejarah,
visi: v.visi,
misi: v.misi,
lambang: v.lambang,
maskot: v.maskot,
profilPerbekelId: v.profilPerbekelId,
},
});
}
console.log("profile desa success ...");
})().then(() => prisma.$disconnect()).catch((e) => {
console.error(e)
prisma.$disconnect()
for (const v of profilePerbekel) {
await prisma.profilPerbekel.upsert({
where: {
id: v.id,
},
update: {
biodata: v.biodata,
pengalaman: v.pengalaman,
pengalamanOrganisasi: v.pengalamanOrganisasi,
programUnggulan: v.programUnggulan,
},
create: {
id: v.id,
biodata: v.biodata,
pengalaman: v.pengalaman,
pengalamanOrganisasi: v.pengalamanOrganisasi,
programUnggulan: v.programUnggulan,
},
});
}
console.log("profile perbekel success ...");
})()
.then(() => prisma.$disconnect())
.catch((e) => {
console.error(e);
prisma.$disconnect();
});
process.on("exit", () => {
prisma.$disconnect();
});
process.on('exit', () => {
prisma.$disconnect()
})
process.on('SIGINT', () => {
prisma.$disconnect()
process.exit(0)
})
process.on("SIGINT", () => {
prisma.$disconnect();
process.exit(0);
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

View File

@@ -0,0 +1,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>
)
}

View File

@@ -1,23 +1,34 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import ApiFetch from "@/lib/api-fetch";
import { Prisma } from "@prisma/client";
import { toast } from "react-toastify";
import { proxy } from "valtio";
import { z } from "zod";
// 1. Schema validasi dengan Zod
const templateForm = z.object({
judul: z.string().min(3, "Judul minimal 3 karakter"),
deskripsi: z.string().min(3, "Deskripsi minimal 3 karakter"),
image: z.string().url().min(3, "Image 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({
findMany: {
data: null as
| null
| Prisma.KatagoryBeritaGetPayload<{ omit: { isActive: true } }>[],
| Prisma.KategoriBeritaGetPayload<{ omit: { isActive: true } }>[],
async load() {
const res = await ApiFetch.api.desa.berita.category["find-many"].get();
if (res.status === 200) {
@@ -27,23 +38,12 @@ const category = proxy({
},
});
type BeritaForm = Prisma.BeritaGetPayload<{
select: {
judul: true;
deskripsi: true;
image: true;
content: true;
katagoryBeritaId: true;
};
}>;
// 4. Berita proxy
const berita = proxy({
create: {
form: {} as BeritaForm,
form: { ...defaultForm }, // ✅ ini kunci fix-nya
loading: false,
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);
if (!cek.success) {
const err = `[${cek.error.issues
@@ -51,6 +51,7 @@ const berita = proxy({
.join("\n")}] required`;
return toast.error(err);
}
try {
berita.create.loading = true;
const res = await ApiFetch.api.desa.berita["create"].post(
@@ -58,7 +59,7 @@ const berita = proxy({
);
if (res.status === 200) {
berita.findMany.load();
return toast.success("succes create");
return toast.success("success create");
}
return toast.error("failed create");
@@ -68,23 +69,65 @@ const berita = proxy({
berita.create.loading = false;
}
},
resetForm() {
berita.create.form = { ...defaultForm };
},
},
findMany: {
data: null as
| Prisma.BeritaGetPayload<{ omit: { isActive: true } }>[]
| Prisma.BeritaGetPayload<{
include: {
image: true;
kategoriBerita: true;
};
}>[]
| null,
async load() {
const res = await ApiFetch.api.desa.berita["find-many"].get();
if (res.status === 200) {
berita.findMany.data = (res.data?.data as any) ?? [];
berita.findMany.data = (res.data?.data ) ?? [];
}
},
},
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({
category,
berita,
});
export default stateDashboardBerita;
export default stateDashboardBerita;

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client'
import { RichTextEditor, Link } from '@mantine/tiptap';
import { useEditor } from '@tiptap/react';
@@ -8,28 +9,39 @@ import TextAlign from '@tiptap/extension-text-align';
import Superscript from '@tiptap/extension-superscript';
import SubScript from '@tiptap/extension-subscript';
import { Button, Stack } from '@mantine/core';
import { useEffect } from 'react';
const content =
'<h2 style="text-align: center;">Welcome to Mantine rich text editor</h2><p><code>RichTextEditor</code> component focuses on usability and is designed to be as simple as possible to bring a familiar editing experience to regular users. <code>RichTextEditor</code> is based on <a href="https://tiptap.dev/" rel="noopener noreferrer" target="_blank">Tiptap.dev</a> and supports all of its features:</p><ul><li>General text formatting: <strong>bold</strong>, <em>italic</em>, <u>underline</u>, <s>strike-through</s> </li><li>Headings (h1-h6)</li><li>Sub and super scripts (<sup>&lt;sup /&gt;</sup> and <sub>&lt;sub /&gt;</sub> tags)</li><li>Ordered and bullet lists</li><li>Text align&nbsp;</li><li>And all <a href="https://tiptap.dev/extensions" target="_blank" rel="noopener noreferrer">other extensions</a></li></ul>';
// const content =
// '<h2 style="text-align: center;">Welcome to Mantine rich text editor</h2><p><code>RichTextEditor</code> component focuses on usability and is designed to be as simple as possible to bring a familiar editing experience to regular users. <code>RichTextEditor</code> is based on <a href="https://tiptap.dev/" rel="noopener noreferrer" target="_blank">Tiptap.dev</a> and supports all of its features:</p><ul><li>General text formatting: <strong>bold</strong>, <em>italic</em>, <u>underline</u>, <s>strike-through</s> </li><li>Headings (h1-h6)</li><li>Sub and super scripts (<sup>&lt;sup /&gt;</sup> and <sub>&lt;sub /&gt;</sub> tags)</li><li>Ordered and bullet lists</li><li>Text align&nbsp;</li><li>And all <a href="https://tiptap.dev/extensions" target="_blank" rel="noopener noreferrer">other extensions</a></li></ul>';
export function BeritaEditor({ onSubmit }: { onSubmit: (val: string) => void }) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Superscript,
SubScript,
Highlight,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
content,
immediatelyRender: false
});
if (!editor) {
return null;
}
export function BeritaEditor({
onEditorReady,
showSubmit = true,
onSubmit,
}: {
onEditorReady?: (editor: any | null) => void;
onSubmit?: (val: string) => void;
showSubmit?: boolean;
}) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Superscript,
SubScript,
Highlight,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
content: '',
immediatelyRender: false
});
useEffect(() => {
onEditorReady?.(editor);
}, [editor, onEditorReady] );
if (!editor) return null;
return (
@@ -82,10 +94,12 @@ export function BeritaEditor({ onSubmit }: { onSubmit: (val: string) => void })
<RichTextEditor.Content />
</RichTextEditor>
<Button onClick={() => {
if (!editor) return
onSubmit(editor?.getHTML())
}}>Submit</Button>
{showSubmit && (
<Button onClick={() => {
if (!editor) return
onSubmit?.(editor?.getHTML())
}}>Submit</Button>
)}
</Stack>
);
}

View File

@@ -1,101 +1,358 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
'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 { 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 { ModalKonfirmasiHapus } from '../../_com/modalKonfirmasiHapus';
import stateDashboardBerita from '../../_state/desa/berita';
import { BeritaEditor } from './_com/BeritaEditor';
import colors from '@/con/colors';
function Page() {
return (
<Box>
<Title order={3}>Berita</Title>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<BeritaCreate />
<BeritaList />
</SimpleGrid>
<BeritaCreate />
<BeritaList />
</Box>
);
}
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 (
<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"}>
<SelectCategory onChange={(val) => {
beritaState.berita.create.form.katagoryBeritaId = val.id
}} />
<TextInput onChange={(val) => {
beritaState.berita.create.form.judul = val.target.value
}} label={"Judul"} placeholder='masukkan judul' />
<TextInput onChange={(val) => {
beritaState.berita.create.form.deskripsi = val.target.value
}} label={"Deskripsi"} placeholder='masukkan deskripsi' />
<Center w={200} h={200} bg={"gray"}>
<IconImageInPicture />
</Center>
<BeritaEditor onSubmit={(val) => {
<TextInput
value={beritaState.berita.create.form.judul}
onChange={(val) => {
beritaState.berita.create.form.judul = val.target.value;
}}
label={<Text fz={"sm"} fw={"bold"}>Judul</Text>}
placeholder="masukkan judul"
/>
<SelectCategory
onChange={(val) => {
beritaState.berita.create.form.kategoriBeritaId = val.id;
}}
/>
<TextInput
value={beritaState.berita.create.form.deskripsi}
onChange={(val) => {
beritaState.berita.create.form.deskripsi = val.target.value;
}}
label={<Text fz={"sm"} fw={"bold"}>Deskripsi</Text>}
placeholder="masukkan deskripsi"
/>
beritaState.berita.create.form.content = val
beritaState.berita.create.create()
}} />
<FileInput
label={<Text fz={"sm"} fw={"bold"}>Upload Gambar</Text>}
value={file}
onChange={async (e) => {
if (!e) return;
setFile(e);
const base64 = await e.arrayBuffer().then((buf) =>
"data:image/png;base64," + Buffer.from(buf).toString("base64")
);
setPreviewImage(base64);
}}
/>
{previewImage ? (
<Image alt="" src={previewImage} w={200} h={200} />
) : (
<Center w={200} h={200} bg={"gray"}>
<IconImageInPicture />
</Center>
)}
<Box>
<Text fz={"sm"} fw={"bold"}>Konten</Text>
<BeritaEditor
showSubmit={false}
onEditorReady={(ed) => setEditorInstance(ed)}
/>
</Box>
<Button onClick={handleSubmit}>Simpan Berita</Button>
</Stack>
</Paper>
</Box>
)
);
}
// function BeritaList() {
// const beritaState = useProxy(stateDashboardBerita)
// useShallowEffect(() => {
// beritaState.berita.findMany.load()
// }, [])
// const router = useRouter()
// if (!beritaState.berita.findMany.data) return <Stack py={10}>
// {Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
// </Stack>
// return (
// <Box py={10}>
// <Paper bg={colors['white-1']} p={'md'}>
// <Stack>
// <Text fz={"xl"} fw={"bold"}>List Berita</Text>
// <SimpleGrid cols={{ base: 1, md: 4 }}>
// {beritaState.berita.findMany.data?.map((item) => (
// <Paper key={item.id} bg={colors['BG-trans']} p={'md'}>
// <Box >
// <Flex justify="flex-end" mt={10}>
// <ActionIcon
// onClick={() => beritaState.berita.delete.byId(item.id)}
// disabled={beritaState.berita.delete.loading}
// color={colors['blue-button']} variant='transparent'>
// <IconX size={20} />
// </ActionIcon>
// <ActionIcon onClick={() => {
// router.push("/desa/berita/edit");
// }} color={colors['blue-button']} variant='transparent'>
// <IconEdit size={20} />
// </ActionIcon>
// </Flex>
// <Text fw={"bold"} fz={"sm"}>
// Kategori
// </Text>
// <Text>{item.kategoriBerita?.name}</Text>
// <Text fw={"bold"} fz={"sm"}>
// Judul
// </Text>
// <Text>{item.judul}</Text>
// <Text lineClamp={1} fw={"bold"} fz={"sm"}>
// Deskripsi
// </Text>
// <Text size='sm' lineClamp={2}>{item.deskripsi}</Text>
// <Text fw={"bold"} fz={"sm"}>
// Gambar
// </Text>
// <Image w={{ base: 100, md: 150 }} src={item.image?.link} alt="gambar" />
// </Box>
// </Paper>
// ))}
// </SimpleGrid>
// </Stack>
// </Paper>
// </Box>
// )
// }
function BeritaList() {
const beritaState = useProxy(stateDashboardBerita)
const [modalHapus, setModalHapus] = useState(false)
const [selectedId, setSelectedId] = useState<string | null>(null)
useShallowEffect(() => {
beritaState.berita.findMany.load()
}, [])
if (!beritaState.berita.findMany.data) return <Stack py={10}>
{Array.from({ length: 10 }).map((v, k) => <Skeleton key={k} h={40} />)}
</Stack>
const router = useRouter()
const handleHapus = () => {
if (selectedId) {
beritaState.berita.delete.byId(selectedId)
setModalHapus(false)
setSelectedId(null)
}
}
if (!beritaState.berita.findMany.data) {
return (
<Stack py={10}>
{Array.from({ length: 10 }).map((_, 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>
{beritaState.berita.findMany.data?.map((item) => (
<Text key={item.id}>{item.judul}</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={() => {
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>
</Paper>
{/* Modal Konfirmasi Hapus */}
<ModalKonfirmasiHapus
opened={modalHapus}
onClose={() => setModalHapus(false)}
onConfirm={handleHapus}
text='Apakah anda yakin ingin menghapus berita ini?'
/>
</Box>
)
}
function SelectCategory({ onChange }: {
onChange: (value: Prisma.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} />
return <Group>
<Select placeholder='pilih katagori' 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>
function SelectCategory({
onChange,
}: {
onChange: (value: Prisma.KategoriBeritaGetPayload<{
select: {
name: true;
id: true;
};
}>) => void;
}) {
const categoryState = useProxy(stateDashboardBerita.category);
useShallowEffect(() => {
categoryState.findMany.load();
}, []);
if (!categoryState.findMany.data) {
return <Skeleton height={38} />;
}
return (
<Select
label={<Text fz={"sm"} fw={"bold"}>Kategori</Text>}
placeholder="Pilih kategori"
data={categoryState.findMany.data.map((item) => ({
label: item.name,
value: item.id,
}))}
onChange={(val) => {
const selected = categoryState.findMany.data?.find((item) => item.id === val);
if (selected) {
onChange(selected);
}
}}
searchable
nothingFoundMessage="Tidak ditemukan"
/>
);
}
// function SelectCategory({ onChange }: {
// onChange: (value: Prisma.KategoriBeritaGetPayload<{
// select: {
// name: true,
// id: true
// }
// }>) => void
// }) {
// const beritaState = useProxy(stateDashboardBerita)
// useShallowEffect(() => {
// beritaState.category.findMany.load()
// }, [])
// if (!beritaState.category.findMany.data) return <Skeleton h={40} />
// return <Group>
// <Select placeholder='pilih kategori' label={<Text fz={"sm"} fw={"bold"}>Pilih Kategori</Text>} data={beritaState.category.findMany.data.map((item) => ({
// value: item.id,
// label: item.name
// }))} onChange={(v) => {
// const data = beritaState.category.findMany.data?.find((item) => item.id === v)
// if (!data) return
// onChange(data)
// }} />
// </Group>
// }
export default Page;

View File

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

View File

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

View File

@@ -1,11 +1,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 (
<div>
jumlah-penduduk-usia-kerja-yang-menganggur
</div>
);
}
<Box>
<Stack gap={"xs"}>
<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>
);
}

View File

@@ -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;

View File

@@ -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;

View File

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

View File

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

View File

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

View File

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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 CreateLowongan from './create/createLowongan';
import ListDataLowongan from './listData/page';
function Page() {
return (
<div>
lowongan-kerja-lokal
</div>
<Stack gap={"xs"}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<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>
);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,10 +1,30 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, Stack, Title } from '@mantine/core';
import React from 'react';
import CreatePasarDesa from './create/createPasarDesa';
import ListDataUsaha from './listData/page';
function Page() {
return (
<div>
pasar-desa
</div>
<Stack gap={'xs'}>
<Box>
<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>
);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,93 @@
'use client'
import colors from '@/con/colors';
import { Button, Stack } from '@mantine/core';
import { Link, RichTextEditor } from '@mantine/tiptap';
import Highlight from '@tiptap/extension-highlight';
import SubScript from '@tiptap/extension-subscript';
import Superscript from '@tiptap/extension-superscript';
import TextAlign from '@tiptap/extension-text-align';
import Underline from '@tiptap/extension-underline';
import { useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
const content =
'<h2 style="text-align: center;">Welcome to Mantine rich text editor</h2><p><code>RichTextEditor</code> component focuses on usability and is designed to be as simple as possible to bring a familiar editing experience to regular users. <code>RichTextEditor</code> is based on <a href="https://tiptap.dev/" rel="noopener noreferrer" target="_blank">Tiptap.dev</a> and supports all of its features:</p><ul><li>General text formatting: <strong>bold</strong>, <em>italic</em>, <u>underline</u>, <s>strike-through</s> </li><li>Headings (h1-h6)</li><li>Sub and super scripts (<sup>&lt;sup /&gt;</sup> and <sub>&lt;sub /&gt;</sub> tags)</li><li>Ordered and bullet lists</li><li>Text align&nbsp;</li><li>And all <a href="https://tiptap.dev/extensions" target="_blank" rel="noopener noreferrer">other extensions</a></li></ul>';
export function KeamananEditor({showSubmit = true} : {
showSubmit: boolean
}) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Superscript,
SubScript,
Highlight,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
immediatelyRender: false,
content,
});
return (
<Stack>
<RichTextEditor editor={editor}>
<RichTextEditor.Toolbar sticky stickyOffset={60}>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold />
<RichTextEditor.Italic />
<RichTextEditor.Underline />
<RichTextEditor.Strikethrough />
<RichTextEditor.ClearFormatting />
<RichTextEditor.Highlight />
<RichTextEditor.Code />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.H1 />
<RichTextEditor.H2 />
<RichTextEditor.H3 />
<RichTextEditor.H4 />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Blockquote />
<RichTextEditor.Hr />
<RichTextEditor.BulletList />
<RichTextEditor.OrderedList />
<RichTextEditor.Subscript />
<RichTextEditor.Superscript />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Link />
<RichTextEditor.Unlink />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.AlignLeft />
<RichTextEditor.AlignCenter />
<RichTextEditor.AlignJustify />
<RichTextEditor.AlignRight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Undo />
<RichTextEditor.Redo />
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
<RichTextEditor.Content />
</RichTextEditor>
{showSubmit && (
<Button
mt={10}
bg={colors['blue-button']}
>
Submit
</Button>
)}
</Stack>
);
}

View File

@@ -0,0 +1,95 @@
'use client'
import { Button, Stack } from '@mantine/core';
import { Link, RichTextEditor } from '@mantine/tiptap';
import Highlight from '@tiptap/extension-highlight';
import SubScript from '@tiptap/extension-subscript';
import Superscript from '@tiptap/extension-superscript';
import TextAlign from '@tiptap/extension-text-align';
import Underline from '@tiptap/extension-underline';
import { useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
function KeamananEditorText({ onSubmit, onChange, showSubmit = true, initialContent = '', }: {
onSubmit?: (val: string) => void,
onChange: (val: string) => void,
showSubmit?: boolean,
initialContent?: string }) {
const editor = useEditor({
extensions: [
StarterKit,
Underline,
Link,
Superscript,
SubScript,
Highlight,
TextAlign.configure({ types: ['heading', 'paragraph'] }),
],
immediatelyRender: false,
content: initialContent,
onUpdate : ({editor}) => {
onChange(editor.getHTML())
}
});
return (
<Stack>
<RichTextEditor editor={editor}>
<RichTextEditor.Toolbar sticky stickyOffset={60}>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Bold />
<RichTextEditor.Italic />
<RichTextEditor.Underline />
<RichTextEditor.Strikethrough />
<RichTextEditor.ClearFormatting />
<RichTextEditor.Highlight />
<RichTextEditor.Code />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.H1 />
<RichTextEditor.H2 />
<RichTextEditor.H3 />
<RichTextEditor.H4 />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Blockquote />
<RichTextEditor.Hr />
<RichTextEditor.BulletList />
<RichTextEditor.OrderedList />
<RichTextEditor.Subscript />
<RichTextEditor.Superscript />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Link />
<RichTextEditor.Unlink />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.AlignLeft />
<RichTextEditor.AlignCenter />
<RichTextEditor.AlignJustify />
<RichTextEditor.AlignRight />
</RichTextEditor.ControlsGroup>
<RichTextEditor.ControlsGroup>
<RichTextEditor.Undo />
<RichTextEditor.Redo />
</RichTextEditor.ControlsGroup>
</RichTextEditor.Toolbar>
<RichTextEditor.Content />
</RichTextEditor>
{showSubmit && (
<Button onClick={() => {
if (!editor) return
onSubmit?.(editor?.getHTML())
}}>Submit</Button>
)}
</Stack>
);
}
export default KeamananEditorText;

View File

@@ -1,10 +1,35 @@
import colors from '@/con/colors';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import React from 'react';
import CreateKeamananLingkungan from './ui/tambah_keamanan_lingkungan/page';
import ListKeamananLingkungan from './ui/list_keamanan_lingkungan/page';
function Page() {
return (
<div>
keamanan-lingkungan-pecalang-patwal
</div>
<Box>
<Stack gap={'xs'}>
<Title order={3}>Keamanan Lingkungan </Title>
<Tabs defaultValue="create" color={colors['blue-button']} variant='pills'>
<TabsList mb={10} bg={colors['BG-trans']} p={'xs'}>
<TabsTab value="create" >
Tambah Keamanan Lingkungan
</TabsTab>
<TabsTab value="list" >
List Keamanan Lingkungan
</TabsTab>
</TabsList>
<TabsPanel value="create">
<CreateKeamananLingkungan/>
</TabsPanel>
<TabsPanel value="list">
<ListKeamananLingkungan/>
</TabsPanel>
</Tabs>
</Stack>
</Box>
);
}

View File

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

View File

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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 CreateKontakDarurat from './create/createKontak';
import ListDataKontakDarurat from './listData/page';
function Page() {
return (
<div>
kontak-darurat
</div>
<Stack gap={"xs"}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<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>
);
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 (
<div>
laporan-publik
</div>
);
}
export default Page;
<Stack gap={"xs"}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'} radius={10}>
<Title order={3}>Laporan Publik</Title>
<CreateLaporanPublik/>
<Group>
<Button
mt={10}
bg={colors['blue-button']}
>
Submit
</Button>
</Group>
</Paper>
</Box>
<ListDataLaporanPublik/>
</SimpleGrid>
</Stack>
)
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 CreatePencegahan from './create/createPencegahan';
import ListDataPencegahan from './listData/page';
function Page() {
return (
<div>
pencegahan-kriminalitas
</div>
<Stack gap={"xs"}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<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>
);
}

View File

@@ -1,10 +1,35 @@
import colors from '@/con/colors';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import React from 'react';
import CreatePolsekTerdekat from './ui/tambah_polsek_terdekat/page';
import ListPolsekTerdekat from './ui/list_polsek_terdekat/page';
function Page() {
return (
<div>
polsek-terdekat
</div>
<Box>
<Stack gap={'xs'}>
<Title order={3}>Polsek Terdekat</Title>
<Tabs defaultValue="create" color={colors['blue-button']} variant='pills'>
<TabsList mb={10} bg={colors['BG-trans']} p={'xs'}>
<TabsTab value="create" >
Tambah Polsek Terdekat
</TabsTab>
<TabsTab value="list" >
List Polsek Terdekat
</TabsTab>
</TabsList>
<TabsPanel value="create">
<CreatePolsekTerdekat/>
</TabsPanel>
<TabsPanel value="list">
<ListPolsekTerdekat/>
</TabsPanel>
</Tabs>
</Stack>
</Box>
);
}

View File

@@ -0,0 +1,36 @@
import { Box, Paper, Stack, Table, TableTbody, TableTh, TableThead, TableTr, Title } from '@mantine/core';
import colors from '@/con/colors';
import React from 'react';
function ListPolsekTerdekat() {
return (
<Box>
<Paper bg={colors['white-1']} p={'md'} radius={10}>
<Stack gap={'xs'}>
<Title order={3}>List Polsek Terdekat</Title>
<Box>
<Table striped withTableBorder withColumnBorders withRowBorders>
<TableThead>
<TableTr>
<TableTh>Nama Polsek Terdekat</TableTh>
<TableTh>Jarak Polsek Terdekat</TableTh>
<TableTh>Alamat Polsek Terdekat</TableTh>
<TableTh>Nomor Telepon Polsek Terdekat</TableTh>
<TableTh>Jam Aktif</TableTh>
<TableTh>Deskripsi Polsek Terdekat</TableTh>
<TableTh>Aksi</TableTh>
<TableTh>Detail</TableTh>
</TableTr>
</TableThead>
<TableTbody>
</TableTbody>
</Table>
</Box>
</Stack>
</Paper>
</Box>
);
}
export default ListPolsekTerdekat;

View File

@@ -0,0 +1,56 @@
import colors from '@/con/colors';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Text, TextInput, Title } from '@mantine/core';
import { KeamananEditor } from '../../../_com/keamananEditor';
function CreatePolsekTerdekat() {
return (
<Box>
<Stack gap={'xs'}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={4}>Tambah Polsek Terdekat</Title>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Nama Polsek Terdekat</Text>}
placeholder='Masukkan nama polsek terdekat'
/>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Jarak Polsek Terdekat</Text>}
placeholder='Masukkan jarak polsek terdekat'
/>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Alamat Polsek Terdekat</Text>}
placeholder='Masukkan alamat polsek terdekat'
/>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Nomor Telepon Polsek Terdekat</Text>}
placeholder='Masukkan nomor telepon polsek terdekat'
/>
<TextInput
label={<Text fw={"bold"} fz={"sm"}>Jam Aktif</Text>}
placeholder='Masukkan jam aktif polsek terdekat'
/>
<Box>
<Text fw={"bold"} fz={"sm"}>Deskripsi Polsek Terdekat</Text>
<KeamananEditor
showSubmit={false}
/>
</Box>
<Group>
<Button bg={colors['blue-button']}>Submit</Button>
</Group>
</Stack>
</Paper>
</Box>
<Box>
</Box>
</SimpleGrid>
</Stack>
</Box>
);
}
export default CreatePolsekTerdekat;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 CreateTipsKeamanan from './create/createTips';
import ListDataTipsKeamanan from './listData/page';
function Page() {
return (
<div>
tips-keamanan
</div>
);
<Stack gap={"xs"}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<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;

View File

@@ -1,10 +1,35 @@
import colors from '@/con/colors';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import React from 'react';
import CreateInfoWabahPenyakit from './ui/tambah_wabah_penyakit/page';
import ListInfoWabahPenyakit from './ui/list_wabah_penyakit/page';
function Page() {
return (
<div>
Wabah Penyakit
</div>
<Box>
<Stack gap={'xs'}>
<Title order={3}>Info Wabah/Penyakit</Title>
<Tabs defaultValue="create" color={colors['blue-button']} variant='pills'>
<TabsList mb={10} bg={colors['BG-trans']} p={'xs'}>
<TabsTab value="create" >
Tambah Info Wabah/Penyakit
</TabsTab>
<TabsTab value="list" >
List Info Wabah/Penyakit
</TabsTab>
</TabsList>
<TabsPanel value="create">
<CreateInfoWabahPenyakit/>
</TabsPanel>
<TabsPanel value="list">
<ListInfoWabahPenyakit/>
</TabsPanel>
</Tabs>
</Stack>
</Box>
);
}

View File

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

View File

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

View File

@@ -1,10 +1,34 @@
import colors from '@/con/colors';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import React from 'react';
import CreateKontakDarurat from './ui/tambah_kontak_darurat/page';
import ListKontakDarurat from './ui/list_kontak_darurat/page';
function Page() {
return (
<div>
Kontak Darurat
</div>
<Box>
<Stack gap={'xs'}>
<Title order={3}>Kontak Darurat</Title>
<Tabs defaultValue="create" color={colors['blue-button']} variant='pills'>
<TabsList mb={10} bg={colors['BG-trans']} p={'xs'}>
<TabsTab value="create" >
Tambah Kontak Darurat
</TabsTab>
<TabsTab value="list" >
List Kontak Darurat
</TabsTab>
</TabsList>
<TabsPanel value="create">
<CreateKontakDarurat />
</TabsPanel>
<TabsPanel value="list">
<ListKontakDarurat/>
</TabsPanel>
</Tabs>
</Stack>
</Box>
);
}

View File

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

View File

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

View File

@@ -1,10 +1,34 @@
import colors from '@/con/colors';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import React from 'react';
import CreatePenangananDarurat from './ui/tambah_penanganan_darurat/page';
import ListPenangananDarurat from './ui/list_penanganan_darurat/page';
function Page() {
return (
<div>
Penanganan Darurat
</div>
<Box>
<Stack gap={'xs'}>
<Title order={3}>Penanganan Darurat</Title>
<Tabs defaultValue="create" color={colors['blue-button']} variant='pills'>
<TabsList mb={10} bg={colors['BG-trans']} p={'xs'}>
<TabsTab value="create" >
Tambah Penanganan Darurat
</TabsTab>
<TabsTab value="list" >
List Penanganan Darurat
</TabsTab>
</TabsList>
<TabsPanel value="create">
<CreatePenangananDarurat/>
</TabsPanel>
<TabsPanel value="list">
<ListPenangananDarurat/>
</TabsPanel>
</Tabs>
</Stack>
</Box>
);
}

View File

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

View File

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

View File

@@ -1,10 +1,33 @@
import React from 'react';
import colors from '@/con/colors';
import { Box, Stack, Tabs, TabsList, TabsPanel, TabsTab, Title } from '@mantine/core';
import CreateProgramKesehatan from './ui/tambah_program_kesehatan/page';
import ListProgramKesehatan from './ui/list_program_kesehatan/page';
function Page() {
return (
<div>
Program Kesehatan
</div>
<Box>
<Stack gap={'xs'}>
<Title order={3}>Program Kesehatan</Title>
<Tabs defaultValue="create" color={colors['blue-button']} variant='pills'>
<TabsList mb={10} bg={colors['BG-trans']} p={'xs'}>
<TabsTab value="create" >
Tambah Program Kesehatan
</TabsTab>
<TabsTab value="list" >
List Program Kesehatan
</TabsTab>
</TabsList>
<TabsPanel value="create">
<CreateProgramKesehatan />
</TabsPanel>
<TabsPanel value="list">
<ListProgramKesehatan/>
</TabsPanel>
</Tabs>
</Stack>
</Box>
);
}

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,7 @@ import TextAlign from '@tiptap/extension-text-align';
import Underline from '@tiptap/extension-underline';
import { useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import { useEffect } from 'react';
export function PPIDTextEditor({ onSubmit, onChange, showSubmit = true, initialContent = '', }: {
@@ -32,6 +33,12 @@ export function PPIDTextEditor({ onSubmit, onChange, showSubmit = true, initialC
}
});
useEffect(() => {
if (editor && initialContent !== editor.getHTML()) {
editor.commands.setContent(initialContent || '<p></p>');
}
}, [initialContent, editor]);
return (
<Stack>
<RichTextEditor editor={editor}>

View File

@@ -0,0 +1,44 @@
'use client'
import { Box, Stack, Text } from '@mantine/core';
import dynamic from 'next/dynamic';
const PPIDTextEditor = dynamic(() => import('../../_com/PPIDTextEditor').then(mod => mod.PPIDTextEditor), {
ssr: false,
});
function CreateDasarHukum({
valueJudul,
valueContent,
onJudulChange,
onContentChange,
error
} : {
valueJudul: string;
valueContent: string;
onJudulChange: (val: string) => void;
onContentChange: (val: string) => void;
error?: string;
}) {
return (
<Box>
<Stack gap={"xs"}>
<Text fw={"bold"}>Judul</Text>
<PPIDTextEditor
showSubmit={false}
onChange={onJudulChange}
initialContent={valueJudul}
/>
<Text fw={"bold"}>Content</Text>
<PPIDTextEditor
showSubmit={false}
onChange={onContentChange}
initialContent={valueContent}
/>
{error && <Text c="red" size="sm">{error}</Text>}
</Stack>
</Box>
);
}
export default CreateDasarHukum;

View File

@@ -1,64 +0,0 @@
'use client'
import { Box, Button, Group, Stack, Text } from '@mantine/core';
import { useProxy } from 'valtio/utils';
import stateDasarHukumPPID from '../../../_state/ppid/dasar_hukum/dasarHukum';
import dynamic from 'next/dynamic';
import colors from '@/con/colors';
import { useShallowEffect } from '@mantine/hooks';
const PPIDTextEditor = dynamic(() => import('../../_com/PPIDTextEditor').then(mod => mod.PPIDTextEditor), {
ssr: false,
});
function CreateDasarHukum() {
const dasarHukumState = useProxy(stateDasarHukumPPID)
useShallowEffect(() => {
if (!dasarHukumState.findById.data) {
dasarHukumState.findById.load(""); // biar masuk ke `findFirst` route kamu
}
}, []);
const submit = () => {
if (dasarHukumState.findById.data?.judul && dasarHukumState.findById.data?.content) {
dasarHukumState.update.save(dasarHukumState.findById.data)
}
}
return (
<Box>
<Stack gap={"xs"}>
<Text fw={"bold"}>Judul</Text>
<PPIDTextEditor
showSubmit={false}
key={`judul-${dasarHukumState.findById.data?.id ?? 'new'}`}
onChange={(val) => {
if (dasarHukumState.findById.data) {
dasarHukumState.findById.data.judul = val
}
}}
initialContent={dasarHukumState.findById.data?.judul ?? ''}
/>
<Text fw={"bold"}>Content</Text>
<PPIDTextEditor
showSubmit={false}
key={`content-${dasarHukumState.findById.data?.id ?? 'baru'}`}
onChange={(val) => {
if (dasarHukumState.findById.data) {
dasarHukumState.findById.data.content = val
}
}}
initialContent={dasarHukumState.findById.data?.content ?? ''}
/>
<Group>
<Button
bg={colors['blue-button']}
onClick={submit}
loading={dasarHukumState.update.loading}
>
Submit
</Button>
</Group>
</Stack>
</Box>
);
}
export default CreateDasarHukum;

View File

@@ -1,21 +1,59 @@
'use client'
import colors from '@/con/colors';
import { Box, Paper, SimpleGrid, Stack, Title } from '@mantine/core';
import CreateDasarHukum from './create/page';
import { Box, Button, Group, Paper, SimpleGrid, Stack, Title } from '@mantine/core';
import CreateDasarHukum from './create/create';
import ListDataDasarHukum from './listData/page';
import { useShallowEffect } from '@mantine/hooks';
import { useProxy } from 'valtio/utils';
import stateDasarHukumPPID from '../../_state/ppid/dasar_hukum/dasarHukum';
import { useEffect, useState } from 'react';
function Page() {
const dasarHukumState = useProxy(stateDasarHukumPPID)
const [judul, setJudul] = useState('');
const [content, setContent] = useState('');
useShallowEffect(() => {
if (!dasarHukumState.findById.data) {
dasarHukumState.findById.initialize(); // biar masuk ke `findFirst` route kamu
}
}, []);
useEffect(() => {
if (dasarHukumState.findById.data) {
setJudul(dasarHukumState.findById.data.judul ?? '')
setContent(dasarHukumState.findById.data.content ?? '')
}
}, [dasarHukumState.findById.data])
const submit = () => {
if (dasarHukumState.findById.data) {
dasarHukumState.findById.data.judul = judul;
dasarHukumState.findById.data.content = content;
dasarHukumState.update.save(dasarHukumState.findById.data)
}
}
return (
<Stack gap={"xs"}>
<SimpleGrid cols={{ base: 1, md: 2 }}>
<Box>
<Paper bg={colors['white-1']} p={'md'} radius={10}>
<Title order={3}>Dasar Hukum PPID</Title>
<CreateDasarHukum/>
<Title order={3}>Dasar Hukum PPID</Title>
<CreateDasarHukum onJudulChange={setJudul} onContentChange={setContent} valueJudul={judul} valueContent={content} />
<Group>
<Button
mt={10}
bg={colors['blue-button']}
onClick={submit}
loading={dasarHukumState.update.loading}
>
Submit
</Button>
</Group>
</Paper>
</Box>
<ListDataDasarHukum/>
<ListDataDasarHukum />
</SimpleGrid>
</Stack>
)

View File

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

View File

@@ -19,7 +19,7 @@ function Page() {
<Paper bg={colors['white-1']} p={'md'}>
<Stack gap={"xs"}>
<Title order={3}>Permohonan Informasi Publik</Title>
<Box p={"xl"}>
<Box>
<Table striped withRowBorders withColumnBorders withTableBorder>
<TableThead>
<TableTr>

View File

@@ -0,0 +1,27 @@
'use client'
import { Box, Text } from '@mantine/core';
import { PPIDTextEditor } from '../../_com/PPIDTextEditor';
function MisiPPID({
value,
onChange,
error,
}: {
value: string;
onChange: (val: string) => void;
error?: string;
}) {
return (
<Box>
<Text fw={'bold'}>Misi PPID</Text>
<PPIDTextEditor
showSubmit={false}
initialContent={value}
onChange={onChange}
/>
{error && <Text c="red" size="sm">{error}</Text>}
</Box>
);
}
export default MisiPPID;

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