feat: the Providers table is rendering real data

This commit is contained in:
Pablo Lara
2024-07-29 12:41:02 +02:00
parent 0035c8c08e
commit 9fd642fe0e
8 changed files with 385 additions and 33 deletions
+19 -30
View File
@@ -1,20 +1,11 @@
import { Spacer } from "@nextui-org/react";
import React from "react";
import { CustomTable, Header, ModalWrap } from "@/components";
import { ColumnsProviders, DataTable, Header, ModalWrap } from "@/components";
import { getProvider } from "@/lib/actions";
const visibleColumns: string[] = [
"account",
"group",
"scan_status",
"last_scan",
"next_scan",
"resources",
"added",
"actions",
];
export default function Providers() {
export default async function Providers() {
const providers = await getProvider();
const onSave = async () => {
"use server";
// event we want to pass down, ex. console.log("### hello");
@@ -24,23 +15,21 @@ export default function Providers() {
<>
<Header title="Providers" icon="fluent:cloud-sync-24-regular" />
<Spacer />
<CustomTable
initialVisibleColumns={visibleColumns}
initialRowsPerPage={10}
selectionMode={"none"}
/>
<Spacer />
<ModalWrap
modalTitle="Modal Title"
modalBody={
<>
<p>Modal body content</p>
</>
}
actionButtonLabel="Save"
onAction={onSave}
openButtonLabel="Open Modal"
/>
<div className="flex flex-col items-end w-full">
<ModalWrap
modalTitle="Modal Title"
modalBody={
<>
<p>Modal body content</p>
</>
}
actionButtonLabel="Save"
onAction={onSave}
openButtonLabel="Add Cloud Accounts"
/>
<Spacer y={6} />
<DataTable columns={ColumnsProviders} data={providers.providers.data} />
</div>
</>
);
}
+1 -1
View File
@@ -1,7 +1,7 @@
export * from "./providers/DateWithTime";
export * from "./providers/ProviderInfo";
export * from "./providers/ScanStatus";
export * from "./providers/table/columns";
export * from "./providers/table/ColumnsProviders";
export * from "./providers/table/DataTable";
export * from "./ui/header/Header";
export * from "./ui/modal";
+1 -1
View File
@@ -16,7 +16,7 @@ export const DateWithTime: React.FC<DateWithTimeProps> = ({
return (
<div className="max-w-fit">
<div className="flex flex-col items-center">
<div className="flex flex-col items-start">
<span className="text-md font-semibold">{formattedDate}</span>
{showTime && (
<span className="text-sm text-gray-500">{formattedTime}</span>
@@ -0,0 +1,111 @@
"use client";
import {
Button,
Dropdown,
DropdownItem,
DropdownMenu,
DropdownTrigger,
} from "@nextui-org/react";
import { ColumnDef } from "@tanstack/react-table";
import { add } from "date-fns";
import { VerticalDotsIcon } from "@/components/icons";
import { StatusBadge } from "@/components/ui/table/StatusBadge";
import { ProviderProps } from "@/types";
import { DateWithTime } from "../DateWithTime";
import { ProviderInfo } from "../ProviderInfo";
export const ColumnsProviders: ColumnDef<ProviderProps>[] = [
{
header: "ID",
cell: ({ row }) => <p className="text-medium">{row.index + 1}</p>,
},
{
accessorKey: "account",
header: "Account",
cell: ({ row }) => {
const provider = row.original;
return (
<ProviderInfo
connected={provider.attributes.connection.connected}
provider={provider.attributes.provider}
providerAlias={provider.attributes.alias}
providerId={provider.attributes.provider_id}
/>
);
},
},
{
accessorKey: "status",
header: "Scan Status",
cell: ({ row }) => {
const provider = row.original;
return <StatusBadge status={provider.attributes.status} />;
},
},
{
accessorKey: "lastScan",
header: "Last Scan",
cell: ({ row }) => {
const provider = row.original;
return <DateWithTime dateTime={provider.attributes.updated_at} />;
},
},
{
accessorKey: "nextScan",
header: "Next Scan",
cell: ({ row }) => {
const provider = row.original;
const nextDay = add(new Date(provider.attributes.updated_at), {
hours: 24,
});
return <DateWithTime dateTime={nextDay.toISOString()} />;
},
},
{
accessorKey: "resources",
header: "Resources",
cell: ({ row }) => {
const provider = row.original;
return <p className="font-medium">{provider.attributes.resources}</p>;
},
},
{
accessorKey: "added",
header: "Added",
cell: ({ row }) => {
const provider = row.original;
return (
<DateWithTime
dateTime={provider.attributes.inserted_at}
showTime={false}
/>
);
},
},
{
accessorKey: "actions",
header: () => <div className="text-right">Actions</div>,
id: "actions",
cell: () => {
// const provider = row.original;
return (
<div className="relative flex justify-end items-center gap-2">
<Dropdown className="bg-background border-1 border-default-200">
<DropdownTrigger>
<Button isIconOnly radius="full" size="sm" variant="light">
<VerticalDotsIcon className="text-default-400" />
</Button>
</DropdownTrigger>
<DropdownMenu>
<DropdownItem>Manage</DropdownItem>
<DropdownItem>Delete</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
);
},
},
];
+109
View File
@@ -0,0 +1,109 @@
"use client";
import { Button } from "@nextui-org/react";
import {
ColumnDef,
flexRender,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table/Table";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
}
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});
return (
<>
<div className="rounded-md border w-full">
<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={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="solid"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="solid"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</>
);
}
+25
View File
@@ -0,0 +1,25 @@
import { Chip } from "@nextui-org/react";
import React from "react";
type Status = "completed" | "pending" | "cancelled";
export const statusColorMap: Record<Status, "success" | "danger" | "warning"> =
{
completed: "success",
pending: "warning",
cancelled: "danger",
};
export const StatusBadge = ({ status }: { status: Status }) => {
return (
<Chip
className="capitalize border-none gap-1 text-default-600"
// eslint-disable-next-line security/detect-object-injection
color={statusColorMap[status]}
size="sm"
variant="flat"
>
{status}
</Chip>
);
};
+117
View File
@@ -0,0 +1,117 @@
import * as React from "react";
import { cn } from "@/lib/utils";
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
));
Table.displayName = "Table";
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
));
TableHeader.displayName = "TableHeader";
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
));
TableBody.displayName = "TableBody";
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn(
"border-t bg-slate-100/50 font-medium [&>tr]:last:border-b-0 dark:bg-slate-800/50",
className,
)}
{...props}
/>
));
TableFooter.displayName = "TableFooter";
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-slate-100/50 data-[state=selected]:bg-slate-100 dark:hover:bg-slate-800/50 dark:data-[state=selected]:bg-slate-800",
className,
)}
{...props}
/>
));
TableRow.displayName = "TableRow";
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-medium text-slate-500 [&:has([role=checkbox])]:pr-0 dark:text-slate-400",
className,
)}
{...props}
/>
));
TableHead.displayName = "TableHead";
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
));
TableCell.displayName = "TableCell";
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-slate-500 dark:text-slate-400", className)}
{...props}
/>
));
TableCaption.displayName = "TableCaption";
export {
Table,
TableBody,
TableCaption,
TableCell,
TableFooter,
TableHead,
TableHeader,
TableRow,
};
+2 -1
View File
@@ -3,8 +3,9 @@ import "server-only";
import { parseStringify } from "../utils";
export const getProvider = async () => {
const key = process.env.LOCAL_SITE_URL;
try {
const providers = await fetch("http://localhost:3000/api/providers");
const providers = await fetch(`${key}/api/providers`);
const data = await providers.json();
return parseStringify(data);
} catch (error) {