Ads

Rabu, 05 November 2025

Surat Izin Siswa

import React, { useEffect, useState } from "react"; // Simple single-file React app (Tailwind CSS expected in host project). // Features: // - Registrasi siswa (nama lengkap, kelas, sekolah) // - Profil siswa yang bisa diedit // - Blanko surat izin yang bisa diisi dan disesuaikan // - Generate shareable view-only link (URL contains encoded data) // - Data disimpan di localStorage (prototype). For production, replace with backend + auth. export default function StudentPermissionApp() { const [accounts, setAccounts] = useState(() => { try { return JSON.parse(localStorage.getItem("sia_accounts") || "[]"); } catch (e) { return []; } }); const [currentId, setCurrentId] = useState(null); const [viewingShared, setViewingShared] = useState(null); // decoded shared data useEffect(() => { localStorage.setItem("sia_accounts", JSON.stringify(accounts)); }, [accounts]); useEffect(() => { // If URL has ?share=..., decode and show const params = new URLSearchParams(window.location.search); const s = params.get("share"); if (s) { try { const json = JSON.parse(atob(decodeURIComponent(s))); setViewingShared(json); } catch (e) { console.warn("Invalid share link"); } } }, []); function registerAccount({ name, kelas, sekolah }) { const id = Date.now().toString(); const newAcc = { id, name, kelas, sekolah, createdAt: new Date().toISOString(), profile: { phone: "", notes: "", }, }; setAccounts((s) => [newAcc, ...s]); setCurrentId(id); } function updateProfile(id, patch) { setAccounts((s) => s.map((a) => (a.id === id ? { ...a, ...patch } : a))); } function updateProfileFields(id, fields) { setAccounts((s) => s.map((a) => (a.id === id ? { ...a, profile: { ...a.profile, ...fields } } : a)) ); } function generateShareLink(account, surat) { // Prepare a lightweight object to share (view-only) const payload = { account: { name: account.name, kelas: account.kelas, sekolah: account.sekolah, profile: account.profile }, surat }; const encoded = encodeURIComponent(btoa(JSON.stringify(payload))); const url = `${window.location.origin}${window.location.pathname}?share=${encoded}`; return url; } function renderRegister() { const [name, setName] = [useStateHook(), useStateHook()]; function useStateHook() { const [v, s] = React.useState(""); return { v, s }; } return (

Daftar Akun Siswa

name.s(e.target.value)} className="w-full p-2 border rounded mb-2" /> kelas.s(e.target.value)} className="w-full p-2 border rounded mb-2" /> sekolah.s(e.target.value)} className="w-full p-2 border rounded mb-4" />
); } // Simpler: avoid inner hooks complexity. We'll implement forms inline below. return (

Sistem Izin Orang Tua — Prototype

Registrasi siswa, edit profil, buat blanko surat, dan bagikan link view-only.

Daftar / Akun


Akun yang ada

{accounts.length === 0 &&
Belum ada akun. Buat akun baru.
} {accounts.map((a) => (
{a.name}
{a.kelas} — {a.sekolah}
))}
{!viewingShared && !currentId && (
Pilih akun dari sisi kiri atau buka link share untuk melihat surat.
)} {viewingShared && ( )} {!viewingShared && currentId && ( a.id === currentId)} onUpdate={(patch) => updateProfile(currentId, patch)} onUpdateFields={(fields) => updateProfileFields(currentId, fields)} onGenerate={(surat) => { const acc = accounts.find((a) => a.id === currentId); const link = generateShareLink(acc, surat); navigator.clipboard?.writeText(link); alert("Link dibangun dan disalin ke clipboard:\n" + link); }} /> )}
Prototype — data disimpan hanya di browser. Untuk produksi, tambahkan backend, autentikasi orang tua/guru, dan enkripsi.
); } function RegisterForm({ onCreate }) { const [name, setName] = useState(""); const [kelas, setKelas] = useState(""); const [sekolah, setSekolah] = useState(""); return (
setName(e.target.value)} className="w-full p-2 border rounded mb-2" /> setKelas(e.target.value)} className="w-full p-2 border rounded mb-2" /> setSekolah(e.target.value)} className="w-full p-2 border rounded mb-2" />
); } function AccountEditor({ account, onUpdate, onUpdateFields, onGenerate }) { const [editing, setEditing] = useState(false); const [phone, setPhone] = useState(account?.profile?.phone || ""); const [notes, setNotes] = useState(account?.profile?.notes || ""); useEffect(() => { setPhone(account?.profile?.phone || ""); setNotes(account?.profile?.notes || ""); }, [account]); const [suratTitle, setSuratTitle] = useState("Surat Izin Tidak Masuk"); const [suratIsi, setSuratIsi] = useState("Dengan hormat,\nSaya orang tua/wali dari ... mohon izin karena ...\nTerima kasih."); const [suratTanggal, setSuratTanggal] = useState(new Date().toISOString().slice(0,10)); return (

{account.name}

{account.kelas} — {account.sekolah}
{editing && (
setPhone(e.target.value)} className="w-full p-2 border rounded" /> setNotes(e.target.value)} className="w-full p-2 border rounded" />
)}

Buat / Edit Blanko Surat

setSuratTitle(e.target.value)} className="w-full p-2 border rounded mb-2" /> setSuratTanggal(e.target.value)} className="w-full p-2 border rounded mb-2" />