Spaces:
Build error
Build error
| import { useState, useEffect } from 'react'; | |
| import { X, Plus, Edit2 } from 'lucide-react'; | |
| const INCOME_CATEGORIES = [ | |
| 'Sales', | |
| 'Services', | |
| 'Consulting', | |
| 'Subscriptions', | |
| 'Refunds', | |
| 'Other Income', | |
| ]; | |
| const EXPENSE_CATEGORIES = [ | |
| 'Supplies', | |
| 'Equipment', | |
| 'Marketing', | |
| 'Utilities', | |
| 'Software', | |
| 'Travel', | |
| 'Professional Services', | |
| 'Taxes', | |
| 'Other', | |
| ]; | |
| export default function TransactionModal({ | |
| isOpen, | |
| onClose, | |
| onSubmit, | |
| type = 'income', | |
| editingTransaction = null, | |
| }) { | |
| const [formData, setFormData] = useState({ | |
| amount: '', | |
| category: '', | |
| description: '', | |
| date: new Date().toISOString().split('T')[0], | |
| }); | |
| useEffect(() => { | |
| if (editingTransaction) { | |
| setFormData({ | |
| amount: editingTransaction.amount.toString(), | |
| category: editingTransaction.category, | |
| description: editingTransaction.description || '', | |
| date: editingTransaction.date, | |
| }); | |
| } else { | |
| setFormData({ | |
| amount: '', | |
| category: '', | |
| description: '', | |
| date: new Date().toISOString().split('T')[0], | |
| }); | |
| } | |
| }, [editingTransaction, isOpen]); | |
| if (!isOpen) return null; | |
| const categories = type === 'income' ? INCOME_CATEGORIES : EXPENSE_CATEGORIES; | |
| const handleSubmit = (e) => { | |
| e.preventDefault(); | |
| const amount = parseFloat(formData.amount); | |
| if (!amount || isNaN(amount)) return; | |
| onSubmit({ | |
| id: editingTransaction?.id || Date.now(), | |
| type, | |
| amount: Math.abs(amount), | |
| category: formData.category, | |
| description: formData.description, | |
| date: formData.date, | |
| createdAt: editingTransaction?.createdAt || new Date().toISOString(), | |
| }); | |
| onClose(); | |
| }; | |
| return ( | |
| <div className="fixed inset-0 z-50 overflow-y-auto"> | |
| <div className="flex min-h-full items-center justify-center p-4"> | |
| {/* Backdrop */} | |
| <div | |
| className="fixed inset-0 bg-neutral-900/50 transition-opacity" | |
| onClick={onClose} | |
| /> | |
| {/* Modal */} | |
| <div className="relative bg-white rounded-xl shadow-xl max-w-md w-full animate-fadeIn"> | |
| {/* Header */} | |
| <div className="flex items-center justify-between p-6 border-b border-neutral-100"> | |
| <h2 className="text-lg font-semibold text-neutral-800"> | |
| {editingTransaction ? 'Edit' : 'Add'} {type === 'income' ? 'Income' : 'Expense'} | |
| </h2> | |
| <button | |
| onClick={onClose} | |
| className="p-2 text-neutral-400 hover:text-neutral-600 rounded-lg hover:bg-neutral-100 transition-colors" | |
| > | |
| <X className="w-5 h-5" /> | |
| </button> | |
| </div> | |
| {/* Form */} | |
| <form onSubmit={handleSubmit} className="p-6 space-y-4"> | |
| {/* Amount */} | |
| <div> | |
| <label className="label">Amount</label> | |
| <div className="relative"> | |
| <span className="absolute left-4 top-1/2 -translate-y-1/2 text-neutral-400"> | |
| $ | |
| </span> | |
| <input | |
| type="number" | |
| step="0.01" | |
| min="0" | |
| value={formData.amount} | |
| onChange={(e) => setFormData({ ...formData, amount: e.target.value })} | |
| className="input-field pl-8" | |
| placeholder="0.00" | |
| required | |
| autoFocus | |
| /> | |
| </div> | |
| </div> | |
| {/* Category */} | |
| <div> | |
| <label className="label">Category</label> | |
| <select | |
| value={formData.category} | |
| onChange={(e) => setFormData({ ...formData, category: e.target.value })} | |
| className="input-field" | |
| required | |
| > | |
| <option value="">Select a category</option> | |
| {categories.map((cat) => ( | |
| <option key={cat} value={cat}> | |
| {cat} | |
| </option> | |
| ))} | |
| </select> | |
| </div> | |
| {/* Description */} | |
| <div> | |
| <label className="label">Description (optional)</label> | |
| <input | |
| type="text" | |
| value={formData.description} | |
| onChange={(e) => setFormData({ ...formData, description: e.target.value })} | |
| className="input-field" | |
| placeholder="What was this for?" | |
| /> | |
| </div> | |
| {/* Date */} | |
| <div> | |
| <label className="label">Date</label> | |
| <input | |
| type="date" | |
| value={formData.date} | |
| onChange={(e) => setFormData({ ...formData, date: e.target.value })} | |
| className="input-field" | |
| required | |
| /> | |
| </div> | |
| {/* Actions */} | |
| <div className="flex gap-3 pt-4"> | |
| <button type="button" onClick={onClose} className="btn-secondary flex-1"> | |
| Cancel | |
| </button> | |
| <button type="submit" className={type === 'income' ? 'btn-success flex-1' : 'btn-danger flex-1'}> | |
| {editingTransaction ? ( | |
| <> | |
| <Edit2 className="w-4 h-4 inline mr-2" /> | |
| Save Changes | |
| </> | |
| ) : ( | |
| <> | |
| <Plus className="w-4 h-4 inline mr-2" /> | |
| Add {type === 'income' ? 'Income' : 'Expense'} | |
| </> | |
| )} | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } |