diff --git a/src/app/api/budget-rules/route.ts b/src/app/api/budget-rules/route.ts index 00a8da4..8afd360 100644 --- a/src/app/api/budget-rules/route.ts +++ b/src/app/api/budget-rules/route.ts @@ -29,6 +29,11 @@ export async function POST(req: Request) { const budget = await prisma.budget.findFirst({ where: { id: budgetId, userId: session.user.id } }) if (!budget) return NextResponse.json({ error: 'Budget not found' }, { status: 404 }) + const existing = await prisma.budgetRule.findFirst({ + where: { budgetId, pattern: { equals: pattern, mode: 'insensitive' } }, + }) + if (existing) return NextResponse.json({ error: 'Duplicate rule pattern' }, { status: 409 }) + const rule = await prisma.budgetRule.create({ data: { userId: session.user.id, budgetId, pattern }, select: { id: true, budgetId: true, pattern: true }, diff --git a/src/components/budgets/BudgetRulesDialog.tsx b/src/components/budgets/BudgetRulesDialog.tsx index 2c00a1f..a2a965a 100644 --- a/src/components/budgets/BudgetRulesDialog.tsx +++ b/src/components/budgets/BudgetRulesDialog.tsx @@ -24,10 +24,14 @@ export function BudgetRulesDialog({ open, onOpenChange, budgetId, budgetName, ru const router = useRouter() const [pattern, setPattern] = useState('') const [adding, setAdding] = useState(false) + const [dupError, setDupError] = useState(false) async function handleAdd(e: React.FormEvent) { e.preventDefault() if (!pattern.trim()) return + const isDup = rules.some((r) => r.pattern.toLowerCase() === pattern.trim().toLowerCase()) + if (isDup) { setDupError(true); return } + setDupError(false) setAdding(true) await fetch('/api/budget-rules', { method: 'POST', @@ -36,6 +40,7 @@ export function BudgetRulesDialog({ open, onOpenChange, budgetId, budgetName, ru }) setPattern('') setAdding(false) + setDupError(false) router.refresh() } @@ -78,17 +83,22 @@ export function BudgetRulesDialog({ open, onOpenChange, budgetId, budgetName, ru )} -