2025-05-16 15:54:17 +02:00
|
|
|
"use client";
|
2024-11-11 14:56:33 +01:00
|
|
|
|
2025-07-16 20:16:18 +02:00
|
|
|
import {ColumnDef} from "@tanstack/react-table";
|
2024-11-11 14:56:33 +01:00
|
|
|
import {
|
|
|
|
|
DropdownMenu,
|
|
|
|
|
DropdownMenuContent,
|
|
|
|
|
DropdownMenuItem,
|
2025-05-16 15:54:17 +02:00
|
|
|
DropdownMenuLabel,
|
|
|
|
|
DropdownMenuSeparator,
|
|
|
|
|
DropdownMenuTrigger,
|
2024-11-11 14:56:33 +01:00
|
|
|
} from "@/components/ui/dropdown-menu";
|
2025-07-16 20:16:18 +02:00
|
|
|
import {Button} from "@/components/ui/button";
|
|
|
|
|
import {Download, MoreHorizontal, Trash2} from "lucide-react";
|
|
|
|
|
import {ReloadIcon} from "@radix-ui/react-icons";
|
2025-08-02 20:13:29 +02:00
|
|
|
import {
|
|
|
|
|
getFileUrlPresignedLocal,
|
|
|
|
|
getFileUrlPreSignedS3Action
|
|
|
|
|
} from "@/features/upload/private/upload.action";
|
2025-07-16 20:16:18 +02:00
|
|
|
import {useMutation} from "@tanstack/react-query";
|
|
|
|
|
import {createRestorationAction, deleteBackupAction} from "@/features/dashboard/restore/restore.action";
|
|
|
|
|
import {toast} from "sonner";
|
|
|
|
|
import {useRouter} from "next/navigation";
|
|
|
|
|
import {StatusBadge} from "@/components/wrappers/common/status-badge";
|
2025-08-29 15:31:23 +02:00
|
|
|
import {Backup, DatabaseWith} from "@/db/schema/07_database";
|
2025-07-27 21:26:31 +02:00
|
|
|
import {TooltipCustom} from "@/components/wrappers/common/tooltip-custom";
|
2025-08-29 15:31:23 +02:00
|
|
|
import {Setting} from "@/db/schema/01_setting";
|
2025-08-03 12:17:10 +02:00
|
|
|
import {SafeActionResult} from "next-safe-action";
|
|
|
|
|
import {ZodString} from "zod";
|
|
|
|
|
import {ServerActionResult} from "@/types/action-type";
|
2025-09-22 20:31:29 +02:00
|
|
|
import {cn} from "@/lib/utils";
|
|
|
|
|
import {Tooltip, TooltipContent, TooltipProvider, TooltipTrigger} from "@/components/ui/tooltip";
|
2025-11-01 14:50:08 +01:00
|
|
|
import {MemberWithUser} from "@/db/schema/03_organization";
|
2025-11-08 20:07:49 +01:00
|
|
|
import {formatLocalizedDate} from "@/utils/date-formatting";
|
2026-01-02 15:32:36 +01:00
|
|
|
import {formatBytes, isImportedFilename} from "@/utils/text";
|
2026-01-17 15:57:37 +01:00
|
|
|
import {DatabaseActionsCell} from "@/components/wrappers/dashboard/database/backup/actions/backup-actions-cell";
|
2024-11-11 14:56:33 +01:00
|
|
|
|
2025-07-16 20:16:18 +02:00
|
|
|
|
2025-11-01 14:50:08 +01:00
|
|
|
export function backupColumns(
|
|
|
|
|
isAlreadyRestore: boolean,
|
|
|
|
|
settings: Setting,
|
|
|
|
|
database: DatabaseWith,
|
|
|
|
|
activeMember: MemberWithUser
|
|
|
|
|
): ColumnDef<Backup>[] {
|
2025-07-16 20:16:18 +02:00
|
|
|
return [
|
2025-09-22 20:31:29 +02:00
|
|
|
{
|
|
|
|
|
id: "availability",
|
|
|
|
|
cell: ({row}) => {
|
|
|
|
|
const colorStatus = row.original.deletedAt != null ? "bg-red-400 border-red-600" : "bg-green-400 border-green-600";
|
|
|
|
|
return (
|
|
|
|
|
<TooltipProvider>
|
|
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
|
|
|
|
<div className={cn("w-5 h-5 rounded-full border-4", colorStatus)}/>
|
|
|
|
|
</TooltipTrigger>
|
|
|
|
|
{row.original.deletedAt != null && (
|
|
|
|
|
<TooltipContent>
|
2025-11-08 20:07:49 +01:00
|
|
|
<p>{formatLocalizedDate(row.original.deletedAt)}</p>
|
2025-09-22 20:31:29 +02:00
|
|
|
</TooltipContent>
|
|
|
|
|
)}
|
|
|
|
|
</Tooltip>
|
|
|
|
|
</TooltipProvider>
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
},
|
2025-07-16 20:16:18 +02:00
|
|
|
{
|
|
|
|
|
accessorKey: "id",
|
|
|
|
|
header: "Reference",
|
2025-12-26 16:08:36 +01:00
|
|
|
cell: ({row}) => {
|
|
|
|
|
const fileName = row.original.file
|
|
|
|
|
const reference = row.original.id
|
|
|
|
|
const isImported = isImportedFilename(`${fileName}`)
|
|
|
|
|
return isImported ? `${reference} (imported)` : `${reference}`
|
|
|
|
|
},
|
2024-11-11 19:12:34 +01:00
|
|
|
},
|
2026-01-02 15:32:36 +01:00
|
|
|
{
|
|
|
|
|
accessorKey: "fileSize",
|
|
|
|
|
header: "Size",
|
|
|
|
|
cell: ({row}) => {
|
|
|
|
|
return formatBytes(row.getValue("fileSize"))
|
|
|
|
|
},
|
|
|
|
|
},
|
2025-07-16 20:16:18 +02:00
|
|
|
{
|
|
|
|
|
accessorKey: "createdAt",
|
|
|
|
|
header: "Created At",
|
|
|
|
|
cell: ({row}) => {
|
2025-11-08 20:07:49 +01:00
|
|
|
return formatLocalizedDate(row.getValue("createdAt"))
|
2025-07-16 20:16:18 +02:00
|
|
|
},
|
2024-11-11 14:56:33 +01:00
|
|
|
},
|
2025-07-16 20:16:18 +02:00
|
|
|
{
|
|
|
|
|
accessorKey: "status",
|
|
|
|
|
header: "Status",
|
|
|
|
|
cell: ({row}) => {
|
|
|
|
|
return <StatusBadge status={row.getValue("status")}/>;
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: "actions",
|
2026-01-17 19:55:17 +01:00
|
|
|
cell: ({row}) => <DatabaseActionsCell isAlreadyRestore={isAlreadyRestore} activeMember={activeMember} backup={row.original}/>,
|
2024-11-11 14:56:33 +01:00
|
|
|
},
|
2026-01-17 19:55:17 +01:00
|
|
|
// {
|
|
|
|
|
// id: "actions",
|
|
|
|
|
// cell: ({row, table}) => {
|
|
|
|
|
// const status = row.getValue("status");
|
|
|
|
|
// const rowData: Backup = row.original;
|
|
|
|
|
// const fileName = rowData.file;
|
|
|
|
|
//
|
|
|
|
|
// const router = useRouter();
|
|
|
|
|
//
|
|
|
|
|
// const mutationRestore = useMutation({
|
|
|
|
|
// mutationFn: async () => {
|
|
|
|
|
// const restoration = await createRestorationAction({
|
|
|
|
|
// backupId: rowData.id,
|
|
|
|
|
// databaseId: rowData.databaseId,
|
|
|
|
|
// });
|
|
|
|
|
// // @ts-ignore
|
|
|
|
|
// if (restoration.data.success) {
|
|
|
|
|
// // @ts-ignore
|
|
|
|
|
// toast.success(restoration.data.actionSuccess?.message || "Restoration created successfully!");
|
|
|
|
|
// router.refresh();
|
|
|
|
|
// } else {
|
|
|
|
|
// // @ts-ignore
|
|
|
|
|
// toast.error(restoration.serverError || "Failed to create restoration.");
|
|
|
|
|
// }
|
|
|
|
|
// },
|
|
|
|
|
// });
|
|
|
|
|
//
|
|
|
|
|
// const mutationDeleteBackup = useMutation({
|
|
|
|
|
// mutationFn: async () => {
|
|
|
|
|
// const deletion = await deleteBackupAction({
|
|
|
|
|
// backupId: rowData.id,
|
|
|
|
|
// databaseId: rowData.databaseId,
|
|
|
|
|
// status: rowData.status,
|
|
|
|
|
// file: rowData.file ?? "",
|
|
|
|
|
// projectSlug: database.project?.slug!
|
|
|
|
|
// });
|
|
|
|
|
// // @ts-ignore
|
|
|
|
|
// if (deletion.data.success) {
|
|
|
|
|
// // @ts-ignore
|
|
|
|
|
// toast.success(deletion.data.actionSuccess.message);
|
|
|
|
|
// router.refresh();
|
|
|
|
|
// } else {
|
|
|
|
|
// // @ts-ignore
|
|
|
|
|
// toast.error(deletion.data.actionError.message);
|
|
|
|
|
// }
|
|
|
|
|
// },
|
|
|
|
|
// });
|
|
|
|
|
//
|
|
|
|
|
// const handleRestore = async () => {
|
|
|
|
|
// await mutationRestore.mutateAsync();
|
|
|
|
|
// };
|
|
|
|
|
//
|
|
|
|
|
// const handleDelete = async () => {
|
|
|
|
|
// await mutationDeleteBackup.mutateAsync();
|
|
|
|
|
// };
|
|
|
|
|
//
|
|
|
|
|
// const handleDownload = async (fileName: string) => {
|
|
|
|
|
//
|
|
|
|
|
// let url: string = "";
|
|
|
|
|
// let data: SafeActionResult<string, ZodString, readonly [], {
|
|
|
|
|
// _errors?: string[] | undefined;
|
|
|
|
|
// }, readonly [], ServerActionResult<string>, object> | undefined
|
|
|
|
|
//
|
|
|
|
|
// if (settings.storage == "local") {
|
|
|
|
|
// data = await getFileUrlPresignedLocal({fileName: fileName!})
|
|
|
|
|
// } else if (settings.storage == "s3") {
|
|
|
|
|
// data = await getFileUrlPreSignedS3Action(`backups/${database.project?.slug}/${fileName}`);
|
|
|
|
|
// }
|
|
|
|
|
// if (data?.data?.success) {
|
|
|
|
|
// url = data.data.value ?? "";
|
|
|
|
|
// } else {
|
|
|
|
|
// // @ts-ignore
|
|
|
|
|
// const errorMessage = data?.data?.actionError?.message || "Failed to get file!";
|
|
|
|
|
// toast.error(errorMessage);
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// window.open(url, "_self");
|
|
|
|
|
// };
|
|
|
|
|
//
|
|
|
|
|
// return (
|
|
|
|
|
// <>
|
|
|
|
|
// {(rowData.deletedAt == null && activeMember.role != "member") && (
|
|
|
|
|
// <DropdownMenu>
|
|
|
|
|
// <DropdownMenuTrigger asChild>
|
|
|
|
|
// <Button variant="ghost" className="h-8 w-8 p-0">
|
|
|
|
|
// <span className="sr-only">Open menu</span>
|
|
|
|
|
// <MoreHorizontal className="h-4 w-4"/>
|
|
|
|
|
// </Button>
|
|
|
|
|
// </DropdownMenuTrigger>
|
|
|
|
|
// <DropdownMenuContent align="end">
|
|
|
|
|
// <DropdownMenuLabel>Actions</DropdownMenuLabel>
|
|
|
|
|
// {status == "success" ? (
|
|
|
|
|
// <>
|
|
|
|
|
// <TooltipCustom disabled={isAlreadyRestore}
|
|
|
|
|
// text="Already a restoration waiting">
|
|
|
|
|
// <DropdownMenuItem
|
|
|
|
|
// disabled={mutationRestore.isPending || isAlreadyRestore}
|
|
|
|
|
// onClick={async () => {
|
|
|
|
|
// await handleRestore();
|
|
|
|
|
// }}
|
|
|
|
|
// >
|
|
|
|
|
// <ReloadIcon/> Restore
|
|
|
|
|
// </DropdownMenuItem>
|
|
|
|
|
// </TooltipCustom>
|
|
|
|
|
// <DropdownMenuItem
|
|
|
|
|
// onClick={async () => {
|
|
|
|
|
// await handleDownload(fileName ?? "");
|
|
|
|
|
// }}
|
|
|
|
|
// >
|
|
|
|
|
// <Download/> Download
|
|
|
|
|
// </DropdownMenuItem>
|
|
|
|
|
// </>
|
|
|
|
|
// ) : null}
|
|
|
|
|
// <DropdownMenuSeparator/>
|
|
|
|
|
// <DropdownMenuItem
|
|
|
|
|
// className="text-red-600"
|
|
|
|
|
// onClick={async () => {
|
|
|
|
|
// await handleDelete();
|
|
|
|
|
// }}
|
|
|
|
|
// >
|
|
|
|
|
// <Trash2/> Delete
|
|
|
|
|
// </DropdownMenuItem>
|
|
|
|
|
// </DropdownMenuContent>
|
|
|
|
|
// </DropdownMenu>
|
|
|
|
|
// )}
|
|
|
|
|
// </>
|
|
|
|
|
//
|
|
|
|
|
// );
|
|
|
|
|
// },
|
|
|
|
|
// },
|
2025-07-16 20:16:18 +02:00
|
|
|
];
|
|
|
|
|
}
|