import React, { useState, useEffect, useMemo } from 'react';
import {
Users, Briefcase, FileText, Calculator, MessageSquare,
LayoutDashboard, LogOut, AlertCircle, CheckCircle, PieChart,
Download, FileSpreadsheet, TrendingUp, ChevronRight
} from 'lucide-react';
// --- KONFIGURASI TEMA & ROLE ---
const ROLES = {
DIREKTUR: { id: 'direktur', name: 'Yudis (Direktur)', pin: '1111' },
COO: { id: 'coo', name: 'Ramadhan (COO)', pin: '2222' },
FINANCE: { id: 'finance', name: 'Desi (Finance & Legal)', pin: '3333' },
HR: { id: 'hr', name: 'Irfan (HR Manager)', pin: '4444' },
INTERN: { id: 'intern', name: 'Intern / PKL', pin: '5555' }
};
// --- KOMPONEN UTAMA (APP) ---
export default function App() {
const [user, setUser] = useState(null);
const [activeTab, setActiveTab] = useState('dashboard');
useEffect(() => {
const savedUser = localStorage.getItem('rcc_user');
if (savedUser) setUser(JSON.parse(savedUser));
}, []);
const handleLogin = (roleKey, pin) => {
const role = ROLES[roleKey];
if (pin === role.pin) {
const userData = { role: role.id, name: role.name };
setUser(userData);
localStorage.setItem('rcc_user', JSON.stringify(userData));
if (role.id === 'finance') setActiveTab('finance');
else if (role.id === 'intern') setActiveTab('intern');
else setActiveTab('dashboard');
} else {
alert("PIN Salah! (Petunjuk: Coba 1111, 2222, 3333, 4444, 5555)");
}
};
const handleLogout = () => {
setUser(null);
localStorage.removeItem('rcc_user');
setActiveTab('dashboard');
};
if (!user) return
;
return (
{activeTab === 'dashboard' && }
{activeTab === 'memo' && }
{activeTab === 'finance' && }
{activeTab === 'hpp' && }
{activeTab === 'docs' && }
{activeTab === 'intern' && }
);
}
// --- KOMPONEN: LOGIN ---
function LoginScreen({ onLogin }) {
const [selectedRole, setSelectedRole] = useState('DIREKTUR');
const [pin, setPin] = useState('');
return (
{/* Background Ornaments */}
RCC
Majukan Skill
PORTAL MANAJEMEN EKSEKUTIF
setPin(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && onLogin(selectedRole, pin)}
/>
);
}
// --- KOMPONEN: SIDEBAR & HEADER ---
function Sidebar({ user, activeTab, setActiveTab, onLogout }) {
const navItems = [
{ id: 'dashboard', label: 'Overview', icon: LayoutDashboard, roles: ['direktur', 'coo'] },
{ id: 'memo', label: 'Komunikasi Internal', icon: MessageSquare, roles: ['direktur', 'coo', 'finance', 'hr', 'intern'] },
{ id: 'finance', label: 'Keuangan', icon: Briefcase, roles: ['direktur', 'coo', 'finance'] },
{ id: 'hpp', label: 'Kalkulator HPP', icon: Calculator, roles: ['direktur', 'coo', 'finance'] },
{ id: 'docs', label: 'Dokumen & SOP', icon: FileText, roles: ['direktur', 'coo', 'finance', 'hr'] },
{ id: 'intern', label: 'Manajemen Intern', icon: Users, roles: ['direktur', 'coo', 'hr', 'intern'] },
];
return (
RC
Workspace
PT Ranstra Creative
);
}
function Header({ title, user }) {
return (
);
}
function getTabTitle(tab) {
const titles = {
dashboard: 'Executive Overview', memo: 'Pusat Komunikasi', finance: 'Laporan Finansial',
hpp: 'Simulasi HPP & Harga', docs: 'Arsip & Legalitas', intern: 'Portal SDM & Intern'
};
return titles[tab] || 'Dashboard';
}
// --- MODUL 1: DASHBOARD OVERVIEW ---
function DashboardOverview({ user, setActiveTab }) {
return (
Distribusi Revenue (KBLI)
Pintasan Tindakan Cepat
);
}
function StatCard({ title, value, sub, color, icon: Icon }) {
const styles = {
green: 'from-emerald-400 to-green-500 shadow-green-500/20 text-green-700 bg-green-50',
blue: 'from-[#1A237E] to-blue-600 shadow-blue-500/20 text-blue-700 bg-blue-50',
yellow: 'from-[#FFD700] to-orange-400 shadow-orange-500/20 text-orange-800 bg-orange-50',
purple: 'from-purple-500 to-indigo-500 shadow-purple-500/20 text-purple-700 bg-purple-50'
}[color];
const [gradient, shadow, textColor, bgColor] = styles.split(' ');
return (
);
}
function ProgressBar({ label, percent, color }) {
return (
);
}
// --- MODUL 2: HPP CALCULATOR (FITUR UTAMA) ---
function HPPCalculator({ user }) {
const [data, setData] = useState({
gajiOwner: 5000000, gajiTim: 10000000, sewa: 2000000, listrik: 500000, ads: 1500000, software: 500000,
lisensiTahun: 3600000, hargaAset: 12000000, umurAset: 2,
material: 200000, feeFreelance: 500000, transport: 100000, buffer: 10,
targetProyek: 10, kapasitasMaks: 15, targetMargin: 50, jenisKeuntungan: 'margin', pajak: 11
});
const [skenario, setSkenario] = useState(1);
const handleChange = (e) => {
const { name, value } = e.target;
setData(prev => ({ ...prev, [name]: parseFloat(value) || 0 }));
};
const calc = useMemo(() => {
const targetProyekReal = Math.max(1, Math.round(data.targetProyek * skenario));
const fixedTotal = data.gajiOwner + data.gajiTim + data.sewa + data.listrik + data.ads + data.software;
const depTotal = (data.lisensiTahun / 12) + (data.umurAset > 0 ? (data.hargaAset / data.umurAset) / 12 : 0);
const varDasar = data.material + data.feeFreelance + data.transport;
const varTotal = varDasar + (varDasar * (data.buffer / 100));
const bebanTetapPerProyek = (fixedTotal + depTotal) / targetProyekReal;
const hppPerProyek = bebanTetapPerProyek + varTotal;
let hargaDasar = data.jenisKeuntungan === 'margin'
? hppPerProyek / (1 - (data.targetMargin / 100))
: hppPerProyek * (1 + (data.targetMargin / 100));
const pajakNominal = hargaDasar * (data.pajak / 100);
const hargaJualRekomendasi = hargaDasar + pajakNominal;
const profitPerProyek = hargaDasar - hppPerProyek;
const netProfitBulan = profitPerProyek * targetProyekReal;
const marginKontribusi = hargaDasar - varTotal;
const bepUnit = marginKontribusi > 0 ? Math.ceil((fixedTotal + depTotal) / marginKontribusi) : 0;
return {
targetProyekReal, fixedTotal, depTotal, varTotal, hppPerProyek,
hargaJualRekomendasi, bepUnit, netProfitBulan, pajakNominal, profitPerProyek, hargaDasar
};
}, [data, skenario]);
const formatRp = (num) => new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(num);
// Perhitungan Data Donut Chart
const totalBiaya = calc.hargaJualRekomendasi;
const pctFixed = (((calc.fixedTotal + calc.depTotal) / calc.targetProyekReal) / totalBiaya) * 100;
const pctVar = (calc.varTotal / totalBiaya) * 100;
const pctTax = (calc.pajakNominal / totalBiaya) * 100;
if (!['direktur', 'coo', 'finance'].includes(user.role)) {
return
Akses Ditolak. Anda tidak memiliki izin melihat modul keuangan.
;
}
return (
{/* FORM INPUT AREA */}
{/* SECTION 1 */}
1. Biaya Tetap & Operasional (Bulanan)
Beban yang harus dibayar ada/tidak ada proyek
{/* SECTION 2 & 3 */}
2. Penyusutan Tahunan
Dibagi otomatis per bulan
3. Biaya Variabel
Keluar untuk setiap 1 proyek baru
{/* SECTION 4 */}
4. Parameter Bisnis & Target
{/* DASHBOARD OUTPUT AREA (Kanan - STICKY) */}
Harga Jual Rekomendasi
{formatRp(calc.hargaJualRekomendasi)}
Per 1 Proyek (Termasuk Pajak {data.pajak}%)
{/* SVG Donut Chart Pure CSS/SVG */}
Margin
{data.targetMargin}%
Target Balik Modal (BEP)
Wajib Closing {calc.bepUnit} Proyek/Bln
);
}
function InputRp({ label, name, value, onChange }) {
return (
);
}
function LegendItem({ color, label, value }) {
return (
)
}
// --- MODUL LENGKAP LAINNYA (UI Placeholder) ---
function MemoModule({ user }) {
return (
Pusat Komunikasi
{['direktur', 'coo', 'hr'].includes(user.role) && }
Urgent
Update Harga Paket Majukan Skill
Dari: Direktur • 10 Apr 2026
);
}
function FinanceModule({ user }) {
if (!['direktur', 'coo', 'finance'].includes(user.role)) return
Akses Ditolak
;
return (
Buku Kas Digital
Modul pencatatan transaksi sedang dalam antrean pengembangan fase 2 (Integrasi Google Sheets).
);
}
function DocumentModule({ user }) {
if (user.role === 'intern') return
Akses Ditolak
;
return (
Arsip & Legalitas
| Nama Dokumen |
Status |
Aksi |
| SOP Pembuatan Reels Instagram |
Aktif |
|
);
}
function InternModule({ user }) {
if (user.role === 'finance') return
Akses Ditolak
;
return (
Portal SDM & Intern
Absensi Hari Ini
);
}