Working on the hidde restore action if already a restauration is waiting.

This commit is contained in:
charles-gauthereau
2024-12-10 11:22:36 +01:00
parent ee8cc6137e
commit 5bd24574c3
12 changed files with 221 additions and 195 deletions
@@ -2,16 +2,14 @@
import {useMutation} from "@tanstack/react-query";
import {backupButtonAction} from "@/components/wrappers/BackupButton/backup-button.action";
import {toast} from "sonner";
import {Backup} from "@prisma/client";
import {useRouter} from "next/navigation";
import {ActionResult} from "@/types/action-type";
import {ButtonWithLoading} from "@/components/wrappers/Button/ButtonWithLoading/ButtonWithLoading";
export type BackupButtonProps = {
databaseId: string;
disable: boolean
}
export const BackupButton = (props: BackupButtonProps) => {
const router = useRouter();
const mutation = useMutation({
@@ -30,9 +28,9 @@ export const BackupButton = (props: BackupButtonProps) => {
await mutation.mutateAsync(props.databaseId)
}
return (
<ButtonWithLoading
disabled={props.disable}
text="Backup"
isPending={mutation.isPending}
size={"default"}
@@ -0,0 +1,40 @@
"use client"
import {Card, CardContent, CardHeader} from "@/components/ui/card";
import {formatDateLastContact} from "@/utils/date-formatting";
import {Database} from "@prisma/client";
export type DatabaseKpiPro = {
successRate: any,
database: Database
}
export const DatabaseKpi = (props: DatabaseKpiPro) => {
return (
<div className="flex flex-col sm:flex-row sm:justify-between gap-8 mb-6">
<Card className="w-full sm:w-auto flex-1">
<CardHeader className="font-bold text-xl">
Backups
</CardHeader>
<CardContent></CardContent>
</Card>
<Card className="w-full sm:w-auto flex-1">
<CardHeader className="font-bold text-xl">
Success rate
</CardHeader>
<CardContent>
{props.successRate ?? "Unavailable for now."}
</CardContent>
</Card>
<Card className="w-full sm:w-auto flex-1">
<CardHeader className="font-bold text-xl">
Last contact
</CardHeader>
<CardContent>
{formatDateLastContact(props.database.lastContact)}
</CardContent>
</Card>
</div>
)
}
@@ -6,7 +6,8 @@ import {Backup, Restauration} from "@prisma/client";
export type DatabaseTabsProps = {
backups: Backup[]
// restaurations: Restauration[]
restaurations: Restauration[]
isAlreadyRestore: boolean
}
export const DatabaseTabs = (props: DatabaseTabsProps) => {
@@ -19,12 +20,12 @@ export const DatabaseTabs = (props: DatabaseTabsProps) => {
</TabsList>
<TabsContent className="h-full justify-between" value="backup">
<DataTableWithPagination columns={backupColumns} data={props.backups}/>
<DataTableWithPagination columns={backupColumns} data={props.backups} extendedProps={props.isAlreadyRestore}/>
</TabsContent>
{/*<TabsContent className="h-full justify-between" value="restore">*/}
{/* <DataTableWithPagination columns={restoreColumns} data={props.restaurations}/>*/}
{/*</TabsContent>*/}
<TabsContent className="h-full justify-between" value="restore">
<DataTableWithPagination columns={restoreColumns} data={props.restaurations}/>
</TabsContent>
</Tabs>
)
}
@@ -0,0 +1,34 @@
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
import {PropsWithChildren} from "react";
export type TooltipCustomProps = PropsWithChildren<{
text: string
disabled?: any
}>;
export function TooltipCustom(props: TooltipCustomProps) {
return (
<>
{props.disabled ?
<TooltipProvider>
<Tooltip>
<TooltipTrigger className="w-full">
{props.children}
</TooltipTrigger>
<TooltipContent>
<p>{props.text}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
:
props.children
}
</>
)
}
@@ -11,9 +11,10 @@ import {TablePagination} from "@/components/wrappers/table/table-pagination";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
extendedProps?: any
}
export function DataTableWithPagination<TData, TValue>({columns, data}: DataTableProps<TData, TValue>) {
export function DataTableWithPagination<TData, TValue>({columns, data, extendedProps}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = useState<SortingState>([])
@@ -27,11 +28,14 @@ export function DataTableWithPagination<TData, TValue>({columns, data}: DataTabl
state: {
sorting,
},
meta: {
extendedProps: extendedProps,
}
})
return (
<div className="flex flex-col justify-between h-full">
<DataTable table={table}/>
<DataTable table={table} />
<TablePagination table={table} pageSizeOptions={[5, 10, 20, 50, 100]}/>
</div>
)
+5 -55
View File
@@ -2,7 +2,7 @@ import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow} from "@/c
import {flexRender} from "@tanstack/react-table";
export type dataTableProps = {
table: any
table: any,
}
export const DataTable = ({table}: dataTableProps) => {
@@ -10,7 +10,6 @@ export const DataTable = ({table}: dataTableProps) => {
return (
<div className="rounded-md border w-full ">
<Table className="w-full">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
@@ -38,7 +37,10 @@ export const DataTable = ({table}: dataTableProps) => {
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
@@ -55,55 +57,3 @@ export const DataTable = ({table}: dataTableProps) => {
</div>
)
}
// import { useMemo } from "react";
// import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
// import { flexRender } from "@tanstack/react-table";
//
// export type dataTableProps = {
// table: any;
// };
//
// export const DataTable = ({ table }: dataTableProps) => {
// // Memoize the header and rows to prevent unnecessary re-renders
// const headerGroups = useMemo(() => table.getHeaderGroups(), [table]);
// const rows = useMemo(() => table.getRowModel().rows, [table]);
//
// return (
// <div className="rounded-md border w-full ">
// <Table className="w-full">
// <TableHeader>
// {headerGroups.map((headerGroup) => (
// <TableRow key={headerGroup.id}>
// {headerGroup.headers.map((header) => (
// <TableHead key={header.id}>
// {header.isPlaceholder
// ? null
// : flexRender(header.column.columnDef.header, header.getContext())}
// </TableHead>
// ))}
// </TableRow>
// ))}
// </TableHeader>
// <TableBody>
// {rows?.length ? (
// rows.map((row) => (
// <TableRow key={row.id} data-state={row.getIsSelected() && "selected"}>
// {row.getVisibleCells().map((cell) => (
// <TableCell key={cell.id}>
// {flexRender(cell.column.columnDef.cell, cell.getContext())}
// </TableCell>
// ))}
// </TableRow>
// ))
// ) : (
// <TableRow>
// <TableCell colSpan={table.getAllColumns().length} className="h-24 text-center">
// No results.
// </TableCell>
// </TableRow>
// )}
// </TableBody>
// </Table>
// </div>
// );
// };
+58 -15
View File
@@ -13,7 +13,12 @@ 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 {createRestaurationAction} from "@/features/restore/restore.action";
import {toast} from "sonner";
import {useRouter} from "next/navigation";
import {TooltipCustom} from "@/components/wrappers/TooltipCustom/TooltipCustom";
export const backupColumns: ColumnDef<Backup>[] = [
@@ -37,7 +42,41 @@ export const backupColumns: ColumnDef<Backup>[] = [
},
{
id: "actions",
cell: ({row}) => {
cell: ({row, table}) => {
const status = row.getValue("status")
const rowData: Backup = row.original
const fileName = rowData.file
// @ts-ignore
const {extendedProps} = table.options.meta;
const isRestauring = !!rowData.restaurations.find(restauration => restauration.status === "waiting");
const router = useRouter()
const handleDownload = async (fileName: string) => {
const url = await getFileUrlPresignedLocal(fileName)
window.open(url, '_self');
}
const mutationRestore = useMutation({
mutationFn: async () => {
const restauration = await createRestaurationAction({
backupId: rowData.id,
databaseId: rowData.databaseId
})
if (restauration.data.success) {
toast.success(restauration.data.actionSuccess?.message || "Restauration created successfully!");
router.refresh()
} else {
toast.error(restauration.serverError || "Failed to create restauration.");
}
}})
const handleRestore = async () => {
await mutationRestore.mutateAsync()
}
return (
<DropdownMenu>
@@ -49,24 +88,28 @@ export const backupColumns: ColumnDef<Backup>[] = [
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem onClick={() => {
}}>
<ReloadIcon/> Restore
</DropdownMenuItem>
<DropdownMenuItem onClick={() => {
}}>
<Download/> Download
</DropdownMenuItem>
{status == "success" ?
<>
<TooltipCustom disabled={extendedProps} text="Already a restauration waiting">
<DropdownMenuItem disabled={mutationRestore.isPending || extendedProps}
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={() => {
}}>
<Trash2/> Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
+48
View File
@@ -0,0 +1,48 @@
"use server"
import {userAction} from "@/safe-actions";
import {z} from "zod";
import {prisma} from "@/prisma";
import {ServerActionResult} from "@/types/action-type";
import {Restauration} from "@prisma/client";
export const createRestaurationAction = userAction
.schema(z.object({
backupId: z.string(),
databaseId: z.string(),
}))
.action(async ({parsedInput, ctx}): Promise<ServerActionResult<Restauration>> => {
try {
const restauration = await prisma.restauration.create({
data: {
databaseId: parsedInput.databaseId,
backupId: parsedInput.backupId,
status: "waiting",
},
});
return {
success: true,
value: restauration,
actionSuccess: {
message: "Restauration has been successfully created.",
messageParams: { restaurationId: restauration.id },
},
};
} catch (error) {
console.error("Error creating restauration:", error);
return {
success: false,
actionError: {
message: "Failed to create backup.",
status: 500,
cause: error instanceof Error ? error.message : "Unknown error",
messageParams: { message: "Error creating the restauration" },
},
};
}
});
+1
View File
@@ -10,6 +10,7 @@ export function timeAgo(rawDate: string | number | Date) {
}
export function formatDateLastContact(lastContact: string | number | Date | null) {
console.log(lastContact)
return lastContact
? format(new Date(lastContact), 'dd/MM/yyyy HH:mm')
: "Never connected."