import { LinearGradient } from 'expo-linear-gradient'; import { useFocusEffect, useRoute } from '@react-navigation/native'; import type { RouteProp } from '@react-navigation/native'; import { useCallback, useEffect, useState } from 'react'; import { Alert, Image, KeyboardAvoidingView, Platform, Pressable, ScrollView, StyleSheet, Text, TextInput, View, } from 'react-native'; import { colors } from '../theme/colors'; import { formatRibuan, formatRupiah } from '../utils/format'; import { getCategoryIcon3d } from '../utils/categoryIcons3d'; import { addTransaction, getCategories, getSettingNumber } from '../db/repositories'; import type { Category, TransactionType } from '../types'; import type { RootTabParamList } from '../navigation'; export function AddTransactionScreen() { const route = useRoute>(); const [type, setType] = useState( route.params?.type ?? 'expense' ); // Quick action dari Beranda: ganti tipe setiap kali params berubah useEffect(() => { const t = route.params?.type; if (t) { setType(t); loadCategories(t); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [route.params?.type]); const [amountText, setAmountText] = useState(''); const [categories, setCategories] = useState([]); const [selectedCategoryId, setSelectedCategoryId] = useState(null); const [note, setNote] = useState(''); const [percent, setPercent] = useState(10); const [saving, setSaving] = useState(false); const loadCategories = useCallback(async (t: TransactionType) => { const cats = await getCategories(t); setCategories(cats); setSelectedCategoryId(cats[0]?.id ?? null); }, []); useFocusEffect( useCallback(() => { let active = true; (async () => { const p = await getSettingNumber('auto_save_percent', 10); if (active) setPercent(p); })(); loadCategories(type); return () => { active = false; }; }, [loadCategories, type]) ); const amount = Number(amountText.replace(/[^0-9]/g, '')) || 0; const savedAmount = type === 'income' ? Math.round((amount * percent) / 100) : 0; const switchType = (t: TransactionType) => { setType(t); setAmountText(''); loadCategories(t); }; const handleSave = async () => { if (amount <= 0) { Alert.alert('Nominal kosong', 'Isi nominal dulu ya, Mas.'); return; } setSaving(true); try { await addTransaction({ type, amount, categoryId: selectedCategoryId, note: note.trim(), }); if (type === 'income') { Alert.alert( 'Pemasukan tercatat ✅', `${formatRupiah(amount)} masuk, dan ${formatRupiah(savedAmount)} otomatis disisihkan ke Tabungan (${percent}%).` ); } else { Alert.alert('Pengeluaran tercatat ✅', `${formatRupiah(amount)} dicatat sebagai pengeluaran.`); } setAmountText(''); setNote(''); setSelectedCategoryId(categories[0]?.id ?? null); } catch (e) { Alert.alert('Gagal menyimpan', String(e)); } finally { setSaving(false); } }; return ( {/* Toggle tipe */} switchType('income')} > + Pemasukan switchType('expense')} > - Pengeluaran {/* Nominal */} NOMINAL setAmountText(t.replace(/[^0-9]/g, ''))} placeholder="Rp 0" placeholderTextColor={colors.border} keyboardType="number-pad" autoFocus /> {/* Auto-sisih info */} {type === 'income' && amount > 0 && ( {formatRupiah(savedAmount)} otomatis masuk Tabungan ({percent}%) )} {/* Kategori */} Kategori {categories.map((c) => { const selected = c.id === selectedCategoryId; const icon3d = getCategoryIcon3d(c.name, type); return ( setSelectedCategoryId(c.id)} > {icon3d ? ( ) : ( {c.icon} )} {c.name} ); })} {/* Catatan */} Catatan (opsional) {/* Tombol Simpan fixed */} [styles.savePressable, pressed && styles.pressed]} > {saving ? 'Menyimpan...' : 'Simpan'} ); } const styles = StyleSheet.create({ flex: { flex: 1, backgroundColor: colors.background }, container: { flex: 1 }, content: { padding: 16, paddingBottom: 110 }, toggleRow: { flexDirection: 'row', backgroundColor: colors.surface, borderRadius: 14, borderWidth: 1, borderColor: colors.border, padding: 4, marginBottom: 12, }, toggleBtn: { flex: 1, alignItems: 'center', paddingVertical: 10, borderRadius: 10, }, toggleIncome: { backgroundColor: colors.incomeSoft }, toggleExpense: { backgroundColor: colors.expenseSoft }, toggleText: { fontSize: 14, color: colors.textMuted }, card: { backgroundColor: colors.surface, borderRadius: 16, padding: 16, borderWidth: 1, borderColor: colors.border, }, amountCard: { marginBottom: 12, overflow: 'hidden', }, amountIncome: { borderColor: '#BBF7D0' }, amountExpense: { borderColor: '#FECACA' }, amountCoin: { position: 'absolute', right: -14, top: -4, width: 114, height: 115, opacity: 0.3, transform: [{ perspective: 600 }, { rotateY: '-20deg' }], }, cardLabel: { fontSize: 11, fontWeight: '700', letterSpacing: 1, color: colors.textMuted, }, amountInput: { fontSize: 34, fontWeight: '800', color: colors.text, paddingVertical: 4, }, amountPreview: { fontSize: 13, color: colors.textMuted }, saveHintCard: { flexDirection: 'row', alignItems: 'center', gap: 8, backgroundColor: colors.primarySoft, borderColor: colors.primarySoft, marginBottom: 12, }, saveHintCoin: { width: 26, height: 26 }, saveHintText: { color: colors.primaryDark, fontWeight: '600', fontSize: 13, flex: 1 }, sectionTitle: { fontSize: 14, fontWeight: '700', color: colors.text, marginBottom: 8, marginTop: 8, }, categoryGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 8 }, categoryChip: { flexDirection: 'row', alignItems: 'center', gap: 6, backgroundColor: colors.surface, borderWidth: 1, borderColor: colors.border, borderRadius: 12, paddingHorizontal: 12, paddingVertical: 8, }, categoryChipSelected: { borderColor: colors.primary, backgroundColor: colors.primarySoft, }, categoryIcon: { fontSize: 16 }, categoryIcon3d: { width: 22, height: 22 }, categoryName: { fontSize: 13, color: colors.text }, noteInput: { paddingVertical: 10, fontSize: 14, color: colors.text }, saveBar: { padding: 16, paddingBottom: 84, backgroundColor: colors.background, borderTopWidth: 1, borderTopColor: colors.border, }, savePressable: { borderRadius: 14, shadowColor: colors.primaryDark, shadowOpacity: 0.25, shadowRadius: 8, shadowOffset: { width: 0, height: 4 }, elevation: 4, }, saveButton: { backgroundColor: colors.primary, borderRadius: 14, paddingVertical: 15, alignItems: 'center', }, saveButtonText: { color: colors.white, fontWeight: '700', fontSize: 16 }, pressed: { opacity: 0.85 }, });