This commit is contained in:
2026-01-23 20:35:40 +03:00
parent cf3b0e48ec
commit 66c151653e
137 changed files with 41495 additions and 0 deletions

View File

@@ -0,0 +1,195 @@
import { Form } from "@remix-run/react";
import { useState, useEffect } from "react";
import { Input } from "~/components/ui/Input";
import { Button } from "~/components/ui/Button";
import { Flex } from "~/components/layout/Flex";
import { EXPENSE_CATEGORIES } from "~/lib/constants";
import type { Expense } from "@prisma/client";
interface ExpenseFormProps {
expense?: Expense;
onCancel: () => void;
errors?: Record<string, string>;
isLoading: boolean;
}
export function ExpenseForm({
expense,
onCancel,
errors = {},
isLoading,
}: ExpenseFormProps) {
const [formData, setFormData] = useState({
description: expense?.description || "",
category: expense?.category || "",
amount: expense?.amount?.toString() || "",
expenseDate: expense?.expenseDate
? new Date(expense.expenseDate).toISOString().split('T')[0]
: new Date().toISOString().split('T')[0],
});
// Reset form data when expense changes
useEffect(() => {
if (expense) {
setFormData({
description: expense.description || "",
category: expense.category || "",
amount: expense.amount?.toString() || "",
expenseDate: expense.expenseDate
? new Date(expense.expenseDate).toISOString().split('T')[0]
: new Date().toISOString().split('T')[0],
});
} else {
setFormData({
description: "",
category: "",
amount: "",
expenseDate: new Date().toISOString().split('T')[0],
});
}
}, [expense]);
const handleInputChange = (field: string, value: string) => {
setFormData(prev => ({
...prev,
[field]: value,
}));
};
const isEditing = !!expense;
return (
<Form method="post" className="space-y-6">
<input
type="hidden"
name="_action"
value={isEditing ? "update" : "create"}
/>
{isEditing && (
<input type="hidden" name="id" value={expense.id} />
)}
{/* Description */}
<div>
<label htmlFor="description" className="block text-sm font-medium text-gray-700 mb-2">
وصف المصروف *
</label>
<Input
id="description"
name="description"
type="text"
value={formData.description}
onChange={(e) => handleInputChange("description", e.target.value)}
placeholder="أدخل وصف المصروف"
error={errors.description}
required
disabled={isLoading}
/>
{errors.description && (
<p className="mt-1 text-sm text-red-600">{errors.description}</p>
)}
</div>
{/* Category */}
<div>
<label htmlFor="category" className="block text-sm font-medium text-gray-700 mb-2">
الفئة *
</label>
<select
id="category"
name="category"
value={formData.category}
onChange={(e) => handleInputChange("category", e.target.value)}
required
disabled={isLoading}
className={`
w-full px-3 py-2 border rounded-lg shadow-sm
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500
disabled:bg-gray-50 disabled:text-gray-500
${errors.category
? 'border-red-300 focus:ring-red-500 focus:border-red-500'
: 'border-gray-300'
}
`}
>
<option value="">اختر الفئة</option>
{EXPENSE_CATEGORIES.map((cat) => (
<option key={cat.value} value={cat.value}>
{cat.label}
</option>
))}
</select>
{errors.category && (
<p className="mt-1 text-sm text-red-600">{errors.category}</p>
)}
</div>
{/* Amount */}
<div>
<label htmlFor="amount" className="block text-sm font-medium text-gray-700 mb-2">
المبلغ *
</label>
<Input
id="amount"
name="amount"
type="number"
step="0.01"
min="0.01"
value={formData.amount}
onChange={(e) => handleInputChange("amount", e.target.value)}
placeholder="0.00"
error={errors.amount}
required
disabled={isLoading}
dir="ltr"
/>
{errors.amount && (
<p className="mt-1 text-sm text-red-600">{errors.amount}</p>
)}
</div>
{/* Expense Date */}
<div>
<label htmlFor="expenseDate" className="block text-sm font-medium text-gray-700 mb-2">
تاريخ المصروف
</label>
<Input
id="expenseDate"
name="expenseDate"
type="date"
value={formData.expenseDate}
onChange={(e) => handleInputChange("expenseDate", e.target.value)}
error={errors.expenseDate}
disabled={isLoading}
/>
{errors.expenseDate && (
<p className="mt-1 text-sm text-red-600">{errors.expenseDate}</p>
)}
</div>
{/* Form Actions */}
<Flex justify="end" className="pt-4 gap-2 border-t">
<Button
type="button"
variant="outline"
onClick={onCancel}
disabled={isLoading}
className="w-20"
>
إلغاء
</Button>
<Button
type="submit"
disabled={isLoading || !formData.description.trim() || !formData.category || !formData.amount}
className="bg-blue-600 hover:bg-blue-700"
>
{isLoading
? (isEditing ? "جاري التحديث..." : "جاري الإنشاء...")
: (isEditing ? "تحديث المصروف" : "إنشاء المصروف")
}
</Button>
</Flex>
</Form>
);
}