feat: add ReconcileDateRangeCard to settings page; enhance date picker layout in statistics and transaction filters components
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 1m24s

This commit is contained in:
Julien Froidefond
2025-12-20 12:05:30 +01:00
parent 8b81dfe8c0
commit 53798176a0
7 changed files with 439 additions and 70 deletions

View File

@@ -0,0 +1,69 @@
import { NextRequest, NextResponse } from "next/server";
import { revalidatePath } from "next/cache";
import { transactionService } from "@/services/transaction.service";
import { requireAuth } from "@/lib/auth-utils";
export async function POST(request: NextRequest) {
const authError = await requireAuth();
if (authError) return authError;
try {
const body = await request.json();
const { startDate, endDate, reconciled = true } = body;
if (!endDate) {
return NextResponse.json(
{ error: "endDate is required" },
{ status: 400 },
);
}
// Validate date format (YYYY-MM-DD)
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (startDate && !dateRegex.test(startDate)) {
return NextResponse.json(
{ error: "Invalid startDate format. Expected YYYY-MM-DD" },
{ status: 400 },
);
}
if (!dateRegex.test(endDate)) {
return NextResponse.json(
{ error: "Invalid endDate format. Expected YYYY-MM-DD" },
{ status: 400 },
);
}
if (startDate && startDate > endDate) {
return NextResponse.json(
{ error: "startDate must be before or equal to endDate" },
{ status: 400 },
);
}
const result = await transactionService.reconcileByDateRange(
startDate,
endDate,
reconciled,
);
// Revalider le cache des pages
revalidatePath("/transactions", "page");
revalidatePath("/statistics", "page");
revalidatePath("/dashboard", "page");
revalidatePath("/settings", "page");
return NextResponse.json(result, {
headers: {
"Cache-Control": "no-store",
},
});
} catch (error) {
console.error("Error reconciling transactions by date range:", error);
const errorMessage =
error instanceof Error
? error.message
: "Failed to reconcile transactions";
return NextResponse.json({ error: errorMessage }, { status: 500 });
}
}

View File

@@ -8,6 +8,7 @@ import {
OFXInfoCard,
BackupCard,
PasswordCard,
ReconcileDateRangeCard,
} from "@/components/settings";
import { useBankingData } from "@/lib/hooks";
import type { BankingData } from "@/lib/types";
@@ -125,6 +126,8 @@ export default function SettingsPage() {
<PasswordCard />
<ReconcileDateRangeCard />
<DangerZoneCard
categorizedCount={categorizedCount}
onClearCategories={clearAllCategories}

View File

@@ -950,52 +950,57 @@ export default function StatisticsPage() {
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<div className="p-4 space-y-4">
<div className="p-3 flex gap-4">
<div className="space-y-2">
<label className="text-sm font-medium">
Date de début
</label>
<CalendarComponent
mode="single"
selected={customStartDate}
onSelect={(date) => {
setCustomStartDate(date);
if (date && customEndDate && date > customEndDate) {
setCustomEndDate(undefined);
}
}}
locale={fr}
/>
<div className="scale-90 origin-top-left">
<CalendarComponent
mode="single"
selected={customStartDate}
onSelect={(date) => {
setCustomStartDate(date);
if (date && customEndDate && date > customEndDate) {
setCustomEndDate(undefined);
}
}}
locale={fr}
/>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">
Date de fin
</label>
<CalendarComponent
mode="single"
selected={customEndDate}
onSelect={(date) => {
if (
date &&
customStartDate &&
date < customStartDate
) {
return;
}
setCustomEndDate(date);
if (date && customStartDate) {
setIsCustomDatePickerOpen(false);
}
}}
disabled={(date) => {
if (!customStartDate) return true;
return date < customStartDate;
}}
locale={fr}
/>
<div className="scale-90 origin-top-left">
<CalendarComponent
mode="single"
selected={customEndDate}
onSelect={(date) => {
if (
date &&
customStartDate &&
date < customStartDate
) {
return;
}
setCustomEndDate(date);
if (date && customStartDate) {
setIsCustomDatePickerOpen(false);
}
}}
disabled={(date) => {
if (!customStartDate) return true;
return date < customStartDate;
}}
locale={fr}
/>
</div>
</div>
{customStartDate && customEndDate && (
<div className="flex gap-2 pt-2 border-t">
</div>
{customStartDate && customEndDate && (
<div className="flex gap-2 pt-2 border-t px-3 pb-3">
<Button
variant="outline"
size="sm"
@@ -1016,7 +1021,6 @@ export default function StatisticsPage() {
</Button>
</div>
)}
</div>
</PopoverContent>
</Popover>
)}