mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Testing in local validated.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import {ColumnDef} from "@tanstack/react-table";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -9,134 +9,143 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Download, MoreHorizontal, Trash2 } from "lucide-react";
|
||||
import { ReloadIcon } from "@radix-ui/react-icons";
|
||||
import { Backup } from "@prisma/client";
|
||||
import { getFileUrlPresignedLocal } from "@/features/upload/private/upload.action";
|
||||
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";
|
||||
import { TooltipCustom } from "@/components/wrappers/common/tooltipCustom/TooltipCustom";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {Download, MoreHorizontal, Trash2} from "lucide-react";
|
||||
import {ReloadIcon} from "@radix-ui/react-icons";
|
||||
import {getFileUrlPresignedLocal} from "@/features/upload/private/upload.action";
|
||||
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";
|
||||
import {TooltipCustom} from "@/components/wrappers/common/tooltipCustom/TooltipCustom";
|
||||
import {Backup} from "@/db/schema/06_database";
|
||||
import {formatFrenchDate} from "@/utils/date-formatting";
|
||||
|
||||
export const backupColumns: ColumnDef<Backup>[] = [
|
||||
{
|
||||
accessorKey: "id",
|
||||
header: "Reference",
|
||||
},
|
||||
{
|
||||
accessorKey: "createdAt",
|
||||
header: "Created At",
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue("createdAt")).toLocaleString("fr-FR");
|
||||
|
||||
export function backupColumns(isAlreadyRestore: boolean): ColumnDef<Backup>[] {
|
||||
return [
|
||||
|
||||
{
|
||||
accessorKey: "id",
|
||||
header: "Reference",
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: "Status",
|
||||
cell: ({ row }) => {
|
||||
return <StatusBadge status={row.getValue("status")} />;
|
||||
{
|
||||
accessorKey: "createdAt",
|
||||
header: "Created At",
|
||||
cell: ({row}) => {
|
||||
return formatFrenchDate(row.getValue("createdAt"))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
cell: ({ row, table }) => {
|
||||
const status = row.getValue("status");
|
||||
const rowData: Backup = row.original;
|
||||
const fileName = rowData.file;
|
||||
// @ts-ignore
|
||||
const { extendedProps } = table.options.meta;
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: "Status",
|
||||
cell: ({row}) => {
|
||||
return <StatusBadge status={row.getValue("status")}/>;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
cell: ({row, table}) => {
|
||||
const status = row.getValue("status");
|
||||
const rowData: Backup = row.original;
|
||||
const fileName = rowData.file;
|
||||
|
||||
const router = useRouter();
|
||||
const router = useRouter();
|
||||
|
||||
const mutationRestore = useMutation({
|
||||
mutationFn: async () => {
|
||||
const restoration = await createRestorationAction({
|
||||
backupId: rowData.id,
|
||||
databaseId: rowData.databaseId,
|
||||
});
|
||||
if (restoration.data.success) {
|
||||
toast.success(restoration.data.actionSuccess?.message || "Restoration created successfully!");
|
||||
router.refresh();
|
||||
} else {
|
||||
toast.error(restoration.serverError || "Failed to create restoration.");
|
||||
}
|
||||
},
|
||||
});
|
||||
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 restoration = await deleteBackupAction({
|
||||
backupId: rowData.id,
|
||||
databaseId: rowData.databaseId,
|
||||
});
|
||||
if (restoration.data.success) {
|
||||
toast.success(restoration.data.actionSuccess.message);
|
||||
router.refresh();
|
||||
} else {
|
||||
toast.error(restoration.data.actionError.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
const mutationDeleteBackup = useMutation({
|
||||
mutationFn: async () => {
|
||||
const restoration = await deleteBackupAction({
|
||||
backupId: rowData.id,
|
||||
databaseId: rowData.databaseId,
|
||||
});
|
||||
// @ts-ignore
|
||||
if (restoration.data.success) {
|
||||
// @ts-ignore
|
||||
toast.success(restoration.data.actionSuccess.message);
|
||||
router.refresh();
|
||||
} else {
|
||||
// @ts-ignore
|
||||
toast.error(restoration.data.actionError.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const handleRestore = async () => {
|
||||
await mutationRestore.mutateAsync();
|
||||
};
|
||||
const handleRestore = async () => {
|
||||
await mutationRestore.mutateAsync();
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
await mutationDeleteBackup.mutateAsync();
|
||||
};
|
||||
const handleDelete = async () => {
|
||||
await mutationDeleteBackup.mutateAsync();
|
||||
};
|
||||
|
||||
const handleDownload = async (fileName: string) => {
|
||||
const url = await getFileUrlPresignedLocal(fileName);
|
||||
window.open(url, "_self");
|
||||
};
|
||||
const handleDownload = async (fileName: string) => {
|
||||
const url = await getFileUrlPresignedLocal(fileName);
|
||||
window.open(url, "_self");
|
||||
};
|
||||
|
||||
return (
|
||||
<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={extendedProps} text="Already a restoration waiting">
|
||||
return (
|
||||
<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
|
||||
disabled={mutationRestore.isPending || extendedProps}
|
||||
onClick={async () => {
|
||||
await handleRestore();
|
||||
await handleDownload(fileName ?? "");
|
||||
}}
|
||||
>
|
||||
<ReloadIcon /> Restore
|
||||
<Download/> Download
|
||||
</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>
|
||||
);
|
||||
</>
|
||||
) : null}
|
||||
<DropdownMenuSeparator/>
|
||||
<DropdownMenuItem
|
||||
className="text-red-600"
|
||||
onClick={async () => {
|
||||
await handleDelete();
|
||||
}}
|
||||
>
|
||||
<Trash2/> Delete
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
];
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { MoreHorizontal } from "lucide-react";
|
||||
import { ReloadIcon } from "@radix-ui/react-icons";
|
||||
import { StatusBadge } from "@/components/wrappers/common/status-badge";
|
||||
import {Restoration} from "@/db/schema/06_database";
|
||||
import {formatFrenchDate} from "@/utils/date-formatting";
|
||||
|
||||
export const restoreColumns: ColumnDef<Restoration>[] = [
|
||||
{
|
||||
@@ -17,7 +18,7 @@ export const restoreColumns: ColumnDef<Restoration>[] = [
|
||||
accessorKey: "createdAt",
|
||||
header: "Created At",
|
||||
cell: ({ row }) => {
|
||||
return new Date(row.getValue("createdAt")).toLocaleString("fr-FR");
|
||||
return formatFrenchDate(row.getValue("createdAt"))
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use server"
|
||||
import { userAction } from "@/safe-actions";
|
||||
import { z } from "zod";
|
||||
import { ServerActionResult } from "@/types/action-type";
|
||||
@@ -58,6 +59,7 @@ export const deleteBackupAction = userAction
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Create Restoration Action (Drizzle version)
|
||||
export const createRestorationAction = userAction
|
||||
.schema(
|
||||
|
||||
Reference in New Issue
Block a user