Compare commits
5 Commits
nico/3-feb
...
nico/5-feb
| Author | SHA1 | Date | |
|---|---|---|---|
| f550e29a75 | |||
| bb7384f1e5 | |||
| df154806f7 | |||
| 25000d0b0f | |||
| bbd52fb6f5 |
@@ -50,6 +50,7 @@
|
|||||||
"add": "^2.0.6",
|
"add": "^2.0.6",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"animate.css": "^4.1.1",
|
"animate.css": "^4.1.1",
|
||||||
|
"async-mutex": "^0.5.0",
|
||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"bun": "^1.2.2",
|
"bun": "^1.2.2",
|
||||||
"chart.js": "^4.4.8",
|
"chart.js": "^4.4.8",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import maskotDesa from "../../../data/desa/profile/maskot_desa.json";
|
|||||||
import profilePerbekel from "../../../data/desa/profile/profil_perbekel.json";
|
import profilePerbekel from "../../../data/desa/profile/profil_perbekel.json";
|
||||||
import profileDesaImage from "../../../data/desa/profile/profileDesaImage.json";
|
import profileDesaImage from "../../../data/desa/profile/profileDesaImage.json";
|
||||||
import sejarahDesa from "../../../data/desa/profile/sejarah_desa.json";
|
import sejarahDesa from "../../../data/desa/profile/sejarah_desa.json";
|
||||||
|
import visiMisiDesa from "../../../data/desa/profile/visi_misi_desa.json";
|
||||||
|
|
||||||
export async function seedProfileDesa() {
|
export async function seedProfileDesa() {
|
||||||
// =========== SEJARAH DESA ===========
|
// =========== SEJARAH DESA ===========
|
||||||
@@ -26,6 +27,26 @@ export async function seedProfileDesa() {
|
|||||||
|
|
||||||
console.log("sejarah desa success ...");
|
console.log("sejarah desa success ...");
|
||||||
|
|
||||||
|
// =========== VISI MISI DESA ===========
|
||||||
|
for (const l of visiMisiDesa) {
|
||||||
|
await prisma.visiMisiDesa.upsert({
|
||||||
|
where: {
|
||||||
|
id: l.id,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
visi: l.visi,
|
||||||
|
misi: l.misi,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: l.id,
|
||||||
|
visi: l.visi,
|
||||||
|
misi: l.misi,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("visi misi desa success ...");
|
||||||
|
|
||||||
// =========== MASKOT DESA ===========
|
// =========== MASKOT DESA ===========
|
||||||
for (const l of maskotDesa) {
|
for (const l of maskotDesa) {
|
||||||
await prisma.maskotDesa.upsert({
|
await prisma.maskotDesa.upsert({
|
||||||
|
|||||||
@@ -60,3 +60,12 @@ export async function seedDataPerpustakaan() {
|
|||||||
}
|
}
|
||||||
console.log("✅ Data perpustakaan seeded successfully");
|
console.log("✅ Data perpustakaan seeded successfully");
|
||||||
}
|
}
|
||||||
|
if (import.meta.main) {
|
||||||
|
seedDataPerpustakaan()
|
||||||
|
.then(() => {
|
||||||
|
console.log("seed data perpustakaan success");
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log("gagal seed data perpustakaan", JSON.stringify(err));
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -138,5 +138,61 @@
|
|||||||
"deskripsi": "<p>Cerita edukatif yang mengenalkan sains kepada anak dengan bahasa sederhana</p>",
|
"deskripsi": "<p>Cerita edukatif yang mengenalkan sains kepada anak dengan bahasa sederhana</p>",
|
||||||
"kategoriId": "cmkqb11mc000104jibqh7bdzu",
|
"kategoriId": "cmkqb11mc000104jibqh7bdzu",
|
||||||
"imageName": "G0iELZb2DhQDCCP5OdzJR-desktop.webp"
|
"imageName": "G0iELZb2DhQDCCP5OdzJR-desktop.webp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cml7fq776000104jscnj58sgm",
|
||||||
|
"judul": "Pedagogy of the Oppressed",
|
||||||
|
"deskripsi": "<p>Klasik pemikiran pendidikan kritis; menggali hubungan guru-murid dan peran pendidikan dalam pembebasan sosial</p>",
|
||||||
|
"kategoriId": "cmkqb11mc000104jibq97bdzu",
|
||||||
|
"imageName": "pendidikan-1.webp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cml7fqurm000204js5p60hkym",
|
||||||
|
"judul": "The Courage to Teach",
|
||||||
|
"deskripsi": "<p>Tentang refleksi diri seorang pendidik; cocok untuk pengajar yang ingin lebih dari sekedar “metode mengajar”</p>",
|
||||||
|
"kategoriId": "cmkqb11mc000104jibq97bdzu",
|
||||||
|
"imageName": "pendidikan-2.webp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cml7fqurm000204js5p60hkzn",
|
||||||
|
"judul": "A Brief History of Time",
|
||||||
|
"deskripsi": "<p>Penjelasan kosmologi yang terkenal dunia; sains kompleks dibahas dengan bahasa yang bisa dinikmati pembaca umum</p>",
|
||||||
|
"kategoriId": "cmkqb11mc000104jibqa7bdzu",
|
||||||
|
"imageName": "ilmiah-1.webp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cml7fqurm000204js5p60hkao",
|
||||||
|
"judul": "The Selfish Gene",
|
||||||
|
"deskripsi": "<p>Membawa perspektif baru tentang evolusi melalui “gen” sebagai unit seleksi</p>",
|
||||||
|
"kategoriId": "cmkqb11mc000104jibqa7bdzu",
|
||||||
|
"imageName": "ilmiah-2.webp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cml7fx09c000304jshams3xbg",
|
||||||
|
"judul": "A Little Life",
|
||||||
|
"deskripsi": "<p>Novel yang menggambarkan hidup seorang remaja yang mengalami kehidupan yang sangat sulit</p>",
|
||||||
|
"kategoriId": "cmkqb11mc000104jibqb7bdzu",
|
||||||
|
"imageName": "drama-1.webp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cml7fx09c000304jshams3xch",
|
||||||
|
"judul": "Death of a Salesman",
|
||||||
|
"deskripsi": "<p>Drama teater klasik Amerika tentang harapan, keluarga, dan realitas hidup.</p>",
|
||||||
|
"kategoriId": "cmkqb11mc000104jibqb7bdzu",
|
||||||
|
"imageName": "drama-2.webp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cml7fx09c000304jshams3xdi",
|
||||||
|
"judul": "How Not to Die",
|
||||||
|
"deskripsi": "<p>Panduan berbasis penelitian tentang pola makan untuk mencegah dan menangani penyakit.</p>",
|
||||||
|
"kategoriId": "cmkqb11mc000104jibqg7bdzu",
|
||||||
|
"imageName": "kesehatan-1.webp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cml7fx09c000304jshams3xej",
|
||||||
|
"judul": "The Body Keeps the Score",
|
||||||
|
"deskripsi": "<p>Fokus pada trauma, otak & tubuh; penting untuk memahami kesehatan mental secara mendalam.</p>",
|
||||||
|
"kategoriId": "cmkqb11mc000104jibqg7bdzu",
|
||||||
|
"imageName": "kesehatan-2.webp"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
177
prisma/lib/create_file_share_folder.ts
Normal file
177
prisma/lib/create_file_share_folder.ts
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
// import { getValidAuthToken } from "../../src/lib/seafile-auth-service";
|
||||||
|
|
||||||
|
// type CdnItem = {
|
||||||
|
// name: string;
|
||||||
|
// path: string;
|
||||||
|
// cdnUrl: string;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// type DirItem = {
|
||||||
|
// type: "file" | "dir";
|
||||||
|
// name: string;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const BASE_URL = process.env.SEAFILE_BASE_URL!;
|
||||||
|
// const REPO_ID = process.env.SEAFILE_REPO_ID!;
|
||||||
|
|
||||||
|
// // folder yang dishare (RELATIVE, tanpa slash depan)
|
||||||
|
// const DIR_TARGET = "asset-web";
|
||||||
|
|
||||||
|
// // 🔑 TOKEN DIRECTORY SHARE (/d/{token})
|
||||||
|
// const PUBLIC_SHARE_TOKEN = process.env.SEAFILE_PUBLIC_SHARE_TOKEN!;
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Ambil list file dari repo (butuh token sekali)
|
||||||
|
// */
|
||||||
|
// async function getDirItems(): Promise<DirItem[]> {
|
||||||
|
// const token = await getValidAuthToken();
|
||||||
|
|
||||||
|
// // Validasi bahwa semua variabel lingkungan telah diatur
|
||||||
|
// if (!BASE_URL) {
|
||||||
|
// throw new Error('SEAFILE_BASE_URL environment variable is not set');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!REPO_ID) {
|
||||||
|
// throw new Error('SEAFILE_REPO_ID environment variable is not set');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Bangun URL dan pastikan valid
|
||||||
|
// const url = `${BASE_URL}/api2/repos/${REPO_ID}/dir/?p=/${DIR_TARGET}`;
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// new URL(url); // Ini akan melempar error jika URL tidak valid
|
||||||
|
// } catch (error) {
|
||||||
|
// throw new Error(`Invalid URL constructed: ${url}. Error: ${error}`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const res = await fetch(url, {
|
||||||
|
// headers: {
|
||||||
|
// Authorization: `Token ${token}`,
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (!res.ok) {
|
||||||
|
// const text = await res.text();
|
||||||
|
// throw new Error(`Failed get dir items: ${text}`);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return res.json();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Build PUBLIC CDN URL
|
||||||
|
// */
|
||||||
|
// function buildPublicCdnUrl(fileName: string) {
|
||||||
|
// return `${BASE_URL}/d/${PUBLIC_SHARE_TOKEN}/files/?p=${encodeURIComponent(
|
||||||
|
// fileName,
|
||||||
|
// )}&raw=1`;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Ambil semua PUBLIC CDN URL
|
||||||
|
// */
|
||||||
|
// export async function getAllPublicCdnUrls(): Promise<CdnItem[]> {
|
||||||
|
// const items = await getDirItems();
|
||||||
|
|
||||||
|
// return items
|
||||||
|
// .filter((item) => item.type === "file")
|
||||||
|
// .map((file) => {
|
||||||
|
// const path = `${DIR_TARGET}/${file.name}`;
|
||||||
|
// return {
|
||||||
|
// name: file.name,
|
||||||
|
// path,
|
||||||
|
// cdnUrl: buildPublicCdnUrl(file.name),
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Run langsung (optional)
|
||||||
|
// */
|
||||||
|
// if (import.meta.main) {
|
||||||
|
// const data = await getAllPublicCdnUrls();
|
||||||
|
// console.log(data);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import { getValidAuthToken } from "../../src/lib/seafile-auth-service";
|
||||||
|
|
||||||
|
type CdnItem = {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
cdnUrl: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type DirItem = {
|
||||||
|
type: "file" | "dir";
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ✅ PAKAI ENV YANG BENAR
|
||||||
|
const BASE_URL = process.env.SEAFILE_URL!;
|
||||||
|
const REPO_ID = process.env.SEAFILE_REPO_ID!;
|
||||||
|
const PUBLIC_SHARE_TOKEN = process.env.SEAFILE_PUBLIC_SHARE_TOKEN!;
|
||||||
|
|
||||||
|
// folder yang dishare (RELATIVE, TANPA slash depan)
|
||||||
|
const DIR_TARGET = "asset-web";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ambil list file dari repo (token dipakai SEKALI)
|
||||||
|
*/
|
||||||
|
async function getDirItems(): Promise<DirItem[]> {
|
||||||
|
if (!BASE_URL || !REPO_ID) {
|
||||||
|
throw new Error("SEAFILE env not configured correctly");
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = await getValidAuthToken();
|
||||||
|
|
||||||
|
const url = `${BASE_URL}/api2/repos/${REPO_ID}/dir/?p=/${DIR_TARGET}`;
|
||||||
|
|
||||||
|
const res = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Token ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const text = await res.text();
|
||||||
|
throw new Error(`Failed get dir items: ${text}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build PUBLIC CDN URL (DIRECTORY SHARE)
|
||||||
|
*/
|
||||||
|
function buildPublicCdnUrl(fileName: string) {
|
||||||
|
const fullPath = `/${DIR_TARGET}/${fileName}`;
|
||||||
|
return `${BASE_URL}/d/${PUBLIC_SHARE_TOKEN}/files/?p=${encodeURIComponent(
|
||||||
|
fullPath,
|
||||||
|
)}&raw=1`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ambil semua PUBLIC CDN URL
|
||||||
|
*/
|
||||||
|
export async function getAllPublicCdnUrls(): Promise<CdnItem[]> {
|
||||||
|
const items = await getDirItems();
|
||||||
|
|
||||||
|
return items
|
||||||
|
.filter((item) => item.type === "file")
|
||||||
|
.map((file) => ({
|
||||||
|
name: file.name,
|
||||||
|
path: `${DIR_TARGET}/${file.name}`,
|
||||||
|
cdnUrl: buildPublicCdnUrl(file.name),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run langsung
|
||||||
|
*/
|
||||||
|
if (import.meta.main) {
|
||||||
|
const data = await getAllPublicCdnUrls();
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { getValidAuthToken } from "../../src/lib/seafile-auth-service";
|
||||||
|
|
||||||
type DirItem = {
|
type DirItem = {
|
||||||
type: "file" | "dir";
|
type: "file" | "dir";
|
||||||
name: string;
|
name: string;
|
||||||
@@ -5,7 +7,6 @@ type DirItem = {
|
|||||||
size?: number;
|
size?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const TOKEN = process.env.SEAFILE_TOKEN!;
|
|
||||||
const REPO_ID = process.env.SEAFILE_REPO_ID!;
|
const REPO_ID = process.env.SEAFILE_REPO_ID!;
|
||||||
|
|
||||||
// ⛔ PENTING: RELATIVE PATH (tanpa slash depan)
|
// ⛔ PENTING: RELATIVE PATH (tanpa slash depan)
|
||||||
@@ -13,11 +14,12 @@ const DIR_TARGET = "asset-web";
|
|||||||
|
|
||||||
const BASE_URL = process.env.SEAFILE_URL;
|
const BASE_URL = process.env.SEAFILE_URL;
|
||||||
|
|
||||||
const headers = {
|
|
||||||
Authorization: `Token ${TOKEN}`,
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getDirItems(): Promise<DirItem[]> {
|
async function getDirItems(): Promise<DirItem[]> {
|
||||||
|
const token = await getValidAuthToken();
|
||||||
|
const headers = {
|
||||||
|
Authorization: `Token ${token}`,
|
||||||
|
};
|
||||||
|
|
||||||
const res = await fetch(`${BASE_URL}/repos/${REPO_ID}/dir/?p=${DIR_TARGET}`, {
|
const res = await fetch(`${BASE_URL}/repos/${REPO_ID}/dir/?p=${DIR_TARGET}`, {
|
||||||
headers,
|
headers,
|
||||||
});
|
});
|
||||||
@@ -30,6 +32,11 @@ async function getDirItems(): Promise<DirItem[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getDownloadUrl(filePath: string): Promise<string> {
|
async function getDownloadUrl(filePath: string): Promise<string> {
|
||||||
|
const token = await getValidAuthToken();
|
||||||
|
const headers = {
|
||||||
|
Authorization: `Token ${token}`,
|
||||||
|
};
|
||||||
|
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${BASE_URL}/repos/${REPO_ID}/file/?p=${encodeURIComponent(filePath)}&reuse=1`,
|
`${BASE_URL}/repos/${REPO_ID}/file/?p=${encodeURIComponent(filePath)}&reuse=1`,
|
||||||
{ headers },
|
{ headers },
|
||||||
|
|||||||
71
prisma/lib/get_shared_images.ts
Normal file
71
prisma/lib/get_shared_images.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
//ini code awal cari image by folder di seafile
|
||||||
|
|
||||||
|
|
||||||
|
type CdnItem = {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
cdnUrl: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const BASE_URL = "https://cld-dkr-makuro-seafile.wibudev.com";
|
||||||
|
const SHARE_ID = "3325e9db2c504ebf9584";
|
||||||
|
|
||||||
|
// https://cld-dkr-makuro-seafile.wibudev.com/d/3a9a9ecb5e244f4da8ae/
|
||||||
|
// https://cld-dkr-makuro-seafile.wibudev.com/d/3a9a9ecb5e244f4da8ae/files/?p=-M_tICRVz6ZxOfvkuHQgU-mobile.webp&raw=1
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build CDN URL langsung (tanpa API, tanpa token)
|
||||||
|
*/
|
||||||
|
export function buildCdnUrl(filePath: string) {
|
||||||
|
// filePath contoh: "banner/home.jpg"
|
||||||
|
return `${BASE_URL}/f/${SHARE_ID}/${filePath}?raw=1`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ambil daftar file dari PUBLIC SHARE (optional)
|
||||||
|
* Tidak pakai token
|
||||||
|
*/
|
||||||
|
async function getPublicDirItems(path = "/"): Promise<any[]> {
|
||||||
|
const res = await fetch(
|
||||||
|
`${BASE_URL}/api/v2.1/share-links/${SHARE_ID}/dir/?p=${encodeURIComponent(
|
||||||
|
path,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const text = await res.text();
|
||||||
|
throw new Error(`Failed get public dir items: ${text}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ambil semua CDN URL dari folder public share
|
||||||
|
*/
|
||||||
|
export async function getAllCdnUrls(
|
||||||
|
dirPath = "/",
|
||||||
|
): Promise<CdnItem[]> {
|
||||||
|
const items = await getPublicDirItems(dirPath);
|
||||||
|
|
||||||
|
return items
|
||||||
|
.filter((item: any) => item.type === "file")
|
||||||
|
.map((file: any) => {
|
||||||
|
const filePath =
|
||||||
|
dirPath === "/"
|
||||||
|
? file.name
|
||||||
|
: `${dirPath.replace(/\/$/, "")}/${file.name}`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: file.name,
|
||||||
|
path: filePath,
|
||||||
|
cdnUrl: buildCdnUrl(filePath),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(import.meta.main) {
|
||||||
|
const allCdnUrls = await getAllCdnUrls();
|
||||||
|
console.log(allCdnUrls);
|
||||||
|
}
|
||||||
33
prisma/lib/get_sharef.ts
Normal file
33
prisma/lib/get_sharef.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
const BASE_URL = "https://cld-dkr-makuro-seafile.wibudev.com";
|
||||||
|
const ADMIN_TOKEN = process.env.SEAFILE_TOKEN!;
|
||||||
|
const REPO_ID = process.env.SEAFILE_REPO_ID!;
|
||||||
|
|
||||||
|
export async function createFileShareForFolder() {
|
||||||
|
const res = await fetch(`${BASE_URL}/api/v2.1/share-links/`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Token ${ADMIN_TOKEN}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
repo_id: REPO_ID,
|
||||||
|
path: "/asset-web", // FOLDER
|
||||||
|
permission: "r",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const text = await res.text();
|
||||||
|
throw new Error(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
console.log("FILE SHARE LINK:", data);
|
||||||
|
|
||||||
|
// data.link -> https://domain/f/XXXX/
|
||||||
|
// data.token / data.id (tergantung versi)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.meta.main) {
|
||||||
|
await createFileShareForFolder();
|
||||||
|
}
|
||||||
@@ -65,6 +65,7 @@ import { seedBimbinganBelajar } from "./_seeder_list/pendidikan/seed_bimbingan_b
|
|||||||
import { seedDataPendidikan } from "./_seeder_list/pendidikan/seed_data_pendidikan";
|
import { seedDataPendidikan } from "./_seeder_list/pendidikan/seed_data_pendidikan";
|
||||||
import { seedPendidikanNonFormal } from "./_seeder_list/pendidikan/seed_pendidikan_non_formal";
|
import { seedPendidikanNonFormal } from "./_seeder_list/pendidikan/seed_pendidikan_non_formal";
|
||||||
import { seedDataPerpustakaan } from "./_seeder_list/pendidikan/seed_data_perpustakaan";
|
import { seedDataPerpustakaan } from "./_seeder_list/pendidikan/seed_data_perpustakaan";
|
||||||
|
import { seedProfilPpd } from "./_seeder_list/ppid/profil-ppid/seed_profil_ppd";
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
// Always run seedAssets to handle new images without duplication
|
// Always run seedAssets to handle new images without duplication
|
||||||
@@ -179,6 +180,9 @@ import { seedDataPerpustakaan } from "./_seeder_list/pendidikan/seed_data_perpus
|
|||||||
|
|
||||||
// // =========== MENU PPID ===========
|
// // =========== MENU PPID ===========
|
||||||
|
|
||||||
|
// // =========== SUBMENU PROFIL PPID ===========
|
||||||
|
await seedProfilPpd();
|
||||||
|
|
||||||
// // =========== SUBMENU STRUKTUR PPID ===========
|
// // =========== SUBMENU STRUKTUR PPID ===========
|
||||||
await seedPegawaiPpid();
|
await seedPegawaiPpid();
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
import { getAllDownloadUrls } from "./lib/get_images";
|
import { getAllPublicCdnUrls } from "./lib/create_file_share_folder";
|
||||||
|
|
||||||
export default async function seedAssets() {
|
export default async function seedAssets() {
|
||||||
const images = await getAllDownloadUrls();
|
const images = await getAllPublicCdnUrls();
|
||||||
|
|
||||||
for (const img of images) {
|
for (const img of images) {
|
||||||
try {
|
try {
|
||||||
@@ -20,7 +20,7 @@ export default async function seedAssets() {
|
|||||||
name: img.name,
|
name: img.name,
|
||||||
category: "image",
|
category: "image",
|
||||||
mimeType: "image/webp",
|
mimeType: "image/webp",
|
||||||
link: img.downloadUrl,
|
link: img.cdnUrl,
|
||||||
path: "images",
|
path: "images",
|
||||||
realName: img.name,
|
realName: img.name,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
@@ -38,12 +38,12 @@ export default async function seedAssets() {
|
|||||||
console.log("🎉 Image seeding completed");
|
console.log("🎉 Image seeding completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (import.meta.main) {
|
if (import.meta.main) {
|
||||||
// seedAssets()
|
seedAssets()
|
||||||
// .then(() => {
|
.then(() => {
|
||||||
// console.log("seed assets success");
|
console.log("seed assets success");
|
||||||
// })
|
})
|
||||||
// .catch((err) => {
|
.catch((err) => {
|
||||||
// console.log("gagal seed assets", JSON.stringify(err));
|
console.log("gagal seed assets", JSON.stringify(err));
|
||||||
// });
|
});
|
||||||
// }
|
}
|
||||||
|
|||||||
21
public/manifest.json
Normal file
21
public/manifest.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "Desa Darmasaba",
|
||||||
|
"short_name": "Darmasaba",
|
||||||
|
"description": "Website resmi Desa Darmasaba, Kabupaten Badung, Bali",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"theme_color": "#1e40af",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/darmasaba-icon.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/darmasaba-icon.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -57,12 +57,24 @@ const stateProfilePPID = proxy({
|
|||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.data = result.data;
|
this.data = result.data;
|
||||||
return result.data;
|
return result.data;
|
||||||
} else throw new Error(result.message || "Gagal memuat data profile");
|
} else {
|
||||||
|
// Jika pesan adalah "Data tidak ditemukan" atau "Belum ada data profil PPID yang aktif",
|
||||||
|
// tetap simpan sebagai error tapi tidak perlu menampilkan toast error karena ini bukan error sebenarnya
|
||||||
|
if (result.message === "Data tidak ditemukan" || result.message === "Belum ada data profil PPID yang aktif") {
|
||||||
|
this.error = result.message;
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
throw new Error(result.message || "Gagal memuat data profile");
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg = (err as Error).message;
|
const msg = (err as Error).message;
|
||||||
this.error = msg;
|
this.error = msg;
|
||||||
console.error("Load profile error:", msg);
|
console.error("Load profile error:", msg);
|
||||||
toast.error("Gagal memuat data profile");
|
// Hanya tampilkan toast error jika bukan karena data tidak ditemukan
|
||||||
|
if (msg !== "Data tidak ditemukan" && msg !== "Belum ada data profil PPID yang aktif") {
|
||||||
|
toast.error("Gagal memuat data profile");
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|||||||
@@ -20,12 +20,25 @@ export default async function profilePerbekelFindById(request: Request) {
|
|||||||
}, { status: 400 });
|
}, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await prisma.profilPerbekel.findUnique({
|
let data;
|
||||||
|
|
||||||
|
// Special handling for 'edit' - get the first/only record
|
||||||
|
if (id === 'edit') {
|
||||||
|
data = await prisma.profilPerbekel.findFirst({
|
||||||
|
where: { isActive: true },
|
||||||
|
include: {
|
||||||
|
image: true,
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: 'asc' } // Get the oldest one first
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data = await prisma.profilPerbekel.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
include: {
|
include: {
|
||||||
image: true,
|
image: true,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return Response.json({
|
return Response.json({
|
||||||
|
|||||||
@@ -26,9 +26,19 @@ export default async function lambangDesaFindById(request: Request) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await prisma.lambangDesa.findUnique({
|
let data;
|
||||||
where: { id },
|
|
||||||
});
|
// Special handling for 'edit' - get the first/only record
|
||||||
|
if (id === 'edit') {
|
||||||
|
data = await prisma.lambangDesa.findFirst({
|
||||||
|
where: { isActive: true },
|
||||||
|
orderBy: { createdAt: 'asc' } // Get the oldest one first
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data = await prisma.lambangDesa.findUnique({
|
||||||
|
where: { id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return Response.json(
|
return Response.json(
|
||||||
|
|||||||
@@ -20,16 +20,33 @@ export default async function maskotDesaFindById(request: Request){
|
|||||||
}, {status: 400})
|
}, {status: 400})
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await prisma.maskotDesa.findUnique({
|
let data;
|
||||||
where: { id },
|
|
||||||
include: {
|
// Special handling for 'edit' - get the first/only record
|
||||||
images: {
|
if (id === 'edit') {
|
||||||
include: {
|
data = await prisma.maskotDesa.findFirst({
|
||||||
image: true,
|
where: { isActive: true },
|
||||||
|
include: {
|
||||||
|
images: {
|
||||||
|
include: {
|
||||||
|
image: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: 'asc' } // Get the oldest one first
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data = await prisma.maskotDesa.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: {
|
||||||
|
images: {
|
||||||
|
include: {
|
||||||
|
image: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
if(!data) {
|
if(!data) {
|
||||||
return Response.json({
|
return Response.json({
|
||||||
|
|||||||
@@ -20,9 +20,19 @@ export default async function sejarahDesaFindById(request: Request) {
|
|||||||
}, {status: 400})
|
}, {status: 400})
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await prisma.sejarahDesa.findUnique({
|
let data;
|
||||||
where: { id },
|
|
||||||
})
|
// Special handling for 'edit' - get the first/only record
|
||||||
|
if (id === 'edit') {
|
||||||
|
data = await prisma.sejarahDesa.findFirst({
|
||||||
|
where: { isActive: true },
|
||||||
|
orderBy: { createdAt: 'asc' } // Get the oldest one first
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data = await prisma.sejarahDesa.findUnique({
|
||||||
|
where: { id },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return Response.json({
|
return Response.json({
|
||||||
|
|||||||
@@ -20,9 +20,19 @@ export default async function visiMisiDesaFindById(request: Request) {
|
|||||||
}, {status: 400})
|
}, {status: 400})
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await prisma.visiMisiDesa.findUnique({
|
let data;
|
||||||
where: { id },
|
|
||||||
})
|
// Special handling for 'edit' - get the first/only record
|
||||||
|
if (id === 'edit') {
|
||||||
|
data = await prisma.visiMisiDesa.findFirst({
|
||||||
|
where: { isActive: true },
|
||||||
|
orderBy: { createdAt: 'asc' } // Get the oldest one first
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data = await prisma.visiMisiDesa.findUnique({
|
||||||
|
where: { id },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return Response.json({
|
return Response.json({
|
||||||
|
|||||||
@@ -20,18 +20,39 @@ export default async function handler(request: Request) {
|
|||||||
}, { status: 400 });
|
}, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await prisma.profilePPID.findUnique({
|
let data;
|
||||||
where: { id },
|
|
||||||
include: {
|
// Special handling for 'edit' - get the first/only record
|
||||||
image: true,
|
if (id === 'edit') {
|
||||||
}
|
data = await prisma.profilePPID.findFirst({
|
||||||
});
|
where: { isActive: true },
|
||||||
|
include: {
|
||||||
|
image: true,
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: 'asc' } // Get the oldest one first
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
data = await prisma.profilePPID.findUnique({
|
||||||
|
where: { id },
|
||||||
|
include: {
|
||||||
|
image: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return Response.json({
|
// Untuk ID 'edit', kembalikan pesan khusus karena mungkin memang belum ada data
|
||||||
success: false,
|
if (id === 'edit') {
|
||||||
message: "Data tidak ditemukan",
|
return Response.json({
|
||||||
}, { status: 404 });
|
success: false,
|
||||||
|
message: "Belum ada data profil PPID yang aktif",
|
||||||
|
}, { status: 404 });
|
||||||
|
} else {
|
||||||
|
return Response.json({
|
||||||
|
success: false,
|
||||||
|
message: "Data tidak ditemukan",
|
||||||
|
}, { status: 404 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.json({
|
return Response.json({
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ function VisiMisiDesa() {
|
|||||||
state.findUnique.load('edit');
|
state.findUnique.load('edit');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { data, loading } = state.findUnique;
|
const { data, loading, error } = state.findUnique;
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<Box py="xl">
|
<Box py="xl">
|
||||||
<Skeleton h={500} radius="lg" />
|
<Skeleton h={500} radius="lg" />
|
||||||
@@ -23,6 +23,21 @@ function VisiMisiDesa() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (error || !data) {
|
||||||
|
return (
|
||||||
|
<Box py="xl">
|
||||||
|
<Paper p="xl" radius="lg" shadow="md" withBorder>
|
||||||
|
<Title order={2} c="red" ta="center" mb="md">
|
||||||
|
Terjadi Kesalahan
|
||||||
|
</Title>
|
||||||
|
<Text ta="center" c="dimmed">
|
||||||
|
{error || 'Data visi dan misi desa tidak ditemukan'}
|
||||||
|
</Text>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Stack align="center" gap="xl">
|
<Stack align="center" gap="xl">
|
||||||
|
|||||||
@@ -1,7 +1,16 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import colors from '@/con/colors';
|
import colors from '@/con/colors';
|
||||||
import { Stack, Box, Text, Paper, Skeleton, Center, Title } from '@mantine/core';
|
import {
|
||||||
import React from 'react';
|
Stack,
|
||||||
|
Box,
|
||||||
|
Text,
|
||||||
|
Paper,
|
||||||
|
Skeleton,
|
||||||
|
Center,
|
||||||
|
Title,
|
||||||
|
Pagination
|
||||||
|
} from '@mantine/core';
|
||||||
|
import React, { useState } from 'react';
|
||||||
import BackButton from '../../desa/layanan/_com/BackButto';
|
import BackButton from '../../desa/layanan/_com/BackButto';
|
||||||
import { BarChart } from '@mantine/charts';
|
import { BarChart } from '@mantine/charts';
|
||||||
import { useProxy } from 'valtio/utils';
|
import { useProxy } from 'valtio/utils';
|
||||||
@@ -13,11 +22,15 @@ function Page() {
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
loading,
|
loading
|
||||||
} = state.findMany
|
} = state.findMany
|
||||||
|
|
||||||
|
const [activePage, setActivePage] = useState(1);
|
||||||
|
const itemsPerPage = 3;
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
state.findMany.load()
|
// Muat semua data tanpa batasan jumlah per halaman
|
||||||
|
state.findMany.load() // Ambil banyak data sekaligus
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading || !data) {
|
||||||
@@ -44,6 +57,11 @@ function Page() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter data untuk halaman saat ini
|
||||||
|
const startIndex = (activePage - 1) * itemsPerPage;
|
||||||
|
const endIndex = startIndex + itemsPerPage;
|
||||||
|
const currentData = data.slice(startIndex, endIndex);
|
||||||
|
|
||||||
const chartData = data
|
const chartData = data
|
||||||
.filter(item => item?.name && typeof item.value === 'number')
|
.filter(item => item?.name && typeof item.value === 'number')
|
||||||
.map((item) => ({
|
.map((item) => ({
|
||||||
@@ -74,9 +92,9 @@ function Page() {
|
|||||||
</Box>
|
</Box>
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
<Stack gap="lg" justify="center">
|
<Stack gap="lg" justify="center">
|
||||||
{data.map((v, k) => {
|
{currentData.map((v, k) => {
|
||||||
return (
|
return (
|
||||||
<Paper p="xl" key={k}>
|
<Paper p="xl" key={`${startIndex + k}`}>
|
||||||
<Title order={3} fw="bold">
|
<Title order={3} fw="bold">
|
||||||
{v.name}
|
{v.name}
|
||||||
</Title>
|
</Title>
|
||||||
@@ -90,6 +108,18 @@ function Page() {
|
|||||||
</Paper>
|
</Paper>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{/* Pagination */}
|
||||||
|
<Center mt="xl">
|
||||||
|
<Pagination
|
||||||
|
total={Math.ceil(data.length / itemsPerPage)}
|
||||||
|
value={activePage}
|
||||||
|
onChange={setActivePage}
|
||||||
|
size="lg"
|
||||||
|
radius="md"
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
|
||||||
<Box style={{ width: '100%', overflowX: 'auto' }}>
|
<Box style={{ width: '100%', overflowX: 'auto' }}>
|
||||||
<Paper p="xl">
|
<Paper p="xl">
|
||||||
<Title order={3} fw="bold" pb="md">
|
<Title order={3} fw="bold" pb="md">
|
||||||
|
|||||||
@@ -162,6 +162,9 @@ function Page() {
|
|||||||
p="lg"
|
p="lg"
|
||||||
shadow="sm"
|
shadow="sm"
|
||||||
style={{
|
style={{
|
||||||
|
height: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
transform: 'translateY(-4px)',
|
transform: 'translateY(-4px)',
|
||||||
boxShadow: '0 8px 20px rgba(0,0,0,0.1)',
|
boxShadow: '0 8px 20px rgba(0,0,0,0.1)',
|
||||||
@@ -169,7 +172,7 @@ function Page() {
|
|||||||
transition: 'transform 0.2s ease, box-shadow 0.2s ease',
|
transition: 'transform 0.2s ease, box-shadow 0.2s ease',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm" style={{ flex: 1 }}>
|
||||||
<Text
|
<Text
|
||||||
c={colors['blue-button']}
|
c={colors['blue-button']}
|
||||||
lineClamp={2}
|
lineClamp={2}
|
||||||
@@ -190,7 +193,7 @@ function Page() {
|
|||||||
: '-'}
|
: '-'}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Box>
|
<Box style={{ flex: 1 }}>
|
||||||
<Text fw="bold" fz="sm">
|
<Text fw="bold" fz="sm">
|
||||||
Penanganan:
|
Penanganan:
|
||||||
</Text>
|
</Text>
|
||||||
@@ -257,6 +260,7 @@ function Page() {
|
|||||||
onClick={() => router.push(`/darmasaba/keamanan/laporan-publik/${v.id}`)}
|
onClick={() => router.push(`/darmasaba/keamanan/laporan-publik/${v.id}`)}
|
||||||
size={mobile ? 'sm' : 'md'}
|
size={mobile ? 'sm' : 'md'}
|
||||||
fullWidth
|
fullWidth
|
||||||
|
style={{ marginTop: 'auto' }}
|
||||||
>
|
>
|
||||||
{mobile ? 'Detail' : 'Lihat Detail Kronologi'}
|
{mobile ? 'Detail' : 'Lihat Detail Kronologi'}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ function Page() {
|
|||||||
<SimpleGrid
|
<SimpleGrid
|
||||||
px={{ base: 20, md: 100 }}
|
px={{ base: 20, md: 100 }}
|
||||||
cols={{ base: 1, md: 2 }}
|
cols={{ base: 1, md: 2 }}
|
||||||
spacing="xl"
|
spacing={{ base: 'md', md: 'xl' }}
|
||||||
>
|
>
|
||||||
<Paper p="xl" radius="xl" shadow="lg" >
|
<Paper p="xl" radius="xl" shadow="lg" bg="white">
|
||||||
<Title order={2} c={colors['blue-button']} fw="bold" lh={1.2}>
|
<Title order={2} c={colors['blue-button']} fw="bold" lh={1.2}>
|
||||||
Program Keamanan Berjalan
|
Program Keamanan Berjalan
|
||||||
</Title>
|
</Title>
|
||||||
@@ -85,9 +85,9 @@ function Page() {
|
|||||||
<SimpleGrid
|
<SimpleGrid
|
||||||
px={{ base: 20, md: 100 }}
|
px={{ base: 20, md: 100 }}
|
||||||
cols={{ base: 1, md: 2 }}
|
cols={{ base: 1, md: 2 }}
|
||||||
spacing="xl"
|
spacing={{ base: 'md', md: 'xl' }}
|
||||||
>
|
>
|
||||||
<Paper p="xl" radius="xl" shadow="lg" >
|
<Paper p="xl" radius="xl" shadow="lg" bg="white">
|
||||||
<Title order={2} c={colors['blue-button']} fw="bold" lh={1.2}>
|
<Title order={2} c={colors['blue-button']} fw="bold" lh={1.2}>
|
||||||
Program Keamanan Berjalan
|
Program Keamanan Berjalan
|
||||||
</Title>
|
</Title>
|
||||||
@@ -108,6 +108,7 @@ function Page() {
|
|||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
backgroundColor: colors['blue-button'],
|
backgroundColor: colors['blue-button'],
|
||||||
transition: 'all 0.2s ease',
|
transition: 'all 0.2s ease',
|
||||||
|
marginBottom: '10px' // Add space between items
|
||||||
}}
|
}}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push(`/darmasaba/keamanan/pencegahan-kriminalitas/${item.id}`)
|
router.push(`/darmasaba/keamanan/pencegahan-kriminalitas/${item.id}`)
|
||||||
@@ -160,7 +161,7 @@ function Page() {
|
|||||||
{findFirst.loading ? (
|
{findFirst.loading ? (
|
||||||
<Center><Skeleton h={400} /></Center>
|
<Center><Skeleton h={400} /></Center>
|
||||||
) : findFirst.data ? (
|
) : findFirst.data ? (
|
||||||
<Paper p="xl" radius="xl" shadow="lg">
|
<Paper p="xl" radius="xl" shadow="lg" bg="white">
|
||||||
{findFirst.data?.linkVideo ? (
|
{findFirst.data?.linkVideo ? (
|
||||||
<Box
|
<Box
|
||||||
component="iframe"
|
component="iframe"
|
||||||
@@ -168,7 +169,7 @@ function Page() {
|
|||||||
width="100%"
|
width="100%"
|
||||||
height={300}
|
height={300}
|
||||||
allowFullScreen
|
allowFullScreen
|
||||||
style={{ borderRadius: 8 }}
|
style={{ borderRadius: 8, marginBottom: 15 }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Text fz={{ base: 'xs', md: 'sm' }} c="dimmed" lh={1.4}>
|
<Text fz={{ base: 'xs', md: 'sm' }} c="dimmed" lh={1.4}>
|
||||||
|
|||||||
@@ -19,19 +19,27 @@ function Content({ kategoriBuku }: { kategoriBuku: string }) {
|
|||||||
const searchQuery = searchParams.get('search') || '';
|
const searchQuery = searchParams.get('search') || '';
|
||||||
const router = useTransitionRouter()
|
const router = useTransitionRouter()
|
||||||
|
|
||||||
|
// Convert kebab-case back to original category name format
|
||||||
|
// This reverses the transformation done in layoutTabs: item.name.toLowerCase().replace(/\s+/g, '-')
|
||||||
|
const convertKebabCaseToOriginal = (kebabStr: string): string => {
|
||||||
|
// Replace hyphens with spaces
|
||||||
|
return kebabStr.replace(/-/g, ' ');
|
||||||
|
};
|
||||||
|
|
||||||
const decodedKategoriBuku = decodeURIComponent(kategoriBuku);
|
const decodedKategoriBuku = decodeURIComponent(kategoriBuku);
|
||||||
|
const originalKategoriName = convertKebabCaseToOriginal(decodedKategoriBuku);
|
||||||
|
|
||||||
const loadData = useCallback(async (searchQuery: string = '', page: number = 1) => {
|
const loadData = useCallback(async (searchQuery: string = '', page: number = 1) => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const currentKategoriFilter = decodedKategoriBuku.toLowerCase() === 'semua' ? '' : decodedKategoriBuku;
|
const currentKategoriFilter = decodedKategoriBuku.toLowerCase() === 'semua' ? '' : originalKategoriName;
|
||||||
await state.dataPerpustakaan.findMany.load(page, 3, searchQuery, currentKategoriFilter);
|
await state.dataPerpustakaan.findMany.load(page, 3, searchQuery, currentKategoriFilter);
|
||||||
setCurrentPage(page);
|
setCurrentPage(page);
|
||||||
setTotalPages(state.dataPerpustakaan.findMany.totalPages);
|
setTotalPages(state.dataPerpustakaan.findMany.totalPages);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
}, [state.dataPerpustakaan.findMany, decodedKategoriBuku]);
|
}, [state.dataPerpustakaan.findMany, originalKategoriName, decodedKategoriBuku]);
|
||||||
|
|
||||||
useShallowEffect(() => {
|
useShallowEffect(() => {
|
||||||
loadData(searchQuery);
|
loadData(searchQuery);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ function Page() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// LOADING SKELETON
|
// LOADING SKELETON
|
||||||
if (!allList.profile.data)
|
if (allList.profile.loading)
|
||||||
return (
|
return (
|
||||||
<Stack bg={colors.Bg} py="xl" gap="22">
|
<Stack bg={colors.Bg} py="xl" gap="22">
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
@@ -53,6 +53,50 @@ function Page() {
|
|||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ERROR HANDLING
|
||||||
|
if (allList.profile.error)
|
||||||
|
return (
|
||||||
|
<Stack bg={colors.Bg} py="xl" gap="22" justify="center" align="center">
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<Paper p="xl" bg={colors['white-trans-1']} radius="lg" shadow="xl">
|
||||||
|
{allList.profile.error === "Belum ada data profil PPID yang aktif" ? (
|
||||||
|
<>
|
||||||
|
<Title order={3} ta="center" c={"orange"} fw={700}>
|
||||||
|
Data Profile PPID Belum Tersedia
|
||||||
|
</Title>
|
||||||
|
<Text ta="center" mt="md">Mohon maaf, data profil PPID belum tersedia saat ini</Text>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Title order={3} ta="center" c={"red"} fw={700}>
|
||||||
|
Gagal Memuat Data Profile
|
||||||
|
</Title>
|
||||||
|
<Text ta="center" mt="md">Silakan coba beberapa saat lagi</Text>
|
||||||
|
<Text ta="center" size="sm" c={"gray"} mt="sm">
|
||||||
|
Error: {allList.profile.error}
|
||||||
|
</Text>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
|
||||||
|
// NO DATA HANDLING
|
||||||
|
if (!allList.profile.data)
|
||||||
|
return (
|
||||||
|
<Stack bg={colors.Bg} py="xl" gap="22" justify="center" align="center">
|
||||||
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
|
<Paper p="xl" bg={colors['white-trans-1']} radius="lg" shadow="xl">
|
||||||
|
<Title order={3} ta="center" c={"orange"} fw={700}>
|
||||||
|
Data Profile PPID Belum Tersedia
|
||||||
|
</Title>
|
||||||
|
<Text ta="center" mt="md">Mohon maaf, data profil PPID belum tersedia saat ini</Text>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
|
||||||
const dataArray = Array.isArray(allList.profile.data)
|
const dataArray = Array.isArray(allList.profile.data)
|
||||||
? allList.profile.data
|
? allList.profile.data
|
||||||
: [allList.profile.data];
|
: [allList.profile.data];
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ const getWorkStatus = (day: string, currentTime: string): { status: string; mess
|
|||||||
|
|
||||||
let workHoursMessage = "";
|
let workHoursMessage = "";
|
||||||
if (["Senin", "Selasa", "Rabu", "Kamis"].includes(day)) {
|
if (["Senin", "Selasa", "Rabu", "Kamis"].includes(day)) {
|
||||||
workHoursMessage = "07:30 - 15:10";
|
workHoursMessage = "07:30 - 15:30";
|
||||||
} else if (day === "Jumat") {
|
} else if (day === "Jumat") {
|
||||||
workHoursMessage = "07:30 - 12:00";
|
workHoursMessage = "07:30 - 12:00";
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/lib/seafile-auth-service.ts
Normal file
17
src/lib/seafile-auth-service.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Function to get the static authentication token from environment variables
|
||||||
|
export async function getValidAuthToken(): Promise<string> {
|
||||||
|
const staticToken = process.env.SEAFILE_TOKEN;
|
||||||
|
|
||||||
|
if (!staticToken) {
|
||||||
|
throw new Error('SEAFILE_TOKEN environment variable is not set');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Using static SEAFILE_TOKEN from environment variables");
|
||||||
|
|
||||||
|
return staticToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to check if the token is set (always true since we're using static token)
|
||||||
|
export function isTokenValid(): boolean {
|
||||||
|
return !!process.env.SEAFILE_TOKEN;
|
||||||
|
}
|
||||||
68
x.json
68
x.json
@@ -1,26 +1,4 @@
|
|||||||
[
|
[
|
||||||
{
|
|
||||||
"type": "repo",
|
|
||||||
"id": "8814bfe1-30d5-4e77-ab36-3122fa59a022",
|
|
||||||
"owner": "0535ccb6211642c0a628f521577863a0@auth.local",
|
|
||||||
"owner_name": "nico",
|
|
||||||
"owner_contact_email": "nico@bip.com",
|
|
||||||
"name": "desa-darmasaba",
|
|
||||||
"mtime": 1769484147,
|
|
||||||
"modifier_email": "0535ccb6211642c0a628f521577863a0@auth.local",
|
|
||||||
"modifier_contact_email": "nico@bip.com",
|
|
||||||
"modifier_name": "nico",
|
|
||||||
"mtime_relative": "<time datetime=\"2026-01-27T11:22:27\" is=\"relative-time\" title=\"Tue, 27 Jan 2026 11:22:27 +0800\" >1 minutes ago</time>",
|
|
||||||
"size": 184351,
|
|
||||||
"size_formatted": "180.0\u00a0KB",
|
|
||||||
"encrypted": false,
|
|
||||||
"permission": "rw",
|
|
||||||
"virtual": false,
|
|
||||||
"root": "",
|
|
||||||
"head_commit_id": "253fd9604e54804a22cbf701bcc551e311e4a428",
|
|
||||||
"version": 1,
|
|
||||||
"salt": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "repo",
|
"type": "repo",
|
||||||
"id": "f0e9ee4a-fd13-49a2-81c0-f253951d063a",
|
"id": "f0e9ee4a-fd13-49a2-81c0-f253951d063a",
|
||||||
@@ -28,18 +6,40 @@
|
|||||||
"owner_name": "nico",
|
"owner_name": "nico",
|
||||||
"owner_contact_email": "nico@bip.com",
|
"owner_contact_email": "nico@bip.com",
|
||||||
"name": "My Library",
|
"name": "My Library",
|
||||||
"mtime": 1769483609,
|
"mtime": 1770175246,
|
||||||
"modifier_email": "0535ccb6211642c0a628f521577863a0@auth.local",
|
"modifier_email": "0535ccb6211642c0a628f521577863a0@auth.local",
|
||||||
"modifier_contact_email": "nico@bip.com",
|
"modifier_contact_email": "nico@bip.com",
|
||||||
"modifier_name": "nico",
|
"modifier_name": "nico",
|
||||||
"mtime_relative": "<time datetime=\"2026-01-27T11:13:29\" is=\"relative-time\" title=\"Tue, 27 Jan 2026 11:13:29 +0800\" >10 minutes ago</time>",
|
"mtime_relative": "<time datetime=\"2026-02-04T11:20:46\" is=\"relative-time\" title=\"Wed, 04 Feb 2026 11:20:46 +0800\" >1 day ago</time>",
|
||||||
"size": 19392875,
|
"size": 8418588,
|
||||||
"size_formatted": "18.5\u00a0MB",
|
"size_formatted": "8.0\u00a0MB",
|
||||||
"encrypted": false,
|
"encrypted": false,
|
||||||
"permission": "rw",
|
"permission": "rw",
|
||||||
"virtual": false,
|
"virtual": false,
|
||||||
"root": "",
|
"root": "",
|
||||||
"head_commit_id": "22374e711577dcfb152fe07921458302feb9970a",
|
"head_commit_id": "d864f18510f72fbf7ed8601d89ced2cb92466057",
|
||||||
|
"version": 1,
|
||||||
|
"salt": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "repo",
|
||||||
|
"id": "8814bfe1-30d5-4e77-ab36-3122fa59a022",
|
||||||
|
"owner": "0535ccb6211642c0a628f521577863a0@auth.local",
|
||||||
|
"owner_name": "nico",
|
||||||
|
"owner_contact_email": "nico@bip.com",
|
||||||
|
"name": "desa-darmasaba",
|
||||||
|
"mtime": 1769486218,
|
||||||
|
"modifier_email": "0535ccb6211642c0a628f521577863a0@auth.local",
|
||||||
|
"modifier_contact_email": "nico@bip.com",
|
||||||
|
"modifier_name": "nico",
|
||||||
|
"mtime_relative": "<time datetime=\"2026-01-27T11:56:58\" is=\"relative-time\" title=\"Tue, 27 Jan 2026 11:56:58 +0800\" >9 days ago</time>",
|
||||||
|
"size": 5064580,
|
||||||
|
"size_formatted": "4.8\u00a0MB",
|
||||||
|
"encrypted": false,
|
||||||
|
"permission": "rw",
|
||||||
|
"virtual": false,
|
||||||
|
"root": "",
|
||||||
|
"head_commit_id": "1ddc57ecf377741d0b3abcf5a56a9ed0612c553c",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"salt": ""
|
"salt": ""
|
||||||
},
|
},
|
||||||
@@ -48,12 +48,12 @@
|
|||||||
"id": "9ef9d5e2-6df0-4635-ae1b-53ed52801524",
|
"id": "9ef9d5e2-6df0-4635-ae1b-53ed52801524",
|
||||||
"name": "RND",
|
"name": "RND",
|
||||||
"owner": "Organization",
|
"owner": "Organization",
|
||||||
"mtime": 1768959408,
|
"mtime": 1770263243,
|
||||||
"mtime_relative": "<time datetime=\"2026-01-21T09:36:48\" is=\"relative-time\" title=\"Wed, 21 Jan 2026 09:36:48 +0800\" >6 days ago</time>",
|
"mtime_relative": "<time datetime=\"2026-02-05T11:47:23\" is=\"relative-time\" title=\"Thu, 05 Feb 2026 11:47:23 +0800\" >28 minutes ago</time>",
|
||||||
"modifier_email": "dfa07fcb3e27453e969492855a2d6606@auth.local",
|
"modifier_email": "ecb9357c59024141bc1731a3e10900ea@auth.local",
|
||||||
"modifier_contact_email": "jun@bip.com",
|
"modifier_contact_email": "inno@bip.com",
|
||||||
"modifier_name": "jun",
|
"modifier_name": "inno",
|
||||||
"size": 1197897275,
|
"size": 1198188885,
|
||||||
"size_formatted": "1.1\u00a0GB",
|
"size_formatted": "1.1\u00a0GB",
|
||||||
"encrypted": false,
|
"encrypted": false,
|
||||||
"permission": "rw",
|
"permission": "rw",
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
"share_from_contact_email": "wibu@bip.com",
|
"share_from_contact_email": "wibu@bip.com",
|
||||||
"share_type": "public",
|
"share_type": "public",
|
||||||
"root": "",
|
"root": "",
|
||||||
"head_commit_id": "5199e27babe4fd797e64beba62777a1dde58077b",
|
"head_commit_id": "f79f42f47c790bf5f14adb64bd644ec02aaf15bb",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"salt": ""
|
"salt": ""
|
||||||
}
|
}
|
||||||
|
|||||||
16
x.sh
16
x.sh
@@ -1,23 +1,23 @@
|
|||||||
|
|
||||||
# ambil token
|
# # ambil token
|
||||||
# curl -X POST https://cld-dkr-makuro-seafile.wibudev.com/api2/auth-token/ \
|
# curl -X POST https://cld-dkr-makuro-seafile.wibudev.com/api2/auth-token/ \
|
||||||
# -d "username=nico@bip.com" \
|
# -d "username=nico@bip.com" \
|
||||||
# -d "password=Production_123"
|
# -d "password=Production_123"
|
||||||
|
|
||||||
# ambil list repo / library
|
# # ambil list repo / library
|
||||||
|
|
||||||
TOKEN=20a19f4a04032215d50ce53292e6abdd38b9f806
|
# TOKEN=20a19f4a04032215d50ce53292e6abdd38b9f806
|
||||||
# curl -X GET \
|
# curl -X GET \
|
||||||
# https://cld-dkr-makuro-seafile.wibudev.com/api2/repos/ \
|
# https://cld-dkr-makuro-seafile.wibudev.com/api2/repos/ \
|
||||||
# -H "Authorization: Token $TOKEN"
|
# -H "Authorization: Token $TOKEN"
|
||||||
|
|
||||||
# REPO_NAME=desa-darmasaba
|
# REPO_NAME=desa-darmasaba
|
||||||
DIR_TARGET=asset-web
|
# DIR_TARGET=asset-web
|
||||||
|
|
||||||
REPO_ID=f0e9ee4a-fd13-49a2-81c0-f253951d063a
|
# REPO_ID=f0e9ee4a-fd13-49a2-81c0-f253951d063a
|
||||||
curl -X GET \
|
# curl -X GET \
|
||||||
"https://cld-dkr-makuro-seafile.wibudev.com/api2/repos/$REPO_ID/dir/?p=$DIR_TARGET" \
|
# "https://cld-dkr-makuro-seafile.wibudev.com/api2/repos/$REPO_ID/dir/?p=$DIR_TARGET" \
|
||||||
-H "Authorization: Token $TOKEN"
|
# -H "Authorization: Token $TOKEN"
|
||||||
|
|
||||||
# curl -X GET \
|
# curl -X GET \
|
||||||
# "https://cld-dkr-makuro-seafile.wibudev.com/api2/repos/$REPO_ID/file/?p=$DIR_TARGET/buku6.jpg" \
|
# "https://cld-dkr-makuro-seafile.wibudev.com/api2/repos/$REPO_ID/file/?p=$DIR_TARGET/buku6.jpg" \
|
||||||
|
|||||||
Reference in New Issue
Block a user