Utility Functions (Snippets) 🛠️
Ini gudang fungsi-fungsi kecil ("Helper") yang wajib ada di folder src/utils atau src/lib.
Tinggal copas aja kalau butuh.
1. Format Rupiah (IDR) 💰
Wajib buat Marketplace Desa atau PPDB (bayar SPP).
export const formatRupiah = (number: number) => {
return new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(number);
};
// Cara pakai:
// formatRupiah(50000) -> Output: "Rp 50.000"2. Format Tanggal Indo 📅
Biar tanggalnya gak bahasa Inggris (e.g., "17 August 1945").
export const formatDateIndo = (dateString: string | Date) => {
return new Date(dateString).toLocaleDateString("id-ID", {
day: "numeric",
month: "long",
year: "numeric",
weekday: "long", // senin, selasa...
});
};
// Cara pakai:
// formatDateIndo("2025-08-17") -> Output: "Minggu, 17 Agustus 2025"3. Class Name Merger (cn) 🎨
Kalau lo pake Tailwind + Shadcn UI, fungsi ini wajib ada buat ngegabungin class biar gak konflik/tabrakan. Syarat: install dulu npm i clsx tailwind-merge
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// Cara pakai:
// <div className={cn("bg-red-500 p-4", props.className)}>
// (Class bawaan bg-red-500 bakal aman gak ketimpa sembarangan)4. Delay / Sleep ⏳
Buat simulasi loading pas lagi testing komponen loading.tsx.
export const wait = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));
// Cara pakai:
// await wait(2000); // Nunggu 2 detik baru kode bawahnya jalan5. Format File Size (Bytes to MB) 📦
Pas lo upload file pake UploadThing, ukuran filenya itu dalam Bytes (angka doang). User gak ngerti "2048000 bytes". Mereka ngertinya "2 MB". Pake fungsi ini.
export function formatBytes(bytes: number, decimals = 2) {
if (!+bytes) return "0 Bytes";
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
}
// Cara pakai:
// formatBytes(2500000) -> Output: "2.38 MB"6. Slug Generator (SEO Friendly URL) 🔗
Kalau lo bikin blog atau Produk Desa. Judul: "Cara Ternak Lele 2024" -> URL: /blog/cara-ternak-lele-2024
export function slugify(text: string) {
return text
.toString()
.toLowerCase()
.trim()
.replace(/\s+/g, "-") // Ganti spasi jadi strip
.replace(/[^\w\-]+/g, "") // Buang karakter aneh (?!@#)
.replace(/\-\-+/g, "-"); // Ganti strip dobel jadi satu
}
// Cara pakai:
// slugify("Halo Dunia! Apa Kabar?") -> Output: "halo-dunia-apa-kabar"7. Truncate Text (Potong Teks) ✂️
Buat nampilin cuplikan berita di halaman depan (Dashboard). Kalau teksnya kepanjangan, potong terus kasih titik-titik "...".
export function truncate(str: string, length: number) {
if (!str) return "";
if (str.length <= length) return str;
return str.slice(0, length) + "...";
}
// Cara pakai:
// truncate("Ini adalah deskripsi produk yang sangat panjang sekali lho", 20)
// Output: "Ini adalah deskripsi..."8. Upload dan hapus File (Cloudinary) 📸
Kalo lo pake Cloudinary sebagai penyimpanan file lo dan ingin upload dan hapus file. Buat fungsi ini:
Fungsi Upload:
export const uploadToCloudinarySigned = async (
file,
folder = "ggs",
resourceType = "image",
) => {
// VALIDASI UKURAN GAMBAR - 1MB
let LIMIT_MB = 1;
// jika ukuran gambar lebih besar dari limit lempar error
if (file.size > LIMIT_MB * 1024 * 1024) {
throw new Error(`Ukuran file terlalu besar! Maksimal ${LIMIT_MB}MB.`);
}
try {
// MINTA TANDA TANGAN KE SERVER
const signRes = await fetch("/api/cloudinary/sign", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ folder }),
});
if (!signRes.ok) throw new Error("Gagal mendapatkan izin upload.");
const signData = await signRes.json();
// UPLOAD KE CLOUDINARY DENGAN TANDA TANGAN
const formData = new FormData(); // formdata untuk upload
formData.append("file", file); // file dari input
formData.append("api_key", signData.apiKey); // api key cloudinary
formData.append("timestamp", signData.timestamp); // timestamp dari server
formData.append("signature", signData.signature); // tandatangan dari server
formData.append("folder", folder); // folder tujuan di cloudinary
// URL API Cloudinary => resource_type: image
const uploadUrl = `https://api.cloudinary.com/v1_1/${signData.cloudName}/${resourceType}/upload`;
// Lakukan upload
const uploadRes = await fetch(uploadUrl, {
method: "POST",
body: formData,
});
// jika gagal lempar error
if (!uploadRes.ok) {
const err = await uploadRes.json();
throw new Error(err.error?.message || "Gagal upload ke cloud.");
}
// ambil hasil upload
const result = await uploadRes.json();
// KEMBALIKAN URL FILE
return result.secure_url;
} catch (error) {
throw error;
}
};- Cara pakai:
const imageUrl = await uploadToCloudinarySigned(file, "ggs", "image");Fungsi Hapus:
export const deleteFromCloudinary = async (url, resourceType = "image") => {
if (!url) return; // jika tidak ada url, tidak perlu dihapus
// Ekstrak Public ID dari URL
// Public ID: folder/filename
try {
const parts = url.split("/"); // pecah berdasarkan '/'
const uploadIndex = parts.indexOf("upload"); // cari index 'upload'
if (uploadIndex === -1) return; // jika tidak ada 'upload', keluar
// Ambil bagian setelah 'version' (v123...)
const pathParts = parts.slice(uploadIndex + 2);
const fileNameWithExt = pathParts.join("/"); // gabungkan kembali menjadi path file
// Hapus ekstensi (.jpg, .docx) untuk mendapatkan public_id murni
const publicId = fileNameWithExt.substring(
0,
fileNameWithExt.lastIndexOf("."),
);
// Panggil API Delete
const res = await fetch("/api/cloudinary/delete", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ publicId, resourceType }),
});
if (!res.ok) throw new Error("Gagal menghapus file lama");
return true;
} catch (error) {
console.error("Gagal hapus file:", error);
return false; // Kembalikan false jika gagal agar ui tidak error
}
};- Cara Pakai:
const imageUrl = await uploadToCloudinarySigned(url, "image");9. Format Angka (Ribuan) 📈
Buat format angka ribuan. Pake fungsi ini:
export const formatSingkatNomer = (number) => {
if (number === null || number === undefined) return "0";
return new Intl.NumberFormat("id-ID", {
notation: "compact",
compactDisplay: "short",
maximumFractionDigits: 1,
}).format(number);
};
// cara pakai => formatSingkatNomer(50000) => "50k"