Add ability to view attempts

This commit is contained in:
Nicolas Mowen 2024-12-28 13:52:06 -07:00
parent f84713487f
commit fd07fa5124

View File

@ -14,7 +14,7 @@ import { toast } from "sonner";
import useSWR from "swr"; import useSWR from "swr";
export default function FaceLibrary() { export default function FaceLibrary() {
const [page, setPage] = useState<string>(); const [page, setPage] = useState<string>("attempts");
const [pageToggle, setPageToggle] = useOptimisticState(page, setPage, 100); const [pageToggle, setPageToggle] = useOptimisticState(page, setPage, 100);
const tabsRef = useRef<HTMLDivElement | null>(null); const tabsRef = useRef<HTMLDivElement | null>(null);
@ -23,7 +23,8 @@ export default function FaceLibrary() {
const { data: faceData, mutate: refreshFaces } = useSWR("faces"); const { data: faceData, mutate: refreshFaces } = useSWR("faces");
const faces = useMemo<string[]>( const faces = useMemo<string[]>(
() => (faceData ? Object.keys(faceData) : []), () =>
faceData ? Object.keys(faceData).filter((face) => face != "debug") : [],
[faceData], [faceData],
); );
const faceImages = useMemo<string[]>( const faceImages = useMemo<string[]>(
@ -31,6 +32,11 @@ export default function FaceLibrary() {
[pageToggle, faceData], [pageToggle, faceData],
); );
const faceAttempts = useMemo<string[]>(
() => faceData?.["debug"] || [],
[faceData],
);
useEffect(() => { useEffect(() => {
if (!pageToggle && faces) { if (!pageToggle && faces) {
setPageToggle(faces[0]); setPageToggle(faces[0]);
@ -105,10 +111,24 @@ export default function FaceLibrary() {
} }
}} }}
> >
{faceAttempts.length > 0 && (
<>
<ToggleGroupItem
value="attempts"
className={`flex scroll-mx-10 items-center justify-between gap-2 ${pageToggle == "attempts" ? "" : "*:text-muted-foreground"}`}
data-nav-item="attempts"
aria-label="Select attempts"
>
<div>Attempts</div>
</ToggleGroupItem>
<div>|</div>
</>
)}
{Object.values(faces).map((item) => ( {Object.values(faces).map((item) => (
<ToggleGroupItem <ToggleGroupItem
key={item} key={item}
className={`flex scroll-mx-10 items-center justify-between gap-2 ${page == "UI settings" ? "last:mr-20" : ""} ${pageToggle == item ? "" : "*:text-muted-foreground"}`} className={`flex scroll-mx-10 items-center justify-between gap-2 ${pageToggle == item ? "" : "*:text-muted-foreground"}`}
value={item} value={item}
data-nav-item={item} data-nav-item={item}
aria-label={`Select ${item}`} aria-label={`Select ${item}`}
@ -121,20 +141,100 @@ export default function FaceLibrary() {
</div> </div>
</ScrollArea> </ScrollArea>
</div> </div>
{pageToggle && ( {pageToggle &&
<div className="scrollbar-container flex flex-wrap gap-2 overflow-y-scroll"> (pageToggle == "attempts" ? (
{faceImages.map((image: string) => ( <AttemptsGrid attemptImages={faceAttempts} />
<FaceImage key={image} name={pageToggle} image={image} /> ) : (
))} <FaceGrid
<Button faceImages={faceImages}
key="upload" pageToggle={pageToggle}
className="size-40" setUpload={setUpload}
onClick={() => setUpload(true)} />
))}
</div>
);
}
type AttemptsGridProps = {
attemptImages: string[];
};
function AttemptsGrid({ attemptImages }: AttemptsGridProps) {
return (
<div className="scrollbar-container flex flex-wrap gap-2 overflow-y-scroll">
{attemptImages.map((image: string) => (
<FaceImage key={image} name={"something"} image={image} />
))}
</div>
);
}
type FaceAttemptProps = {
name: string;
image: string;
};
function FaceAttempt({ name, image }: FaceAttemptProps) {
const [hovered, setHovered] = useState(false);
const onDelete = useCallback(() => {
axios
.post(`/faces/${name}/delete`, { ids: [image] })
.then((resp) => {
if (resp.status == 200) {
toast.error(`Successfully deleted face.`, { position: "top-center" });
}
})
.catch((error) => {
if (error.response?.data?.message) {
toast.error(`Failed to delete: ${error.response.data.message}`, {
position: "top-center",
});
} else {
toast.error(`Failed to delete: ${error.message}`, {
position: "top-center",
});
}
});
}, [name, image]);
return (
<div
className="relative h-40"
onMouseEnter={isDesktop ? () => setHovered(true) : undefined}
onMouseLeave={isDesktop ? () => setHovered(false) : undefined}
onClick={isDesktop ? undefined : () => setHovered(!hovered)}
>
{hovered && (
<div className="absolute right-1 top-1">
<Chip
className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500"
onClick={() => onDelete()}
> >
<LuImagePlus className="size-10" /> <LuTrash className="size-4 fill-destructive text-destructive" />
</Button> </Chip>
</div> </div>
)} )}
<img
className="h-40 rounded-md"
src={`${baseUrl}clips/faces/${name}/${image}`}
/>
</div>
);
}
type FaceGridProps = {
faceImages: string[];
pageToggle: string;
setUpload: (upload: boolean) => void;
};
function FaceGrid({ faceImages, pageToggle, setUpload }: FaceGridProps) {
return (
<div className="scrollbar-container flex flex-wrap gap-2 overflow-y-scroll">
{faceImages.map((image: string) => (
<FaceImage key={image} name={pageToggle} image={image} />
))}
<Button key="upload" className="size-40" onClick={() => setUpload(true)}>
<LuImagePlus className="size-10" />
</Button>
</div> </div>
); );
} }