Enforce that all step images need to be classified

This commit is contained in:
Nicolas Mowen 2025-11-09 13:27:01 -07:00
parent b62de79c39
commit 292d024aac
2 changed files with 50 additions and 14 deletions

View File

@ -148,6 +148,8 @@
"step3": { "step3": {
"selectImagesPrompt": "Select all images with: {{className}}", "selectImagesPrompt": "Select all images with: {{className}}",
"selectImagesDescription": "Click on images to select them. Click Continue when you're done with this class.", "selectImagesDescription": "Click on images to select them. Click Continue when you're done with this class.",
"allImagesRequired_one": "Please classify all images. {{count}} image remaining.",
"allImagesRequired_other": "Please classify all images. {{count}} images remaining.",
"generating": { "generating": {
"title": "Generating Sample Images", "title": "Generating Sample Images",
"description": "Frigate is pulling representative images from your recordings. This may take a moment..." "description": "Frigate is pulling representative images from your recordings. This may take a moment..."

View File

@ -10,6 +10,12 @@ import useSWR from "swr";
import { baseUrl } from "@/api/baseUrl"; import { baseUrl } from "@/api/baseUrl";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import { TooltipPortal } from "@radix-ui/react-tooltip";
export type Step3FormData = { export type Step3FormData = {
examplesGenerated: boolean; examplesGenerated: boolean;
@ -317,6 +323,19 @@ export default function Step3ChooseExamples({
return unclassifiedImages.length === 0; return unclassifiedImages.length === 0;
}, [unclassifiedImages]); }, [unclassifiedImages]);
// For state models on the last class, require all images to be classified
const isLastClass = currentClassIndex === allClasses.length - 1;
const canProceed = useMemo(() => {
if (
step1Data.modelType === "state" &&
isLastClass &&
!allImagesClassified
) {
return false;
}
return true;
}, [step1Data.modelType, isLastClass, allImagesClassified]);
const handleBack = useCallback(() => { const handleBack = useCallback(() => {
if (currentClassIndex > 0) { if (currentClassIndex > 0) {
const previousClass = allClasses[currentClassIndex - 1]; const previousClass = allClasses[currentClassIndex - 1];
@ -438,20 +457,35 @@ export default function Step3ChooseExamples({
<Button type="button" onClick={handleBack} className="sm:flex-1"> <Button type="button" onClick={handleBack} className="sm:flex-1">
{t("button.back", { ns: "common" })} {t("button.back", { ns: "common" })}
</Button> </Button>
<Button <Tooltip>
type="button" <TooltipTrigger asChild>
onClick={ <Button
allImagesClassified type="button"
? handleContinue onClick={
: handleContinueClassification allImagesClassified
} ? handleContinue
variant="select" : handleContinueClassification
className="flex items-center justify-center gap-2 sm:flex-1" }
disabled={!hasGenerated || isGenerating || isProcessing} variant="select"
> className="flex items-center justify-center gap-2 sm:flex-1"
{isProcessing && <ActivityIndicator className="size-4" />} disabled={
{t("button.continue", { ns: "common" })} !hasGenerated || isGenerating || isProcessing || !canProceed
</Button> }
>
{isProcessing && <ActivityIndicator className="size-4" />}
{t("button.continue", { ns: "common" })}
</Button>
</TooltipTrigger>
{!canProceed && (
<TooltipPortal>
<TooltipContent>
{t("wizard.step3.allImagesRequired", {
count: unclassifiedImages.length,
})}
</TooltipContent>
</TooltipPortal>
)}
</Tooltip>
</div> </div>
)} )}
</div> </div>