/* * Copyright (c) 2021 - 2025, Ludvig Lundgren and the autobrr contributors. * SPDX-License-Identifier: GPL-2.0-or-later */ import * as React from "react"; import { formatDistanceToNowStrict } from "date-fns"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { CellContext } from "@tanstack/react-table"; import { ArrowPathIcon, CheckIcon } from "@heroicons/react/24/solid"; import { ClockIcon, XMarkIcon, NoSymbolIcon, ArrowDownTrayIcon, ArrowTopRightOnSquareIcon, DocumentTextIcon } from "@heroicons/react/24/outline"; import { APIClient } from "@api/APIClient"; import { FilterKeys } from "@api/query_keys"; import { classNames, humanFileSize, simplifyDate } from "@utils"; import { ExternalLink } from "../ExternalLink"; import { toast } from "@components/hot-toast"; import Toast from "@components/notifications/Toast"; import { RingResizeSpinner } from "@components/Icons"; import { Tooltip } from "@components/tooltips/Tooltip"; export const NameCell = (props: CellContext) => (
{String(props.cell.getValue())}
Category: {props.row.original.category} Size: {humanFileSize(props.row.original.size)} Misc: {`${props.row.original.resolution} ${props.row.original.source} ${props.row.original.codec ?? ""} ${props.row.original.container}`}
); export const LinksCell = (props: CellContext) => { return (
} title="Details" >
{props.row.original.name} {props.row.original.indexer.identifier} {props.row.original.protocol} {props.row.original.implementation} {props.row.original.announce_type} {props.row.original.category} {props.row.original.uploader} {humanFileSize(props.row.original.size)} {props.row.original.title} {props.row.original.year > 0 && {props.row.original.year.toString()}} {props.row.original.season > 0 && {props.row.original.season.toString()}} {props.row.original.episode > 0 && {props.row.original.episode.toString()}} {props.row.original.resolution} {props.row.original.source} {props.row.original.codec} {props.row.original.hdr} {props.row.original.group} {props.row.original.container} {props.row.original.origin}
{props.row.original.download_url && ( )} {props.row.original.info_url && ( )}
); }; export const AgeCell = ({cell}: CellContext) => (
{formatDistanceToNowStrict(new Date(cell.getValue() as string), {addSuffix: false})}
); export const IndexerCell = (props: CellContext) => (
{props.row.original.indexer.name ? props.row.original.indexer.name : props.row.original.indexer.identifier}
); export const TitleCell = ({cell}: CellContext) => (
{cell.getValue()}
); interface RetryActionButtonProps { status: ReleaseActionStatus; } interface RetryAction { releaseId: number; actionId: number; } const RetryActionButton = ({ status }: RetryActionButtonProps) => { const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: (vars: RetryAction) => APIClient.release.replayAction(vars.releaseId, vars.actionId), onSuccess: () => { // Invalidate filters just in case, most likely not necessary but can't hurt. queryClient.invalidateQueries({ queryKey: FilterKeys.lists() }); toast.custom((t) => ( )); } }); const replayAction = () => { console.log("replay action"); mutation.mutate({ releaseId: status.release_id,actionId: status.id }); }; return ( ); }; interface StatusCellMapEntry { colors: string; icon: React.ReactElement; textFormatter: (status: ReleaseActionStatus) => React.ReactElement; } const StatusCellMap: Record = { "PUSH_ERROR": { colors: "bg-red-100 text-red-800 hover:bg-red-275", icon: