mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "Status" AS ENUM ('waiting', 'ongoing', 'failed', 'success');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Agent" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"last_contact" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Agent_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Database" (
|
||||
"id" TEXT NOT NULL,
|
||||
"agent_id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"backup_policy" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Database_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Backup" (
|
||||
"id" TEXT NOT NULL,
|
||||
"database_id" TEXT NOT NULL,
|
||||
"status" "Status" NOT NULL DEFAULT 'waiting',
|
||||
"file" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Backup_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Restore" (
|
||||
"id" TEXT NOT NULL,
|
||||
"backup_id" TEXT NOT NULL,
|
||||
"status" "Status" NOT NULL DEFAULT 'waiting',
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Restore_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
@@ -1,27 +1,23 @@
|
||||
'use client'
|
||||
|
||||
import React, {useState} from 'react'
|
||||
import {Card} from "@/components/ui/card"
|
||||
import {cn} from "@/lib/utils";
|
||||
import {
|
||||
Pagination, PaginationContent,
|
||||
PaginationEllipsis,
|
||||
PaginationItem,
|
||||
PaginationLink,
|
||||
PaginationNext, PaginationPrevious
|
||||
} from "@/components/ui/pagination";
|
||||
|
||||
import {PaginationNavigation} from "@/components/wrappers/pagination/pagination-navigation";
|
||||
|
||||
export type cardsWithPaginationProps = {
|
||||
className?: string
|
||||
data: Array<{}>;
|
||||
cardItem: React.ComponentType;
|
||||
cardsPerPage?: number
|
||||
numberOfColumns?: number
|
||||
maxVisiblePages?: number
|
||||
}
|
||||
|
||||
|
||||
export const CardsWithPagination = (props: cardsWithPaginationProps) => {
|
||||
|
||||
const {data, cardItem, cardsPerPage = 5, numberOfColumns = 1} = props
|
||||
const {className, data, cardItem, cardsPerPage = 5, numberOfColumns = 1, maxVisiblePages = 3} = props
|
||||
|
||||
const CardItem = cardItem
|
||||
|
||||
@@ -32,119 +28,34 @@ export const CardsWithPagination = (props: cardsWithPaginationProps) => {
|
||||
const indexOfFirstCard = indexOfLastCard - cardsPerPage
|
||||
const currentCards = data.slice(indexOfFirstCard, indexOfLastCard)
|
||||
|
||||
const handlePageChange = (pageNumber: number) => {
|
||||
const goToPage = (pageNumber: number) => {
|
||||
setCurrentPage(pageNumber)
|
||||
}
|
||||
|
||||
const renderPaginationItems = () => {
|
||||
const items = []
|
||||
const maxVisiblePages = 3
|
||||
|
||||
if (totalPages <= maxVisiblePages) {
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
items.push(
|
||||
<PaginationItem key={i}>
|
||||
<PaginationLink
|
||||
onClick={() => handlePageChange(i)}
|
||||
isActive={currentPage === i}
|
||||
>
|
||||
{i}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (currentPage <= 2) {
|
||||
for (let i = 1; i <= maxVisiblePages; i++) {
|
||||
items.push(
|
||||
<PaginationItem key={i}>
|
||||
<PaginationLink
|
||||
onClick={() => handlePageChange(i)}
|
||||
isActive={currentPage === i}
|
||||
>
|
||||
{i}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
)
|
||||
}
|
||||
items.push(
|
||||
<PaginationItem key="ellipsis1">
|
||||
<PaginationEllipsis/>
|
||||
</PaginationItem>
|
||||
)
|
||||
} else if (currentPage >= totalPages - 1) {
|
||||
items.push(
|
||||
<PaginationItem key="ellipsis2">
|
||||
<PaginationEllipsis/>
|
||||
</PaginationItem>
|
||||
)
|
||||
for (let i = totalPages - 2; i <= totalPages; i++) {
|
||||
items.push(
|
||||
<PaginationItem key={i}>
|
||||
<PaginationLink
|
||||
onClick={() => handlePageChange(i)}
|
||||
isActive={currentPage === i}
|
||||
>
|
||||
{i}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
)
|
||||
}
|
||||
} else {
|
||||
items.push(
|
||||
<PaginationItem key="ellipsis3">
|
||||
<PaginationEllipsis/>
|
||||
</PaginationItem>
|
||||
)
|
||||
for (let i = currentPage - 1; i <= currentPage + 1; i++) {
|
||||
items.push(
|
||||
<PaginationItem key={i}>
|
||||
<PaginationLink
|
||||
onClick={() => handlePageChange(i)}
|
||||
isActive={currentPage === i}
|
||||
>
|
||||
{i}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
)
|
||||
}
|
||||
items.push(
|
||||
<PaginationItem key="ellipsis4">
|
||||
<PaginationEllipsis/>
|
||||
</PaginationItem>
|
||||
)
|
||||
}
|
||||
const goToPrevPage = () => {
|
||||
goToPage(Math.max(1, currentPage - 1))
|
||||
}
|
||||
|
||||
return items
|
||||
const goToNextPage = () => {
|
||||
goToPage(Math.min(totalPages, currentPage + 1))
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="">
|
||||
<div className={cn("", className)}>
|
||||
<div className={cn(`grid auto-rows-min gap-4 md:grid-cols-${numberOfColumns}`)}>
|
||||
{currentCards.map((card, key) => (
|
||||
<CardItem key={key} {...card}/>
|
||||
))}
|
||||
</div>
|
||||
<Pagination className="mt-8">
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
onClick={() => handlePageChange(Math.max(1, currentPage - 1))}
|
||||
aria-disabled={currentPage === 1}
|
||||
tabIndex={currentPage === 1 ? -1 : 0}
|
||||
/>
|
||||
</PaginationItem>
|
||||
{renderPaginationItems()}
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
onClick={() => handlePageChange(Math.min(totalPages, currentPage + 1))}
|
||||
aria-disabled={currentPage === totalPages}
|
||||
tabIndex={currentPage === totalPages ? -1 : 0}
|
||||
/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
<PaginationNavigation
|
||||
className="mt-8 justify-end"
|
||||
totalPages={totalPages}
|
||||
currentPage={currentPage}
|
||||
goToPage={goToPage}
|
||||
goToPrevPage={goToPrevPage}
|
||||
goToNextPage={goToNextPage}
|
||||
maxVisiblePages={maxVisiblePages}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import {PaginationEllipsis, PaginationItem, PaginationLink} from "@/components/ui/pagination";
|
||||
|
||||
|
||||
export type paginationItemsProps = {
|
||||
totalPages: number
|
||||
currentPage: number
|
||||
handlePageChange: (page: number) => void
|
||||
maxVisiblePages?: number
|
||||
}
|
||||
|
||||
|
||||
export const PaginationIndexes = (props: paginationItemsProps) => {
|
||||
|
||||
const {totalPages, currentPage, handlePageChange, maxVisiblePages = 3} = props
|
||||
|
||||
const items = []
|
||||
|
||||
if (totalPages <= maxVisiblePages) {
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
items.push(
|
||||
<PaginationItem key={i}>
|
||||
<PaginationLink
|
||||
onClick={() => handlePageChange(i)}
|
||||
isActive={currentPage === i}
|
||||
>
|
||||
{i}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (currentPage <= 2) {
|
||||
for (let i = 1; i <= maxVisiblePages; i++) {
|
||||
items.push(
|
||||
<PaginationItem key={i}>
|
||||
<PaginationLink
|
||||
onClick={() => handlePageChange(i)}
|
||||
isActive={currentPage === i}
|
||||
>
|
||||
{i}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
)
|
||||
}
|
||||
items.push(
|
||||
<PaginationItem key="ellipsis1">
|
||||
<PaginationEllipsis/>
|
||||
</PaginationItem>
|
||||
)
|
||||
} else if (currentPage >= totalPages - 1) {
|
||||
items.push(
|
||||
<PaginationItem key="ellipsis2">
|
||||
<PaginationEllipsis/>
|
||||
</PaginationItem>
|
||||
)
|
||||
for (let i = totalPages - 2; i <= totalPages; i++) {
|
||||
items.push(
|
||||
<PaginationItem key={i}>
|
||||
<PaginationLink
|
||||
onClick={() => handlePageChange(i)}
|
||||
isActive={currentPage === i}
|
||||
>
|
||||
{i}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
)
|
||||
}
|
||||
} else {
|
||||
items.push(
|
||||
<PaginationItem key="ellipsis3">
|
||||
<PaginationEllipsis/>
|
||||
</PaginationItem>
|
||||
)
|
||||
for (let i = currentPage - 1; i <= currentPage + 1; i++) {
|
||||
items.push(
|
||||
<PaginationItem key={i}>
|
||||
<PaginationLink
|
||||
onClick={() => handlePageChange(i)}
|
||||
isActive={currentPage === i}
|
||||
>
|
||||
{i}
|
||||
</PaginationLink>
|
||||
</PaginationItem>
|
||||
)
|
||||
}
|
||||
items.push(
|
||||
<PaginationItem key="ellipsis4">
|
||||
<PaginationEllipsis/>
|
||||
</PaginationItem>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import {
|
||||
Pagination,
|
||||
PaginationContent,
|
||||
PaginationItem,
|
||||
PaginationNext,
|
||||
PaginationPrevious
|
||||
} from "@/components/ui/pagination";
|
||||
import {PaginationIndexes} from "@/components/wrappers/pagination/pagination-indexes";
|
||||
import {cn} from "@/lib/utils";
|
||||
|
||||
|
||||
export type paginationNavigationProps = {
|
||||
className?: string
|
||||
totalPages: number
|
||||
currentPage: number
|
||||
goToPage: (page: number) => void
|
||||
goToPrevPage: () => void
|
||||
goToNextPage: () => void
|
||||
maxVisiblePages?: number
|
||||
}
|
||||
|
||||
|
||||
export const PaginationNavigation = (props: paginationNavigationProps) => {
|
||||
|
||||
const {className, totalPages, currentPage, goToPage, goToPrevPage, goToNextPage, maxVisiblePages = 3} = props
|
||||
|
||||
return (
|
||||
<Pagination className={cn("w-auto m-0", className)}>
|
||||
<PaginationContent>
|
||||
<PaginationItem>
|
||||
<PaginationPrevious onClick={goToPrevPage}/>
|
||||
</PaginationItem>
|
||||
<PaginationIndexes totalPages={totalPages} currentPage={currentPage} handlePageChange={goToPage}
|
||||
maxVisiblePages={maxVisiblePages}/>
|
||||
<PaginationItem>
|
||||
<PaginationNext onClick={goToNextPage}/>
|
||||
</PaginationItem>
|
||||
</PaginationContent>
|
||||
</Pagination>
|
||||
)
|
||||
}
|
||||
@@ -1,22 +1,33 @@
|
||||
import {Badge} from "@/components/ui/badge";
|
||||
import {cn} from "@/lib/utils";
|
||||
|
||||
export type cardsWithPaginationProps = {
|
||||
status: "waiting" | "ongoing" | "failed" | "success";
|
||||
export type statusBadgeProps = {
|
||||
status: "pending" | "processing" | "success" | "failed"
|
||||
}
|
||||
|
||||
|
||||
export const StatusBadge = ({status}: cardsWithPaginationProps) => {
|
||||
export const StatusBadge = ({status}: statusBadgeProps) => {
|
||||
|
||||
let style = "";
|
||||
|
||||
switch (status) {
|
||||
case 'waiting':
|
||||
return <Badge variant="outline" className="text-yellow-500 border-2 border-yellow-500">waiting</Badge>
|
||||
case 'ongoing':
|
||||
return <Badge variant="outline" className="text-orange-500 border-2 border-orange-500">ongoing</Badge>
|
||||
case 'pending':
|
||||
style = "text-yellow-500 border-yellow-500"
|
||||
break
|
||||
case 'processing':
|
||||
style = "text-orange-500 border-orange-500"
|
||||
break
|
||||
case 'failed':
|
||||
return <Badge variant="outline" className="text-red-500 border-2 border-red-500">failed</Badge>
|
||||
style = "text-red-500 border-red-500"
|
||||
break
|
||||
case 'success':
|
||||
return <Badge variant="outline" className="text-green-500 border-2 border-green-500">success</Badge>
|
||||
style = "text-green-500 border-green-500"
|
||||
break
|
||||
default:
|
||||
throw Error
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge variant="outline" className={cn("w-20 border-2 justify-center")}>{status}</Badge>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
"use client"
|
||||
|
||||
import {useState} from "react";
|
||||
|
||||
import {
|
||||
ColumnDef, getCoreRowModel, getPaginationRowModel, getSortedRowModel, SortingState, useReactTable
|
||||
} from "@tanstack/react-table"
|
||||
import {DataTable} from "@/components/wrappers/table/data-table";
|
||||
import {TablePagination} from "@/components/wrappers/table/table-pagination";
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[]
|
||||
data: TData[]
|
||||
}
|
||||
|
||||
export function DataTableWithPagination<TData, TValue>({columns, data}: DataTableProps<TData, TValue>) {
|
||||
|
||||
const [sorting, setSorting] = useState<SortingState>([])
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
state: {
|
||||
sorting,
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DataTable table={table}/>
|
||||
<TablePagination table={table}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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) => {
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
</TableHead>
|
||||
)
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows?.length ? (
|
||||
table.getRowModel().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.columns.length} className="h-24 text-center">
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import {PaginationNavigation} from "@/components/wrappers/pagination/pagination-navigation";
|
||||
|
||||
|
||||
export type paginationNavigationProps = {
|
||||
className?: string
|
||||
table: any
|
||||
maxVisiblePages?: number
|
||||
}
|
||||
|
||||
|
||||
export const TablePaginationNavigation = (props: paginationNavigationProps) => {
|
||||
|
||||
const {className, table, maxVisiblePages = 3} = props
|
||||
|
||||
|
||||
const totalPages = table.getPageCount()
|
||||
const currentPage = table.getState().pagination.pageIndex + 1
|
||||
|
||||
const goToPage = (page: number) => {
|
||||
table.setPageIndex(page - 1)
|
||||
}
|
||||
|
||||
const goToPrevPage = () => {
|
||||
if (table.getCanPreviousPage()) table.previousPage()
|
||||
}
|
||||
|
||||
const goToNextPage = () => {
|
||||
if (table.getCanNextPage()) table.nextPage()
|
||||
}
|
||||
|
||||
return (
|
||||
<PaginationNavigation
|
||||
className={className}
|
||||
totalPages={totalPages}
|
||||
currentPage={currentPage}
|
||||
goToPage={goToPage}
|
||||
goToPrevPage={goToPrevPage}
|
||||
goToNextPage={goToNextPage}
|
||||
maxVisiblePages={maxVisiblePages}/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select";
|
||||
import {cn} from "@/lib/utils";
|
||||
|
||||
export type tablePaginationSizeProps = {
|
||||
className?: string
|
||||
table: any
|
||||
pageSizeOptions?: number[]
|
||||
}
|
||||
|
||||
|
||||
export const TablePaginationSize = (props: tablePaginationSizeProps) => {
|
||||
|
||||
const {className, table, pageSizeOptions = [10, 20, 30, 40, 50]} = props
|
||||
|
||||
return (
|
||||
<div className={cn("flex items-center space-x-2", className)}>
|
||||
<p className="whitespace-nowrap text-sm font-medium">Rows per page</p>
|
||||
<Select
|
||||
value={`${table.getState().pagination.pageSize}`}
|
||||
onValueChange={(value) => {
|
||||
table.setPageSize(Number(value))
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-[4.5rem]">
|
||||
<SelectValue placeholder={table.getState().pagination.pageSize}/>
|
||||
</SelectTrigger>
|
||||
<SelectContent side="top">
|
||||
{pageSizeOptions.map((pageSize) => (
|
||||
<SelectItem key={pageSize} value={`${pageSize}`}>
|
||||
{pageSize}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
"use client"
|
||||
|
||||
import {TablePaginationNavigation} from "@/components/wrappers/table/table-pagination-navigation";
|
||||
import {TablePaginationSize} from "@/components/wrappers/table/table-pagination-size";
|
||||
import {cn} from "@/lib/utils";
|
||||
|
||||
interface tablePaginationProps {
|
||||
className?: string
|
||||
table: any
|
||||
maxVisiblePages?: number
|
||||
}
|
||||
|
||||
export function TablePagination(props: tablePaginationProps) {
|
||||
|
||||
const {className, table, maxVisiblePages = 3} = props
|
||||
|
||||
return (
|
||||
<div className={cn("flex justify-between mt-8", className)}>
|
||||
<TablePaginationSize table={table}/>
|
||||
<TablePaginationNavigation table={table} maxVisiblePages={maxVisiblePages}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
"use client"
|
||||
|
||||
import {ColumnDef} from "@tanstack/react-table"
|
||||
import {StatusBadge} from "@/components/wrappers/status-badge";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel, DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {MoreHorizontal} from "lucide-react";
|
||||
|
||||
export type Backup = {
|
||||
id: string
|
||||
createdAt: string
|
||||
status: "pending" | "processing" | "success" | "failed"
|
||||
}
|
||||
|
||||
export const columns: ColumnDef<Backup>[] = [
|
||||
{
|
||||
accessorKey: "id",
|
||||
header: "Reference",
|
||||
},
|
||||
{
|
||||
accessorKey: "createdAt",
|
||||
header: "Created At",
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: "Status",
|
||||
cell: ({row}) => {
|
||||
return <StatusBadge status={row.getValue("status")}/>
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
cell: ({row}) => {
|
||||
const payment = row.original
|
||||
|
||||
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>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(payment.id)}
|
||||
>
|
||||
Copy payment ID
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator/>
|
||||
<DropdownMenuItem>View customer</DropdownMenuItem>
|
||||
<DropdownMenuItem>View payment details</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,65 @@
|
||||
"use client"
|
||||
|
||||
import {ColumnDef} from "@tanstack/react-table"
|
||||
import {StatusBadge} from "@/components/wrappers/status-badge";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel, DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import {Button} from "@/components/ui/button";
|
||||
import {MoreHorizontal} from "lucide-react";
|
||||
|
||||
export type Restore = {
|
||||
id: string
|
||||
createdAt: string
|
||||
status: "pending" | "processing" | "success" | "failed"
|
||||
}
|
||||
|
||||
export const columns: ColumnDef<Restore>[] = [
|
||||
{
|
||||
accessorKey: "id",
|
||||
header: "Reference",
|
||||
},
|
||||
{
|
||||
accessorKey: "createdAt",
|
||||
header: "Created At",
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: "Status",
|
||||
cell: ({row}) => {
|
||||
return <StatusBadge status={row.getValue("status")}/>
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
cell: ({row}) => {
|
||||
const payment = row.original
|
||||
|
||||
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>
|
||||
<DropdownMenuItem
|
||||
onClick={() => navigator.clipboard.writeText(payment.id)}
|
||||
>
|
||||
Copy payment ID
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator/>
|
||||
<DropdownMenuItem>View customer</DropdownMenuItem>
|
||||
<DropdownMenuItem>View payment details</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -1123,6 +1123,18 @@
|
||||
dependencies:
|
||||
"@tanstack/query-core" "5.59.20"
|
||||
|
||||
"@tanstack/react-table@^8.20.5":
|
||||
version "8.20.5"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.20.5.tgz#19987d101e1ea25ef5406dce4352cab3932449d8"
|
||||
integrity sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==
|
||||
dependencies:
|
||||
"@tanstack/table-core" "8.20.5"
|
||||
|
||||
"@tanstack/table-core@8.20.5":
|
||||
version "8.20.5"
|
||||
resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.20.5.tgz#3974f0b090bed11243d4107283824167a395cf1d"
|
||||
integrity sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==
|
||||
|
||||
"@types/cookie@0.6.0":
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5"
|
||||
@@ -1595,9 +1607,9 @@ camelcase-css@^2.0.1:
|
||||
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
|
||||
|
||||
caniuse-lite@^1.0.30001579:
|
||||
version "1.0.30001680"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz#5380ede637a33b9f9f1fc6045ea99bd142f3da5e"
|
||||
integrity sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==
|
||||
version "1.0.30001679"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001679.tgz#18c573b72f72ba70822194f6c39e7888597f9e32"
|
||||
integrity sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==
|
||||
|
||||
chalk@^4.0.0:
|
||||
version "4.1.2"
|
||||
@@ -3377,7 +3389,7 @@ path-scurry@^1.11.1:
|
||||
lru-cache "^10.2.0"
|
||||
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
|
||||
picocolors@^1.0.0, picocolors@^1.1.1:
|
||||
picocolors@^1.0.0, picocolors@^1.1.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||
@@ -3456,12 +3468,12 @@ postcss@8.4.31:
|
||||
source-map-js "^1.0.2"
|
||||
|
||||
postcss@^8, postcss@^8.4.23:
|
||||
version "8.4.48"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.48.tgz#765f3f8abaa2a2b065cdddbc57ad4cb5a76e515f"
|
||||
integrity sha512-GCRK8F6+Dl7xYniR5a4FYbpBzU8XnZVeowqsQFYdcXuSbChgiks7qybSkbvnaeqv0G0B+dd9/jJgH8kkLDQeEA==
|
||||
version "8.4.47"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.47.tgz#5bf6c9a010f3e724c503bf03ef7947dcb0fea365"
|
||||
integrity sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==
|
||||
dependencies:
|
||||
nanoid "^3.3.7"
|
||||
picocolors "^1.1.1"
|
||||
picocolors "^1.1.0"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
preact-render-to-string@5.2.3:
|
||||
|
||||
Reference in New Issue
Block a user