@ragrabbit/mcp

by madarco
Verified
"use client"; import { CheckCircle2, Loader2, Moon, Pause, Play } from "@repo/design/base/icons"; import { EasyTooltip } from "@repo/design/components/tooltip/tooltip"; import { cn } from "@repo/design/lib/utils"; import { Button, buttonVariants } from "@repo/design/shadcn/button"; import { useAction } from "next-safe-action/hooks"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { getPendingCountAction, runProcessingNowAction } from "../actions.processing"; import { useIndexes } from "../providers/indexes-provider"; export default function FrontendJobProcessor() { const sleepAfter = 5; // * 3 seconds, will wake up on mouse move anyway const pollingInterval = 3000; const [pendingCount, setPendingCount] = useState<number | undefined>(undefined); const [sleep, setSleep] = useState(0); const { executeAsync: executeGetPendingCount, isExecuting: isExecutingGetPendingCount } = useAction(getPendingCountAction); const { executeAsync: executeProcessing, isExecuting: isExecutingProcessing } = useAction(runProcessingNowAction); const [isPaused, setIsPaused] = useState(true); const router = useRouter(); const { patch: patchIndex } = useIndexes(); async function executeJob(wakeUp?: boolean) { // Avoid executing a call if still doing the previous: if (isExecutingProcessing) return; if (!wakeUp && isExecutingGetPendingCount) return; // Don't do any call if it goes to sleep to not overload the server: if (sleep > sleepAfter) return; let res; const currentPauseState = wakeUp ?? isPaused; if (currentPauseState) { res = await executeGetPendingCount({}); } else { res = await executeProcessing({}); // If we processed an item, refresh the page data if (res?.data?.processedIndexId) { await patchIndex([{ id: res.data.processedIndexId, status: "DONE" }]); } } setPendingCount(res.data.count); if (res.data.count === 0) { setSleep(sleep + 1); } } // Execute every 3 seconds, goes to sleep if no jobs are pending after 60 seconds useEffect(() => { const interval = setInterval(executeJob, pollingInterval); return () => clearInterval(interval); }, [ sleep, executeProcessing, executeGetPendingCount, isPaused, isExecutingGetPendingCount, isExecutingProcessing, router, ]); // Add mouse movement detection to wake up the processor useEffect(() => { function handleMouseMove() { if (sleep > 0) { setSleep(0); } } window.addEventListener("mousemove", handleMouseMove); return () => window.removeEventListener("mousemove", handleMouseMove); }, [sleep]); async function wakeUp() { setSleep(0); // Resume processing setIsPaused(false); // Immediately execute processing with forced pause state await executeJob(false); } const isSleeping = sleep > sleepAfter; const isExecuting = !isPaused && isExecutingProcessing; return ( <> <EasyTooltip tooltip={ isSleeping ? "Jobs Runner sleeping" : isPaused ? "Execution paused" : isExecuting ? "Executing Job..." : "Ready to execute: don't close this tab" } > <div className={cn( buttonVariants({ variant: "outline" }), "flex items-center gap-2", isSleeping ? "opacity-50 cursor-pointer" : "" )} aria-label={isSleeping ? "Wake up" : isPaused ? "Resume" : "Pause"} > <span className={cn( "rounded-md p-1", isSleeping ? "bg-blue-500 text-white" : isPaused ? "bg-yellow-200" : "bg-green-500 text-white" )} > {isSleeping ? ( <Moon className="h-4 w-4" /> ) : !isPaused ? ( isExecuting ? ( <Loader2 className="h-4 w-4 animate-spin" /> ) : ( <Play className="h-4 w-4" /> ) ) : ( <Pause className="h-4 w-4" /> )} </span> Jobs:{" "} {pendingCount === undefined ? ( "..." ) : pendingCount === 0 ? ( <span className="flex items-center gap-1"> All done <CheckCircle2 className="h-4 w-4 text-green-500" /> </span> ) : ( <span>{pendingCount}</span> )} </div> </EasyTooltip> <div> <Button variant="outline" size="icon" onClick={() => (isPaused ? wakeUp() : setIsPaused(true))}> {isSleeping || isPaused ? <Play className="h-4 w-4" /> : <Pause className="h-4 w-4" />} </Button> </div> </> ); }