feat(wds): add manual sync trigger button with force-run mode

- triggerForceSync() resets error steps, clears lastSessionDate, starts sync immediately
- Force mode uses extended night window (22:00-05:00) instead of weekend-only
- API action 'trigger' starts sync in background, returns immediately
- 'Porneste sync' button in header (hidden when already running)
- Respects __parcelSyncRunning guard to prevent concurrent runs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
AI Assistant
2026-03-30 01:59:07 +03:00
parent 4410e968db
commit 730eee6c8a
3 changed files with 98 additions and 6 deletions
+16
View File
@@ -16,6 +16,7 @@ import {
AlertTriangle,
WifiOff,
Activity,
Play,
} from "lucide-react";
import { Button } from "@/shared/components/ui/button";
import { Input } from "@/shared/components/ui/input";
@@ -251,6 +252,21 @@ export default function WeekendDeepSyncPage() {
{lastRefresh.toLocaleTimeString("ro-RO", { hour: "2-digit", minute: "2-digit", second: "2-digit" })}
</span>
)}
{syncStatus !== "running" && (
<Button
variant="outline"
size="sm"
className="text-indigo-600 border-indigo-300 hover:bg-indigo-50 dark:text-indigo-400 dark:border-indigo-700 dark:hover:bg-indigo-950/30"
disabled={actionLoading}
onClick={() => {
if (window.confirm("Pornesti sincronizarea manuala? Va procesa toti pasii pending."))
void doAction({ action: "trigger" });
}}
>
<Play className="h-4 w-4 mr-1" />
Porneste sync
</Button>
)}
<Button
variant="ghost"
size="sm"
+14 -1
View File
@@ -3,6 +3,7 @@ import { PrismaClient, Prisma } from "@prisma/client";
import {
isWeekendWindow,
getWeekendSyncActivity,
triggerForceSync,
} from "@/modules/parcel-sync/services/weekend-deep-sync";
const prisma = new PrismaClient();
@@ -151,13 +152,25 @@ export async function GET() {
export async function POST(request: Request) {
// Auth handled by middleware (route is not excluded)
const body = (await request.json()) as {
action: "add" | "remove" | "reset" | "reset_all" | "set_priority";
action: "add" | "remove" | "reset" | "reset_all" | "set_priority" | "trigger";
siruta?: string;
name?: string;
county?: string;
priority?: number;
};
// Trigger is handled separately — starts sync immediately
if (body.action === "trigger") {
const result = await triggerForceSync();
if (!result.started) {
return NextResponse.json(
{ error: result.reason },
{ status: 409 },
);
}
return NextResponse.json({ ok: true, message: "Sincronizare pornita" });
}
const state = await getOrCreateState();
switch (body.action) {