mirror of
https://github.com/Since-3/since3-portal.git
synced 2026-10-12 20:51:17 +00:00
78 lines
2.8 KiB
TypeScript
78 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef, useState, useTransition } from "react";
|
|
import { LoaderCircleIcon, RefreshCwIcon } from "lucide-react";
|
|
import { cn } from "cn";
|
|
import { Button } from "@/components/ui/button";
|
|
import { checkProduct, checkStaleProducts } from "@/modules/shared/actions/product-checks";
|
|
import { formatDateTime } from "@/modules/shared/lib/info-format";
|
|
import type { Product } from "@/modules/shared/schemas/info";
|
|
|
|
export function ProductCheckLine({
|
|
profileId,
|
|
productId,
|
|
lastCheck,
|
|
}: {
|
|
profileId: string;
|
|
productId: string;
|
|
lastCheck: Product["lastCheck"];
|
|
}) {
|
|
const [pending, startTransition] = useTransition();
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
function handleCheck() {
|
|
setError(null);
|
|
startTransition(async () => {
|
|
const response = await checkProduct(profileId, productId);
|
|
if ("error" in response) setError(response.error);
|
|
});
|
|
}
|
|
|
|
return (
|
|
<div className="flex items-center gap-1 text-small">
|
|
<span aria-live="polite" className={cn(lastCheck && !lastCheck.ok ? "text-signal-error" : "text-muted-foreground")}>
|
|
{pending
|
|
? "Wird geprüft..."
|
|
: error ??
|
|
(lastCheck
|
|
? `${lastCheck.ok ? "Erreichbar" : lastCheck.error ?? "Nicht erreichbar"}${
|
|
lastCheck.ok && lastCheck.responseMs !== null ? ` · ${lastCheck.responseMs} ms` : ""
|
|
} · geprüft ${formatDateTime(lastCheck.checkedAt)}`
|
|
: "Noch nicht geprüft")}
|
|
</span>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
className="size-6"
|
|
onClick={handleCheck}
|
|
disabled={pending}
|
|
aria-label="Erreichbarkeit jetzt prüfen"
|
|
title="Erreichbarkeit jetzt prüfen"
|
|
>
|
|
<RefreshCwIcon className={cn("size-3.5", pending && "animate-spin")} />
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function AutoCheck({ profileId }: { profileId: string }) {
|
|
const [running, setRunning] = useState(true);
|
|
const started = useRef(false);
|
|
|
|
useEffect(() => {
|
|
if (started.current) return;
|
|
started.current = true;
|
|
checkStaleProducts(profileId)
|
|
.catch(() => undefined)
|
|
.finally(() => setRunning(false));
|
|
}, [profileId]);
|
|
|
|
if (!running) return null;
|
|
|
|
return (
|
|
<span className="inline-flex items-center gap-1.5 text-small text-muted-foreground" aria-live="polite">
|
|
<LoaderCircleIcon className="size-3.5 animate-spin" /> Erreichbarkeit wird geprüft...
|
|
</span>
|
|
);
|
|
}
|