chore: init from v0

This commit is contained in:
Julien Froidefond
2025-11-27 09:51:18 +01:00
commit e9e44916fd
109 changed files with 15966 additions and 0 deletions

View File

@@ -0,0 +1,74 @@
import { NextResponse } from "next/server"
import { prisma } from "@/lib/prisma"
import type { Account } from "@/lib/types"
export async function POST(request: Request) {
try {
const account: Omit<Account, "id"> = await request.json()
const created = await prisma.account.create({
data: {
name: account.name,
bankId: account.bankId,
accountNumber: account.accountNumber,
type: account.type,
folderId: account.folderId,
balance: account.balance,
currency: account.currency,
lastImport: account.lastImport,
},
})
return NextResponse.json(created)
} catch (error) {
console.error("Error creating account:", error)
return NextResponse.json({ error: "Failed to create account" }, { status: 500 })
}
}
export async function PUT(request: Request) {
try {
const account: Account = await request.json()
const updated = await prisma.account.update({
where: { id: account.id },
data: {
name: account.name,
bankId: account.bankId,
accountNumber: account.accountNumber,
type: account.type,
folderId: account.folderId,
balance: account.balance,
currency: account.currency,
lastImport: account.lastImport,
},
})
return NextResponse.json(updated)
} catch (error) {
console.error("Error updating account:", error)
return NextResponse.json({ error: "Failed to update account" }, { status: 500 })
}
}
export async function DELETE(request: Request) {
try {
const { searchParams } = new URL(request.url)
const id = searchParams.get("id")
if (!id) {
return NextResponse.json({ error: "Account ID is required" }, { status: 400 })
}
// Transactions will be deleted automatically due to onDelete: Cascade
await prisma.account.delete({
where: { id },
})
return NextResponse.json({ success: true })
} catch (error) {
console.error("Error deleting account:", error)
return NextResponse.json({ error: "Failed to delete account" }, { status: 500 })
}
}

View File

@@ -0,0 +1,79 @@
import { NextResponse } from "next/server"
import { prisma } from "@/lib/prisma"
import type { Category } from "@/lib/types"
export async function POST(request: Request) {
try {
const category: Omit<Category, "id"> = await request.json()
const created = await prisma.category.create({
data: {
name: category.name,
color: category.color,
icon: category.icon,
keywords: JSON.stringify(category.keywords),
parentId: category.parentId,
},
})
return NextResponse.json({
...created,
keywords: JSON.parse(created.keywords),
})
} catch (error) {
console.error("Error creating category:", error)
return NextResponse.json({ error: "Failed to create category" }, { status: 500 })
}
}
export async function PUT(request: Request) {
try {
const category: Category = await request.json()
const updated = await prisma.category.update({
where: { id: category.id },
data: {
name: category.name,
color: category.color,
icon: category.icon,
keywords: JSON.stringify(category.keywords),
parentId: category.parentId,
},
})
return NextResponse.json({
...updated,
keywords: JSON.parse(updated.keywords),
})
} catch (error) {
console.error("Error updating category:", error)
return NextResponse.json({ error: "Failed to update category" }, { status: 500 })
}
}
export async function DELETE(request: Request) {
try {
const { searchParams } = new URL(request.url)
const id = searchParams.get("id")
if (!id) {
return NextResponse.json({ error: "Category ID is required" }, { status: 400 })
}
// Remove category from transactions (set to null)
await prisma.transaction.updateMany({
where: { categoryId: id },
data: { categoryId: null },
})
await prisma.category.delete({
where: { id },
})
return NextResponse.json({ success: true })
} catch (error) {
console.error("Error deleting category:", error)
return NextResponse.json({ error: "Failed to delete category" }, { status: 500 })
}
}

View File

@@ -0,0 +1,94 @@
import { NextResponse } from "next/server"
import { prisma } from "@/lib/prisma"
import type { Folder } from "@/lib/types"
export async function POST(request: Request) {
try {
const folder: Omit<Folder, "id"> = await request.json()
const created = await prisma.folder.create({
data: {
name: folder.name,
parentId: folder.parentId,
color: folder.color,
icon: folder.icon,
},
})
return NextResponse.json(created)
} catch (error) {
console.error("Error creating folder:", error)
return NextResponse.json({ error: "Failed to create folder" }, { status: 500 })
}
}
export async function PUT(request: Request) {
try {
const folder: Folder = await request.json()
const updated = await prisma.folder.update({
where: { id: folder.id },
data: {
name: folder.name,
parentId: folder.parentId,
color: folder.color,
icon: folder.icon,
},
})
return NextResponse.json(updated)
} catch (error) {
console.error("Error updating folder:", error)
return NextResponse.json({ error: "Failed to update folder" }, { status: 500 })
}
}
export async function DELETE(request: Request) {
try {
const { searchParams } = new URL(request.url)
const id = searchParams.get("id")
if (!id) {
return NextResponse.json({ error: "Folder ID is required" }, { status: 400 })
}
const folder = await prisma.folder.findUnique({
where: { id },
include: { children: true },
})
if (!folder) {
return NextResponse.json({ error: "Folder not found" }, { status: 404 })
}
// Move accounts to root (null)
await prisma.account.updateMany({
where: { folderId: id },
data: { folderId: null },
})
// Move subfolders to parent
if (folder.parentId) {
await prisma.folder.updateMany({
where: { parentId: id },
data: { parentId: folder.parentId },
})
} else {
// If no parent, move to null (root)
await prisma.folder.updateMany({
where: { parentId: id },
data: { parentId: null },
})
}
await prisma.folder.delete({
where: { id },
})
return NextResponse.json({ success: true })
} catch (error) {
console.error("Error deleting folder:", error)
return NextResponse.json({ error: "Failed to delete folder" }, { status: 500 })
}
}

79
app/api/banking/route.ts Normal file
View File

@@ -0,0 +1,79 @@
import { NextResponse } from "next/server"
import { prisma } from "@/lib/prisma"
import type { BankingData } from "@/lib/types"
export async function GET() {
try {
const [accounts, transactions, folders, categories, categoryRules] = await Promise.all([
prisma.account.findMany({
include: {
folder: true,
},
}),
prisma.transaction.findMany({
include: {
account: true,
category: true,
},
}),
prisma.folder.findMany(),
prisma.category.findMany(),
prisma.categoryRule.findMany(),
])
// Transform Prisma models to match our types
const data: BankingData = {
accounts: accounts.map((a) => ({
id: a.id,
name: a.name,
bankId: a.bankId,
accountNumber: a.accountNumber,
type: a.type as "CHECKING" | "SAVINGS" | "CREDIT_CARD" | "OTHER",
folderId: a.folderId,
balance: a.balance,
currency: a.currency,
lastImport: a.lastImport,
})),
transactions: transactions.map((t) => ({
id: t.id,
accountId: t.accountId,
date: t.date,
amount: t.amount,
description: t.description,
type: t.type as "DEBIT" | "CREDIT",
categoryId: t.categoryId,
isReconciled: t.isReconciled,
fitId: t.fitId,
memo: t.memo ?? undefined,
checkNum: t.checkNum ?? undefined,
})),
folders: folders.map((f) => ({
id: f.id,
name: f.name,
parentId: f.parentId,
color: f.color,
icon: f.icon,
})),
categories: categories.map((c) => ({
id: c.id,
name: c.name,
color: c.color,
icon: c.icon,
keywords: JSON.parse(c.keywords) as string[],
parentId: c.parentId,
})),
categoryRules: categoryRules.map((r) => ({
id: r.id,
categoryId: r.categoryId,
pattern: r.pattern,
isRegex: r.isRegex,
})),
}
return NextResponse.json(data)
} catch (error) {
console.error("Error fetching banking data:", error)
return NextResponse.json({ error: "Failed to fetch data" }, { status: 500 })
}
}

View File

@@ -0,0 +1,101 @@
import { NextResponse } from "next/server"
import { prisma } from "@/lib/prisma"
import type { Transaction } from "@/lib/types"
export async function POST(request: Request) {
try {
const transactions: Transaction[] = await request.json()
// Filter out duplicates based on fitId
const existingTransactions = await prisma.transaction.findMany({
where: {
accountId: { in: transactions.map((t) => t.accountId) },
fitId: { in: transactions.map((t) => t.fitId) },
},
select: {
accountId: true,
fitId: true,
},
})
const existingSet = new Set(
existingTransactions.map((t) => `${t.accountId}-${t.fitId}`),
)
const newTransactions = transactions.filter(
(t) => !existingSet.has(`${t.accountId}-${t.fitId}`),
)
if (newTransactions.length === 0) {
return NextResponse.json({ count: 0, transactions: [] })
}
const created = await prisma.transaction.createMany({
data: newTransactions.map((t) => ({
accountId: t.accountId,
date: t.date,
amount: t.amount,
description: t.description,
type: t.type,
categoryId: t.categoryId,
isReconciled: t.isReconciled,
fitId: t.fitId,
memo: t.memo,
checkNum: t.checkNum,
})),
})
return NextResponse.json({ count: created.count, transactions: newTransactions })
} catch (error) {
console.error("Error creating transactions:", error)
return NextResponse.json({ error: "Failed to create transactions" }, { status: 500 })
}
}
export async function PUT(request: Request) {
try {
const transaction: Transaction = await request.json()
const updated = await prisma.transaction.update({
where: { id: transaction.id },
data: {
accountId: transaction.accountId,
date: transaction.date,
amount: transaction.amount,
description: transaction.description,
type: transaction.type,
categoryId: transaction.categoryId,
isReconciled: transaction.isReconciled,
fitId: transaction.fitId,
memo: transaction.memo,
checkNum: transaction.checkNum,
},
})
return NextResponse.json(updated)
} catch (error) {
console.error("Error updating transaction:", error)
return NextResponse.json({ error: "Failed to update transaction" }, { status: 500 })
}
}
export async function DELETE(request: Request) {
try {
const { searchParams } = new URL(request.url)
const id = searchParams.get("id")
if (!id) {
return NextResponse.json({ error: "Transaction ID is required" }, { status: 400 })
}
await prisma.transaction.delete({
where: { id },
})
return NextResponse.json({ success: true })
} catch (error) {
console.error("Error deleting transaction:", error)
return NextResponse.json({ error: "Failed to delete transaction" }, { status: 500 })
}
}