diff --git a/src/app/admin/(dashboard)/_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik.ts b/src/app/admin/(dashboard)/_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik.ts
index 0a452cdd..0cd22ea0 100644
--- a/src/app/admin/(dashboard)/_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik.ts
+++ b/src/app/admin/(dashboard)/_state/ppid/permohonan_informasi_publik/permohonanInformasiPublik.ts
@@ -6,145 +6,176 @@ import { z } from "zod";
const templateForm = z.object({
name: z.string().min(3, "Nama minimal 3 karakter"),
- nik: z.string().min(3, "NIK minimal 3 karakter"),
- notelp: z.string().min(3, "Nomor Telepon minimal 3 karakter"),
+ nik: z
+ .string()
+ .min(3, "NIK minimal 3 karakter")
+ .max(16, "NIK maksimal 16 angka"),
+ notelp: z
+ .string()
+ .min(3, "Nomor Telepon minimal 3 karakter")
+ .max(15, "Nomor Telepon maksimal 15 angka"),
alamat: z.string().min(3, "Alamat minimal 3 karakter"),
email: z.string().min(3, "Email minimal 3 karakter"),
jenisInformasiDimintaId: z.string().nonempty(),
caraMemperolehInformasiId: z.string().nonempty(),
caraMemperolehSalinanInformasiId: z.string().nonempty(),
-})
+});
const jenisInformasiDiminta = proxy({
- findMany: {
- data: null as
- | null
- | Prisma.JenisInformasiDimintaGetPayload<{ omit: { isActive: true } }>[],
- async load(){
- const res = await ApiFetch.api.ppid.permohonaninformasipublik.jenisInformasi["find-many"].get();
- if (res.status === 200) {
- jenisInformasiDiminta.findMany.data = res.data?.data ?? [];
- }
- }
- }
-})
+ findMany: {
+ data: null as
+ | null
+ | Prisma.JenisInformasiDimintaGetPayload<{ omit: { isActive: true } }>[],
+ async load() {
+ const res =
+ await ApiFetch.api.ppid.permohonaninformasipublik.jenisInformasi[
+ "find-many"
+ ].get();
+ if (res.status === 200) {
+ jenisInformasiDiminta.findMany.data = res.data?.data ?? [];
+ }
+ },
+ },
+});
const caraMemperolehInformasi = proxy({
- findMany: {
- data: null as
- | null
- | Prisma.CaraMemperolehInformasiGetPayload<{ omit: { isActive: true } }>[],
- async load() {
- const res = await ApiFetch.api.ppid.permohonaninformasipublik.memperolehInformasi["find-many"].get();
- if (res.status === 200) {
- caraMemperolehInformasi.findMany.data = res.data?.data ?? [];
- }
- }
- }
-})
+ findMany: {
+ data: null as
+ | null
+ | Prisma.CaraMemperolehInformasiGetPayload<{
+ omit: { isActive: true };
+ }>[],
+ async load() {
+ const res =
+ await ApiFetch.api.ppid.permohonaninformasipublik.memperolehInformasi[
+ "find-many"
+ ].get();
+ if (res.status === 200) {
+ caraMemperolehInformasi.findMany.data = res.data?.data ?? [];
+ }
+ },
+ },
+});
const caraMemperolehSalinanInformasi = proxy({
- findMany: {
- data: null as
- | null
- | Prisma.CaraMemperolehSalinanInformasiGetPayload<{ omit: { isActive: true } }>[],
- async load() {
- const res = await ApiFetch.api.ppid.permohonaninformasipublik.salinanInformasi["find-many"].get();
- if (res.status === 200) {
- caraMemperolehSalinanInformasi.findMany.data = res.data?.data ?? [];
- }
- }
- }
-})
-console.log(caraMemperolehSalinanInformasi)
+ findMany: {
+ data: null as
+ | null
+ | Prisma.CaraMemperolehSalinanInformasiGetPayload<{
+ omit: { isActive: true };
+ }>[],
+ async load() {
+ const res =
+ await ApiFetch.api.ppid.permohonaninformasipublik.salinanInformasi[
+ "find-many"
+ ].get();
+ if (res.status === 200) {
+ caraMemperolehSalinanInformasi.findMany.data = res.data?.data ?? [];
+ }
+ },
+ },
+});
+console.log(caraMemperolehSalinanInformasi);
-type PermohonanInformasiPublikForm = Prisma.PermohonanInformasiPublikGetPayload<{
+type PermohonanInformasiPublikForm =
+ Prisma.PermohonanInformasiPublikGetPayload<{
select: {
- name: true;
- nik: true;
- notelp: true;
- alamat: true;
- email: true;
- jenisInformasiDimintaId: true;
- caraMemperolehInformasiId: true;
- caraMemperolehSalinanInformasiId: true;
+ name: true;
+ nik: true;
+ notelp: true;
+ alamat: true;
+ email: true;
+ jenisInformasiDimintaId: true;
+ caraMemperolehInformasiId: true;
+ caraMemperolehSalinanInformasiId: true;
};
-}>;
+ }>;
const statepermohonanInformasiPublik = proxy({
- create: {
- form: {} as PermohonanInformasiPublikForm,
- loading: false,
- async create(){
- const cek = templateForm.safeParse(statepermohonanInformasiPublik.create.form);
- if(!cek.success) {
- const err = `[${cek.error.issues
- .map((v) => `${v.path.join(".")}`)
- .join("\n")}] required`;
- return toast.error(err);
- }
- try {
- statepermohonanInformasiPublik.create.loading = true;
- const res = await ApiFetch.api.ppid.permohonaninformasipublik["create"].post(statepermohonanInformasiPublik.create.form);
- if (res.status === 200) {
- statepermohonanInformasiPublik.findMany.load();
- return toast.success("Sukses menambahkan");
- }
- return toast.error("failed create");
- } catch (error) {
- console.log((error as Error).message);
- } finally {
- statepermohonanInformasiPublik.create.loading = false;
- }
+ create: {
+ form: {} as PermohonanInformasiPublikForm,
+ loading: false,
+ async create() {
+ const cek = templateForm.safeParse(
+ statepermohonanInformasiPublik.create.form
+ );
+
+ if (!cek.success) {
+ toast.error(cek.error.issues.map((i) => i.message).join("\n"));
+ return false; // ⬅️ tambahkan return false
+ }
+
+ try {
+ statepermohonanInformasiPublik.create.loading = true;
+ const res = await ApiFetch.api.ppid.permohonaninformasipublik[
+ "create"
+ ].post(statepermohonanInformasiPublik.create.form);
+
+ if (res.data?.success === false) {
+ toast.error(res.data?.message);
+ return false; // ⬅️ gagal
}
+
+ toast.success("Sukses menambahkan");
+ return true; // ⬅️ sukses
+ } catch {
+ toast.error("Terjadi kesalahan server");
+ return false;
+ } finally {
+ statepermohonanInformasiPublik.create.loading = false;
+ }
},
- findMany: {
- data: null as
- | Prisma.PermohonanInformasiPublikGetPayload<{ include: {
- caraMemperolehSalinanInformasi: true,
- jenisInformasiDiminta: true,
- caraMemperolehInformasi: true,
- } }>[]
- | null,
- async load() {
- const res = await ApiFetch.api.ppid.permohonaninformasipublik["find-many"].get();
- if (res.status === 200) {
- statepermohonanInformasiPublik.findMany.data = res.data?.data ?? [];
- }
- }
- },
- findUnique: {
- data: null as Prisma.PermohonanInformasiPublikGetPayload<{
+ },
+ findMany: {
+ data: null as
+ | Prisma.PermohonanInformasiPublikGetPayload<{
include: {
- jenisInformasiDiminta: true,
- caraMemperolehInformasi: true,
- caraMemperolehSalinanInformasi: true,
+ caraMemperolehSalinanInformasi: true;
+ jenisInformasiDiminta: true;
+ caraMemperolehInformasi: true;
};
- }> | null,
- async load(id: string) {
- try {
- const res = await fetch(`/api/ppid/permohonaninformasipublik/${id}`);
- if (res.ok) {
- const data = await res.json();
- statepermohonanInformasiPublik.findUnique.data = data.data ?? null;
- } else {
- console.error("Failed to fetch program inovasi:", res.statusText);
- statepermohonanInformasiPublik.findUnique.data = null;
- }
- } catch (error) {
- console.error("Error fetching program inovasi:", error);
- statepermohonanInformasiPublik.findUnique.data = null;
- }
- },
- },
-
-})
+ }>[]
+ | null,
+ async load() {
+ const res = await ApiFetch.api.ppid.permohonaninformasipublik[
+ "find-many"
+ ].get();
+ if (res.status === 200) {
+ statepermohonanInformasiPublik.findMany.data = res.data?.data ?? [];
+ }
+ },
+ },
+ findUnique: {
+ data: null as Prisma.PermohonanInformasiPublikGetPayload<{
+ include: {
+ jenisInformasiDiminta: true;
+ caraMemperolehInformasi: true;
+ caraMemperolehSalinanInformasi: true;
+ };
+ }> | null,
+ async load(id: string) {
+ try {
+ const res = await fetch(`/api/ppid/permohonaninformasipublik/${id}`);
+ if (res.ok) {
+ const data = await res.json();
+ statepermohonanInformasiPublik.findUnique.data = data.data ?? null;
+ } else {
+ console.error("Failed to fetch program inovasi:", res.statusText);
+ statepermohonanInformasiPublik.findUnique.data = null;
+ }
+ } catch (error) {
+ console.error("Error fetching program inovasi:", error);
+ statepermohonanInformasiPublik.findUnique.data = null;
+ }
+ },
+ },
+});
const statepermohonanInformasiPublikForm = proxy({
- statepermohonanInformasiPublik,
- jenisInformasiDiminta,
- caraMemperolehInformasi,
- caraMemperolehSalinanInformasi,
-})
+ statepermohonanInformasiPublik,
+ jenisInformasiDiminta,
+ caraMemperolehInformasi,
+ caraMemperolehSalinanInformasi,
+});
export default statepermohonanInformasiPublikForm;
diff --git a/src/app/admin/(dashboard)/_state/ppid/permohonan_keberatan_informasi_publik/permohonanKeberatanInformasi.ts b/src/app/admin/(dashboard)/_state/ppid/permohonan_keberatan_informasi_publik/permohonanKeberatanInformasi.ts
index fc316fa9..b1545785 100644
--- a/src/app/admin/(dashboard)/_state/ppid/permohonan_keberatan_informasi_publik/permohonanKeberatanInformasi.ts
+++ b/src/app/admin/(dashboard)/_state/ppid/permohonan_keberatan_informasi_publik/permohonanKeberatanInformasi.ts
@@ -5,82 +5,99 @@ import { proxy } from "valtio";
import { z } from "zod";
const templateForm = z.object({
- name: z.string().min(3, "Nama minimal 3 karakter"),
- email: z.string().min(3, "Email minimal 3 karakter"),
- notelp: z.string().min(3, "Nomor Telepon minimal 3 karakter"),
- alasan: z.string().min(3, "Alasan minimal 3 karakter"),
-})
+ name: z.string().min(3, "Nama minimal 3 karakter"),
+ email: z.string().min(3, "Email minimal 3 karakter"),
+ notelp: z
+ .string()
+ .min(3, "Nomor Telepon minimal 3 karakter")
+ .max(15, "Nomor Telepon maksimal 15 angka"),
+ alasan: z.string().min(3, "Alasan minimal 3 karakter"),
+});
-type PermohonanKeberatanInformasiForm = Prisma.FormulirPermohonanKeberatanGetPayload<{
+type PermohonanKeberatanInformasiForm =
+ Prisma.FormulirPermohonanKeberatanGetPayload<{
select: {
- name: true;
- email: true;
- notelp: true;
- alasan: true;
+ name: true;
+ email: true;
+ notelp: true;
+ alasan: true;
};
-}>;
+ }>;
const permohonanKeberatanInformasi = proxy({
- create: {
- form: {} as PermohonanKeberatanInformasiForm,
- loading: false,
- async create(){
- const cek = templateForm.safeParse(permohonanKeberatanInformasi.create.form);
- if(!cek.success) {
- const err = `[${cek.error.issues
- .map((v) => `${v.path.join(".")}`)
- .join("\n")}] required`;
- return toast.error(err);
- }
- try {
- permohonanKeberatanInformasi.create.loading = true;
- const res = await ApiFetch.api.ppid.permohonankeberataninformasipublik["create"].post(permohonanKeberatanInformasi.create.form);
- if (res.status === 200) {
- permohonanKeberatanInformasi.findMany.load();
- return toast.success("Sukses menambahkan");
- }
- return toast.error("failed create");
- } catch (error) {
- console.log((error as Error).message);
- } finally {
- permohonanKeberatanInformasi.create.loading = false;
- }
- },
- },
- findMany: {
- data: null as
- | Prisma.FormulirPermohonanKeberatanGetPayload<{omit: {isActive: true}}>[]
- | null,
- async load() {
- const res = await ApiFetch.api.ppid.permohonankeberataninformasipublik["find-many"].get();
- if (res.status === 200) {
- permohonanKeberatanInformasi.findMany.data = res.data?.data ?? [];
- }
- }
- },
- findUnique: {
- data: null as Prisma.FormulirPermohonanKeberatanGetPayload<{
- omit: {
- isActive: true;
- };
- }> | null,
- async load(id: string) {
- try {
- const res = await fetch(`/api/ppid/permohonankeberataninformasipublik/${id}`);
- if (res.ok) {
- const data = await res.json();
- permohonanKeberatanInformasi.findUnique.data = data.data ?? null;
- } else {
- console.error("Failed to fetch permohonan keberatan informasi:", res.statusText);
- permohonanKeberatanInformasi.findUnique.data = null;
- }
- } catch (error) {
- console.error("Error fetching permohonan keberatan informasi:", error);
- permohonanKeberatanInformasi.findUnique.data = null;
- }
- },
+ create: {
+ form: {} as PermohonanKeberatanInformasiForm,
+ loading: false,
+ async create() {
+ const cek = templateForm.safeParse(
+ permohonanKeberatanInformasi.create.form
+ );
+ if (!cek.success) {
+ toast.error(cek.error.issues.map((i) => i.message).join("\n"));
+ return false; // ⬅️ tambahkan return false
}
+ try {
+ permohonanKeberatanInformasi.create.loading = true;
+ const res = await ApiFetch.api.ppid.permohonankeberataninformasipublik[
+ "create"
+ ].post(permohonanKeberatanInformasi.create.form);
+ if (res.data?.success === false) {
+ toast.error(res.data?.message);
+ return false; // ⬅️ gagal
+ }
+
+ toast.success("Sukses menambahkan");
+ return true; // ⬅️ sukses
+ } catch {
+ toast.error("Terjadi kesalahan server");
+ return false;
+ } finally {
+ permohonanKeberatanInformasi.create.loading = false;
+ }
+ },
+ },
+ findMany: {
+ data: null as
+ | Prisma.FormulirPermohonanKeberatanGetPayload<{
+ omit: { isActive: true };
+ }>[]
+ | null,
+ async load() {
+ const res = await ApiFetch.api.ppid.permohonankeberataninformasipublik[
+ "find-many"
+ ].get();
+ if (res.status === 200) {
+ permohonanKeberatanInformasi.findMany.data = res.data?.data ?? [];
+ }
+ },
+ },
+ findUnique: {
+ data: null as Prisma.FormulirPermohonanKeberatanGetPayload<{
+ omit: {
+ isActive: true;
+ };
+ }> | null,
+ async load(id: string) {
+ try {
+ const res = await fetch(
+ `/api/ppid/permohonankeberataninformasipublik/${id}`
+ );
+ if (res.ok) {
+ const data = await res.json();
+ permohonanKeberatanInformasi.findUnique.data = data.data ?? null;
+ } else {
+ console.error(
+ "Failed to fetch permohonan keberatan informasi:",
+ res.statusText
+ );
+ permohonanKeberatanInformasi.findUnique.data = null;
+ }
+ } catch (error) {
+ console.error("Error fetching permohonan keberatan informasi:", error);
+ permohonanKeberatanInformasi.findUnique.data = null;
+ }
+ },
+ },
});
export default permohonanKeberatanInformasi;
-
diff --git a/src/app/admin/(dashboard)/ppid/profil-ppid/page.tsx b/src/app/admin/(dashboard)/ppid/profil-ppid/page.tsx
index 1e49f3da..247222cf 100644
--- a/src/app/admin/(dashboard)/ppid/profil-ppid/page.tsx
+++ b/src/app/admin/(dashboard)/ppid/profil-ppid/page.tsx
@@ -30,12 +30,13 @@ function Page() {
return (
-
+
Preview Profil PPID
}
diff --git a/src/app/api/[[...slugs]]/_lib/desa/berita/del.ts b/src/app/api/[[...slugs]]/_lib/desa/berita/del.ts
index bb5df949..0e31e421 100644
--- a/src/app/api/[[...slugs]]/_lib/desa/berita/del.ts
+++ b/src/app/api/[[...slugs]]/_lib/desa/berita/del.ts
@@ -6,33 +6,24 @@ 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",
- };
- }
+ 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
- },
+ include: { image: true, kategoriBerita: true },
});
- if (!berita) {
- return {
- status: 404,
- body: "Berita tidak ditemukan",
- };
- }
+ if (!berita) return { status: 404, body: "Berita tidak ditemukan" };
- // Hapus file gambar dari filesystem jika ada
+ // 1. HAPUS BERITA DULU
+ await prisma.berita.delete({ where: { id } });
+
+ // 2. BARU HAPUS FILE
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 },
});
@@ -41,15 +32,11 @@ const beritaDelete = async (context: Context) => {
}
}
- // Hapus berita dari DB
- await prisma.berita.delete({
- where: { id },
- });
-
return {
success: true,
message: "Berita dan file terkait berhasil dihapus",
};
};
+
export default beritaDelete;
diff --git a/src/app/api/[[...slugs]]/_lib/ppid/permohonan_informasi_publik/create.ts b/src/app/api/[[...slugs]]/_lib/ppid/permohonan_informasi_publik/create.ts
index 4cc99f88..dcee8f4a 100644
--- a/src/app/api/[[...slugs]]/_lib/ppid/permohonan_informasi_publik/create.ts
+++ b/src/app/api/[[...slugs]]/_lib/ppid/permohonan_informasi_publik/create.ts
@@ -3,39 +3,55 @@ import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.PermohonanInformasiPublikGetPayload<{
- select: {
- name: true;
- nik: true;
- email: true;
- notelp: true;
- alamat: true;
- jenisInformasiDimintaId: true;
- caraMemperolehInformasiId: true;
- caraMemperolehSalinanInformasiId: true;
- }
-}>
-export default async function permohonanInformasiPublikCreate(context: Context) {
- const body = context.body as FormCreate;
-
- await prisma.permohonanInformasiPublik.create({
- data: {
- name: body.name,
- nik: body.nik,
- email: body.email,
- notelp: body.notelp,
- alamat: body.alamat,
- jenisInformasiDimintaId: body.jenisInformasiDimintaId,
- caraMemperolehInformasiId: body.caraMemperolehInformasiId,
- caraMemperolehSalinanInformasiId: body.caraMemperolehSalinanInformasiId,
- }
- })
+ select: {
+ name: true;
+ nik: true;
+ email: true;
+ notelp: true;
+ alamat: true;
+ jenisInformasiDimintaId: true;
+ caraMemperolehInformasiId: true;
+ caraMemperolehSalinanInformasiId: true;
+ };
+}>;
+export default async function permohonanInformasiPublikCreate(context: Context) {
+ const body = context.body as FormCreate;
+
+ // ========== VALIDASI NIK ==========
+ if (body.nik && body.nik.length > 16) {
return {
- success: true,
- message: "Permohonan Informasi Publik Berhasil Dibuat",
- data: {
- ...body,
- }
- }
+ success: false,
+ status: 400,
+ message: "Maksimal NIK adalah 16 angka",
+ };
+ }
+
+ // ========== VALIDASI NOMOR TELEPON ==========
+ if (body.notelp && body.notelp.length > 15) {
+ return {
+ success: false,
+ status: 400,
+ message: "Maksimal nomor telepon adalah 15 angka",
+ };
+ }
+
+ await prisma.permohonanInformasiPublik.create({
+ data: {
+ name: body.name,
+ nik: body.nik,
+ email: body.email,
+ notelp: body.notelp,
+ alamat: body.alamat,
+ jenisInformasiDimintaId: body.jenisInformasiDimintaId,
+ caraMemperolehInformasiId: body.caraMemperolehInformasiId,
+ caraMemperolehSalinanInformasiId: body.caraMemperolehSalinanInformasiId,
+ },
+ });
+
+ return {
+ success: true,
+ message: "Permohonan Informasi Publik Berhasil Dibuat",
+ data: { ...body },
+ };
}
-
\ No newline at end of file
diff --git a/src/app/api/[[...slugs]]/_lib/ppid/permohonan_keberatan_informasi_publik/create.ts b/src/app/api/[[...slugs]]/_lib/ppid/permohonan_keberatan_informasi_publik/create.ts
index a6f70308..e63a562c 100644
--- a/src/app/api/[[...slugs]]/_lib/ppid/permohonan_keberatan_informasi_publik/create.ts
+++ b/src/app/api/[[...slugs]]/_lib/ppid/permohonan_keberatan_informasi_publik/create.ts
@@ -3,31 +3,42 @@ import { Prisma } from "@prisma/client";
import { Context } from "elysia";
type FormCreate = Prisma.FormulirPermohonanKeberatanGetPayload<{
- select: {
- name: true;
- email: true;
- notelp: true;
- alasan: true;
- }
-}>
+ select: {
+ name: true;
+ email: true;
+ notelp: true;
+ alasan: true;
+ };
+}>;
-export default async function permohonanKeberatanInformasiPublikCreate(context: Context) {
- const body = context.body as FormCreate;
-
- await prisma.formulirPermohonanKeberatan.create({
- data: {
- name: body.name,
- email: body.email,
- notelp: body.notelp,
- alasan: body.alasan,
- }
- })
+export default async function permohonanKeberatanInformasiPublikCreate(
+ context: Context
+) {
+ const body = context.body as FormCreate;
+ // ========== VALIDASI NOMOR TELEPON ==========
+ if (body.notelp && body.notelp.length > 15) {
return {
- success: true,
- message: "Permohonan Keberatan Informasi Publik Berhasil Dibuat",
- data: {
- ...body,
- }
- }
-}
\ No newline at end of file
+ success: false,
+ status: 400,
+ message: "Maksimal nomor telepon adalah 15 angka",
+ };
+ }
+
+ await prisma.formulirPermohonanKeberatan.create({
+ data: {
+ name: body.name,
+ email: body.email,
+ notelp: body.notelp,
+ alasan: body.alasan,
+ },
+ });
+
+ return {
+ success: true,
+ message: "Permohonan Keberatan Informasi Publik Berhasil Dibuat",
+ data: {
+ ...body,
+ },
+ };
+}
diff --git a/src/app/darmasaba/(pages)/desa/layanan/[id]/page.tsx b/src/app/darmasaba/(pages)/desa/layanan/[id]/page.tsx
index a2c4e9bc..0a9dcd44 100644
--- a/src/app/darmasaba/(pages)/desa/layanan/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/desa/layanan/[id]/page.tsx
@@ -100,7 +100,7 @@ function Page() {
{data.name}
-
+
- {/* Header */}
-
-
-
-
-
-
- Pengumuman Adat & Budaya
-
-
- Informasi dan pengumuman resmi terkait pengumuman adat & budaya di Desa Darmasaba.
-
-
-
-
- {dataPengumuman.map((v, k) => {
- return (
-
- {v.judul}
-
-
-
- {v.tanggal}
-
-
-
- {v.jam}
-
-
-
- {v.lokasi}
-
-
-
- {v.deskripsi}
-
-
- )
- })}
-
-
- );
-}
-
-export default Page;
diff --git a/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/digitalisasi-desa/page.tsx b/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/digitalisasi-desa/page.tsx
deleted file mode 100644
index e6837804..00000000
--- a/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/digitalisasi-desa/page.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import colors from '@/con/colors';
-import { Stack, Box, Container, Text, Paper, Group } from '@mantine/core';
-import React from 'react';
-import BackButton from '../../../layanan/_com/BackButto';
-import { IconCalendar, IconClock, IconMapPin } from '@tabler/icons-react';
-
-const dataPengumuman = [
- {
- id: 1,
- judul: 'Pelatihan Dasar Komputer untuk Lansia dan Ibu Rumah Tangga',
- tanggal: 'Selasa, 30 April 2025',
- jam: '09:00 WITA',
- lokasi: 'Perpustakaan Desa',
- deskripsi: 'Belajar dari nol: cara menyalakan komputer, menulis di Word, membuat email, dan dasar penggunaan HP Android untuk komunikasi dan akses layanan desa online. Terbuka untuk 20 peserta.'
- },
- {
- id: 2,
- judul: 'Pelatihan Dasar Komputer untuk Lansia dan Ibu Rumah Tangga',
- tanggal: 'Selasa, 30 April 2025',
- jam: '09:00 WITA',
- lokasi: 'Perpustakaan Desa',
- deskripsi: 'Belajar dari nol: cara menyalakan komputer, menulis di Word, membuat email, dan dasar penggunaan HP Android untuk komunikasi dan akses layanan desa online. Terbuka untuk 20 peserta.'
- },
- {
- id: 3,
- judul: 'Pelatihan Dasar Komputer untuk Lansia dan Ibu Rumah Tangga',
- tanggal: 'Selasa, 30 April 2025',
- jam: '09:00 WITA',
- lokasi: 'Perpustakaan Desa',
- deskripsi: 'Belajar dari nol: cara menyalakan komputer, menulis di Word, membuat email, dan dasar penggunaan HP Android untuk komunikasi dan akses layanan desa online. Terbuka untuk 20 peserta.'
- },
- {
- id: 4,
- judul: 'Pelatihan Dasar Komputer untuk Lansia dan Ibu Rumah Tangga',
- tanggal: 'Selasa, 30 April 2025',
- jam: '09:00 WITA',
- lokasi: 'Perpustakaan Desa',
- deskripsi: 'Belajar dari nol: cara menyalakan komputer, menulis di Word, membuat email, dan dasar penggunaan HP Android untuk komunikasi dan akses layanan desa online. Terbuka untuk 20 peserta.'
- },
- {
- id: 5,
- judul: 'Pelatihan Dasar Komputer untuk Lansia dan Ibu Rumah Tangga',
- tanggal: 'Selasa, 30 April 2025',
- jam: '09:00 WITA',
- lokasi: 'Perpustakaan Desa',
- deskripsi: 'Belajar dari nol: cara menyalakan komputer, menulis di Word, membuat email, dan dasar penggunaan HP Android untuk komunikasi dan akses layanan desa online. Terbuka untuk 20 peserta.'
- },
-]
-function Page() {
- return (
-
- {/* Header */}
-
-
-
-
-
-
- Pengumuman Digitalisasi Desa
-
-
- Informasi dan pengumuman resmi terkait pengumuman digitalisasi desa
-
-
-
-
- {dataPengumuman.map((v, k) => {
- return (
-
- {v.judul}
-
-
-
- {v.tanggal}
-
-
-
- {v.jam}
-
-
-
- {v.lokasi}
-
-
-
- {v.deskripsi}
-
-
- )
- })}
-
-
- );
-}
-
-export default Page;
diff --git a/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/ekonomi-&-umkm/page.tsx b/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/ekonomi-&-umkm/page.tsx
deleted file mode 100644
index ffb38abc..00000000
--- a/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/ekonomi-&-umkm/page.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import colors from '@/con/colors';
-import { Stack, Box, Container, Text, Paper, Group } from '@mantine/core';
-import React from 'react';
-import BackButton from '../../../layanan/_com/BackButto';
-import { IconCalendar, IconClock, IconMapPin } from '@tabler/icons-react';
-
-const dataPengumuman = [
- {
- id: 1,
- judul: 'Pelatihan Digital Marketing untuk UMKM Desa',
- tanggal: 'Rabu, 23 April 2025',
- jam: '13:00 WITA',
- lokasi: 'Aula Kantor Desa',
- deskripsi: 'Para pelaku UMKM diundang untuk mengikuti pelatihan dasar digital marketing: mulai dari membuat akun bisnis, copywriting produk, hingga promosi melalui media sosial. Peserta akan mendapat sertifikat dan materi pelatihan.'
- },
- {
- id: 2,
- judul: 'Pelatihan Digital Marketing untuk UMKM Desa',
- tanggal: 'Rabu, 23 April 2025',
- jam: '13:00 WITA',
- lokasi: 'Aula Kantor Desa',
- deskripsi: 'Para pelaku UMKM diundang untuk mengikuti pelatihan dasar digital marketing: mulai dari membuat akun bisnis, copywriting produk, hingga promosi melalui media sosial. Peserta akan mendapat sertifikat dan materi pelatihan.'
- },
- {
- id: 3,
- judul: 'Pelatihan Digital Marketing untuk UMKM Desa',
- tanggal: 'Rabu, 23 April 2025',
- jam: '13:00 WITA',
- lokasi: 'Aula Kantor Desa',
- deskripsi: 'Para pelaku UMKM diundang untuk mengikuti pelatihan dasar digital marketing: mulai dari membuat akun bisnis, copywriting produk, hingga promosi melalui media sosial. Peserta akan mendapat sertifikat dan materi pelatihan.'
- },
- {
- id: 4,
- judul: 'Pelatihan Digital Marketing untuk UMKM Desa',
- tanggal: 'Rabu, 23 April 2025',
- jam: '13:00 WITA',
- lokasi: 'Aula Kantor Desa',
- deskripsi: 'Para pelaku UMKM diundang untuk mengikuti pelatihan dasar digital marketing: mulai dari membuat akun bisnis, copywriting produk, hingga promosi melalui media sosial. Peserta akan mendapat sertifikat dan materi pelatihan.'
- },
- {
- id: 5,
- judul: 'Pelatihan Digital Marketing untuk UMKM Desa',
- tanggal: 'Rabu, 23 April 2025',
- jam: '13:00 WITA',
- lokasi: 'Aula Kantor Desa',
- deskripsi: 'Para pelaku UMKM diundang untuk mengikuti pelatihan dasar digital marketing: mulai dari membuat akun bisnis, copywriting produk, hingga promosi melalui media sosial. Peserta akan mendapat sertifikat dan materi pelatihan.'
- },
-]
-function Page() {
- return (
-
- {/* Header */}
-
-
-
-
-
-
- Pengumuman Ekonomi & UMKM
-
-
- Informasi dan pengumuman resmi terkait pengumuman ekonomi & umkm
-
-
-
-
- {dataPengumuman.map((v, k) => {
- return (
-
- {v.judul}
-
-
-
- {v.tanggal}
-
-
-
- {v.jam}
-
-
-
- {v.lokasi}
-
-
-
- {v.deskripsi}
-
-
- )
- })}
-
-
- );
-}
-
-export default Page;
diff --git a/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/lingkungan-&-bencana/page.tsx b/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/lingkungan-&-bencana/page.tsx
deleted file mode 100644
index 50320eeb..00000000
--- a/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/lingkungan-&-bencana/page.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import colors from '@/con/colors';
-import { Stack, Box, Container, Text, Paper, Group } from '@mantine/core';
-import React from 'react';
-import BackButton from '../../../layanan/_com/BackButto';
-import { IconCalendar, IconClock, IconMapPin } from '@tabler/icons-react';
-
-const dataPengumuman = [
- {
- id: 1,
- judul: 'Gotong Royong Bersih Sungai dan Drainase',
- tanggal: 'Minggu, 21 April 2025',
- jam: '06:30 WITA',
- lokasi: 'Titik Kumpul: Poskamling RW 02',
- deskripsi: 'Seluruh warga RW 02 diimbau ikut serta dalam kegiatan bersih-bersih aliran sungai dan saluran air untuk mencegah banjir saat musim hujan. Disediakan alat kebersihan dan konsumsi ringan.'
- },
- {
- id: 2,
- judul: 'Gotong Royong Bersih Sungai dan Drainase',
- tanggal: 'Minggu, 21 April 2025',
- jam: '06:30 WITA',
- lokasi: 'Titik Kumpul: Poskamling RW 02',
- deskripsi: 'Seluruh warga RW 02 diimbau ikut serta dalam kegiatan bersih-bersih aliran sungai dan saluran air untuk mencegah banjir saat musim hujan. Disediakan alat kebersihan dan konsumsi ringan.'
- },
- {
- id: 3,
- judul: 'Gotong Royong Bersih Sungai dan Drainase',
- tanggal: 'Minggu, 21 April 2025',
- jam: '06:30 WITA',
- lokasi: 'Titik Kumpul: Poskamling RW 02',
- deskripsi: 'Seluruh warga RW 02 diimbau ikut serta dalam kegiatan bersih-bersih aliran sungai dan saluran air untuk mencegah banjir saat musim hujan. Disediakan alat kebersihan dan konsumsi ringan.'
- },
- {
- id: 4,
- judul: 'Gotong Royong Bersih Sungai dan Drainase',
- tanggal: 'Minggu, 21 April 2025',
- jam: '06:30 WITA',
- lokasi: 'Titik Kumpul: Poskamling RW 02',
- deskripsi: 'Seluruh warga RW 02 diimbau ikut serta dalam kegiatan bersih-bersih aliran sungai dan saluran air untuk mencegah banjir saat musim hujan. Disediakan alat kebersihan dan konsumsi ringan.'
- },
- {
- id: 5,
- judul: 'Gotong Royong Bersih Sungai dan Drainase',
- tanggal: 'Minggu, 21 April 2025',
- jam: '06:30 WITA',
- lokasi: 'Titik Kumpul: Poskamling RW 02',
- deskripsi: 'Seluruh warga RW 02 diimbau ikut serta dalam kegiatan bersih-bersih aliran sungai dan saluran air untuk mencegah banjir saat musim hujan. Disediakan alat kebersihan dan konsumsi ringan.'
- },
-]
-function Page() {
- return (
-
- {/* Header */}
-
-
-
-
-
-
- Pengumuman Lingkungan & Bencana
-
-
- Informasi dan pengumuman resmi terkait pengumuman lingkungan & bencana
-
-
-
-
- {dataPengumuman.map((v, k) => {
- return (
-
- {v.judul}
-
-
-
- {v.tanggal}
-
-
-
- {v.jam}
-
-
-
- {v.lokasi}
-
-
-
- {v.deskripsi}
-
-
- )
- })}
-
-
- );
-}
-
-export default Page;
diff --git a/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/pendidikan-&-kepemudaan/page.tsx b/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/pendidikan-&-kepemudaan/page.tsx
deleted file mode 100644
index 003785fc..00000000
--- a/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/pendidikan-&-kepemudaan/page.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import colors from '@/con/colors';
-import { Stack, Box, Container, Text, Paper, Group } from '@mantine/core';
-import React from 'react';
-import BackButton from '../../../layanan/_com/BackButto';
-import { IconCalendar, IconClock, IconMapPin } from '@tabler/icons-react';
-
-const dataPengumuman = [
- {
- id: 1,
- judul: 'Pendaftaran Bimbingan Belajar Gratis untuk Pelajar',
- tanggal: 'Sabtu, 20 April 2025',
- jam: '08:00 WITA',
- lokasi: 'Balai Banjar Desa Darmasaba',
- deskripsi: 'Dalam rangka meningkatkan kesadaran kesehatan, Pemerintah Desa Darmasaba bekerja sama dengan Puskesmas Abiansemal akan mengadakan pemeriksaan kesehatan gratis meliputi cek tekanan darah, kolesterol, gula darah, dan konsultasi gizi.'
- },
- {
- id: 2,
- judul: 'Lomba Video Pendek Hari Lingkungan',
- tanggal: 'Deadline: 28 April 2025',
- jam: '08:00 WITA',
- lokasi: 'Online Submission',
- deskripsi: 'Karang Taruna Desa mengadakan lomba video pendek bertema "Lingkunganku, Tanggung Jawabku". Pemenang akan diumumkan saat acara Hari Desa Hijau. Total hadiah Rp1.000.000.'
- },
- {
- id: 3,
- judul: 'Pendaftaran Bimbingan Belajar Gratis untuk Pelajar',
- tanggal: 'Sabtu, 20 April 2025',
- jam: '08:00 WITA',
- lokasi: 'Balai Banjar Desa Darmasaba',
- deskripsi: 'Dalam rangka meningkatkan kesadaran kesehatan, Pemerintah Desa Darmasaba bekerja sama dengan Puskesmas Abiansemal akan mengadakan pemeriksaan kesehatan gratis meliputi cek tekanan darah, kolesterol, gula darah, dan konsultasi gizi.'
- },
- {
- id: 4,
- judul: 'Lomba Video Pendek Hari Lingkungan',
- tanggal: 'Deadline: 28 April 2025',
- jam: '08:00 WITA',
- lokasi: 'Online Submission',
- deskripsi: 'Karang Taruna Desa mengadakan lomba video pendek bertema "Lingkunganku, Tanggung Jawabku". Pemenang akan diumumkan saat acara Hari Desa Hijau. Total hadiah Rp1.000.000.'
- },
- {
- id: 5,
- judul: 'Pendaftaran Bimbingan Belajar Gratis untuk Pelajar',
- tanggal: 'Sabtu, 20 April 2025',
- jam: '08:00 WITA',
- lokasi: 'Balai Banjar Desa Darmasaba',
- deskripsi: 'Dalam rangka meningkatkan kesadaran kesehatan, Pemerintah Desa Darmasaba bekerja sama dengan Puskesmas Abiansemal akan mengadakan pemeriksaan kesehatan gratis meliputi cek tekanan darah, kolesterol, gula darah, dan konsultasi gizi.'
- },
-]
-function Page() {
- return (
-
- {/* Header */}
-
-
-
-
-
-
- Pengumuman Pendidikan & Kepemudaan
-
-
- Informasi dan pengumuman resmi terkait pengumuman pendidikan & kepemudaan
-
-
-
-
- {dataPengumuman.map((v, k) => {
- return (
-
- {v.judul}
-
-
-
- {v.tanggal}
-
-
-
- {v.jam}
-
-
-
- {v.lokasi}
-
-
-
- {v.deskripsi}
-
-
- )
- })}
-
-
- );
-}
-
-export default Page;
diff --git a/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/sosial-&-kesehatan/page.tsx b/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/sosial-&-kesehatan/page.tsx
deleted file mode 100644
index f6924545..00000000
--- a/src/app/darmasaba/(pages)/desa/pengumuman/(kategori)/sosial-&-kesehatan/page.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import colors from '@/con/colors';
-import { Stack, Box, Container, Text, Paper, Group } from '@mantine/core';
-import React from 'react';
-import BackButton from '../../../layanan/_com/BackButto';
-import { IconCalendar, IconClock, IconMapPin } from '@tabler/icons-react';
-
-const dataPengumuman = [
- {
- id: 1,
- judul: 'Pemeriksaan Kesehatan Gratis Untuk Warga Desa',
- tanggal: 'Sabtu, 20 April 2025',
- jam: '08:00 WITA',
- lokasi: 'Balai Banjar Desa Darmasaba',
- deskripsi: 'Dalam rangka meningkatkan kesadaran kesehatan, Pemerintah Desa Darmasaba bekerja sama dengan Puskesmas Abiansemal akan mengadakan pemeriksaan kesehatan gratis meliputi cek tekanan darah, kolesterol, gula darah, dan konsultasi gizi.'
- },
- {
- id: 2,
- judul: 'Pemeriksaan Kesehatan Gratis Untuk Warga Desa',
- tanggal: 'Sabtu, 20 April 2025',
- jam: '08:00 WITA',
- lokasi: 'Balai Banjar Desa Darmasaba',
- deskripsi: 'Dalam rangka meningkatkan kesadaran kesehatan, Pemerintah Desa Darmasaba bekerja sama dengan Puskesmas Abiansemal akan mengadakan pemeriksaan kesehatan gratis meliputi cek tekanan darah, kolesterol, gula darah, dan konsultasi gizi.'
- },
- {
- id: 3,
- judul: 'Pemeriksaan Kesehatan Gratis Untuk Warga Desa',
- tanggal: 'Sabtu, 20 April 2025',
- jam: '08:00 WITA',
- lokasi: 'Balai Banjar Desa Darmasaba',
- deskripsi: 'Dalam rangka meningkatkan kesadaran kesehatan, Pemerintah Desa Darmasaba bekerja sama dengan Puskesmas Abiansemal akan mengadakan pemeriksaan kesehatan gratis meliputi cek tekanan darah, kolesterol, gula darah, dan konsultasi gizi.'
- },
- {
- id: 4,
- judul: 'Pemeriksaan Kesehatan Gratis Untuk Warga Desa',
- tanggal: 'Sabtu, 20 April 2025',
- jam: '08:00 WITA',
- lokasi: 'Balai Banjar Desa Darmasaba',
- deskripsi: 'Dalam rangka meningkatkan kesadaran kesehatan, Pemerintah Desa Darmasaba bekerja sama dengan Puskesmas Abiansemal akan mengadakan pemeriksaan kesehatan gratis meliputi cek tekanan darah, kolesterol, gula darah, dan konsultasi gizi.'
- },
- {
- id: 5,
- judul: 'Pemeriksaan Kesehatan Gratis Untuk Warga Desa',
- tanggal: 'Sabtu, 20 April 2025',
- jam: '08:00 WITA',
- lokasi: 'Balai Banjar Desa Darmasaba',
- deskripsi: 'Dalam rangka meningkatkan kesadaran kesehatan, Pemerintah Desa Darmasaba bekerja sama dengan Puskesmas Abiansemal akan mengadakan pemeriksaan kesehatan gratis meliputi cek tekanan darah, kolesterol, gula darah, dan konsultasi gizi.'
- },
-]
-function Page() {
- return (
-
- {/* Header */}
-
-
-
-
-
-
- Pengumuman Sosial & Kesehatan
-
-
- Informasi dan pengumuman resmi terkait pengumuman sosial & kesehatan
-
-
-
-
- {dataPengumuman.map((v, k) => {
- return (
-
- {v.judul}
-
-
-
- {v.tanggal}
-
-
-
- {v.jam}
-
-
-
- {v.lokasi}
-
-
-
- {v.deskripsi}
-
-
- )
- })}
-
-
- );
-}
-
-export default Page;
diff --git a/src/app/darmasaba/(pages)/desa/pengumuman/[name]/[id]/page.tsx b/src/app/darmasaba/(pages)/desa/pengumuman/[name]/[id]/page.tsx
index 82a8ccd5..f103d6f8 100644
--- a/src/app/darmasaba/(pages)/desa/pengumuman/[name]/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/desa/pengumuman/[name]/[id]/page.tsx
@@ -46,8 +46,8 @@ function Page() {
-
-
+
+
{new Date(detail.data?.createdAt).toLocaleDateString('id-ID', {
weekday: 'long',
day: 'numeric',
diff --git a/src/app/darmasaba/(pages)/desa/profile/struktur-perangkat-desa/page.tsx b/src/app/darmasaba/(pages)/desa/profile/struktur-perangkat-desa/page.tsx
index 5ac50922..bc4b3300 100644
--- a/src/app/darmasaba/(pages)/desa/profile/struktur-perangkat-desa/page.tsx
+++ b/src/app/darmasaba/(pages)/desa/profile/struktur-perangkat-desa/page.tsx
@@ -14,6 +14,9 @@ import {
Loader,
Paper,
Stack,
+ Tabs,
+ TabsList,
+ TabsTab,
Text,
TextInput,
Title,
@@ -35,6 +38,7 @@ import { useEffect, useRef, useState } from 'react'
import { useProxy } from 'valtio/utils'
import './struktur.css'
import BackButton from '../_com/BackButto'
+import { useMediaQuery } from '@mantine/hooks'
export default function StrukturPerangkatDesa() {
return (
@@ -231,87 +235,121 @@ function StrukturPerangkatDesaNode() {
p="md"
radius="md"
style={{
- background: colors['blue-button']
+ background: colors['blue-button'],
+ width: '100%', // ⬅️ penting
+ maxWidth: '100%', // ⬅️ penting
+ overflowX: 'auto' // ⬅️ untuk mencegah overflow
}}
>
-
- }
- onChange={(e) => debouncedSearch(e.target.value)}
+
+
+
+ }
+ onChange={(e) => debouncedSearch(e.target.value)}
+ styles={{
+ input: {
+ minWidth: 250,
+ },
+ }}
+ />
+
+
-
-
- }
- c={colors['blue-button']}
- >
- Zoom Out
-
-
-
+
- {Math.round(scale * 100)}%
-
+ }
+ style={{ flexShrink: 0 }} // 👈 pastikan tidak mengecil
+ >
+ Zoom Out
+
- }
- >
- Zoom In
-
+
+ {Math.round(scale * 100)}%
+
-
+ }
+ style={{ flexShrink: 0 }}
+ >
+ Zoom In
+
-
- ) : (
-
- )
- }
- >
- Fullscreen
-
-
-
+
+ Reset
+
+
+
+ ) : (
+
+ )
+ }
+ style={{ flexShrink: 0 }}
+ >
+ {isFullscreen ? 'Exit' : 'Fullscreen'}
+
+
+
+
{/* 🧩 Chart Container */}
@@ -325,15 +363,20 @@ function StrukturPerangkatDesaNode() {
maxWidth: '100%',
padding: '32px 16px',
transition: 'transform 0.2s ease',
- transform: `scale(${scale})`,
- transformOrigin: 'center top',
}}
>
- }
- className="p-organizationchart p-organizationchart-horizontal"
- />
+
+ }
+ className="p-organizationchart p-organizationchart-horizontal"
+ />
+
@@ -345,6 +388,7 @@ function NodeCard({ node, router }: any) {
const name = node?.data?.name || 'Tanpa Nama'
const title = node?.data?.title || 'Tanpa Jabatan'
const hasId = Boolean(node?.data?.id)
+ const isMobile = useMediaQuery("(max-width: 768px)");
return (
@@ -355,9 +399,10 @@ function NodeCard({ node, router }: any) {
withBorder
style={{
...styles,
- width: 240,
- minHeight: 280,
- padding: 20,
+ width: '100%',
+ maxWidth: isMobile ? 200 : 240, // lebih kecil di mobile
+ minHeight: isMobile ? 240 : 280,
+ padding: isMobile ? 16 : 20,
background: 'linear-gradient(135deg, rgba(28,110,164,0.15) 0%, rgba(255,255,255,0.95) 100%)',
borderColor: 'rgba(28, 110, 164, 0.3)',
borderWidth: 2,
diff --git a/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik/[id]/page.tsx b/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik/[id]/page.tsx
index 23c2cee6..16981c7e 100644
--- a/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik/[id]/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/daftar-informasi-publik/[id]/page.tsx
@@ -87,7 +87,7 @@ export default function DetailInformasiPublikUser() {
-
+
Jenis Informasi
@@ -96,7 +96,7 @@ export default function DetailInformasiPublikUser() {
-
+
Tanggal Publikasi
@@ -111,15 +111,19 @@ export default function DetailInformasiPublikUser() {
-
+
Deskripsi
-
+
+
+
diff --git a/src/app/darmasaba/(pages)/ppid/dasar-hukum/page.tsx b/src/app/darmasaba/(pages)/ppid/dasar-hukum/page.tsx
index b9d64d1f..7b66459c 100644
--- a/src/app/darmasaba/(pages)/ppid/dasar-hukum/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/dasar-hukum/page.tsx
@@ -31,7 +31,11 @@ function Page() {
-
+
Dasar Hukum
-
+
Informasi regulasi dan kebijakan resmi yang menjadi dasar hukum
@@ -71,12 +75,15 @@ function Page() {
{
state.create.form.name = val.currentTarget.value;
@@ -607,7 +607,7 @@ const state = useProxy(indeksKepuasanState.responden);
{
state.create.form.tanggal = val.currentTarget.value;
diff --git a/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/page.tsx b/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/page.tsx
index 5a47ba67..6bd6ebd1 100644
--- a/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/permohonan-informasi-publik/page.tsx
@@ -53,23 +53,11 @@ function Page() {
const permohonanInformasiPublikState = useProxy(statePermohonanInformasi);
const router = useRouter();
- const submitForms = () => {
+ const submitForms = async () => {
const { create } = permohonanInformasiPublikState.statepermohonanInformasiPublik;
-
- if (
- create.form.name &&
- create.form.nik &&
- create.form.notelp &&
- create.form.alamat &&
- create.form.email &&
- create.form.jenisInformasiDimintaId &&
- create.form.caraMemperolehInformasiId &&
- create.form.caraMemperolehSalinanInformasiId
- ) {
- create.create();
+ const hasil = await create.create(); // tunggu hasilnya
+ if (hasil) {
router.push('/darmasaba/permohonan/berhasil');
- } else {
- console.log('Validasi gagal, form tidak lengkap');
}
};
diff --git a/src/app/darmasaba/(pages)/ppid/permohonan-keberatan-informasi-publik/page.tsx b/src/app/darmasaba/(pages)/ppid/permohonan-keberatan-informasi-publik/page.tsx
index 4ef775be..871fd6f5 100644
--- a/src/app/darmasaba/(pages)/ppid/permohonan-keberatan-informasi-publik/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/permohonan-keberatan-informasi-publik/page.tsx
@@ -55,17 +55,13 @@ function Page() {
const stateKeberatan = useProxy(permohonanKeberatanInformasi);
const router = useRouter();
- const submit = () => {
- if (
- stateKeberatan.create.form.name &&
- stateKeberatan.create.form.email &&
- stateKeberatan.create.form.notelp &&
- stateKeberatan.create.form.alasan
- ) {
- stateKeberatan.create.create();
+ const submit = async () => {
+ const { create } = stateKeberatan;
+
+ const hasil = await create.create(); // tunggu hasilnya
+
+ if (hasil) {
router.push('/darmasaba/permohonan/berhasil');
- } else {
- console.log('Formulir belum lengkap');
}
};
@@ -190,7 +186,7 @@ function Page() {
Biografi
-
+
+
+
Riwayat Karir
-
+
+
+
+
+
diff --git a/src/app/darmasaba/(pages)/ppid/struktur-ppid/page.tsx b/src/app/darmasaba/(pages)/ppid/struktur-ppid/page.tsx
index 1d921cab..c14914d8 100644
--- a/src/app/darmasaba/(pages)/ppid/struktur-ppid/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/struktur-ppid/page.tsx
@@ -14,6 +14,9 @@ import {
Loader,
Paper,
Stack,
+ Tabs,
+ TabsList,
+ TabsTab,
Text,
TextInput,
Title,
@@ -35,6 +38,7 @@ import { useEffect, useRef, useState } from 'react'
import { useProxy } from 'valtio/utils'
import BackButton from '../../desa/layanan/_com/BackButto'
import './struktur.css'
+import { useMediaQuery } from '@mantine/hooks'
export default function Page() {
return (
@@ -231,87 +235,121 @@ function StrukturOrganisasiPPID() {
p="md"
radius="md"
style={{
- background: colors['blue-button']
+ background: colors['blue-button'],
+ width: '100%', // ⬅️ penting
+ maxWidth: '100%', // ⬅️ penting
+ overflowX: 'auto' // ⬅️ untuk mencegah overflow
}}
>
-
- }
- onChange={(e) => debouncedSearch(e.target.value)}
+
+
+
+ }
+ onChange={(e) => debouncedSearch(e.target.value)}
+ styles={{
+ input: {
+ minWidth: 250,
+ },
+ }}
+ />
+
+
-
-
- }
- c={colors['blue-button']}
- >
- Zoom Out
-
-
-
+
- {Math.round(scale * 100)}%
-
+ }
+ style={{ flexShrink: 0 }} // 👈 pastikan tidak mengecil
+ >
+ Zoom Out
+
- }
- >
- Zoom In
-
+
+ {Math.round(scale * 100)}%
+
-
+ }
+ style={{ flexShrink: 0 }}
+ >
+ Zoom In
+
-
- ) : (
-
- )
- }
- >
- Fullscreen
-
-
-
+
+ Reset
+
+
+
+ ) : (
+
+ )
+ }
+ style={{ flexShrink: 0 }}
+ >
+ {isFullscreen ? 'Exit' : 'Fullscreen'}
+
+
+
+
{/* 🧩 Chart Container */}
@@ -325,15 +363,20 @@ function StrukturOrganisasiPPID() {
maxWidth: '100%',
padding: '32px 16px',
transition: 'transform 0.2s ease',
- transform: `scale(${scale})`,
- transformOrigin: 'center top',
}}
>
- }
- className="p-organizationchart p-organizationchart-horizontal"
- />
+
+ }
+ className="p-organizationchart p-organizationchart-horizontal"
+ />
+
@@ -345,6 +388,7 @@ function NodeCard({ node, router }: any) {
const name = node?.data?.name || 'Tanpa Nama'
const title = node?.data?.title || 'Tanpa Jabatan'
const hasId = Boolean(node?.data?.id)
+ const isMobile = useMediaQuery("(max-width: 768px)");
return (
@@ -355,9 +399,10 @@ function NodeCard({ node, router }: any) {
withBorder
style={{
...styles,
- width: 240,
- minHeight: 280,
- padding: 20,
+ width: '100%',
+ maxWidth: isMobile ? 200 : 240, // lebih kecil di mobile
+ minHeight: isMobile ? 240 : 280,
+ padding: isMobile ? 16 : 20,
background: 'linear-gradient(135deg, rgba(28,110,164,0.15) 0%, rgba(255,255,255,0.95) 100%)',
borderColor: 'rgba(28, 110, 164, 0.3)',
borderWidth: 2,
@@ -411,6 +456,7 @@ function NodeCard({ node, router }: any) {
c={colors['blue-button']}
lineClamp={2}
style={{
+ // fontSize: 'clamp(12px, 4vw, 16px)', // 👈 responsif font size
minHeight: 40,
display: 'flex',
alignItems: 'center',
diff --git a/src/app/darmasaba/(pages)/ppid/visi-misi-ppid/page.tsx b/src/app/darmasaba/(pages)/ppid/visi-misi-ppid/page.tsx
index 85e50080..43440556 100644
--- a/src/app/darmasaba/(pages)/ppid/visi-misi-ppid/page.tsx
+++ b/src/app/darmasaba/(pages)/ppid/visi-misi-ppid/page.tsx
@@ -75,7 +75,7 @@ function Page() {
lh={1.7}
ta="center"
dangerouslySetInnerHTML={{ __html: item.visi }}
- style={{wordBreak: "break-word", whiteSpace: "normal"}}
+ style={{ wordBreak: "break-word", whiteSpace: "normal" }}
/>
@@ -86,12 +86,15 @@ function Page() {
c={colors['blue-button']} mb="sm">
Misi PPID
-
+
+
+
diff --git a/src/app/darmasaba/_com/ModernNewsNotification.tsx b/src/app/darmasaba/_com/ModernNewsNotification.tsx
index ccf338d9..e2663db0 100644
--- a/src/app/darmasaba/_com/ModernNewsNotification.tsx
+++ b/src/app/darmasaba/_com/ModernNewsNotification.tsx
@@ -220,8 +220,9 @@ export default function ModernNewsNotification({
...styles,
position: "fixed",
bottom: "100px",
- right: "24px",
- width: "380px",
+ left: "24px",
+ width: "90vw",
+ maxWidth: 380,
maxHeight: "500px",
boxShadow: "0 8px 32px rgba(0,0,0,0.12)",
borderRadius: "16px",
@@ -290,7 +291,7 @@ export default function ModernNewsNotification({
color={item.type === "berita" ? "blue" : "orange"}
variant="light"
>
- {item.type === "berita" ? "📰 Berita" : "📢 Pengumuman"}
+ {item.type === "berita" ? "Berita" : "Pengumuman"}
@@ -321,8 +322,9 @@ export default function ModernNewsNotification({
...styles,
position: "fixed",
bottom: "100px",
- right: "24px",
- width: "380px",
+ left: "24px",
+ width: "90vw",
+ maxWidth: 380,
boxShadow: "0 8px 32px rgba(0,0,0,0.15)",
borderRadius: "12px",
overflow: "hidden",
@@ -350,7 +352,6 @@ export default function ModernNewsNotification({
size="md"
color={currentNews?.type === "berita" ? "blue" : "orange"}
variant="light"
- leftSection={currentNews?.type === "berita" ? "📰" : "📢"}
>
{currentNews?.type === "berita"
? "Berita Terbaru"
diff --git a/src/app/darmasaba/_com/main-page/kepuasan/index.tsx b/src/app/darmasaba/_com/main-page/kepuasan/index.tsx
index f7480598..d303760c 100644
--- a/src/app/darmasaba/_com/main-page/kepuasan/index.tsx
+++ b/src/app/darmasaba/_com/main-page/kepuasan/index.tsx
@@ -4,7 +4,7 @@ import indeksKepuasanState from "@/app/admin/(dashboard)/_state/landing-page/ind
import colors from "@/con/colors";
import { BarChart, PieChart } from '@mantine/charts';
import { Box, Button, Center, Container, Flex, Modal, Paper, Select, SimpleGrid, Skeleton, Stack, Text, TextInput, Title } from "@mantine/core";
-import { useDisclosure, useShallowEffect } from "@mantine/hooks";
+import { useDisclosure, useMediaQuery, useShallowEffect } from "@mantine/hooks";
import { useState } from "react";
import { useProxy } from "valtio/utils";
@@ -25,6 +25,7 @@ function Kepuasan() {
const [donutDataKelompokUmur, setDonutDataKelompokUmur] = useState([]);
const [barChartData, setBarChartData] = useState>([]);
const [opened, { open, close }] = useDisclosure(false)
+ const isMobile = useMediaQuery("(max-width: 768px)");
const resetForm = () => {
state.create.form = {
@@ -41,7 +42,7 @@ function Kepuasan() {
indeksKepuasanState.jenisKelaminResponden.findMany.load()
indeksKepuasanState.pilihanRatingResponden.findMany.load()
indeksKepuasanState.kelompokUmurResponden.findMany.load()
- },[])
+ }, [])
const handleSubmit = async () => {
try {
@@ -82,13 +83,13 @@ function Kepuasan() {
// Update gender chart data
setDonutDataJenisKelamin([
- { name: 'Laki-laki', value: totalLaki, color: colors['blue-button'] },
+ { name: 'Laki-laki', value: totalLaki, color: '#52ABE3FF' },
{ name: 'Perempuan', value: totalPerempuan, color: '#10A85AFF' },
]);
// Update rating chart data
setDonutDataRating([
- { name: 'Sangat Baik', value: totalSangatBaik, color: colors['blue-button'] },
+ { name: 'Sangat Baik', value: totalSangatBaik, color: '#52ABE3FF' },
{ name: 'Baik', value: totalBaik, color: '#10A85AFF' },
{ name: 'Kurang Baik', value: totalKurangBaik, color: '#FFA500' },
{ name: 'Sangat Kurang Baik', value: totalSangatKurangBaik, color: '#FF4500' },
@@ -96,7 +97,7 @@ function Kepuasan() {
// Update age group chart data
setDonutDataKelompokUmur([
- { name: 'Muda', value: totalMuda, color: colors['blue-button'] },
+ { name: 'Muda', value: totalMuda, color: '#52ABE3FF' },
{ name: 'Dewasa', value: totalDewasa, color: '#10A85AFF' },
{ name: 'Lansia', value: totalLansia, color: '#FFA500' },
]);
@@ -220,10 +221,13 @@ function Kepuasan() {
@@ -259,10 +263,10 @@ function Kepuasan() {
withTooltip
tooltipAnimationDuration={200}
withLabels
- labelsPosition="outside"
+ labelsPosition="inside" // 👈 ini yang penting!
labelsType="percent"
withLabelsLine
- size={250}
+ size={isMobile ? 180 : 250} // 👈 kecilkan ukuran di mobile
data={donutDataRating}
/>
@@ -302,10 +306,10 @@ function Kepuasan() {
withTooltip
tooltipAnimationDuration={200}
withLabels
- labelsPosition="outside"
+ labelsPosition="inside"// 👈 ini yang penting!
labelsType="percent"
withLabelsLine
- size={250}
+ size={isMobile ? 180 : 250} // 👈 kecilkan ukuran di mobile
data={donutDataKelompokUmur}
/>
@@ -494,6 +498,8 @@ function Kepuasan() {
{
state.create.form.name = val.currentTarget.value;
@@ -619,7 +627,7 @@ function Kepuasan() {
{
state.create.form.tanggal = val.currentTarget.value;
diff --git a/src/app/darmasaba/_com/main-page/landing-page/index.tsx b/src/app/darmasaba/_com/main-page/landing-page/index.tsx
index 563f1dc1..8204b784 100644
--- a/src/app/darmasaba/_com/main-page/landing-page/index.tsx
+++ b/src/app/darmasaba/_com/main-page/landing-page/index.tsx
@@ -154,7 +154,7 @@ function LandingPage() {
return (
-
+
diff --git a/src/app/darmasaba/_com/main-page/penghargaan/index.tsx b/src/app/darmasaba/_com/main-page/penghargaan/index.tsx
index 2b4877eb..0c487411 100644
--- a/src/app/darmasaba/_com/main-page/penghargaan/index.tsx
+++ b/src/app/darmasaba/_com/main-page/penghargaan/index.tsx
@@ -4,7 +4,7 @@ import penghargaanState from "@/app/admin/(dashboard)/_state/desa/penghargaan";
import { Stack, Box, Container, Button, Text, Loader, Paper, Center, ActionIcon } from "@mantine/core";
import { IconAward, IconArrowRight, IconPlayerPlay } from "@tabler/icons-react";
import { useTransitionRouter } from 'next-view-transitions';
-import { useEffect, useState } from "react";
+import { useEffect, useState, useRef } from "react";
import { useProxy } from "valtio/utils";
import { useMediaQuery } from "@mantine/hooks";
@@ -15,16 +15,35 @@ function Penghargaan() {
const isMobile = useMediaQuery('(max-width: 768px)');
const [isVideoLoaded, setIsVideoLoaded] = useState(false);
const [showVideo, setShowVideo] = useState(true);
+ const [videoError, setVideoError] = useState(false);
+ const videoRef = useRef(null);
- // Opsional: deteksi iOS
- const isIOS = typeof window !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent);
+ // Deteksi iOS dengan lebih akurat
+ const isIOS = typeof window !== 'undefined' && (
+ /iPad|iPhone|iPod/.test(navigator.userAgent) ||
+ (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) // iPad dengan iPadOS 13+
+ );
useEffect(() => {
- if (isIOS) {
- // Di iOS, jangan andalkan autoplay — tampilkan kontrol
- setShowVideo(false);
+ // Di iOS, coba autoplay dulu, kalau gagal tampilkan fallback
+ if (isIOS && videoRef.current) {
+ const playPromise = videoRef.current.play();
+
+ if (playPromise !== undefined) {
+ playPromise
+ .then(() => {
+ // Autoplay berhasil
+ setShowVideo(true);
+ setIsVideoLoaded(true);
+ })
+ .catch(() => {
+ // Autoplay gagal, tampilkan fallback
+ setShowVideo(false);
+ setVideoError(true);
+ });
+ }
}
- }, []);
+ }, [isIOS]);
useEffect(() => {
const loadData = async () => {
@@ -38,42 +57,99 @@ function Penghargaan() {
loadData();
}, []);
+ const handlePlayVideo = () => {
+ setShowVideo(true);
+ setVideoError(false);
+
+ // Paksa play video setelah user interaction
+ setTimeout(() => {
+ if (videoRef.current) {
+ videoRef.current.play().catch(err => {
+ console.error("Video play error:", err);
+ setVideoError(true);
+ });
+ }
+ }, 100);
+ };
+
// kalau mobile ambil 1 data aja, kalau desktop ambil 3
const data = state.findMany.data?.slice(0, isMobile ? 1 : 3);
return (
-
- {showVideo ? (
+
+ {/* Video Layer */}
+ {showVideo && !videoError && (
- ) : (
- // Fallback: tampilkan poster + play button
+ )}
+
+ {/* Fallback Image + Play Button */}
+ {(!showVideo || videoError) && (
setShowVideo(true)}
+ onClick={handlePlayVideo}
style={{
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ width: '100%',
+ height: '100%',
backgroundImage: "url('/mangupuraaward.jpeg')",
backgroundSize: 'cover',
backgroundPosition: 'center',
+ backgroundRepeat: 'no-repeat',
cursor: 'pointer',
+ zIndex: 0,
}}
>
-
-
-
+
+
+
)}
+ {/* Overlay Gradient + Content */}
(null);
- const lastPengumumanId = useRef(null);
+ const lastBeritaTimestamp = useRef(null);
+ const lastPengumumanTimestamp = useRef(null);
// Inisialisasi dari localStorage
useEffect(() => {
- const savedBerita = localStorage.getItem("lastSeenBeritaId");
- const savedPengumuman = localStorage.getItem("lastSeenPengumumanId");
- if (savedBerita) lastBeritaId.current = savedBerita;
- if (savedPengumuman) lastPengumumanId.current = savedPengumuman;
+ const savedBeritaTs = localStorage.getItem("lastSeenBeritaTs");
+ const savedPengumumanTs = localStorage.getItem("lastSeenPengumumanTs");
+ if (savedBeritaTs) lastBeritaTimestamp.current = savedBeritaTs;
+ if (savedPengumumanTs) lastPengumumanTimestamp.current = savedPengumumanTs;
}, []);
// Load data utama (untuk card)
@@ -70,28 +70,49 @@ export default function Page() {
if (result.success && Array.isArray(result.news)) {
const news = result.news as NewsItem[];
- // Ambil ID terbaru
const latestBerita = news.find((n) => n.type === "berita");
const latestPengumuman = news.find((n) => n.type === "pengumuman");
- const isNewBerita = latestBerita && lastBeritaId.current !== null && latestBerita.id !== lastBeritaId.current;
- const isNewPengumuman = latestPengumuman && lastPengumumanId.current !== null && latestPengumuman.id !== lastPengumumanId.current;
+ const latestBeritaTs = latestBerita?.timestamp
+ ? new Date(latestBerita.timestamp).toISOString()
+ : null;
+ const latestPengumumanTs = latestPengumuman?.timestamp
+ ? new Date(latestPengumuman.timestamp).toISOString()
+ : null;
- // Simpan ID terbaru ke ref
- if (latestBerita) lastBeritaId.current = (latestBerita.id);
- if (latestPengumuman) lastPengumumanId.current = (latestPengumuman.id);
+ // Inisialisasi flag
+ let isNewBerita = false;
+ let isNewPengumuman = false;
- // Jika ini bukan inisialisasi pertama, tampilkan notifikasi
- if (lastBeritaId.current !== null || lastPengumumanId.current !== null) {
- if (isNewBerita || isNewPengumuman) {
- const count = (isNewBerita ? 1 : 0) + (isNewPengumuman ? 1 : 0);
- setNewItemCount(count);
- setHasNewContent(true);
+ // Deteksi berita baru
+ if (latestBeritaTs) {
+ if (lastBeritaTimestamp.current === null) {
+ // Pertama kali: simpan tanpa notifikasi
+ lastBeritaTimestamp.current = latestBeritaTs;
+ localStorage.setItem("lastSeenBeritaTs", latestBeritaTs);
+ } else if (latestBeritaTs > lastBeritaTimestamp.current) {
+ isNewBerita = true;
+ lastBeritaTimestamp.current = latestBeritaTs;
}
- } else {
- // Simpan ke localStorage saat pertama kali
- if (latestBerita) localStorage.setItem("lastSeenBeritaId", (latestBerita.id));
- if (latestPengumuman) localStorage.setItem("lastSeenPengumumanId", (latestPengumuman.id));
+ }
+
+ // Deteksi pengumuman baru
+ if (latestPengumumanTs) {
+ if (lastPengumumanTimestamp.current === null) {
+ // Pertama kali: simpan tanpa notifikasi
+ lastPengumumanTimestamp.current = latestPengumumanTs;
+ localStorage.setItem("lastSeenPengumumanTs", latestPengumumanTs);
+ } else if (latestPengumumanTs > lastPengumumanTimestamp.current) {
+ isNewPengumuman = true;
+ lastPengumumanTimestamp.current = latestPengumumanTs;
+ }
+ }
+
+ // 🔔 Trigger notifikasi hanya jika ada yang benar-benar BARU
+ if (isNewBerita || isNewPengumuman) {
+ const count = (isNewBerita ? 1 : 0) + (isNewPengumuman ? 1 : 0);
+ setNewItemCount(count);
+ setHasNewContent(true); // ✅ INI YANG KAMU LUPA!
}
setNotificationNews(news);
@@ -113,13 +134,17 @@ export default function Page() {
}, []);
const handleSeen = () => {
- setHasNewContent(false);
- setNewItemCount(0);
- const latestBerita = notificationNews.find(n => n.type === "berita");
- const latestPengumuman = notificationNews.find(n => n.type === "pengumuman");
- if (latestBerita) localStorage.setItem("lastSeenBeritaId", String(latestBerita.id));
- if (latestPengumuman) localStorage.setItem("lastSeenPengumumanId", String(latestPengumuman.id));
-};
+ setHasNewContent(false);
+ setNewItemCount(0);
+ const latestBerita = notificationNews.find(n => n.type === "berita");
+ const latestPengumuman = notificationNews.find(n => n.type === "pengumuman");
+ if (latestBerita) {
+ localStorage.setItem("lastSeenBeritaTs", new Date(latestBerita.timestamp!).toISOString());
+ }
+ if (latestPengumuman) {
+ localStorage.setItem("lastSeenPengumumanTs", new Date(latestPengumuman.timestamp!).toISOString());
+ }
+ };
return (