import React, { useState, useEffect, useMemo } from 'react';
import {
Home, ArrowDownCircle, ArrowUpCircle, CreditCard, Settings,
Bell, PieChart, Plus, Trash2, Edit2, X, ChevronRight,
TrendingDown, Wallet, ShieldCheck, CheckCircle2,
Globe, Clock, BarChart2, Menu, Smartphone, ArrowRight
} from 'lucide-react';
const defaultCategories = ['Makanan', 'Transportasi', 'Kebutuhan', 'Hiburan', 'Lainnya'];
const defaultUser = { name: 'SobatQ', pin: '123' };
const formatRupiah = (number) => {
return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(number);
};
const DonutChart = ({ data }) => {
const total = data.reduce((sum, item) => sum + item.value, 0);
if (total === 0) return
Belum ada data
;
let cumulativePercent = 0;
return (
Total
{formatRupiah(total).replace('Rp', '')}
);
};
export default function App() {
const [isReady, setIsReady] = useState(false);
const [screen, setScreen] = useState('welcome'); // welcome, pin, main
const [activeTab, setActiveTab] = useState('dashboard');
const [pin, setPin] = useState('');
const [userConfig, setUserConfig] = useState(defaultUser);
const [transactions, setTransactions] = useState([]);
const [categories, setCategories] = useState(defaultCategories);
const [filterMonth, setFilterMonth] = useState(new Date().getMonth());
const [filterYear, setFilterYear] = useState(new Date().getFullYear());
useEffect(() => {
const savedConfig = localStorage.getItem('duweQ_config');
const savedTransactions = localStorage.getItem('duweQ_transactions');
const savedCategories = localStorage.getItem('duweQ_categories');
if (savedConfig) setUserConfig(JSON.parse(savedConfig));
if (savedTransactions) setTransactions(JSON.parse(savedTransactions));
if (savedCategories) setCategories(JSON.parse(savedCategories));
setTimeout(() => setIsReady(true), 800);
}, []);
useEffect(() => {
if (isReady) {
localStorage.setItem('duweQ_config', JSON.stringify(userConfig));
localStorage.setItem('duweQ_transactions', JSON.stringify(transactions));
localStorage.setItem('duweQ_categories', JSON.stringify(categories));
}
}, [userConfig, transactions, categories, isReady]);
const currentMonthTransactions = useMemo(() => {
return transactions.filter(t => {
const d = new Date(t.date);
return d.getMonth() === filterMonth && d.getFullYear() === filterYear;
});
}, [transactions, filterMonth, filterYear]);
const totalIncome = currentMonthTransactions.filter(t => t.type === 'income').reduce((sum, t) => sum + t.amount, 0);
const totalExpense = currentMonthTransactions.filter(t => t.type === 'expense').reduce((sum, t) => sum + t.amount, 0);
const totalCredit = currentMonthTransactions.filter(t => t.type === 'credit').reduce((sum, t) => sum + t.amount, 0);
const currentBalance = totalIncome - totalExpense - totalCredit;
const expenseByCategory = useMemo(() => {
const expenses = currentMonthTransactions.filter(t => t.type === 'expense');
const grouped = expenses.reduce((acc, curr) => {
acc[curr.category] = (acc[curr.category] || 0) + curr.amount;
return acc;
}, {});
const colors = ['#f87171', '#fbbf24', '#34d399', '#60a5fa', '#a78bfa', '#f472b6'];
return Object.keys(grouped).map((key, index) => ({
name: key,
value: grouped[key],
color: colors[index % colors.length]
})).sort((a, b) => b.value - a.value);
}, [currentMonthTransactions]);
const isOverspending = (totalExpense + totalCredit) > totalIncome && totalIncome > 0;
if (!isReady) {
return (
);
}
if (screen === 'welcome') {
return (
{/* Navbar */}
{/* Hero Section */}
Catat Keuanganmu,
Wujudkan Masa Depanmu
Kelola pengeluaran, pemasukan, dan kredit dengan mudah dalam satu aplikasi online.
Akses kapan saja, di mana saja melalui browser kamu.
{/* Central Mockup Area */}
{/* Floating Left Card (Pemasukan) */}
{/* Floating Right Card (Pengeluaran) */}
{/* The Phone Mockup */}
setScreen('pin')}
className="w-[300px] h-[620px] bg-white border-[12px] border-gray-900 rounded-[3rem] shadow-2xl relative overflow-hidden cursor-pointer hover:-translate-y-2 transition-transform duration-300 group z-10"
>
{/* Phone Dynamic Island / Notch */}
{/* Mockup Screen Content */}
{/* Mockup Header */}
Ringkasan Keuangan
Bulan Ini ▾
{/* Mockup Green Card */}
Saldo Saat Ini
Rp 5.240.000
{/* Mockup Categories */}
Kategori Transaksi
Lihat Semua >
{/* Mockup Chart */}
Kebutuhan50%
Transportasi20%
Makanan20%
{/* Mockup Bottom Nav */}
{/* Click Overlay */}
Buka Aplikasi
{/* Feature Cards Bottom (Sesuai Brosur) */}
Aman & Terpercaya
Data keuanganmu terlindungi dengan sistem keamanan tinggi.
Mudah & Cepat
Catat transaksi kapan saja, di mana saja hanya dalam hitungan detik.
Laporan Lengkap
Pantau keuanganmu dengan laporan visual yang mudah dipahami.
Pengingat Pintar
Ingatkan tagihan & cicilan tepat waktu, tidak ada yang terlewat.
);
}
if (screen === 'pin') {
const handlePinPress = (digit) => {
if (pin.length < 3) {
const newPin = pin + digit;
setPin(newPin);
if (newPin.length === 3) {
setTimeout(() => {
if (newPin === userConfig.pin) {
setScreen('main');
setPin('');
} else {
alert('PIN Salah!');
setPin('');
}
}, 300);
}
}
};
return (
Masukkan PIN
Halo {userConfig.name}, selamat datang kembali!
{[0, 1, 2].map((i) => (
i ? 'bg-white scale-110 shadow-lg' : 'bg-green-800'}`} />
))}
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 'C', 0, '<'].map((key) => (
))}
);
}
return (
{/* Header */}
duweQ
Hai, {userConfig.name} 👋
{isOverspending && }
{/* Main Content Area */}
{/* TAB: DASHBOARD */}
{activeTab === 'dashboard' && (
{/* Filter */}
{/* Warning Notif */}
{isOverspending && (
Peringatan Keuangan!
Pengeluaran & cicilanmu bulan ini melebihi pendapatan.
)}
{/* Balance Card 3D Effect */}
Saldo Saat Ini
{formatRupiah(currentBalance)}
Pemasukan
{formatRupiah(totalIncome)}
Pengeluaran
{formatRupiah(totalExpense + totalCredit)}
{/* Categories & Chart */}
Ringkasan Bulan Ini
{expenseByCategory.slice(0, 4).map((cat, i) => (
{cat.name}
{Math.round((cat.value / (totalExpense||1)) * 100)}%
))}
{/* Category List Clickable */}
Kategori Pengeluaran
{expenseByCategory.map((cat, i) => (
setActiveTab('expense')}>
{formatRupiah(cat.value)}
))}
{expenseByCategory.length === 0 &&
Belum ada pengeluaran.
}
)}
{/* TAB: MANAGER (Income, Expense, Credit) */}
{['income', 'expense', 'credit'].includes(activeTab) && (
)}
{/* TAB: SETTINGS */}
{activeTab === 'settings' && (
)}
{/* Bottom Navigation */}
} label="Beranda" active={activeTab === 'dashboard'} onClick={() => setActiveTab('dashboard')} />
} label="Masuk" active={activeTab === 'income'} onClick={() => setActiveTab('income')} color="text-green-400" />
} label="Keluar" active={activeTab === 'expense'} onClick={() => setActiveTab('expense')} color="text-red-400" />
} label="Kredit" active={activeTab === 'credit'} onClick={() => setActiveTab('credit')} color="text-yellow-400" />
} label="Profil" active={activeTab === 'settings'} onClick={() => setActiveTab('settings')} />
);
}
function NavButton({ icon, label, active, onClick, color = "text-gray-300" }) {
return (
);
}
function TransactionManager({ type, transactions, setTransactions, categories, setCategories }) {
const [isFormOpen, setIsFormOpen] = useState(false);
const [formData, setFormData] = useState({ id: null, amount: '', note: '', date: new Date().toISOString().split('T')[0], category: categories[0] });
const [newCat, setNewCat] = useState('');
const titles = {
income: { title: 'Pemasukan', icon:
},
expense: { title: 'Pengeluaran', icon:
},
credit: { title: 'Cicilan Kredit', icon:
}
};
const currentList = transactions.filter(t => t.type === type).sort((a, b) => new Date(b.date) - new Date(a.date));
const handleSave = () => {
if (!formData.amount || !formData.date) return alert('Lengkapi data!');
const newTx = {
...formData,
amount: parseFloat(formData.amount),
type: type,
id: formData.id || Date.now().toString()
};
if (formData.id) {
setTransactions(transactions.map(t => t.id === formData.id ? newTx : t));
} else {
setTransactions([...transactions, newTx]);
}
setIsFormOpen(false);
setFormData({ id: null, amount: '', note: '', date: new Date().toISOString().split('T')[0], category: categories[0] });
};
const handleEdit = (tx) => {
setFormData(tx);
setIsFormOpen(true);
};
const handleDelete = (id) => {
if (confirm('Hapus data ini?')) {
setTransactions(transactions.filter(t => t.id !== id));
}
};
const handleAddCategory = () => {
if(newCat && !categories.includes(newCat)) {
setCategories([...categories, newCat]);
setFormData({...formData, category: newCat});
setNewCat('');
}
};
return (
{titles[type].icon} {titles[type].title}
{isFormOpen && (
{formData.id ? 'Edit' : 'Tambah'} {titles[type].title}
)}
{currentList.map(tx => (
{formatRupiah(tx.amount)}
{new Date(tx.date).toLocaleDateString('id-ID', {day: 'numeric', month: 'short'})}
{type === 'expense' && <>•{tx.category}>}
{tx.note && <>•{tx.note}>}
))}
{currentList.length === 0 && (
Belum ada data {titles[type].title.toLowerCase()}.
)}
);
}