Working on bugs, add delete restore. Review the auth style. working on the RBAC.

This commit is contained in:
charlesgauthereau
2025-07-26 16:34:06 +02:00
parent f8f599e4dd
commit 1ffbd6efc8
23 changed files with 419 additions and 194 deletions
+96 -6
View File
@@ -1,15 +1,33 @@
"use client";
import { ColumnDef } from "@tanstack/react-table";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import { MoreHorizontal } from "lucide-react";
import {MoreHorizontal, Trash2} 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 {Backup, Restoration} from "@/db/schema/06_database";
import {formatFrenchDate} from "@/utils/date-formatting";
import {useMutation} from "@tanstack/react-query";
import {
deleteBackupAction,
deleteRestoreAction,
rerunRestorationAction
} from "@/features/dashboard/restore/restore.action";
import {toast} from "sonner";
import {useRouter} from "next/navigation";
import {TooltipCustom} from "@/components/wrappers/common/tooltipCustom/TooltipCustom";
export const restoreColumns: ColumnDef<Restoration>[] = [
export function restoreColumns(isAlreadyRestore: boolean): ColumnDef<Restoration>[] {
return[
{
accessorKey: "id",
header: "Reference",
@@ -31,6 +49,58 @@ export const restoreColumns: ColumnDef<Restoration>[] = [
{
id: "actions",
cell: ({ row }) => {
const status = row.getValue("status");
const router = useRouter();
const rowData: Restoration = row.original;
const mutationDeleteRestore = useMutation({
mutationFn: async () => {
const restoration = await deleteRestoreAction({
restorationId: rowData.id,
});
// @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 mutationRerunRestore = useMutation({
mutationFn: async () => {
const restoration = await rerunRestorationAction({
restorationId: rowData.id,
});
// @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 handleDelete = async () => {
await mutationDeleteRestore.mutateAsync();
};
const handleRerunRestore = async () => {
await mutationRerunRestore.mutateAsync();
}
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -42,8 +112,26 @@ export const restoreColumns: ColumnDef<Restoration>[] = [
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem onClick={() => {}}>
<ReloadIcon /> Rerun
<TooltipCustom disabled={isAlreadyRestore} text="Already a restoration waiting">
<DropdownMenuItem
disabled={mutationRerunRestore.isPending || isAlreadyRestore}
onClick={async () => {
await handleRerunRestore();
}}
>
<ReloadIcon/> Rerun
</DropdownMenuItem>
</TooltipCustom>
<DropdownMenuSeparator/>
<DropdownMenuItem
disabled={status == "waiting"}
className="text-red-600"
onClick={async () => {
await handleDelete();
}}
>
<Trash2/> Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -51,3 +139,5 @@ export const restoreColumns: ColumnDef<Restoration>[] = [
},
},
];
}
@@ -8,6 +8,43 @@ import { db } from "@/db";
import { and, eq } from "drizzle-orm";
import {Backup, Restoration} from "@/db/schema/06_database";
export const deleteRestoreAction = userAction
.schema(
z.object({
restorationId: z.string(),
})
)
.action(async ({ parsedInput }): Promise<ServerActionResult<Backup>> => {
try {
await db
.delete(drizzleDb.schemas.restoration)
.where(and(eq(drizzleDb.schemas.restoration.id, parsedInput.restorationId)))
.execute();
return {
success: true,
actionSuccess: {
message: "Restoration deleted successfully.",
},
};
} catch (error) {
return {
success: false,
actionError: {
message: "Failed to delete restoration.",
status: 500,
cause: error instanceof Error ? error.message : "Unknown error",
messageParams: { message: "Error deleting the restoration" },
},
};
}
});
export const deleteBackupAction = userAction
.schema(
z.object({
@@ -60,6 +97,58 @@ export const deleteBackupAction = userAction
});
export const rerunRestorationAction = userAction
.schema(
z.object({
restorationId: z.string(),
})
)
.action(async ({ parsedInput }): Promise<ServerActionResult<Restoration>> => {
try {
const updateResult = await db
.update(drizzleDb.schemas.restoration)
.set({ status: "waiting" })
.where(eq(drizzleDb.schemas.restoration.id, parsedInput.restorationId))
.returning()
.execute();
const updatedRestoration = updateResult[0];
if (!updatedRestoration) {
return {
success: false,
actionError: {
message: "Restoration not found.",
status: 404,
cause: "No restoration with the given ID exists.",
messageParams: { message: "Restoration not found" },
},
};
}
return {
success: true,
value: updatedRestoration,
actionSuccess: {
message: "Restoration has been requeued.",
messageParams: { restorationId: updatedRestoration.id },
},
};
} catch (error) {
return {
success: false,
actionError: {
message: "Failed to rerun restoration.",
status: 500,
cause: error instanceof Error ? error.message : "Unknown error",
messageParams: { message: "Error updating the restoration" },
},
};
}
});
// Create Restoration Action (Drizzle version)
export const createRestorationAction = userAction
.schema(