import React, { useState, useEffect } from 'react';
import {
Users,
School,
FileText,
LogOut,
CheckCircle,
XCircle,
Menu,
X,
Trash2,
Printer,
Settings,
PlusCircle,
Edit,
Save,
Heading,
ArrowUp,
ArrowDown,
Link,
Eye,
EyeOff,
Lock,
Mail,
Search,
Upload,
File,
Image as ImageIcon,
Layout // Icon untuk Branding
} from 'lucide-react';
// --- DATA MOCKUP & KONFIGURASI ---
const JENJANG_LIST = ['KB', 'RA', 'MI', 'MTs', 'MA'];
// Konfigurasi Default Aplikasi (Bisa diedit Superadmin)
const INITIAL_APP_CONFIG = {
ppdbName: 'PPDB Online',
foundationName: 'Yayasan Hidayatun Najah Tuban',
logoUrl: null, // Jika null, pakai icon default
themeColor: 'emerald' // Bisa dikembangkan untuk tema warna
};
// Konfigurasi Awal Formulir
const INITIAL_FORM_CONFIG = [
// Bagian 1
{ id: 'h1', label: 'Data Pribadi Calon Siswa', type: 'header', key: 'sec_pribadi' },
{ id: 'f1', label: 'Nama Lengkap', type: 'text', key: 'namaLengkap', required: true },
{ id: 'f2', label: 'NIK', type: 'number', key: 'nik', required: true },
{ id: 'f3', label: 'Pilih Jenjang', type: 'select', key: 'jenjang', options: JENJANG_LIST, required: true, allowCustom: false },
{
id: 'f_prog',
label: 'Program / Peminatan',
type: 'select',
key: 'program',
required: true,
dependsOn: 'jenjang',
visibleForJenjang: ['MA', 'MTs'],
optionsMapping: {
'MA': ['IPA', 'IPS', 'Bahasa', 'Keagamaan'],
'MTs': ['Reguler', 'Tahfidz', 'Bilingual']
}
},
{ id: 'f4', label: 'Tempat Lahir', type: 'text', key: 'tempatLahir', required: true },
{ id: 'f5', label: 'Tanggal Lahir', type: 'date', key: 'tanggalLahir', required: true },
// Bagian 2
{ id: 'h2', label: 'Data Sekolah Sebelumnya', type: 'header', key: 'sec_sekolah' },
{ id: 'f6', label: 'Asal Sekolah', type: 'text', key: 'asalSekolah', required: true },
// Bagian 3
{ id: 'h3', label: 'Data Orang Tua / Wali', type: 'header', key: 'sec_ortu' },
{ id: 'f7', label: 'Nama Ayah Kandung', type: 'text', key: 'namaAyah', required: true },
{ id: 'f8', label: 'No. HP / WhatsApp', type: 'text', key: 'noHp', required: true },
// Bagian 4: Upload Berkas
{ id: 'h4', label: 'Berkas Persyaratan', type: 'header', key: 'sec_berkas' },
{ id: 'f9', label: 'Scan Kartu Keluarga (KK)', type: 'file', key: 'file_kk', required: false },
{ id: 'f10', label: 'Pas Foto (3x4)', type: 'file', key: 'file_foto', required: false },
];
const INITIAL_USERS = [
{ id: 1, name: 'Ketua Yayasan', email: 'super@yayasan.com', role: 'superadmin', password: '123' },
{ id: 2, name: 'Admin MI', email: 'admin.mi@yayasan.com', role: 'admin_jenjang', jenjang: 'MI', password: '123' },
{ id: 3, name: 'Admin MTs', email: 'admin.mts@yayasan.com', role: 'admin_jenjang', jenjang: 'MTs', password: '123' },
{ id: 4, name: 'Budi Santoso', email: 'siswa@test.com', role: 'pendaftar', password: '123' }
];
const INITIAL_REGISTRATIONS = [
{
id: 'REG-001',
userId: 4,
namaLengkap: 'Budi Santoso',
nik: '3523000000000001',
jenjang: 'MI',
program: 'Tahfidz',
asalSekolah: 'TK Pertiwi',
namaAyah: 'Slamet',
noHp: '081234567890',
tempatLahir: 'Tuban',
tanggalLahir: '2015-05-20',
status: 'Diterima',
tanggalDaftar: '2023-05-20'
}
];
// --- KOMPONEN UTAMA ---
export default function App() {
const [currentUser, setCurrentUser] = useState(null);
const [users, setUsers] = useState(INITIAL_USERS);
const [registrations, setRegistrations] = useState(INITIAL_REGISTRATIONS);
const [formConfig, setFormConfig] = useState(INITIAL_FORM_CONFIG);
const [appConfig, setAppConfig] = useState(INITIAL_APP_CONFIG); // State Global App Config
const [currentView, setCurrentView] = useState('login');
const handleLogin = (email, password) => {
const user = users.find(u => u.email === email && u.password === password);
if (user) {
setCurrentUser(user);
setCurrentView('dashboard');
} else {
alert('Email atau password salah!');
}
};
const handleLogout = () => {
setCurrentUser(null);
setCurrentView('login');
};
const handleRegister = (name, email, password) => {
const newUser = {
id: users.length + 1,
name,
email,
password,
role: 'pendaftar'
};
setUsers([...users, newUser]);
alert('Pendaftaran akun berhasil! Silakan login.');
setCurrentView('login');
};
if (currentView === 'login') {
return
setCurrentView('register')} />;
}
if (currentView === 'register') {
return setCurrentView('login')} />;
}
return (
{currentUser.role === 'superadmin' && (
)}
{currentUser.role === 'admin_jenjang' && (
)}
{currentUser.role === 'pendaftar' && (
)}
);
}
// --- SCREEN COMPONENTS ---
function LoginScreen({ appConfig, onLogin, onGoToRegister }) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
{appConfig.logoUrl ? (

) : (
)}
{appConfig.ppdbName}
{appConfig.foundationName}
Belum punya akun?
Akun Demo (Klik untuk menyalin):
{setEmail('super@yayasan.com'); setPassword('123')}}>👑 Superadmin: super@yayasan.com / 123
{setEmail('admin.mi@yayasan.com'); setPassword('123')}}>🏫 Admin MI: admin.mi@yayasan.com / 123
{setEmail('siswa@test.com'); setPassword('123')}}>👤 Pendaftar: siswa@test.com / 123
);
}
function RegisterScreen({ appConfig, onRegister, onBack }) {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
return (
{appConfig.logoUrl && (

)}
Pendaftaran Akun
{appConfig.foundationName}
);
}
function DashboardLayout({ user, onLogout, children, appConfig }) {
const [isSidebarOpen, setSidebarOpen] = useState(false);
return (
{appConfig.ppdbName}
{user.name.charAt(0)}
{user.name}
{user.role.replace('_', ' ')}
{appConfig.foundationName}
);
}
// 1. SUPERADMIN VIEW
function SuperAdminView({ users, setUsers, registrations, setRegistrations, formConfig, setFormConfig, appConfig, setAppConfig }) {
const [tab, setTab] = useState('pendaftar'); // pendaftar | users | form | settings
return (
} color="bg-blue-500" />
} color="bg-purple-500" />
} color="bg-orange-500" />
r.status === 'Menunggu').length} icon={} color="bg-yellow-500" />
{tab === 'pendaftar' ? (
) : tab === 'users' ? (
) : tab === 'form' ? (
) : (
)}
);
}
// APP SETTINGS EDITOR (NEW COMPONENT)
function AppSettingsEditor({ appConfig, setAppConfig }) {
const [formData, setFormData] = useState({ ...appConfig });
const handleLogoUpload = (e) => {
const file = e.target.files[0];
if (file) {
if (file.size > 2 * 1024 * 1024) {
alert("Ukuran logo terlalu besar! Maksimal 2MB.");
return;
}
const reader = new FileReader();
reader.onloadend = () => {
setFormData(prev => ({ ...prev, logoUrl: reader.result }));
};
reader.readAsDataURL(file);
}
};
const handleSave = (e) => {
e.preventDefault();
setAppConfig(formData);
alert('Pengaturan aplikasi berhasil disimpan!');
};
return (
Identitas Aplikasi & Yayasan
Sesuaikan nama aplikasi, yayasan, dan logo yang tampil di halaman login/daftar.
);
}
// EDITOR FORMULIR
function FormEditor({ formConfig, setFormConfig }) {
const [isEditing, setIsEditing] = useState(false);
const [editId, setEditId] = useState(null);
const [formData, setFormData] = useState({
label: '',
type: 'text',
key: '',
options: '',
required: true,
allowCustom: false,
dependsOn: '',
visibleForJenjang: []
});
const resetForm = () => {
setFormData({ label: '', type: 'text', key: '', options: '', required: true, allowCustom: false, dependsOn: '', visibleForJenjang: [] });
setEditId(null);
setIsEditing(false);
};
const handleDelete = (id) => {
if (confirm('Yakin ingin menghapus field ini? PERINGATAN: Menghapus field sistem dapat mengganggu tampilan tabel data.')) {
setFormConfig(formConfig.filter(f => f.id !== id));
}
};
const handleMoveUp = (index) => {
if (index === 0) return;
const newConfig = [...formConfig];
const temp = newConfig[index];
newConfig[index] = newConfig[index - 1];
newConfig[index - 1] = temp;
setFormConfig(newConfig);
};
const handleMoveDown = (index) => {
if (index === formConfig.length - 1) return;
const newConfig = [...formConfig];
const temp = newConfig[index];
newConfig[index] = newConfig[index + 1];
newConfig[index + 1] = temp;
setFormConfig(newConfig);
};
const handleEditClick = (field) => {
setEditId(field.id);
let optionsString = '';
if (field.dependsOn && field.optionsMapping) {
optionsString = Object.entries(field.optionsMapping)
.map(([key, opts]) => `${key} : ${opts.join(', ')}`)
.join(' | ');
} else {
optionsString = field.options ? field.options.join(', ') : '';
}
setFormData({
label: field.label,
type: field.type,
key: field.key,
options: optionsString,
required: field.required,
allowCustom: field.allowCustom || false,
dependsOn: field.dependsOn || '',
visibleForJenjang: field.visibleForJenjang || []
});
setIsEditing(true);
};
const handleSave = (e) => {
e.preventDefault();
let optionsArray = undefined;
let optionsMapping = undefined;
if (formData.type === 'select') {
if (formData.dependsOn) {
optionsMapping = {};
const groups = formData.options.split('|');
groups.forEach(group => {
const [parentKey, opts] = group.split(':');
if (parentKey && opts) {
optionsMapping[parentKey.trim()] = opts.split(',').map(o => o.trim());
}
});
} else {
optionsArray = formData.options.split(',').map(o => o.trim());
}
}
let key = formData.key;
if (!key && !editId) {
key = formData.label.toLowerCase().replace(/[^a-zA-Z0-9]/g, '');
}
const newFieldData = {
label: formData.label,
type: formData.type,
key: key,
required: formData.type === 'header' ? false : formData.required,
options: optionsArray,
optionsMapping: optionsMapping,
allowCustom: formData.type === 'select' ? formData.allowCustom : false,
dependsOn: formData.type === 'select' ? formData.dependsOn : undefined,
visibleForJenjang: formData.visibleForJenjang
};
if (editId) {
const updatedConfig = formConfig.map(f => {
if (f.id === editId) {
return { ...f, ...newFieldData };
}
return f;
});
setFormConfig(updatedConfig);
} else {
const id = `f${Date.now()}`;
setFormConfig([...formConfig, { id, ...newFieldData }]);
}
resetForm();
};
const toggleJenjang = (jenjang) => {
const current = formData.visibleForJenjang || [];
if (current.includes(jenjang)) {
setFormData({ ...formData, visibleForJenjang: current.filter(j => j !== jenjang) });
} else {
setFormData({ ...formData, visibleForJenjang: [...current, jenjang] });
}
};
const availableParents = formConfig.filter(f => f.type === 'select' && f.id !== editId);
return (
Editor Formulir Pendaftaran
Atur pertanyaan, urutan, dan visibilitas per jenjang.
{!isEditing && (
)}
{isEditing && (
{editId ? : }
{editId ? 'Edit Item' : 'Tambah Item Baru'}
)}
{formConfig.map((field, index) => (
{index + 1}
{field.label}
Key: {field.key}
Tipe: {field.type.toUpperCase()}
{field.dependsOn && (Ikut {field.dependsOn})}
{field.visibleForJenjang && field.visibleForJenjang.length > 0 ? (
Hanya: {field.visibleForJenjang.join(', ')}
) : (
Semua Jenjang
)}
))}
);
}
// 2. ADMIN JENJANG VIEW
function AdminJenjangView({ user, registrations, setRegistrations, formConfig }) {
const myRegistrations = registrations.filter(r => r.jenjang === user.jenjang);
return (
Portal Admin Jenjang: {user.jenjang}
Anda hanya dapat melihat dan mengelola calon siswa yang mendaftar di jenjang {user.jenjang}.
} color="bg-emerald-600" />
r.status === 'Diterima').length} icon={} color="bg-blue-500" />
r.status === 'Menunggu').length} icon={} color="bg-yellow-500" />
Daftar Calon Siswa {user.jenjang}
{myRegistrations.length === 0 ? (
Belum ada pendaftar untuk jenjang ini.
) : (
)}
);
}
// 3. PENDAFTAR VIEW
function PendaftarView({ user, registrations, setRegistrations, formConfig, setFormConfig }) {
const myRegistration = registrations.find(r => r.userId === user.id);
const [isEditing, setIsEditing] = useState(!myRegistration);
const initialData = {};
formConfig.forEach(field => {
if(field.type !== 'header') {
initialData[field.key] = '';
}
});
initialData['namaLengkap'] = user.name;
const [formData, setFormData] = useState(myRegistration || initialData);
const handleChange = (key, value) => {
const newData = { ...formData, [key]: value };
// Reset dependent fields if parent changes
formConfig.forEach(f => {
if (f.dependsOn === key) {
newData[f.key] = '';
}
});
setFormData(newData);
};
// HANDLE FILE UPLOAD
const handleFileChange = (key, e) => {
const file = e.target.files[0];
if (file) {
if (file.size > 2 * 1024 * 1024) { // 2MB Limit
alert("Ukuran file terlalu besar! Maksimal 2MB.");
e.target.value = null; // Reset input
return;
}
// Convert to Base64 for demo purposes
const reader = new FileReader();
reader.onloadend = () => {
setFormData(prev => ({ ...prev, [key]: reader.result }));
};
reader.readAsDataURL(file);
}
};
const handleSubmit = (e) => {
e.preventDefault();
let configUpdated = false;
const newFormConfig = formConfig.map(field => {
if (field.type === 'select' && field.allowCustom) {
const submittedValue = formData[field.key];
if (!submittedValue) return field;
if (field.dependsOn) {
const parentValue = formData[field.dependsOn];
if (parentValue && field.optionsMapping) {
const currentOptions = field.optionsMapping[parentValue] || [];
if (!currentOptions.includes(submittedValue)) {
configUpdated = true;
return {
...field,
optionsMapping: {
...field.optionsMapping,
[parentValue]: [...currentOptions, submittedValue]
}
};
}
}
} else if (field.options) {
if (!field.options.includes(submittedValue)) {
configUpdated = true;
return {
...field,
options: [...field.options, submittedValue]
};
}
}
}
return field;
});
if (configUpdated) {
setFormConfig(newFormConfig);
}
if (myRegistration) {
const updated = registrations.map(r => r.userId === user.id ? { ...r, ...formData } : r);
setRegistrations(updated);
alert('Data berhasil diperbarui!');
} else {
const newReg = {
id: `REG-${Math.floor(Math.random() * 1000)}`,
userId: user.id,
...formData,
status: 'Menunggu',
tanggalDaftar: new Date().toISOString().split('T')[0]
};
setRegistrations([...registrations, newReg]);
alert('Formulir berhasil dikirim!');
}
setIsEditing(false);
};
if (!isEditing && myRegistration) {
return (
Status: {myRegistration.status}
ID Pendaftaran: {myRegistration.id}
{formConfig.map(field => {
if (field.visibleForJenjang && field.visibleForJenjang.length > 0) {
if (!myRegistration.jenjang || !field.visibleForJenjang.includes(myRegistration.jenjang)) {
return null;
}
}
if (field.type === 'header') {
return (
{field.label}
);
}
const value = myRegistration[field.key];
return (
{field.type === 'file' ? (
value ? (
value.startsWith('data:image') ? (

) : (
Unduh Berkas
)
) :
Tidak ada file
) : (
{value || '-'}
)}
);
})}
);
}
return (
Formulir Pendaftaran Siswa Baru
);
}
// --- SHARED COMPONENTS ---
function StatCard({ title, value, icon, color }) {
return (
);
}
function RegistrationTable({ registrations, setRegistrations, readOnly = false, isJenjangRestricted = false, formConfig }) {
const updateStatus = (id, newStatus) => {
const updated = registrations.map(r => r.id === id ? { ...r, status: newStatus } : r);
setRegistrations(updated);
};
return (
| ID |
Nama Siswa |
Jenjang |
Status |
{!readOnly && Aksi | }
{registrations.map(reg => (
| {reg.id} |
{reg.namaLengkap}
{reg.asalSekolah || '-'}
|
{reg.jenjang}
|
{reg.status}
|
{!readOnly && (
|
)}
))}
);
}
// USER MANAGEMENT TABLE
function UserManagementTable({ users, setUsers }) {
const [editingUser, setEditingUser] = useState(null);
const [formData, setFormData] = useState({ name: '', email: '', password: '' });
const [searchQuery, setSearchQuery] = useState(''); // STATE PENCARIAN
const handleDelete = (id, role) => {
if (role === 'superadmin') {
alert("Tidak dapat menghapus Superadmin!");
return;
}
if (confirm('Apakah Anda yakin ingin menghapus akun pengguna ini secara permanen?')) {
setUsers(users.filter(u => u.id !== id));
}
};
const startEdit = (user) => {
setEditingUser(user.id);
setFormData({ name: user.name, email: user.email, password: user.password });
};
const handleSave = (e) => {
e.preventDefault();
setUsers(users.map(u => u.id === editingUser ? { ...u, ...formData } : u));
setEditingUser(null);
setFormData({ name: '', email: '', password: '' });
};
// LOGIKA FILTER PENCARIAN
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
user.email.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
{/* MODAL EDIT */}
{editingUser && (
)}
{/* INPUT PENCARIAN */}
{/* TABLE */}
| Nama |
Email |
Role |
Aksi |
{filteredUsers.length > 0 ? (
filteredUsers.map(u => (
| {u.name} |
{u.email} |
{u.role.replace('_', ' ')} {u.jenjang ? `(${u.jenjang})` : ''}
|
{u.role !== 'superadmin' && (
)}
|
))
) : (
|
Tidak ditemukan pengguna dengan kata kunci "{searchQuery}"
|
)}
);
}