From 513e2efdbc1d131e7993f18fe2162db6bcaa1cdb Mon Sep 17 00:00:00 2001 From: Dita Aji Pratama Date: Sat, 18 Apr 2026 12:21:04 +0700 Subject: [PATCH] feat: create TransactionForm component - Add useState hooks for form inputs - Add type selector (income/expense) - Add TextInput components - Add category selector with ScrollView - Add TouchableOpacity for buttons --- components/TransactionForm.tsx | 224 +++++++++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 components/TransactionForm.tsx diff --git a/components/TransactionForm.tsx b/components/TransactionForm.tsx new file mode 100644 index 0000000..599dedd --- /dev/null +++ b/components/TransactionForm.tsx @@ -0,0 +1,224 @@ +import React, { useState } from 'react'; +import { + View, + Text, + TextInput, + TouchableOpacity, + StyleSheet, + ScrollView, +} from 'react-native'; +import { COLORS, FONTS } from '../constants/theme'; +import { Transaction } from '../types'; +import { generateId, getCurrentDate } from '../utils/helpers'; + +interface TransactionFormProps { + onAdd: (transaction: Transaction) => void; +} + +const CATEGORIES = [ + 'makanan', 'transport', 'belanja', 'hiburan', 'kesehatan', + 'pendidikan', 'investasi', 'gaji', 'lainnya' +]; + +export const TransactionForm: React.FC = ({ onAdd }) => { + const [amount, setAmount] = useState(''); + const [description, setDescription] = useState(''); + const [type, setType] = useState<'income' | 'expense'>('expense'); + const [category, setCategory] = useState('lainnya'); + + const handleSubmit = () => { + if (!amount || !description) return; + + const transaction: Transaction = { + id: generateId(), + amount: parseInt(amount, 10), + description, + type, + category, + date: getCurrentDate(), + }; + + onAdd(transaction); + setAmount(''); + setDescription(''); + }; + + return ( + + Tambah Transaksi + + + setType('expense')} + > + + Pengeluaran + + + setType('income')} + > + + Pemasukan + + + + + + + + + Kategori + + + {CATEGORIES.map((cat) => ( + setCategory(cat)} + > + + {cat} + + + ))} + + + + + Simpan + + + ); +}; + +const styles = StyleSheet.create({ + container: { + backgroundColor: COLORS.card, + margin: 16, + marginTop: 0, + padding: 16, + borderRadius: 12, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 3, + }, + title: { + fontSize: FONTS.regular, + fontWeight: 'bold', + color: COLORS.text, + marginBottom: 16, + }, + typeContainer: { + flexDirection: 'row', + marginBottom: 16, + gap: 8, + }, + typeButton: { + flex: 1, + padding: 12, + borderRadius: 8, + alignItems: 'center', + backgroundColor: COLORS.background, + }, + typeButtonActiveExpense: { + backgroundColor: COLORS.expense, + }, + typeButtonActiveIncome: { + backgroundColor: COLORS.income, + }, + typeText: { + fontSize: FONTS.regular, + color: COLORS.textSecondary, + }, + typeTextActive: { + color: COLORS.card, + fontWeight: '600', + }, + input: { + backgroundColor: COLORS.background, + borderRadius: 8, + padding: 12, + marginBottom: 12, + fontSize: FONTS.regular, + color: COLORS.text, + }, + categoryLabel: { + fontSize: FONTS.small, + color: COLORS.textSecondary, + marginBottom: 8, + }, + categoryContainer: { + flexDirection: 'row', + gap: 8, + marginBottom: 16, + }, + categoryButton: { + paddingHorizontal: 12, + paddingVertical: 8, + borderRadius: 16, + backgroundColor: COLORS.background, + }, + categoryButtonActive: { + backgroundColor: COLORS.primary, + }, + categoryText: { + fontSize: FONTS.small, + color: COLORS.textSecondary, + }, + categoryTextActive: { + color: COLORS.card, + }, + submitButton: { + backgroundColor: COLORS.primary, + padding: 14, + borderRadius: 8, + alignItems: 'center', + }, + submitText: { + color: COLORS.card, + fontSize: FONTS.regular, + fontWeight: '600', + }, +}); \ No newline at end of file