CoolFace
Apppublic

spdniloy/anycoder-380d34cb

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
TransactionModal.js191 linesDownload Raw Back to components
1import { useState, useEffect } from 'react';2import { X, Plus, Edit2 } from 'lucide-react';3 4const INCOME_CATEGORIES = [5  'Sales',6  'Services',7  'Consulting',8  'Subscriptions',9  'Refunds',10  'Other Income',11];12 13const EXPENSE_CATEGORIES = [14  'Supplies',15  'Equipment',16  'Marketing',17  'Utilities',18  'Software',19  'Travel',20  'Professional Services',21  'Taxes',22  'Other',23];24 25export default function TransactionModal({26  isOpen,27  onClose,28  onSubmit,29  type = 'income',30  editingTransaction = null,31}) {32  const [formData, setFormData] = useState({33    amount: '',34    category: '',35    description: '',36    date: new Date().toISOString().split('T')[0],37  });38 39  useEffect(() => {40    if (editingTransaction) {41      setFormData({42        amount: editingTransaction.amount.toString(),43        category: editingTransaction.category,44        description: editingTransaction.description || '',45        date: editingTransaction.date,46      });47    } else {48      setFormData({49        amount: '',50        category: '',51        description: '',52        date: new Date().toISOString().split('T')[0],53      });54    }55  }, [editingTransaction, isOpen]);56 57  if (!isOpen) return null;58 59  const categories = type === 'income' ? INCOME_CATEGORIES : EXPENSE_CATEGORIES;60 61  const handleSubmit = (e) => {62    e.preventDefault();63    const amount = parseFloat(formData.amount);64    if (!amount || isNaN(amount)) return;65 66    onSubmit({67      id: editingTransaction?.id || Date.now(),68      type,69      amount: Math.abs(amount),70      category: formData.category,71      description: formData.description,72      date: formData.date,73      createdAt: editingTransaction?.createdAt || new Date().toISOString(),74    });75    onClose();76  };77 78  return (79    <div className="fixed inset-0 z-50 overflow-y-auto">80      <div className="flex min-h-full items-center justify-center p-4">81        {/* Backdrop */}82        <div83          className="fixed inset-0 bg-neutral-900/50 transition-opacity"84          onClick={onClose}85        />86 87        {/* Modal */}88        <div className="relative bg-white rounded-xl shadow-xl max-w-md w-full animate-fadeIn">89          {/* Header */}90          <div className="flex items-center justify-between p-6 border-b border-neutral-100">91            <h2 className="text-lg font-semibold text-neutral-800">92              {editingTransaction ? 'Edit' : 'Add'} {type === 'income' ? 'Income' : 'Expense'}93            </h2>94            <button95              onClick={onClose}96              className="p-2 text-neutral-400 hover:text-neutral-600 rounded-lg hover:bg-neutral-100 transition-colors"97            >98              <X className="w-5 h-5" />99            </button>100          </div>101 102          {/* Form */}103          <form onSubmit={handleSubmit} className="p-6 space-y-4">104            {/* Amount */}105            <div>106              <label className="label">Amount</label>107              <div className="relative">108                <span className="absolute left-4 top-1/2 -translate-y-1/2 text-neutral-400">109                  $110                </span>111                <input112                  type="number"113                  step="0.01"114                  min="0"115                  value={formData.amount}116                  onChange={(e) => setFormData({ ...formData, amount: e.target.value })}117                  className="input-field pl-8"118                  placeholder="0.00"119                  required120                  autoFocus121                />122              </div>123            </div>124 125            {/* Category */}126            <div>127              <label className="label">Category</label>128              <select129                value={formData.category}130                onChange={(e) => setFormData({ ...formData, category: e.target.value })}131                className="input-field"132                required133              >134                <option value="">Select a category</option>135                {categories.map((cat) => (136                  <option key={cat} value={cat}>137                    {cat}138                  </option>139                ))}140              </select>141            </div>142 143            {/* Description */}144            <div>145              <label className="label">Description (optional)</label>146              <input147                type="text"148                value={formData.description}149                onChange={(e) => setFormData({ ...formData, description: e.target.value })}150                className="input-field"151                placeholder="What was this for?"152              />153            </div>154 155            {/* Date */}156            <div>157              <label className="label">Date</label>158              <input159                type="date"160                value={formData.date}161                onChange={(e) => setFormData({ ...formData, date: e.target.value })}162                className="input-field"163                required164              />165            </div>166 167            {/* Actions */}168            <div className="flex gap-3 pt-4">169              <button type="button" onClick={onClose} className="btn-secondary flex-1">170                Cancel171              </button>172              <button type="submit" className={type === 'income' ? 'btn-success flex-1' : 'btn-danger flex-1'}>173                {editingTransaction ? (174                  <>175                    <Edit2 className="w-4 h-4 inline mr-2" />176                    Save Changes177                  </>178                ) : (179                  <>180                    <Plus className="w-4 h-4 inline mr-2" />181                    Add {type === 'income' ? 'Income' : 'Expense'}182                  </>183                )}184              </button>185            </div>186          </form>187        </div>188      </div>189    </div>190  );191}