autobrr/web/src/screens/settings/Releases.tsx
KaiserBh db7ab7c99a
feat(web): migrate Tanstack Query to v5 (#1277)
* feat: migrate to v5

* refactor: Revise error handling in QueryClient for compatibility with React Query v5

The `useErrorBoundary` option has been renamed to `throwOnError` and suspense have been removed: more on suspense more on suspense.
https://tanstack.com/query/v5/docs/react/guides/migrating-to-v5#new-hooks-for-suspense

* refactor: Callbacks on useQuery (and QueryObserver) have been removed

onSuccess, onError and onSettled have been removed from Queries. They haven't been touched for Mutations. Please see this https://github.com/TanStack/query/discussions/5279 for motivations behind this change and what to do instead.

* refactor: change to isPending, isLoading have been renamed for mutations.

Also, they are using object now:
- useQuery(key, fn, options)
+ useQuery({ queryKey, queryFn, ...options })

* refactor: change to placeHolderData.

Removed keepPreviousData in favor of placeholderData identity function
https://tanstack.com/query/v5/docs/react/guides/migrating-to-v5#removed-keeppreviousdata-in-favor-of-placeholderdata-identity-function

* fix: useSuspenseQuery instead of useQuery.

* fix(web): more useSuspenseQuery substitutions

* whoops - nobody saw that okay?

* fix pnpm lockfile

* fix pnpm lockfile again

---------

Co-authored-by: martylukyy <35452459+martylukyy@users.noreply.github.com>
Co-authored-by: soup <soup@r4tio.dev>
2023-12-25 15:37:29 +01:00

141 lines
5 KiB
TypeScript

/*
* Copyright (c) 2021 - 2023, Ludvig Lundgren and the autobrr contributors.
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import { useRef, useState } from "react";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { toast } from "react-hot-toast";
import { APIClient } from "@api/APIClient";
import Toast from "@components/notifications/Toast";
import { releaseKeys } from "@screens/releases/ReleaseTable";
import { useToggle } from "@hooks/hooks";
import { DeleteModal } from "@components/modals";
import { Section } from "./_components";
const ReleaseSettings = () => (
<Section
title="Releases"
description="Manage release history."
>
<div className="border border-red-500 rounded">
<div className="py-6 px-4 sm:p-6">
<div>
<h2 className="text-lg leading-4 font-bold text-gray-900 dark:text-white">Danger zone</h2>
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
This will clear release history in your database
</p>
</div>
</div>
<div className="py-6 px-4 sm:p-6">
<DeleteReleases />
</div>
</div>
</Section>
);
const getDurationLabel = (durationValue: number): string => {
const durationOptions: Record<number, string> = {
0: "all time",
1: "1 hour",
12: "12 hours",
24: "1 day",
168: "1 week",
720: "1 month",
2160: "3 months",
4320: "6 months",
8760: "1 year"
};
return durationOptions[durationValue] || "Invalid duration";
};
function DeleteReleases() {
const queryClient = useQueryClient();
const [duration, setDuration] = useState<string>("");
const [parsedDuration, setParsedDuration] = useState<number>(0);
const cancelModalButtonRef = useRef<HTMLInputElement | null>(null);
const [deleteModalIsOpen, toggleDeleteModal] = useToggle(false);
const deleteOlderMutation = useMutation({
mutationFn: (olderThan: number) => APIClient.release.delete(olderThan),
onSuccess: () => {
if (parsedDuration === 0) {
toast.custom((t) => (
<Toast type="success" body={"All releases were deleted."} t={t} />
));
} else {
toast.custom((t) => (
<Toast type="success" body={`Releases older than ${getDurationLabel(parsedDuration)} were deleted.`} t={t} />
));
}
// Invalidate filters just in case, most likely not necessary but can't hurt.
queryClient.invalidateQueries({ queryKey: releaseKeys.lists() });
}
});
const deleteOlderReleases = () => {
if (isNaN(parsedDuration) || parsedDuration < 0) {
toast.custom((t) => <Toast type="error" body={"Please select a valid duration."} t={t} />);
return;
}
deleteOlderMutation.mutate(parsedDuration);
};
return (
<div className="flex flex-col sm:flex-row gap-2 justify-between items-center rounded-md">
<DeleteModal
isOpen={deleteModalIsOpen}
isLoading={deleteOlderMutation.isPending}
toggle={toggleDeleteModal}
buttonRef={cancelModalButtonRef}
deleteAction={deleteOlderReleases}
title="Remove releases"
text={`Are you sure you want to remove releases older than ${getDurationLabel(parsedDuration)}? This action cannot be undone.`}
/>
<label htmlFor="duration" className="flex flex-col">
<p className="text-sm font-medium text-gray-900 dark:text-white">Delete</p>
<p className="text-sm text-gray-500 dark:text-gray-400">Delete releases older than select duration</p>
</label>
<div className="flex flex-wrap gap-2">
<select
name="duration"
id="duration"
className="focus:outline-none focus:ring-1 focus:ring-offset-0 focus:ring-blue-500 dark:focus:ring-blue-500 rounded-md sm:text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white"
value={duration}
onChange={(e) => {
const parsedDuration = parseInt(e.target.value, 10);
setParsedDuration(parsedDuration);
setDuration(e.target.value);
}}
>
<option value="">Select duration</option>
<option value="1">1 hour</option>
<option value="12">12 hours</option>
<option value="24">1 day</option>
<option value="168">1 week</option>
<option value="720">1 month</option>
<option value="2160">3 months</option>
<option value="4320">6 months</option>
<option value="8760">1 year</option>
<option value="0">Delete everything</option>
</select>
<button
type="button"
onClick={toggleDeleteModal}
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-red-700 hover:text-red-800 dark:text-white bg-red-200 dark:bg-red-700 hover:bg-red-300 dark:hover:bg-red-800 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-red-600"
>
Delete
</button>
</div>
</div>
);
}
export default ReleaseSettings;