Fix QC Kak Inno & Kak Ayu Tanggal 15 Oct
This commit is contained in:
@@ -82,6 +82,7 @@
|
|||||||
"react-simple-toasts": "^6.1.0",
|
"react-simple-toasts": "^6.1.0",
|
||||||
"react-toastify": "^11.0.5",
|
"react-toastify": "^11.0.5",
|
||||||
"react-transition-group": "^4.4.5",
|
"react-transition-group": "^4.4.5",
|
||||||
|
"react-zoom-pan-pinch": "^3.7.0",
|
||||||
"readdirp": "^4.1.1",
|
"readdirp": "^4.1.1",
|
||||||
"recharts": "^2.15.3",
|
"recharts": "^2.15.3",
|
||||||
"sharp": "^0.34.3",
|
"sharp": "^0.34.3",
|
||||||
|
|||||||
@@ -1606,7 +1606,7 @@ model Pembiayaan {
|
|||||||
ApbDesa ApbDesa[] @relation("ApbDesaPembiayaan")
|
ApbDesa ApbDesa[] @relation("ApbDesaPembiayaan")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================= INOVASI ========================================= //
|
// ========================================= MENU INOVASI ========================================= //
|
||||||
// ========================================= DESA DIGITAL / SMART VILLAGE ========================================= //
|
// ========================================= DESA DIGITAL / SMART VILLAGE ========================================= //
|
||||||
model DesaDigital {
|
model DesaDigital {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ const berita = proxy({
|
|||||||
loading: false,
|
loading: false,
|
||||||
search: "",
|
search: "",
|
||||||
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
||||||
berita.findMany.loading = true; // ✅ Akses langsung via nama path
|
const startTime = Date.now();
|
||||||
|
berita.findMany.loading = true;
|
||||||
berita.findMany.page = page;
|
berita.findMany.page = page;
|
||||||
berita.findMany.search = search;
|
berita.findMany.search = search;
|
||||||
|
|
||||||
@@ -98,7 +99,14 @@ const berita = proxy({
|
|||||||
berita.findMany.data = [];
|
berita.findMany.data = [];
|
||||||
berita.findMany.totalPages = 1;
|
berita.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
|
// pastikan minimal 300ms sebelum loading = false (biar UX smooth)
|
||||||
|
const elapsed = Date.now() - startTime;
|
||||||
|
const minDelay = 300;
|
||||||
|
const delay = elapsed < minDelay ? minDelay - elapsed : 0;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
berita.findMany.loading = false;
|
berita.findMany.loading = false;
|
||||||
|
}, delay);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ const dataPerpustakaan = proxy({
|
|||||||
loading: false,
|
loading: false,
|
||||||
search: "",
|
search: "",
|
||||||
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
load: async (page = 1, limit = 10, search = "", kategori = "") => {
|
||||||
|
const startTime = Date.now();
|
||||||
dataPerpustakaan.findMany.loading = true; // ✅ Akses langsung via nama path
|
dataPerpustakaan.findMany.loading = true; // ✅ Akses langsung via nama path
|
||||||
dataPerpustakaan.findMany.page = page;
|
dataPerpustakaan.findMany.page = page;
|
||||||
dataPerpustakaan.findMany.search = search;
|
dataPerpustakaan.findMany.search = search;
|
||||||
@@ -77,7 +78,10 @@ const dataPerpustakaan = proxy({
|
|||||||
if (search) query.search = search;
|
if (search) query.search = search;
|
||||||
if (kategori) query.kategori = kategori;
|
if (kategori) query.kategori = kategori;
|
||||||
|
|
||||||
const res = await ApiFetch.api.pendidikan.perpustakaandigital.dataperpustakaan["findMany"].get({ query });
|
const res =
|
||||||
|
await ApiFetch.api.pendidikan.perpustakaandigital.dataperpustakaan[
|
||||||
|
"findMany"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
dataPerpustakaan.findMany.data = res.data.data ?? [];
|
dataPerpustakaan.findMany.data = res.data.data ?? [];
|
||||||
@@ -91,7 +95,14 @@ const dataPerpustakaan = proxy({
|
|||||||
dataPerpustakaan.findMany.data = [];
|
dataPerpustakaan.findMany.data = [];
|
||||||
dataPerpustakaan.findMany.totalPages = 1;
|
dataPerpustakaan.findMany.totalPages = 1;
|
||||||
} finally {
|
} finally {
|
||||||
|
// pastikan minimal 300ms sebelum loading = false (biar UX smooth)
|
||||||
|
const elapsed = Date.now() - startTime;
|
||||||
|
const minDelay = 300;
|
||||||
|
const delay = elapsed < minDelay ? minDelay - elapsed : 0;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
dataPerpustakaan.findMany.loading = false;
|
dataPerpustakaan.findMany.loading = false;
|
||||||
|
}, delay);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -115,7 +126,10 @@ const dataPerpustakaan = proxy({
|
|||||||
if (search) query.search = search;
|
if (search) query.search = search;
|
||||||
if (kategori) query.kategori = kategori;
|
if (kategori) query.kategori = kategori;
|
||||||
|
|
||||||
const res = await ApiFetch.api.pendidikan.perpustakaandigital.dataperpustakaan["findManyAll"].get({ query });
|
const res =
|
||||||
|
await ApiFetch.api.pendidikan.perpustakaandigital.dataperpustakaan[
|
||||||
|
"findManyAll"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
dataPerpustakaan.findManyAll.data = res.data.data ?? [];
|
dataPerpustakaan.findManyAll.data = res.data.data ?? [];
|
||||||
@@ -365,7 +379,10 @@ const kategoriBuku = proxy({
|
|||||||
const query: any = { page, limit };
|
const query: any = { page, limit };
|
||||||
if (search) query.search = search;
|
if (search) query.search = search;
|
||||||
|
|
||||||
const res = await ApiFetch.api.pendidikan.perpustakaandigital.kategoribuku["findMany"].get({ query });
|
const res =
|
||||||
|
await ApiFetch.api.pendidikan.perpustakaandigital.kategoribuku[
|
||||||
|
"findMany"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
kategoriBuku.findMany.data = res.data.data ?? [];
|
kategoriBuku.findMany.data = res.data.data ?? [];
|
||||||
@@ -557,7 +574,7 @@ const templatePeminjamanBuku = z.object({
|
|||||||
tanggalPinjam: z.string().min(1, "Tanggal Pinjam harus diisi"),
|
tanggalPinjam: z.string().min(1, "Tanggal Pinjam harus diisi"),
|
||||||
batasKembali: z.string().min(1, "Batas Kembali harus diisi"),
|
batasKembali: z.string().min(1, "Batas Kembali harus diisi"),
|
||||||
tanggalKembali: z.string().min(1, "Tanggal Kembali harus diisi"),
|
tanggalKembali: z.string().min(1, "Tanggal Kembali harus diisi"),
|
||||||
catatan: z.string().min(1, "Catatan harus diisi")
|
catatan: z.string().min(1, "Catatan harus diisi"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultPeminjamanBuku = {
|
const defaultPeminjamanBuku = {
|
||||||
@@ -568,7 +585,7 @@ const defaultPeminjamanBuku = {
|
|||||||
tanggalPinjam: "",
|
tanggalPinjam: "",
|
||||||
batasKembali: "",
|
batasKembali: "",
|
||||||
tanggalKembali: "",
|
tanggalKembali: "",
|
||||||
catatan: ""
|
catatan: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
interface FormEditData {
|
interface FormEditData {
|
||||||
@@ -584,7 +601,7 @@ interface FormEditData {
|
|||||||
batasKembali: string;
|
batasKembali: string;
|
||||||
tanggalKembali: string;
|
tanggalKembali: string;
|
||||||
catatan: string;
|
catatan: string;
|
||||||
status: 'Dipinjam' | 'Dikembalikan' | 'Terlambat' | 'Dibatalkan';
|
status: "Dipinjam" | "Dikembalikan" | "Terlambat" | "Dibatalkan";
|
||||||
}
|
}
|
||||||
|
|
||||||
const editForm: FormEditData = {
|
const editForm: FormEditData = {
|
||||||
@@ -596,8 +613,8 @@ const editForm: FormEditData = {
|
|||||||
batasKembali: "",
|
batasKembali: "",
|
||||||
tanggalKembali: "",
|
tanggalKembali: "",
|
||||||
catatan: "",
|
catatan: "",
|
||||||
status: "Dipinjam"
|
status: "Dipinjam",
|
||||||
}
|
};
|
||||||
|
|
||||||
const peminjamanBuku = proxy({
|
const peminjamanBuku = proxy({
|
||||||
create: {
|
create: {
|
||||||
@@ -651,7 +668,10 @@ const peminjamanBuku = proxy({
|
|||||||
const query: any = { page, limit };
|
const query: any = { page, limit };
|
||||||
if (search) query.search = search;
|
if (search) query.search = search;
|
||||||
|
|
||||||
const res = await ApiFetch.api.pendidikan.perpustakaandigital.peminjamanbuku["findMany"].get({ query });
|
const res =
|
||||||
|
await ApiFetch.api.pendidikan.perpustakaandigital.peminjamanbuku[
|
||||||
|
"findMany"
|
||||||
|
].get({ query });
|
||||||
|
|
||||||
if (res.status === 200 && res.data?.success) {
|
if (res.status === 200 && res.data?.success) {
|
||||||
peminjamanBuku.findMany.data = res.data.data ?? [];
|
peminjamanBuku.findMany.data = res.data.data ?? [];
|
||||||
@@ -720,7 +740,9 @@ const peminjamanBuku = proxy({
|
|||||||
);
|
);
|
||||||
await peminjamanBuku.findMany.load(); // refresh list
|
await peminjamanBuku.findMany.load(); // refresh list
|
||||||
} else {
|
} else {
|
||||||
toast.error(result?.message || "Gagal menghapus Data Peminjaman Buku");
|
toast.error(
|
||||||
|
result?.message || "Gagal menghapus Data Peminjaman Buku"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Gagal delete:", error);
|
console.error("Gagal delete:", error);
|
||||||
@@ -768,7 +790,7 @@ const peminjamanBuku = proxy({
|
|||||||
batasKembali: data.batasKembali,
|
batasKembali: data.batasKembali,
|
||||||
tanggalKembali: data.tanggalKembali,
|
tanggalKembali: data.tanggalKembali,
|
||||||
catatan: data.catatan,
|
catatan: data.catatan,
|
||||||
status: data.status
|
status: data.status,
|
||||||
};
|
};
|
||||||
return data; // Return the loaded data
|
return data; // Return the loaded data
|
||||||
} else {
|
} else {
|
||||||
@@ -811,7 +833,7 @@ const peminjamanBuku = proxy({
|
|||||||
batasKembali: this.form.batasKembali,
|
batasKembali: this.form.batasKembali,
|
||||||
tanggalKembali: this.form.tanggalKembali,
|
tanggalKembali: this.form.tanggalKembali,
|
||||||
catatan: this.form.catatan,
|
catatan: this.form.catatan,
|
||||||
status: this.form.status
|
status: this.form.status,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -830,7 +852,9 @@ const peminjamanBuku = proxy({
|
|||||||
await peminjamanBuku.findMany.load(); // refresh list
|
await peminjamanBuku.findMany.load(); // refresh list
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
throw new Error(result.message || "Gagal update data peminjaman buku");
|
throw new Error(
|
||||||
|
result.message || "Gagal update data peminjaman buku"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating data peminjaman buku:", error);
|
console.error("Error updating data peminjaman buku:", error);
|
||||||
@@ -849,7 +873,7 @@ const peminjamanBuku = proxy({
|
|||||||
peminjamanBuku.update.form = { ...editForm };
|
peminjamanBuku.update.form = { ...editForm };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const perpustakaanDigitalState = proxy({
|
const perpustakaanDigitalState = proxy({
|
||||||
dataPerpustakaan,
|
dataPerpustakaan,
|
||||||
|
|||||||
@@ -701,6 +701,457 @@ export default async function searchFindMany(context: Context) {
|
|||||||
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================= MENU INOVASI ========================================= //
|
||||||
|
// ========================================= DESA DIGITAL / SMART VILLAGE ========================================= //
|
||||||
|
|
||||||
|
if (type === "desaDigital") {
|
||||||
|
const data = await prisma.desaDigital.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= PROGRAM KREATIF ========================================= //
|
||||||
|
|
||||||
|
if (type === "programKreatif") {
|
||||||
|
const data = await prisma.programKreatif.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ slug: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= KOLABORASI INOVASI ========================================= //
|
||||||
|
|
||||||
|
if (type === "kolaborasiInovasi") {
|
||||||
|
const data = await prisma.kolaborasiInovasi.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ slug: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } },
|
||||||
|
{ kolaborator: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "mitraKolaborasi") {
|
||||||
|
const data = await prisma.mitraKolaborasi.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= INFO TEKHNOLOGI TEPAT GUNA ========================================= //
|
||||||
|
|
||||||
|
if (type === "infoTekno") {
|
||||||
|
const data = await prisma.infoTekno.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= LINGKUNGAN ========================================= //
|
||||||
|
// ========================================= PENGELOLAAN SAMPAH ========================================= //
|
||||||
|
|
||||||
|
if (type === "pengelolaanSampah") {
|
||||||
|
const data = await prisma.pengelolaanSampah.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "keteranganBankSampahTerdekat") {
|
||||||
|
const data = await prisma.keteranganBankSampahTerdekat.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ alamat: { contains: query, mode: "insensitive" } },
|
||||||
|
{ namaTempatMaps: { contains: query, mode: "insensitive" } },
|
||||||
|
{ linkPetunjukArah: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= PORGRAM PENGHIJAUAN ========================================= //
|
||||||
|
|
||||||
|
if (type === "programPenghijauan") {
|
||||||
|
const data = await prisma.programPenghijauan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= DATA LINGKUNGAN DESA ========================================= //
|
||||||
|
|
||||||
|
if (type === "dataLingkunganDesa") {
|
||||||
|
const data = await prisma.dataLingkunganDesa.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= GOTONG ROYONG ========================================= //
|
||||||
|
|
||||||
|
if (type === "gotongRoyong") {
|
||||||
|
const data = await prisma.kegiatanDesa.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsiSingkat: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsiLengkap: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= EDUKASI LINGKUNGAN ========================================= //
|
||||||
|
|
||||||
|
if (type === "tujuanEdukasiLingkungan") {
|
||||||
|
const data = await prisma.tujuanEdukasiLingkungan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "materiEdukasiLingkungan") {
|
||||||
|
const data = await prisma.materiEdukasiLingkungan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "contohEdukasiLingkungan") {
|
||||||
|
const data = await prisma.contohEdukasiLingkungan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= KONSERVASI ADAT BALI ========================================= //
|
||||||
|
|
||||||
|
if (type === "filosofiTriHita") {
|
||||||
|
const data = await prisma.filosofiTriHita.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "bentukKonservasiBerdasarkanAdat") {
|
||||||
|
const data = await prisma.bentukKonservasiBerdasarkanAdat.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "nilaiKonservasiAdat") {
|
||||||
|
const data = await prisma.nilaiKonservasiAdat.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= MENU PENDIDIKAN ========================================= //
|
||||||
|
// ========================================= INFO SEKOLAH & PAUD ========================================= //
|
||||||
|
|
||||||
|
if (type === "jenjangPendidikan") {
|
||||||
|
const data = await prisma.jenjangPendidikan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ nama: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "lembaga") {
|
||||||
|
const data = await prisma.lembaga.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ nama: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "siswa") {
|
||||||
|
const data = await prisma.siswa.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ nama: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "pengajar") {
|
||||||
|
const data = await prisma.pengajar.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ nama: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= BEASISWA DESA ========================================= //
|
||||||
|
if (type === "keunggulanProgram") {
|
||||||
|
const data = await prisma.keunggulanProgram.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= PROGRAM PENDIDIKAN ANAK ========================================= //
|
||||||
|
if (type === "tujuanProgram") {
|
||||||
|
const data = await prisma.tujuanProgram.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "programUnggulan") {
|
||||||
|
const data = await prisma.programUnggulan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "lokasiJadwalBimbinganBelajarDesa") {
|
||||||
|
const data = await prisma.lokasiJadwalBimbinganBelajarDesa.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "fasilitasBimbinganBelajarDesa") {
|
||||||
|
const data = await prisma.fasilitasBimbinganBelajarDesa.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= PENDIDIKAN NON FORMAL ========================================= //
|
||||||
|
if (type === "tujuanPendidikanNonFormal") {
|
||||||
|
const data = await prisma.tujuanPendidikanNonFormal.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "tempatKegiatan") {
|
||||||
|
const data = await prisma.tempatKegiatan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "jenisProgramYangDiselenggarakan") {
|
||||||
|
const data = await prisma.jenisProgramYangDiselenggarakan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= PERPUSTAKAAN ========================================= //
|
||||||
|
if (type === "dataPerpustakaan") {
|
||||||
|
const data = await prisma.dataPerpustakaan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================= DATA PENDIDIKAN ========================================= //
|
||||||
|
if (type === "dataPendidikan") {
|
||||||
|
const data = await prisma.dataPendidikan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ jumlah: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip,
|
||||||
|
take: limitNum,
|
||||||
|
});
|
||||||
|
return { data, nextPage: data.length < limitNum ? null : pageNum + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
// 🌍 GLOBAL SEARCH — cari di beberapa modul sekaligus
|
// 🌍 GLOBAL SEARCH — cari di beberapa modul sekaligus
|
||||||
const [
|
const [
|
||||||
pejabatdesa,
|
pejabatdesa,
|
||||||
@@ -760,6 +1211,37 @@ export default async function searchFindMany(context: Context) {
|
|||||||
programKemiskinan,
|
programKemiskinan,
|
||||||
sektorUnggulanDesa,
|
sektorUnggulanDesa,
|
||||||
demografiPekerjaan,
|
demografiPekerjaan,
|
||||||
|
desaDigital,
|
||||||
|
programKreatif,
|
||||||
|
kolaborasiInovasi,
|
||||||
|
mitraKolaborasi,
|
||||||
|
infoTekno,
|
||||||
|
pengelolaanSampah,
|
||||||
|
keteranganBankSampahTerdekat,
|
||||||
|
programPenghijauan,
|
||||||
|
dataLingkunganDesa,
|
||||||
|
gotongRoyong,
|
||||||
|
tujuanEdukasiLingkungan,
|
||||||
|
materiEdukasiLingkungan,
|
||||||
|
contohEdukasiLingkungan,
|
||||||
|
filosofiTriHita,
|
||||||
|
bentukKonservasiBerdasarkanAdat,
|
||||||
|
nilaiKonservasiAdat,
|
||||||
|
jenjangPendidikan,
|
||||||
|
lembaga,
|
||||||
|
siswa,
|
||||||
|
pengajar,
|
||||||
|
keunggulanProgram,
|
||||||
|
tujuanProgram,
|
||||||
|
programUnggulan,
|
||||||
|
lokasiJadwalBimbinganBelajarDesa,
|
||||||
|
fasilitasBimbinganBelajarDesa,
|
||||||
|
tujuanPendidikanNonFormal,
|
||||||
|
tempatKegiatan,
|
||||||
|
jenisProgramYangDiselenggarakan,
|
||||||
|
dataPerpustakaan,
|
||||||
|
dataPendidikan
|
||||||
|
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
prisma.pejabatDesa.findMany({
|
prisma.pejabatDesa.findMany({
|
||||||
where: { name: { contains: query, mode: "insensitive" } },
|
where: { name: { contains: query, mode: "insensitive" } },
|
||||||
@@ -1097,6 +1579,277 @@ export default async function searchFindMany(context: Context) {
|
|||||||
pekerjaan: { contains: query, mode: "insensitive" }
|
pekerjaan: { contains: query, mode: "insensitive" }
|
||||||
},
|
},
|
||||||
take: limitNum,
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.desaDigital.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.programKreatif.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ slug: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.kolaborasiInovasi.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ slug: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } },
|
||||||
|
{ kolaborator: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.mitraKolaborasi.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.infoTekno.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.pengelolaanSampah.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.keteranganBankSampahTerdekat.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ alamat: { contains: query, mode: "insensitive" } },
|
||||||
|
{ namaTempatMaps: { contains: query, mode: "insensitive" } },
|
||||||
|
{ linkPetunjukArah: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.programPenghijauan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.dataLingkunganDesa.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.kegiatanDesa.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsiSingkat: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsiLengkap: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.tujuanEdukasiLingkungan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.materiEdukasiLingkungan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.contohEdukasiLingkungan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.filosofiTriHita.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.bentukKonservasiBerdasarkanAdat.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.nilaiKonservasiAdat.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.jenjangPendidikan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ nama: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.lembaga.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ nama: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.siswa.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ nama: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.pengajar.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ nama: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.keunggulanProgram.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.tujuanProgram.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.programUnggulan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.lokasiJadwalBimbinganBelajarDesa.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.fasilitasBimbinganBelajarDesa.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.tujuanPendidikanNonFormal.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.tempatKegiatan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.jenisProgramYangDiselenggarakan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.dataPerpustakaan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ judul: { contains: query, mode: "insensitive" } },
|
||||||
|
{ deskripsi: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
|
}),
|
||||||
|
prisma.dataPendidikan.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ name: { contains: query, mode: "insensitive" } },
|
||||||
|
{ jumlah: { contains: query, mode: "insensitive" } }
|
||||||
|
],
|
||||||
|
},
|
||||||
|
take: limitNum,
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -1180,6 +1933,36 @@ export default async function searchFindMany(context: Context) {
|
|||||||
...programKemiskinan.map((b) => ({ type: "programKemiskinan", ...b })),
|
...programKemiskinan.map((b) => ({ type: "programKemiskinan", ...b })),
|
||||||
...sektorUnggulanDesa.map((b) => ({ type: "sektorUnggulanDesa", ...b })),
|
...sektorUnggulanDesa.map((b) => ({ type: "sektorUnggulanDesa", ...b })),
|
||||||
...demografiPekerjaan.map((b) => ({ type: "demografiPekerjaan", ...b })),
|
...demografiPekerjaan.map((b) => ({ type: "demografiPekerjaan", ...b })),
|
||||||
|
...desaDigital.map((b) => ({ type: "desaDigital", ...b })),
|
||||||
|
...programKreatif.map((b) => ({ type: "programKreatif", ...b })),
|
||||||
|
...kolaborasiInovasi.map((b) => ({ type: "kolaborasiInovasi", ...b })),
|
||||||
|
...mitraKolaborasi.map((b) => ({ type: "mitraKolaborasi", ...b })),
|
||||||
|
...infoTekno.map((b) => ({ type: "infoTekno", ...b })),
|
||||||
|
...pengelolaanSampah.map((b) => ({ type: "pengelolaanSampah", ...b })),
|
||||||
|
...keteranganBankSampahTerdekat.map((b) => ({ type: "keteranganBankSampahTerdekat", ...b })),
|
||||||
|
...programPenghijauan.map((b) => ({ type: "programPenghijauan", ...b })),
|
||||||
|
...dataLingkunganDesa.map((b) => ({ type: "dataLingkunganDesa", ...b })),
|
||||||
|
...gotongRoyong.map((b) => ({ type: "gotongRoyong", ...b })),
|
||||||
|
...tujuanEdukasiLingkungan.map((b) => ({ type: "tujuanEdukasiLingkungan", ...b })),
|
||||||
|
...materiEdukasiLingkungan.map((b) => ({ type: "materiEdukasiLingkungan", ...b })),
|
||||||
|
...contohEdukasiLingkungan.map((b) => ({ type: "contohEdukasiLingkungan", ...b })),
|
||||||
|
...filosofiTriHita.map((b) => ({ type: "filosofiTriHita", ...b })),
|
||||||
|
...bentukKonservasiBerdasarkanAdat.map((b) => ({ type: "bentukKonservasiBerdasarkanAdat", ...b })),
|
||||||
|
...nilaiKonservasiAdat.map((b) => ({ type: "nilaiKonservasiAdat", ...b })),
|
||||||
|
...jenjangPendidikan.map((b) => ({ type: "jenjangPendidikan", ...b })),
|
||||||
|
...lembaga.map((b) => ({ type: "lembaga", ...b })),
|
||||||
|
...siswa.map((b) => ({ type: "siswa", ...b })),
|
||||||
|
...pengajar.map((b) => ({ type: "pengajar", ...b })),
|
||||||
|
...keunggulanProgram.map((b) => ({ type: "keunggulanProgram", ...b })),
|
||||||
|
...tujuanProgram.map((b) => ({ type: "tujuanProgram", ...b })),
|
||||||
|
...programUnggulan.map((b) => ({ type: "programUnggulan", ...b })),
|
||||||
|
...tujuanPendidikanNonFormal.map((b) => ({ type: "tujuanPendidikanNonFormal", ...b })),
|
||||||
|
...fasilitasBimbinganBelajarDesa.map((b) => ({ type: "fasilitasBimbinganBelajarDesa", ...b })),
|
||||||
|
...lokasiJadwalBimbinganBelajarDesa.map((b) => ({ type: "lokasiJadwalBimbinganBelajarDesa", ...b })),
|
||||||
|
...tempatKegiatan.map((b) => ({ type: "tempatKegiatan", ...b })),
|
||||||
|
...jenisProgramYangDiselenggarakan.map((b) => ({ type: "jenisProgramYangDiselenggarakan", ...b })),
|
||||||
|
...dataPerpustakaan.map((b) => ({ type: "dataPerpustakaan", ...b })),
|
||||||
|
...dataPendidikan.map((b) => ({ type: "dataPendidikan", ...b })),
|
||||||
|
|
||||||
],
|
],
|
||||||
nextPage: null, // bisa dibuat lebih kompleks kalau perlu
|
nextPage: null, // bisa dibuat lebih kompleks kalau perlu
|
||||||
|
|||||||
@@ -28,6 +28,32 @@ function Page() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add this check before the return statement
|
||||||
|
if (data.length === 0) {
|
||||||
|
return (
|
||||||
|
<Stack pos="relative" bg={colors.Bg} py="xl" gap={22}>
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<BackButton />
|
||||||
|
<Text fz={{ base: 'h1', md: '2.5rem' }} c={colors['blue-button']} fw="bold">
|
||||||
|
Sektor Unggulan Desa Darmasaba
|
||||||
|
</Text>
|
||||||
|
<Text c="dimmed" mt="md">
|
||||||
|
Data sektor unggulan belum tersedia
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const chartData = data
|
||||||
|
.filter(item => item?.name && typeof item.value === 'number')
|
||||||
|
.map((item) => ({
|
||||||
|
id: item.id,
|
||||||
|
sektor: item.name,
|
||||||
|
Ton: item.value,
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
<Stack pos={"relative"} bg={colors.Bg} py={"xl"} gap={"22"}>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
@@ -54,11 +80,7 @@ function Page() {
|
|||||||
<BarChart
|
<BarChart
|
||||||
p={10}
|
p={10}
|
||||||
h={300}
|
h={300}
|
||||||
data={data.map((item) => ({
|
data={chartData}
|
||||||
id: item.id,
|
|
||||||
sektor: item.name,
|
|
||||||
Ton: item.value,
|
|
||||||
}))}
|
|
||||||
dataKey="sektor"
|
dataKey="sektor"
|
||||||
series={[
|
series={[
|
||||||
{ name: 'Ton', color: colors['blue-button'] },
|
{ name: 'Ton', color: colors['blue-button'] },
|
||||||
|
|||||||
@@ -41,6 +41,37 @@ function Page() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
|
return (
|
||||||
|
<Stack pos="relative" bg={colors.Bg} py="xl" gap={22}>
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<Text fz={{ base: 'h1', md: '2.5rem' }} c={colors['blue-button']} fw="bold">
|
||||||
|
Pencegahan Kriminalitas
|
||||||
|
</Text>
|
||||||
|
<Text c={colors['blue-button']} fz={{ base: 'h4', md: 'h3' }}>
|
||||||
|
Keamanan Komunitas & Pencegahan Kriminal
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
<SimpleGrid
|
||||||
|
px={{ base: 20, md: 100 }}
|
||||||
|
cols={{ base: 1, md: 2 }}
|
||||||
|
spacing="xl"
|
||||||
|
>
|
||||||
|
<Paper p="xl" radius="xl" shadow="lg" >
|
||||||
|
<Text fz={{ base: 'h3', md: 'h2' }} c={colors['blue-button']} fw="bold">
|
||||||
|
Program Keamanan Berjalan
|
||||||
|
</Text>
|
||||||
|
<Stack pt={30} gap="lg">
|
||||||
|
<Text c="dimmed">
|
||||||
|
Tidak ada data pencegahan kriminalitas yang cocok
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</Paper>
|
||||||
|
</SimpleGrid>
|
||||||
|
</Stack>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack pos="relative" bg={colors.Bg} py="xl" gap={22}>
|
<Stack pos="relative" bg={colors.Bg} py="xl" gap={22}>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
|||||||
@@ -101,15 +101,30 @@ function Page() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack align="center" gap="sm">
|
<Stack align="center" gap="sm">
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
aspectRatio: '16/9',
|
||||||
|
borderRadius: '12px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Image
|
<Image
|
||||||
src={v.image.link}
|
src={v.image.link}
|
||||||
alt={v.name}
|
alt={v.name}
|
||||||
w={140}
|
fit="cover"
|
||||||
h={140}
|
|
||||||
fit="contain"
|
|
||||||
radius="md"
|
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
transition: 'transform 0.4s ease',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => (e.currentTarget.style.transform = 'scale(1.05)')}
|
||||||
|
onMouseLeave={(e) => (e.currentTarget.style.transform = 'scale(1)')}
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Text ta="center" fw={700} fz="lg" c={colors['blue-button']}>
|
<Text ta="center" fw={700} fz="lg" c={colors['blue-button']}>
|
||||||
{v.name}
|
{v.name}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
import penangananDarurat from '@/app/admin/(dashboard)/_state/kesehatan/penanganan-darurat/penangananDarurat'
|
import penangananDarurat from '@/app/admin/(dashboard)/_state/kesehatan/penanganan-darurat/penangananDarurat'
|
||||||
import colors from '@/con/colors'
|
import colors from '@/con/colors'
|
||||||
import {
|
import {
|
||||||
Badge,
|
|
||||||
Box,
|
Box,
|
||||||
Center,
|
Center,
|
||||||
Grid,
|
Grid,
|
||||||
@@ -106,15 +105,30 @@ function Page() {
|
|||||||
>
|
>
|
||||||
<Stack align="center" gap="md">
|
<Stack align="center" gap="md">
|
||||||
<Center>
|
<Center>
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
aspectRatio: '16/9',
|
||||||
|
borderRadius: '12px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Image
|
<Image
|
||||||
src={v.image.link}
|
src={v.image.link}
|
||||||
alt={v.name}
|
alt={v.name}
|
||||||
h={180}
|
|
||||||
w="100%"
|
|
||||||
radius="md"
|
|
||||||
fit="cover"
|
fit="cover"
|
||||||
style={{ aspectRatio: '4/3' }}
|
loading="lazy"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
transition: 'transform 0.4s ease',
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => (e.currentTarget.style.transform = 'scale(1.05)')}
|
||||||
|
onMouseLeave={(e) => (e.currentTarget.style.transform = 'scale(1)')}
|
||||||
/>
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
</Center>
|
</Center>
|
||||||
<Stack gap={4} w="100%">
|
<Stack gap={4} w="100%">
|
||||||
<Text
|
<Text
|
||||||
@@ -136,9 +150,6 @@ function Page() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
<Badge radius="md" color="blue" variant="light" mt="sm">
|
|
||||||
Darurat
|
|
||||||
</Badge>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -7,16 +7,18 @@ import { IconCalendar, IconInfoCircle, IconPhone, IconSearch } from "@tabler/ico
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useProxy } from "valtio/utils";
|
import { useProxy } from "valtio/utils";
|
||||||
import BackButton from "../../desa/layanan/_com/BackButto";
|
import BackButton from "../../desa/layanan/_com/BackButto";
|
||||||
|
import { useDebouncedValue } from "@mantine/hooks";
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const state = useProxy(posyandustate);
|
const state = useProxy(posyandustate);
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
const [debouncedSearch] = useDebouncedValue(search, 500); // 500ms delay
|
||||||
|
|
||||||
const { data, page, totalPages, loading, load } = state.findMany;
|
const { data, page, totalPages, loading, load } = state.findMany;
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
load(page, 6, search);
|
load(page, 6, debouncedSearch);
|
||||||
}, [page, search]);
|
}, [page, debouncedSearch]);
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -78,9 +78,7 @@ function Page() {
|
|||||||
<Tooltip label={materi.data?.judul} position="top" withArrow>
|
<Tooltip label={materi.data?.judul} position="top" withArrow>
|
||||||
<Stack gap={4} align="center">
|
<Stack gap={4} align="center">
|
||||||
<IconRecycle size={28} color={colors['blue-button']} />
|
<IconRecycle size={28} color={colors['blue-button']} />
|
||||||
<Text fz="h3" fw="bold" c={colors['blue-button']} ta="center">
|
<Text fz="h3" fw="bold" c={colors['blue-button']} ta="center" dangerouslySetInnerHTML={{ __html: materi.data?.judul || '' }} />
|
||||||
{materi.data?.judul}
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import stateStrukturPPID from '@/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID'
|
import stateStrukturPPID from '@/app/admin/(dashboard)/_state/ppid/struktur_ppid/struktur_PPID'
|
||||||
import colors from '@/con/colors'
|
import colors from '@/con/colors'
|
||||||
import {
|
import {
|
||||||
@@ -15,17 +16,27 @@ import {
|
|||||||
Paper,
|
Paper,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
|
TextInput,
|
||||||
Title,
|
Title,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Transition,
|
Transition,
|
||||||
} from '@mantine/core'
|
} from '@mantine/core'
|
||||||
import { IconRefresh, IconSearch, IconUsers } from '@tabler/icons-react'
|
import {
|
||||||
|
IconRefresh,
|
||||||
|
IconSearch,
|
||||||
|
IconUsers,
|
||||||
|
IconZoomIn,
|
||||||
|
IconZoomOut,
|
||||||
|
IconArrowsMaximize,
|
||||||
|
IconArrowsMinimize,
|
||||||
|
} from '@tabler/icons-react'
|
||||||
import { OrganizationChart } from 'primereact/organizationchart'
|
import { OrganizationChart } from 'primereact/organizationchart'
|
||||||
import { useEffect } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useProxy } from 'valtio/utils'
|
import { useProxy } from 'valtio/utils'
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto'
|
import BackButton from '../../desa/layanan/_com/BackButto'
|
||||||
import { useTransitionRouter } from 'next-view-transitions'
|
import { useTransitionRouter } from 'next-view-transitions'
|
||||||
import ScrollToTopButton from '@/app/darmasaba/_com/scrollToTopButton'
|
import ScrollToTopButton from '@/app/darmasaba/_com/scrollToTopButton'
|
||||||
|
import { debounce } from 'lodash'
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
@@ -47,7 +58,6 @@ export default function Page() {
|
|||||||
ta="center"
|
ta="center"
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
fz={{ base: 28, md: 36, lg: 44 }}
|
fz={{ base: 28, md: 36, lg: 44 }}
|
||||||
|
|
||||||
>
|
>
|
||||||
Struktur Organisasi PPID
|
Struktur Organisasi PPID
|
||||||
</Title>
|
</Title>
|
||||||
@@ -70,14 +80,24 @@ export default function Page() {
|
|||||||
function StrukturOrganisasiPPID() {
|
function StrukturOrganisasiPPID() {
|
||||||
const stateOrganisasi: any = useProxy(stateStrukturPPID.pegawai)
|
const stateOrganisasi: any = useProxy(stateStrukturPPID.pegawai)
|
||||||
const router = useTransitionRouter()
|
const router = useTransitionRouter()
|
||||||
|
const chartContainerRef = useRef<HTMLDivElement>(null)
|
||||||
|
const [scale, setScale] = useState(1)
|
||||||
|
const [isFullscreen, setFullscreen] = useState(false)
|
||||||
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
|
|
||||||
|
// debounce untuk pencarian
|
||||||
|
const debouncedSearch = useRef(
|
||||||
|
debounce((value: string) => {
|
||||||
|
setSearchQuery(value)
|
||||||
|
}, 400)
|
||||||
|
).current
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void stateOrganisasi.findMany.load()
|
void stateOrganisasi.findMany.load()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const isLoading =
|
const isLoading =
|
||||||
!stateOrganisasi.findMany.data &&
|
!stateOrganisasi.findMany.data && stateOrganisasi.findMany.loading !== false
|
||||||
stateOrganisasi.findMany.loading !== false
|
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
@@ -93,10 +113,7 @@ function StrukturOrganisasiPPID() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (!stateOrganisasi.findMany.data || stateOrganisasi.findMany.data.length === 0) {
|
||||||
!stateOrganisasi.findMany.data ||
|
|
||||||
stateOrganisasi.findMany.data.length === 0
|
|
||||||
) {
|
|
||||||
return (
|
return (
|
||||||
<Center py={40}>
|
<Center py={40}>
|
||||||
<Stack align="center" gap="md">
|
<Stack align="center" gap="md">
|
||||||
@@ -117,8 +134,7 @@ function StrukturOrganisasiPPID() {
|
|||||||
Data pegawai belum tersedia
|
Data pegawai belum tersedia
|
||||||
</Title>
|
</Title>
|
||||||
<Text c="dimmed" mt="xs">
|
<Text c="dimmed" mt="xs">
|
||||||
Belum ada data pegawai yang tercatat untuk PPID. Silakan coba
|
Belum ada data pegawai yang tercatat untuk PPID.
|
||||||
muat ulang atau periksa sumber data.
|
|
||||||
</Text>
|
</Text>
|
||||||
<Group justify="center" mt="lg">
|
<Group justify="center" mt="lg">
|
||||||
<Button
|
<Button
|
||||||
@@ -129,15 +145,6 @@ function StrukturOrganisasiPPID() {
|
|||||||
>
|
>
|
||||||
Muat Ulang
|
Muat Ulang
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
leftSection={<IconSearch size={16} />}
|
|
||||||
variant="subtle"
|
|
||||||
onClick={() =>
|
|
||||||
stateOrganisasi.findMany.load({ query: { q: '' } })
|
|
||||||
}
|
|
||||||
>
|
|
||||||
Cari Pegawai
|
|
||||||
</Button>
|
|
||||||
</Group>
|
</Group>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Stack>
|
</Stack>
|
||||||
@@ -145,44 +152,39 @@ function StrukturOrganisasiPPID() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Buat struktur organisasi
|
||||||
const posisiMap = new Map<string, any>()
|
const posisiMap = new Map<string, any>()
|
||||||
|
const aktifPegawai = stateOrganisasi.findMany.data.filter((p: any) => p.isActive)
|
||||||
const aktifPegawai = stateOrganisasi.findMany.data.filter((p: any) => p.isActive);
|
|
||||||
|
|
||||||
for (const pegawai of aktifPegawai) {
|
for (const pegawai of aktifPegawai) {
|
||||||
const posisiId = pegawai.posisi.id;
|
const posisiId = pegawai.posisi.id
|
||||||
if (!posisiMap.has(posisiId)) {
|
if (!posisiMap.has(posisiId)) {
|
||||||
posisiMap.set(posisiId, {
|
posisiMap.set(posisiId, {
|
||||||
...pegawai.posisi,
|
...pegawai.posisi,
|
||||||
pegawaiList: [],
|
pegawaiList: [],
|
||||||
children: [],
|
children: [],
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
posisiMap.get(posisiId)!.pegawaiList.push(pegawai);
|
posisiMap.get(posisiId)!.pegawaiList.push(pegawai)
|
||||||
}
|
}
|
||||||
|
|
||||||
const root: any[] = []
|
const root: any[] = []
|
||||||
posisiMap.forEach((posisi) => {
|
posisiMap.forEach((posisi) => {
|
||||||
if (posisi.parentId) {
|
if (posisi.parentId) {
|
||||||
const parent = posisiMap.get(posisi.parentId)
|
const parent = posisiMap.get(posisi.parentId)
|
||||||
if (parent) {
|
if (parent) parent.children.push(posisi)
|
||||||
parent.children.push(posisi)
|
else root.push(posisi)
|
||||||
} else {
|
} else root.push(posisi)
|
||||||
root.push(posisi)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
root.push(posisi)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
function toOrgChartFormat(node: any): any {
|
function toOrgChartFormat(node: any): any {
|
||||||
const pegawai = node.pegawaiList?.[0];
|
const pegawai = node.pegawaiList?.[0]
|
||||||
return {
|
return {
|
||||||
expanded: true,
|
expanded: true,
|
||||||
type: 'person',
|
type: 'person',
|
||||||
styleClass: 'p-person',
|
styleClass: 'p-person',
|
||||||
data: {
|
data: {
|
||||||
id: pegawai?.id || null, // tambahin ini bro
|
id: pegawai?.id || null,
|
||||||
name: pegawai?.namaLengkap || 'Belum ditugaskan',
|
name: pegawai?.namaLengkap || 'Belum ditugaskan',
|
||||||
title: node.nama || 'Tanpa jabatan',
|
title: node.nama || 'Tanpa jabatan',
|
||||||
image: pegawai?.image?.link || '/img/default.png',
|
image: pegawai?.image?.link || '/img/default.png',
|
||||||
@@ -190,28 +192,90 @@ function StrukturOrganisasiPPID() {
|
|||||||
positionId: node.id || null,
|
positionId: node.id || null,
|
||||||
},
|
},
|
||||||
children: node.children?.map(toOrgChartFormat) || [],
|
children: node.children?.map(toOrgChartFormat) || [],
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const chartData = root.map(toOrgChartFormat)
|
let chartData = root.map(toOrgChartFormat)
|
||||||
|
|
||||||
|
// 🔍 filter by search
|
||||||
|
if (searchQuery) {
|
||||||
|
const filterNodes = (nodes: any[]): any[] =>
|
||||||
|
nodes
|
||||||
|
.map((n) => ({
|
||||||
|
...n,
|
||||||
|
children: filterNodes(n.children || []),
|
||||||
|
}))
|
||||||
|
.filter(
|
||||||
|
(n) =>
|
||||||
|
n.data.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
n.data.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
n.children.length > 0
|
||||||
|
)
|
||||||
|
chartData = filterNodes(chartData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🧭 fungsi fullscreen
|
||||||
|
const toggleFullscreen = () => {
|
||||||
|
if (!document.fullscreenElement) {
|
||||||
|
chartContainerRef.current?.requestFullscreen()
|
||||||
|
setFullscreen(true)
|
||||||
|
} else {
|
||||||
|
document.exitFullscreen()
|
||||||
|
setFullscreen(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🧭 fungsi zoom
|
||||||
|
const handleZoomIn = () => setScale((prev) => Math.min(prev + 0.1, 2))
|
||||||
|
const handleZoomOut = () => setScale((prev) => Math.max(prev - 0.1, 0.5))
|
||||||
|
const resetZoom = () => setScale(1)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box py={16} >
|
<Stack align="center" mt="xl">
|
||||||
<Paper
|
{/* 🔍 Search + Zoom + Fullscreen controls */}
|
||||||
radius="md"
|
<Group mb="md" justify="center" gap="sm">
|
||||||
p="md"
|
<TextInput
|
||||||
|
placeholder="Cari nama atau jabatan..."
|
||||||
|
leftSection={<IconSearch size={16} />}
|
||||||
|
onChange={(e) => debouncedSearch(e.target.value)}
|
||||||
|
/>
|
||||||
|
<Button variant="light" size="sm" onClick={handleZoomOut}>
|
||||||
|
<IconZoomOut size={16} />
|
||||||
|
</Button>
|
||||||
|
<Button variant="light" size="sm" onClick={resetZoom}>
|
||||||
|
100%
|
||||||
|
</Button>
|
||||||
|
<Button variant="light" size="sm" onClick={handleZoomIn}>
|
||||||
|
<IconZoomIn size={16} />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="light"
|
||||||
|
size="sm"
|
||||||
|
onClick={toggleFullscreen}
|
||||||
|
leftSection={
|
||||||
|
isFullscreen ? <IconArrowsMinimize size={16} /> : <IconArrowsMaximize size={16} />
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{isFullscreen ? 'Keluar' : 'Fullscreen'}
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{/* Chart Container */}
|
||||||
|
<Box
|
||||||
|
ref={chartContainerRef}
|
||||||
style={{
|
style={{
|
||||||
background: 'rgba(28,110,164,0.2)',
|
overflow: 'auto',
|
||||||
border: `1px solid rgba(255,255,255,0.1)`,
|
transform: `scale(${scale})`,
|
||||||
overflowX: 'auto',
|
transformOrigin: 'center top',
|
||||||
|
transition: 'transform 0.25s ease',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<OrganizationChart
|
<OrganizationChart
|
||||||
value={chartData}
|
value={chartData}
|
||||||
nodeTemplate={(node) => nodeTemplate(node, router)}
|
nodeTemplate={(node) => nodeTemplate(node, router)}
|
||||||
/>
|
/>
|
||||||
</Paper>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
</Stack>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +285,6 @@ function nodeTemplate(node: any, router: ReturnType<typeof useTransitionRouter>)
|
|||||||
const title = node?.data?.title || 'Tanpa Jabatan'
|
const title = node?.data?.title || 'Tanpa Jabatan'
|
||||||
const description = node?.data?.description || ''
|
const description = node?.data?.description || ''
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Transition mounted transition="pop" duration={240}>
|
<Transition mounted transition="pop" duration={240}>
|
||||||
{(styles) => (
|
{(styles) => (
|
||||||
@@ -244,15 +307,15 @@ function nodeTemplate(node: any, router: ReturnType<typeof useTransitionRouter>)
|
|||||||
src={imageSrc}
|
src={imageSrc}
|
||||||
alt={name}
|
alt={name}
|
||||||
radius="md"
|
radius="md"
|
||||||
width={120}
|
width={60}
|
||||||
height={120}
|
height={60}
|
||||||
fit="cover"
|
fit="cover"
|
||||||
style={{
|
style={{
|
||||||
objectFit: 'cover',
|
objectFit: 'cover',
|
||||||
border: '2px solid rgba(255,255,255,0.2)',
|
border: '2px solid rgba(255,255,255,0.2)',
|
||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
}}
|
}}
|
||||||
loading='lazy'
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<Text fw={700}>{name}</Text>
|
<Text fw={700}>{name}</Text>
|
||||||
<Text size="sm" c="dimmed" mt={4}>
|
<Text size="sm" c="dimmed" mt={4}>
|
||||||
|
|||||||
@@ -86,10 +86,15 @@ function NavbarMobile({ listNavbar }: { listNavbar: MenuItem[] }) {
|
|||||||
align="center"
|
align="center"
|
||||||
p="xs"
|
p="xs"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (item.href) {
|
||||||
router.push(item.href);
|
router.push(item.href);
|
||||||
stateNav.mobileOpen = false;
|
stateNav.mobileOpen = false;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
cursor: item.href ? "pointer" : "default",
|
||||||
|
opacity: item.href ? 1 : 0.8
|
||||||
}}
|
}}
|
||||||
style={{ cursor: "pointer" }}
|
|
||||||
>
|
>
|
||||||
<Text c="dark.9" fw={600} fz="md">
|
<Text c="dark.9" fw={600} fz="md">
|
||||||
{item.name}
|
{item.name}
|
||||||
|
|||||||
@@ -2,15 +2,14 @@
|
|||||||
|
|
||||||
import colors from "@/con/colors"
|
import colors from "@/con/colors"
|
||||||
import stateNav from "@/state/state-nav"
|
import stateNav from "@/state/state-nav"
|
||||||
import { ActionIcon, Button, Container, Flex, Image, Stack, Tooltip } from "@mantine/core"
|
import { ActionIcon, Button, Container, Flex, Image, Menu, MenuTarget, Stack, Tooltip } from "@mantine/core"
|
||||||
import { useHover } from "@mantine/hooks"
|
|
||||||
import { IconSearch, IconUser } from "@tabler/icons-react"
|
import { IconSearch, IconUser } from "@tabler/icons-react"
|
||||||
import { useTransitionRouter } from 'next-view-transitions'
|
import { useTransitionRouter } from 'next-view-transitions'
|
||||||
|
import { usePathname, useRouter } from "next/navigation"
|
||||||
import { useSnapshot } from "valtio"
|
import { useSnapshot } from "valtio"
|
||||||
import { MenuItem } from "../../../../types/menu-item"
|
import { MenuItem } from "../../../../types/menu-item"
|
||||||
import { NavbarSearch } from "./NavBarSearch"
|
import { NavbarSearch } from "./NavBarSearch"
|
||||||
import { NavbarSubMenu } from "./NavbarSubMenu"
|
import { NavbarSubMenu } from "./NavbarSubMenu"
|
||||||
import { useRouter } from "next/navigation"
|
|
||||||
|
|
||||||
// contoh state auth (dummy aja dulu, bisa diganti sesuai sistem auth kamu)
|
// contoh state auth (dummy aja dulu, bisa diganti sesuai sistem auth kamu)
|
||||||
const stateAuth = {
|
const stateAuth = {
|
||||||
@@ -21,6 +20,7 @@ export function NavbarMainMenu({ listNavbar }: { listNavbar: MenuItem[] }) {
|
|||||||
const { item, isSearch } = useSnapshot(stateNav)
|
const { item, isSearch } = useSnapshot(stateNav)
|
||||||
const router = useTransitionRouter()
|
const router = useTransitionRouter()
|
||||||
const next = useRouter()
|
const next = useRouter()
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap={0} visibleFrom="sm" bg={colors["white-trans-1"]}>
|
<Stack gap={0} visibleFrom="sm" bg={colors["white-trans-1"]}>
|
||||||
@@ -47,7 +47,12 @@ export function NavbarMainMenu({ listNavbar }: { listNavbar: MenuItem[] }) {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
{listNavbar.map((item, k) => (
|
{listNavbar.map((item, k) => (
|
||||||
<MenuItemCom key={k} item={item} />
|
<MenuItemCom
|
||||||
|
key={k}
|
||||||
|
item={item}
|
||||||
|
isActive={pathname === item.href ||
|
||||||
|
(item.children?.some(child => child.href === pathname))}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<Tooltip label="Search content" position="bottom" withArrow>
|
<Tooltip label="Search content" position="bottom" withArrow>
|
||||||
@@ -88,27 +93,45 @@ export function NavbarMainMenu({ listNavbar }: { listNavbar: MenuItem[] }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function MenuItemCom({ item }: { item: MenuItem }) {
|
function MenuItemCom({ item, isActive = false }: { item: MenuItem, isActive?: boolean }) {
|
||||||
const { ref, hovered } = useHover()
|
|
||||||
const router = useTransitionRouter()
|
const router = useTransitionRouter()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Menu
|
||||||
|
trigger="hover"
|
||||||
|
position="bottom-start"
|
||||||
|
offset={20}
|
||||||
|
width={300}
|
||||||
|
shadow="md"
|
||||||
|
withinPortal
|
||||||
|
onOpen={() => {
|
||||||
|
stateNav.item = item.children || null;
|
||||||
|
stateNav.isSearch = false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuTarget>
|
||||||
<Button
|
<Button
|
||||||
ref={ref}
|
|
||||||
color={hovered ? "gray" : colors["blue-button"]}
|
|
||||||
onMouseEnter={() => {
|
|
||||||
stateNav.item = item.children || null
|
|
||||||
stateNav.isSearch = false
|
|
||||||
}}
|
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
radius="xl"
|
color={isActive ? 'blue' : 'gray'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
router.push(item.href)
|
if (item.href) {
|
||||||
stateNav.clear()
|
router.push(item.href);
|
||||||
|
stateNav.clear();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
styles={{
|
||||||
|
root: {
|
||||||
|
fontWeight: isActive ? 600 : 400,
|
||||||
|
borderBottom: isActive ? `2px solid ${colors['blue-button']}` : 'none',
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
fw={500}
|
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</Button>
|
</Button>
|
||||||
|
</MenuTarget>
|
||||||
|
</Menu>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ import { IconArrowRight } from "@tabler/icons-react";
|
|||||||
import { MenuItem } from "../../../../types/menu-item";
|
import { MenuItem } from "../../../../types/menu-item";
|
||||||
import { useTransitionRouter } from "next-view-transitions";
|
import { useTransitionRouter } from "next-view-transitions";
|
||||||
import colors from "@/con/colors";
|
import colors from "@/con/colors";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
|
||||||
export function NavbarSubMenu({ item }: { item: MenuItem[] | null }) {
|
export function NavbarSubMenu({ item }: { item: MenuItem[] | null }) {
|
||||||
const router = useTransitionRouter();
|
const router = useTransitionRouter();
|
||||||
|
const pathname = usePathname();
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={Math.random().toString(36).slice(2)}
|
key={Math.random().toString(36).slice(2)}
|
||||||
@@ -37,23 +38,24 @@ export function NavbarSubMenu({ item }: { item: MenuItem[] | null }) {
|
|||||||
justify="space-between"
|
justify="space-between"
|
||||||
size="lg"
|
size="lg"
|
||||||
radius="md"
|
radius="md"
|
||||||
color="gray.0"
|
color={pathname === link.href ? 'blue' : 'gray'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (link.href) {
|
||||||
router.push(link.href);
|
router.push(link.href);
|
||||||
stateNav.item = null;
|
stateNav.item = null;
|
||||||
stateNav.isSearch = false;
|
stateNav.isSearch = false;
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
rightSection={<IconArrowRight size={18} />}
|
rightSection={<IconArrowRight size={18} />}
|
||||||
styles={(theme) => ({
|
styles={(theme) => ({
|
||||||
root: {
|
root: {
|
||||||
background: "transparent",
|
background: pathname === link.href ? theme.colors.blue[0] : 'transparent',
|
||||||
color: colors['blue-button'],
|
color: pathname === link.href ? theme.colors.blue[7] : colors['blue-button'],
|
||||||
fontWeight: 500,
|
fontWeight: pathname === link.href ? 600 : 500,
|
||||||
transition: "all 0.2s ease",
|
transition: "all 0.2s ease",
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
background: theme.colors.gray[8],
|
background: pathname === link.href ? theme.colors.blue[1] : theme.colors.gray[0],
|
||||||
boxShadow: `0 0 12px ${theme.colors.blue[6]}55`,
|
}
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import searchState, { debouncedFetch } from '@/app/api/[[...slugs]]/_lib/search/searchState';
|
import searchState, { debouncedFetch } from '@/app/api/[[...slugs]]/_lib/search/searchState';
|
||||||
import { Box, Center, Loader, Stack, Text, TextInput } from '@mantine/core';
|
import { Box, Center, Loader, Modal, Text, TextInput } from '@mantine/core';
|
||||||
import { IconX } from '@tabler/icons-react';
|
import { IconX } from '@tabler/icons-react';
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useSnapshot } from 'valtio';
|
import { useSnapshot } from 'valtio';
|
||||||
import getDetailUrl from './searchUrl';
|
import getDetailUrl from './searchUrl';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function GlobalSearch() {
|
export default function GlobalSearch() {
|
||||||
const snap = useSnapshot(searchState);
|
const snap = useSnapshot(searchState);
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
|
// Toggle modal when there's a query
|
||||||
|
useEffect(() => {
|
||||||
|
setIsOpen(!!snap.query);
|
||||||
|
}, [snap.query]);
|
||||||
|
|
||||||
// Infinite scroll
|
// Infinite scroll
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
const bottom =
|
const bottom = window.innerHeight + window.scrollY >= document.body.offsetHeight - 200;
|
||||||
window.innerHeight + window.scrollY >= document.body.offsetHeight - 200;
|
|
||||||
if (bottom && !snap.loading) searchState.next();
|
if (bottom && !snap.loading) searchState.next();
|
||||||
};
|
};
|
||||||
window.addEventListener('scroll', handleScroll);
|
window.addEventListener('scroll', handleScroll);
|
||||||
@@ -23,15 +26,14 @@ export default function GlobalSearch() {
|
|||||||
}, [snap.loading]);
|
}, [snap.loading]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack maw={800} mx="auto">
|
<Box style={{ position: 'relative', width: '100%' }}>
|
||||||
{/* 🔍 Search input */}
|
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Cari apapun..."
|
placeholder="Cari apapun..."
|
||||||
value={snap.query}
|
value={snap.query}
|
||||||
onChange={(e) => (
|
onChange={(e) => {
|
||||||
searchState.query = e.currentTarget.value,
|
searchState.query = e.currentTarget.value;
|
||||||
debouncedFetch()
|
debouncedFetch();
|
||||||
)}
|
}}
|
||||||
radius="xl"
|
radius="xl"
|
||||||
rightSection={
|
rightSection={
|
||||||
snap.query ? (
|
snap.query ? (
|
||||||
@@ -47,8 +49,29 @@ export default function GlobalSearch() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* 📄 Hasil pencarian */}
|
{/* Modal for search results */}
|
||||||
<div style={{ maxHeight: '400px', overflowY: 'auto' }}>
|
<Modal
|
||||||
|
opened={isOpen && !!snap.query}
|
||||||
|
onClose={() => {
|
||||||
|
searchState.query = '';
|
||||||
|
searchState.results = [];
|
||||||
|
}}
|
||||||
|
withCloseButton={false}
|
||||||
|
size="lg"
|
||||||
|
padding={0}
|
||||||
|
radius="md"
|
||||||
|
style={{ position: 'absolute', top: '100%', left: 0, right: 0, zIndex: 1000 }}
|
||||||
|
styles={{
|
||||||
|
content: { // Changed from 'modal' to 'content'
|
||||||
|
backgroundColor: 'white',
|
||||||
|
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
|
||||||
|
borderRadius: '0.5rem',
|
||||||
|
maxHeight: '400px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box style={{ maxHeight: '400px', overflowY: 'auto' }}>
|
||||||
{snap.results.map((item, i) => (
|
{snap.results.map((item, i) => (
|
||||||
<Box
|
<Box
|
||||||
key={i}
|
key={i}
|
||||||
@@ -60,7 +83,7 @@ export default function GlobalSearch() {
|
|||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
textOverflow: 'ellipsis',
|
textOverflow: 'ellipsis',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
maxWidth: '100%'
|
maxWidth: '100%',
|
||||||
}}
|
}}
|
||||||
onMouseEnter={(e) => (e.currentTarget.style.background = '#f5f5f5')}
|
onMouseEnter={(e) => (e.currentTarget.style.background = '#f5f5f5')}
|
||||||
onMouseLeave={(e) => (e.currentTarget.style.background = 'transparent')}
|
onMouseLeave={(e) => (e.currentTarget.style.background = 'transparent')}
|
||||||
@@ -77,15 +100,13 @@ export default function GlobalSearch() {
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* ⏳ Loader di bawah hasil */}
|
|
||||||
{snap.loading && (
|
{snap.loading && (
|
||||||
<Center py="md">
|
<Center py="md">
|
||||||
<Loader size="sm" />
|
<Loader size="sm" />
|
||||||
</Center>
|
</Center>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Box>
|
||||||
|
</Modal>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ function Kepuasan() {
|
|||||||
<TextInput
|
<TextInput
|
||||||
label="Nama"
|
label="Nama"
|
||||||
type='text'
|
type='text'
|
||||||
placeholder="masukkan nama"
|
placeholder="Masukkan nama"
|
||||||
defaultValue={state.create.form.name}
|
defaultValue={state.create.form.name}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
state.create.form.name = val.currentTarget.value;
|
state.create.form.name = val.currentTarget.value;
|
||||||
|
|||||||
@@ -6,20 +6,19 @@ import {
|
|||||||
Center,
|
Center,
|
||||||
Image,
|
Image,
|
||||||
Paper,
|
Paper,
|
||||||
|
ScrollArea,
|
||||||
SimpleGrid,
|
SimpleGrid,
|
||||||
|
Skeleton,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
Tooltip,
|
useMantineColorScheme
|
||||||
Skeleton,
|
|
||||||
useMantineColorScheme,
|
|
||||||
ScrollArea,
|
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useShallowEffect } from "@mantine/hooks";
|
import { useShallowEffect } from "@mantine/hooks";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
|
import { IconPhotoOff } from "@tabler/icons-react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useTransitionRouter } from "next-view-transitions";
|
import { useTransitionRouter } from "next-view-transitions";
|
||||||
import { useProxy } from "valtio/utils";
|
import { useProxy } from "valtio/utils";
|
||||||
import { Prisma } from "@prisma/client";
|
|
||||||
import { IconPhotoOff } from "@tabler/icons-react";
|
|
||||||
|
|
||||||
type ProgramInovasiItem = Prisma.ProgramInovasiGetPayload<{ include: { image: true } }>;
|
type ProgramInovasiItem = Prisma.ProgramInovasiGetPayload<{ include: { image: true } }>;
|
||||||
|
|
||||||
@@ -30,7 +29,6 @@ function ModuleItem({ data }: { data: ProgramInovasiItem }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div whileHover={{ scale: 1.03 }}>
|
<motion.div whileHover={{ scale: 1.03 }}>
|
||||||
<Tooltip label={`Lihat ${data.name}`} withArrow>
|
|
||||||
<Paper
|
<Paper
|
||||||
onClick={() => router.push(`/darmasaba/program-inovasi/${data.id}`)}
|
onClick={() => router.push(`/darmasaba/program-inovasi/${data.id}`)}
|
||||||
p="lg"
|
p="lg"
|
||||||
@@ -67,7 +65,6 @@ function ModuleItem({ data }: { data: ProgramInovasiItem }) {
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Tooltip>
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,9 +37,13 @@ export default function ProfileView({ data }: ProfileViewProps) {
|
|||||||
<Image
|
<Image
|
||||||
src={data.image.link}
|
src={data.image.link}
|
||||||
alt={data.name || 'Foto profil'}
|
alt={data.name || 'Foto profil'}
|
||||||
fit="cover"
|
fit="contain"
|
||||||
radius="lg"
|
radius="lg"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
|
style={{
|
||||||
|
objectPosition: 'bottom center',
|
||||||
|
transform: 'translateY(10px)', // sedikit turun biar natural
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Stack align="center" gap="xs" w="100%" py="xl">
|
<Stack align="center" gap="xs" w="100%" py="xl">
|
||||||
@@ -49,13 +53,26 @@ export default function ProfileView({ data }: ProfileViewProps) {
|
|||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
<Box pos="absolute" bottom={0} w="100%" p={{ base: 'xs', md: 'md' }}>
|
|
||||||
|
{/* Box nama dan jabatan - sedikit overlap dengan gambar */}
|
||||||
|
<Box
|
||||||
|
pos="absolute"
|
||||||
|
bottom={-20} // bikin naik sedikit ke gambar
|
||||||
|
right={0}
|
||||||
|
w="100%"
|
||||||
|
p={{ base: 'xs', md: 'md' }}
|
||||||
|
style={{ pointerEvents: 'none' }} // biar ga ganggu klik di gambar
|
||||||
|
>
|
||||||
<Card
|
<Card
|
||||||
px="lg"
|
px="lg"
|
||||||
radius="2xl"
|
py="sm"
|
||||||
|
radius="lg"
|
||||||
withBorder
|
withBorder
|
||||||
className="glass3"
|
style={{
|
||||||
style={{ border: '1px solid rgba(255,255,255,0.15)' }}
|
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
|
||||||
|
backdropFilter: 'blur(6px)',
|
||||||
|
pointerEvents: 'auto',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Tooltip label="Jabatan Resmi" withArrow>
|
<Tooltip label="Jabatan Resmi" withArrow>
|
||||||
<Text fz="sm" c="dimmed">
|
<Text fz="sm" c="dimmed">
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export default function SDGS() {
|
|||||||
</Title>
|
</Title>
|
||||||
</Center>
|
</Center>
|
||||||
<Text fz={{ base: "1rem", md: "1.2rem" }} ta="center" c="dimmed" mt="md" maw={820} mx="auto">
|
<Text fz={{ base: "1rem", md: "1.2rem" }} ta="center" c="dimmed" mt="md" maw={820} mx="auto">
|
||||||
SDGs Desa merupakan langkah nyata untuk mewujudkan desa yang maju, inklusif, dan berkelanjutan melalui 17 tujuan pembangunan: dari pengentasan kemiskinan, pendidikan, kesehatan, kesetaraan gender, hingga pelestarian lingkungan.
|
SDGs Desa merupakan langkah nyata untuk mewujudkan desa yang maju, inklusif, dan berkelanjutan melalui 17 tujuan pembangunan dari pengentasan kemiskinan, pendidikan, kesehatan, kesetaraan gender, hingga pelestarian lingkungan.
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Box py={50}>
|
<Box py={50}>
|
||||||
@@ -78,6 +78,9 @@ export default function SDGS() {
|
|||||||
background: "linear-gradient(180deg, #FFFFFF, #F6F8FA)",
|
background: "linear-gradient(180deg, #FFFFFF, #F6F8FA)",
|
||||||
border: "1px solid rgba(0,0,0,0.05)",
|
border: "1px solid rgba(0,0,0,0.05)",
|
||||||
transition: "all 0.3s ease",
|
transition: "all 0.3s ease",
|
||||||
|
height: "100%", // biar tinggi antar card konsisten
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Center mb="lg">
|
<Center mb="lg">
|
||||||
@@ -105,11 +108,21 @@ export default function SDGS() {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Center>
|
</Center>
|
||||||
|
|
||||||
|
{/* Stack isi teks & angka */}
|
||||||
|
<Stack justify="space-between" align="center" gap="xs" h="100%">
|
||||||
<Tooltip label="Nama tujuan SDGs Desa" position="top" withArrow>
|
<Tooltip label="Nama tujuan SDGs Desa" position="top" withArrow>
|
||||||
<Text ta="center" fz={{ base: "lg", md: "xl" }} fw={700} mb="xs">
|
<Text
|
||||||
|
ta="center"
|
||||||
|
fz={{ base: "lg", md: "xl" }}
|
||||||
|
fw={700}
|
||||||
|
mb="xs"
|
||||||
|
style={{ minHeight: mobile ? 60 : 70 }} // biar judulnya punya tinggi tetap
|
||||||
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</Text>
|
</Text>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
<Title
|
<Title
|
||||||
order={2}
|
order={2}
|
||||||
ta="center"
|
ta="center"
|
||||||
@@ -122,9 +135,11 @@ export default function SDGS() {
|
|||||||
>
|
>
|
||||||
{item.jumlah}
|
{item.jumlah}
|
||||||
</Title>
|
</Title>
|
||||||
|
</Stack>
|
||||||
</Paper>
|
</Paper>
|
||||||
))}
|
))}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
|
|
||||||
) : (
|
) : (
|
||||||
<Center mih={200} style={{ flexDirection: "column" }}>
|
<Center mih={200} style={{ flexDirection: "column" }}>
|
||||||
<IconMoodSad size={48} stroke={1.5} style={{ marginBottom: "1rem" }} />
|
<IconMoodSad size={48} stroke={1.5} style={{ marginBottom: "1rem" }} />
|
||||||
|
|||||||
@@ -52,6 +52,37 @@ const getDetailUrl = (item: { type?: string; id: string | number; [key: string]:
|
|||||||
programKemiskinan: '/darmasaba/ekonomi/program-kemiskinan',
|
programKemiskinan: '/darmasaba/ekonomi/program-kemiskinan',
|
||||||
sektorUnggulanDesa: '/darmasaba/ekonomi/sektor-unggulan-desa',
|
sektorUnggulanDesa: '/darmasaba/ekonomi/sektor-unggulan-desa',
|
||||||
demografiPekerjaan: '/darmasaba/ekonomi/demografi-pekerjaan',
|
demografiPekerjaan: '/darmasaba/ekonomi/demografi-pekerjaan',
|
||||||
|
desaDigital: '/darmasaba/inovasi/desa-digital-smart-village',
|
||||||
|
programKreatif: '/darmasaba/inovasi/program-kreatif-desa',
|
||||||
|
kolaborasiInovasi: '/darmasaba/inovasi/kolaborasi-inovasi',
|
||||||
|
mitraKolaborasi: '/darmasaba/inovasi/kolaborasi-inovasi',
|
||||||
|
infoTekno: '/darmasaba/inovasi/info-teknologi-tepat-guna',
|
||||||
|
pengelolaanSampah: '/darmasaba/lingkungan/pengelolaan-sampah-bank-sampah',
|
||||||
|
keteranganBankSampahTerdekat: '/darmasaba/lingkungan/pengelolaan-sampah-bank-sampah',
|
||||||
|
programPenghijauan: '/darmasaba/lingkungan/program-penghijauan',
|
||||||
|
dataLingkunganDesa: '/darmasaba/lingkungan/data-lingkungan-desa',
|
||||||
|
gotongRoyong: '/darmasaba/lingkungan/gotong-royong',
|
||||||
|
tujuanEdukasiLingkungan: '/darmasaba/lingkungan/tujuan-edukasi-lingkungan',
|
||||||
|
materiEdukasiLingkungan: '/darmasaba/lingkungan/materi-edukasi-lingkungan',
|
||||||
|
contohEdukasiLingkungan: '/darmasaba/lingkungan/contoh-edukasi-lingkungan',
|
||||||
|
filosofiTriHita: '/darmasaba/lingkungan/filosofi-tri-hita',
|
||||||
|
bentukKonservasiBerdasarkanAdat: '/darmasaba/lingkungan/bentuk-konservasi-berdasarkan-adat',
|
||||||
|
nilaiKonservasiAdat: '/darmasaba/lingkungan/nilai-konservasi-adat',
|
||||||
|
jenjangPendidikan: '/darmasaba/inovasi/jenjang-pendidikan',
|
||||||
|
lembaga: '/darmasaba/inovasi/lembaga',
|
||||||
|
siswa: '/darmasaba/inovasi/siswa',
|
||||||
|
pengajar: '/darmasaba/inovasi/pengajar',
|
||||||
|
keunggulanProgram: '/darmasaba/inovasi/keunggulan-program',
|
||||||
|
tujuanProgram: '/darmasaba/inovasi/tujuan-program',
|
||||||
|
programUnggulan: '/darmasaba/inovasi/program-unggulan',
|
||||||
|
lokasiJadwalBimbinganBelajarDesa: '/darmasaba/inovasi/lokasi-jadwal-bimbingan-belajar-desa',
|
||||||
|
fasilitasBimbinganBelajarDesa: '/darmasaba/inovasi/fasilitas-bimbingan-belajar-desa',
|
||||||
|
tujuanPendidikanNonFormal: '/darmasaba/inovasi/tujuan-pendidikan-non-formal',
|
||||||
|
tempatKegiatan: '/darmasaba/inovasi/tempat-kegiatan',
|
||||||
|
jenisProgramYangDiselenggarakan: '/darmasaba/inovasi/jenis-program-yang-diselenggarakan',
|
||||||
|
dataPerpustakaan: '/darmasaba/inovasi/data-perpustakaan',
|
||||||
|
dataPendidikan: '/darmasaba/inovasi/data-pendidikan',
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return typeUrlMap[type || ''] || '/darmasaba';
|
return typeUrlMap[type || ''] || '/darmasaba';
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ const navbarListMenu = [
|
|||||||
{
|
{
|
||||||
id: "1",
|
id: "1",
|
||||||
name: "PPID",
|
name: "PPID",
|
||||||
href: "/darmasaba/ppid/profile-ppid",
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "1.1",
|
id: "1.1",
|
||||||
@@ -51,7 +50,6 @@ const navbarListMenu = [
|
|||||||
{
|
{
|
||||||
id: "2",
|
id: "2",
|
||||||
name: "Desa",
|
name: "Desa",
|
||||||
href: "/darmasaba/desa/profile",
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "2.1",
|
id: "2.1",
|
||||||
@@ -94,7 +92,6 @@ const navbarListMenu = [
|
|||||||
{
|
{
|
||||||
id: "3",
|
id: "3",
|
||||||
name: "Kesehatan",
|
name: "Kesehatan",
|
||||||
href: "/darmasaba/kesehatan/posyandu",
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "3.1",
|
id: "3.1",
|
||||||
@@ -136,7 +133,6 @@ const navbarListMenu = [
|
|||||||
{
|
{
|
||||||
id: "4",
|
id: "4",
|
||||||
name: "Keamanan",
|
name: "Keamanan",
|
||||||
href: "/darmasaba/keamanan/keamanan-lingkungan-pecalang-patwal",
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "4.1",
|
id: "4.1",
|
||||||
@@ -173,7 +169,6 @@ const navbarListMenu = [
|
|||||||
{
|
{
|
||||||
id: "5",
|
id: "5",
|
||||||
name: "Ekonomi",
|
name: "Ekonomi",
|
||||||
href: "/darmasaba/ekonomi/pasar-desa",
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "5.1",
|
id: "5.1",
|
||||||
@@ -229,7 +224,6 @@ const navbarListMenu = [
|
|||||||
}, {
|
}, {
|
||||||
id: "6",
|
id: "6",
|
||||||
name: "Inovasi",
|
name: "Inovasi",
|
||||||
href: "/darmasaba/inovasi/desa-digital-smart-village",
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "6.1",
|
id: "6.1",
|
||||||
@@ -266,7 +260,6 @@ const navbarListMenu = [
|
|||||||
}, {
|
}, {
|
||||||
id: "7",
|
id: "7",
|
||||||
name: "Lingkungan",
|
name: "Lingkungan",
|
||||||
href: "/darmasaba/lingkungan/pengelolaan-sampah-bank-sampah",
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "7.1",
|
id: "7.1",
|
||||||
@@ -302,7 +295,6 @@ const navbarListMenu = [
|
|||||||
}, {
|
}, {
|
||||||
id: "8",
|
id: "8",
|
||||||
name: "Pendidikan",
|
name: "Pendidikan",
|
||||||
href: "/darmasaba/pendidikan/info-sekolah",
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: "8.1",
|
id: "8.1",
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
export type MenuItem = {
|
export type MenuItem = {
|
||||||
id: string,
|
id: string;
|
||||||
name: string,
|
name: string;
|
||||||
href: string,
|
href?: string;
|
||||||
children?: MenuItem[]
|
children?: MenuItem[];
|
||||||
}
|
} & (
|
||||||
|
{ href: string; children?: MenuItem[] } |
|
||||||
|
{ children: MenuItem[] }
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user