feat: api communication table

This commit is contained in:
mehedi-hasan 2024-04-14 13:54:15 +06:00
parent cba7c522c3
commit dbcf6cab66
20 changed files with 1438 additions and 378 deletions

View File

@ -0,0 +1,20 @@
import ToolsTable from "@/components/api-communication-table/tools-table";
import Breadcrumb from "@/components/breadcrumb";
import { dummyApiCommunications } from "@/constants/data";
import { ApiCommunication } from "@/types/api-communication";
import React from "react";
const breadcrumbItems = [
{ title: "Dashboard", link: "#" },
{ title: "Api Communication" },
];
const Page = () => {
return (
<div className="flex-1 space-y-4 p-4 md:p-8 pt-6">
<Breadcrumb items={breadcrumbItems} />
<ToolsTable apiCommunications={dummyApiCommunications as ApiCommunication[]} />
</div>
);
};
export default Page;

View File

@ -1,8 +1,7 @@
import Breadcrumb from "@/components/breadcrumb";
import ToolsTable from "@/components/organizations-table/tools-table";
import { dummyOrganizations, dummyUsers } from "@/constants/data";
import { dummyOrganizations } from "@/constants/data";
import { Organization } from "@/types/organization";
import { User } from "@/types/user";
import React from "react";
const breadcrumbItems = [

View File

@ -3,38 +3,37 @@ import {
ChevronRightIcon,
DoubleArrowLeftIcon,
DoubleArrowRightIcon,
} from "@radix-ui/react-icons"
import { Table } from "@tanstack/react-table"
} from "@radix-ui/react-icons";
import { Table } from "@tanstack/react-table";
import { Button } from "@/components/ui/button"
import {
import { Button } from "@/components/ui/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
} from "@/components/ui/select";
interface DataTablePaginationProps<TData> {
table: Table<TData>
}
interface DataTablePaginationProps<TData> {
table: Table<TData>;
}
export function DataTablePagination<TData>({
export function DataTablePagination<TData>({
table,
}: DataTablePaginationProps<TData>) {
}: DataTablePaginationProps<TData>) {
return (
<div className="flex items-center justify-between px-2">
<div className="flex-1 text-sm text-muted-foreground">
<div className="grid grid-cols-2 md:grid-cols-[1fr_auto_auto] gap-x-6 gap-y-2">
<div className="text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="flex items-center space-x-6 lg:space-x-8">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium hidden md:block">Rows per page</p>
<p className="text-sm font-medium">Rows per page</p>
<Select
value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => {
table.setPageSize(Number(value))
table.setPageSize(Number(value));
}}
>
<SelectTrigger className="h-8 w-[70px]">
@ -49,14 +48,15 @@ import {
</SelectContent>
</Select>
</div>
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
<div className="flex items-center gap-2">
<div className="text-sm font-medium">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
className="h-8 w-8 p-0 flex"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
@ -83,7 +83,7 @@ import {
</Button>
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
className="h-8 w-8 p-0 flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
@ -93,5 +93,5 @@ import {
</div>
</div>
</div>
)
}
);
}

View File

@ -0,0 +1,68 @@
"use client";
import { AlertModal } from "@/components/alert-modal";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useToast } from "@/components/ui/use-toast";
import { ApiCommunication } from "@/types/api-communication";
import { Edit, MoreHorizontal, Trash } from "lucide-react";
import { useRouter } from "next/navigation";
import { useState } from "react";
interface CellActionProps {
data: ApiCommunication;
}
export const CellAction: React.FC<CellActionProps> = ({ data }) => {
const [loading, setLoading] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const router = useRouter();
const { toast } = useToast();
const onDeleteConfirm = async () => {};
const handleUpdateStatus = async () => {};
return (
<>
<AlertModal
isOpen={isDeleteModalOpen}
onClose={() => setIsDeleteModalOpen(false)}
onConfirm={onDeleteConfirm}
loading={loading}
/>
<DropdownMenu modal={false}>
<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
className="cursor-pointer"
// onClick={() =>
// router.push(`#`)
// }
>
<Edit className="mr-2 h-4 w-4" /> Update
</DropdownMenuItem>
<DropdownMenuItem
className="cursor-pointer"
onClick={() => setIsDeleteModalOpen(true)}
>
<Trash className="mr-2 h-4 w-4" /> Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</>
);
};

View File

@ -0,0 +1,269 @@
"use client";
import { DataTableColumnHeader } from "./data-table-column-header";
import { CellAction } from "./cell-action";
import { ColumnDef } from "@tanstack/react-table";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card";
import { Checkbox } from "@/components/ui/checkbox";
import { Organization } from "@/types/organization";
import { formatDate } from "@/lib/format-date";
import { ApiCommunication } from "@/types/api-communication";
import { ScrollArea, ScrollBar } from "../ui/scroll-area";
export const columns: ColumnDef<ApiCommunication>[] = [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() && "indeterminate")
}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
className="translate-y-[2px]"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
className="translate-y-[2px]"
/>
),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: "id",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="PT" />
),
cell: ({ row }) => {
return (
<div className="w-fit">
<HoverCard>
<HoverCardTrigger>
<p>{row.getValue("id")}</p>
</HoverCardTrigger>
<HoverCardContent>{row.getValue("id")}</HoverCardContent>
</HoverCard>
</div>
);
},
},
{
accessorKey: "messages",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="MESSAGES" />
),
cell: ({ row }) => {
return (
<div className="w-fit">
<HoverCard>
<HoverCardTrigger>
<p className="max-w-[200px] truncate font-medium">
{row.getValue("messages")}
</p>
</HoverCardTrigger>
<HoverCardContent className="p-0 h-72 w-72 lg:h-96 lg:w-96">
<ScrollArea className="h-72 w-72 p-4 lg:h-96 lg:w-96">
{JSON.stringify(row.getValue("messages"), null, 2)}
<ScrollBar orientation="horizontal" />
</ScrollArea>
</HoverCardContent>
</HoverCard>
</div>
);
},
},
{
accessorKey: "completion",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="COMPLETION" />
),
cell: ({ row }) => {
return (
<div className="w-fit">
<HoverCard>
<HoverCardTrigger>
<p className="max-w-[200px] truncate font-medium">
{row.getValue("completion")}
</p>
</HoverCardTrigger>
<HoverCardContent className="p-0 h-72 w-72 lg:h-96 lg:w-96">
<ScrollArea className="h-72 w-72 p-4 lg:h-96 lg:w-96">
{row.getValue("completion")}
<ScrollBar orientation="horizontal" />
</ScrollArea>
</HoverCardContent>
</HoverCard>
</div>
);
},
},
{
accessorKey: "pt",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="PT" />
),
cell: ({ row }) => {
return (
<div className="w-fit">
<HoverCard>
<HoverCardTrigger>
<p>{row.getValue("pt")}</p>
{/* <p className="max-w-[100px] truncate font-medium">
{row.getValue("pt")}
</p> */}
</HoverCardTrigger>
<HoverCardContent
// className="p-0"
>
{/* <ScrollArea className="w-64 p-4"> */}
{row.getValue("pt")}
{/* <ScrollBar orientation="horizontal" /> */}
{/* </ScrollArea> */}
</HoverCardContent>
</HoverCard>
</div>
);
},
},
{
accessorKey: "ct",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="CT" />
),
cell: ({ row }) => {
return (
<div className="w-fit">
<HoverCard>
<HoverCardTrigger>
<p>{row.getValue("ct")}</p>
{/* <p className="max-w-[100px] truncate font-medium">
{row.getValue("ct")}
</p> */}
</HoverCardTrigger>
<HoverCardContent
// className="p-0"
>
{/* <ScrollArea className="w-64 p-4"> */}
{row.getValue("ct")}
{/* <ScrollBar orientation="horizontal" /> */}
{/* </ScrollArea> */}
</HoverCardContent>
</HoverCard>
</div>
);
},
filterFn: (row, id, value) => {
return value.includes(row.getValue(id));
},
},
{
accessorKey: "tt",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="TT" />
),
cell: ({ row }) => {
return (
<div className="w-fit">
<HoverCard>
<HoverCardTrigger>
<p>{row.getValue("tt")}</p>
{/* <p className="max-w-[100px] truncate font-medium">
{row.getValue("tt")}
</p> */}
</HoverCardTrigger>
<HoverCardContent
// className="p-0"
>
{/* <ScrollArea className="w-64 p-4"> */}
{row.getValue("tt")}
{/* <ScrollBar orientation="horizontal" /> */}
{/* </ScrollArea> */}
</HoverCardContent>
</HoverCard>
</div>
);
},
filterFn: (row, id, value) => {
return value.includes(row.getValue(id));
},
},
{
accessorKey: "status",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="STATUS" />
),
cell: ({ row }) => {
return (
<div className="w-fit">
<HoverCard>
<HoverCardTrigger>
<p>{row.getValue("status")}</p>
</HoverCardTrigger>
<HoverCardContent>{row.getValue("status")}</HoverCardContent>
</HoverCard>
</div>
);
},
filterFn: (row, id, value) => {
return value.includes(row.getValue(id));
},
},
{
accessorKey: "evaluation",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="EVALUATION" />
),
cell: ({ row }) => {
return (
<div className="w-fit">
<HoverCard>
<HoverCardTrigger>
{/* <p>{row.getValue("evaluation")}</p> */}
<p className="max-w-[200px] truncate font-medium">
{row.getValue("evaluation")}
</p>
</HoverCardTrigger>
<HoverCardContent className="p-0 h-52">
<ScrollArea className="h-52 p-4">
{row.getValue("evaluation")}
<ScrollBar orientation="horizontal" />
</ScrollArea>
</HoverCardContent>
</HoverCard>
</div>
);
},
},
{
accessorKey: "created_at",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="CREATED AT" />
),
cell: ({ row }) => {
return <p className="w-fit">{formatDate(row.getValue("created_at"))}</p>;
},
filterFn: (row, id, value) => {
return value.includes(row.getValue(id));
},
},
{
id: "actions",
header: ({ column }) => (
<DataTableColumnHeader column={column} title="ACTIONS" />
),
cell: ({ row }) => <CellAction data={row.original} />,
},
];

View File

@ -0,0 +1,72 @@
import {
ArrowDownIcon,
ArrowUpIcon,
CaretSortIcon,
EyeNoneIcon,
} from "@radix-ui/react-icons"
import { Column } from "@tanstack/react-table"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
interface DataTableColumnHeaderProps<TData, TValue>
extends React.HTMLAttributes<HTMLDivElement> {
column: Column<TData, TValue>
title: string
}
export function DataTableColumnHeader<TData, TValue>({
column,
title,
className,
}: DataTableColumnHeaderProps<TData, TValue>) {
if (!column.getCanSort()) {
return <div className={cn(className)}>{title}</div>
}
return (
<div className={cn("flex items-center space-x-2", className)}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="sm"
className="-ml-3 h-8 data-[state=open]:bg-accent"
>
<span>{title}</span>
{column.getIsSorted() === "desc" ? (
<ArrowDownIcon className="ml-2 h-4 w-4" />
) : column.getIsSorted() === "asc" ? (
<ArrowUpIcon className="ml-2 h-4 w-4" />
) : (
<CaretSortIcon className="ml-2 h-4 w-4" />
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuItem onClick={() => column.toggleSorting(false)}>
<ArrowUpIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Asc
</DropdownMenuItem>
<DropdownMenuItem onClick={() => column.toggleSorting(true)}>
<ArrowDownIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Desc
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
<EyeNoneIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
Hide
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
)
}

View File

@ -0,0 +1,152 @@
import * as React from "react";
import { CheckIcon, PlusCircledIcon } from "@radix-ui/react-icons";
import { Column } from "@tanstack/react-table";
import { cn } from "@/lib/utils";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
} from "@/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Separator } from "@/components/ui/separator";
interface DataTableFacetedFilterProps<TData, TValue> {
column?: Column<TData, TValue>;
title?: string;
options: {
label: string;
value: string;
icon?: React.ComponentType<{ className?: string }>;
}[];
}
export function DataTableFacetedFilter<TData, TValue>({
column,
title,
options,
}: DataTableFacetedFilterProps<TData, TValue>) {
const facets = column?.getFacetedUniqueValues();
const selectedValues = new Set(column?.getFilterValue() as string[]);
return (
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" size="sm" className="h-8 border-dashed">
<PlusCircledIcon className="mr-2 h-4 w-4" />
{title}
{selectedValues?.size > 0 && (
<>
<Separator orientation="vertical" className="mx-2 h-4" />
<Badge
variant="secondary"
className="rounded-sm px-1 font-normal lg:hidden"
>
{selectedValues.size}
</Badge>
<div className="hidden space-x-1 lg:flex">
{selectedValues.size > 2 ? (
<Badge
variant="secondary"
className="rounded-sm px-1 font-normal"
>
{selectedValues.size} selected
</Badge>
) : (
options
.filter((option) => selectedValues.has(option.value))
.map((option) => (
<Badge
variant="secondary"
key={option.value}
className="rounded-sm px-1 font-normal"
>
{option.label}
</Badge>
))
)}
</div>
</>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0" align="start">
<Command>
<CommandInput placeholder={title} />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup>
{options.map((option) => {
const isSelected = selectedValues.has(option.value);
return (
<CommandItem
key={option.value}
onSelect={() => {
if (isSelected) {
selectedValues.delete(option.value);
} else {
selectedValues.add(option.value);
}
const filterValues = Array.from(selectedValues);
column?.setFilterValue(
filterValues.length ? filterValues : undefined
);
}}
>
<div
className={cn(
"mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
isSelected
? "bg-primary text-primary-foreground"
: "opacity-50 [&_svg]:invisible"
)}
>
<CheckIcon className={cn("h-4 w-4")} />
</div>
{option.icon && (
<option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
)}
<span
title={option.label}
className="w-[80%] inline-block truncate"
>
{option.label}
</span>
{facets?.get(option.value) && (
<span className="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
{facets.get(option.value)}
</span>
)}
</CommandItem>
);
})}
</CommandGroup>
{selectedValues.size > 0 && (
<>
<CommandSeparator />
<CommandGroup>
<CommandItem
onSelect={() => column?.setFilterValue(undefined)}
className="justify-center text-center"
>
Clear filters
</CommandItem>
</CommandGroup>
</>
)}
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}

View File

@ -0,0 +1,97 @@
import {
ChevronLeftIcon,
ChevronRightIcon,
DoubleArrowLeftIcon,
DoubleArrowRightIcon,
} from "@radix-ui/react-icons";
import { Table } from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
interface DataTablePaginationProps<TData> {
table: Table<TData>;
}
export function DataTablePagination<TData>({
table,
}: DataTablePaginationProps<TData>) {
return (
<div className="grid grid-cols-2 md:grid-cols-[1fr_auto_auto] gap-x-6 gap-y-2">
<div className="text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="flex items-center space-x-2">
<p className="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-[70px]">
<SelectValue placeholder={table.getState().pagination.pageSize} />
</SelectTrigger>
<SelectContent side="top">
{[10, 20, 30, 40, 50].map((pageSize) => (
<SelectItem key={pageSize} value={`${pageSize}`}>
{pageSize}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-center gap-2">
<div className="text-sm font-medium">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
className="h-8 w-8 p-0 flex"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to first page</span>
<DoubleArrowLeftIcon className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="h-8 w-8 p-0"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
<span className="sr-only">Go to previous page</span>
<ChevronLeftIcon className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="h-8 w-8 p-0"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to next page</span>
<ChevronRightIcon className="h-4 w-4" />
</Button>
<Button
variant="outline"
className="h-8 w-8 p-0 flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
<span className="sr-only">Go to last page</span>
<DoubleArrowRightIcon className="h-4 w-4" />
</Button>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,96 @@
"use client";
import { Cross2Icon } from "@radix-ui/react-icons";
import { Table } from "@tanstack/react-table";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
// import { DataTableViewOptions } from "./components/data-table-view-options"
import { DataTableFacetedFilter } from "./data-table-faceted-filter";
import { DataTableViewOptions } from "./data-table-view-options";
import { ApiCommunication } from "@/types/api-communication";
interface DataTableToolbarProps<TData> {
table: Table<TData>;
data: ApiCommunication[];
}
export function DataTableToolbar<TData>({
table,
data,
}: DataTableToolbarProps<TData>) {
const isFiltered = table.getState().columnFilters.length > 0;
const statusFilterOptions = Array.from(
new Set(data.map((d) => d.status.toLocaleLowerCase()))
).map((v) => ({
label: v,
value: v,
}));
// const statusFilterOptions = Array.from(
// new Set(data.map((d) => d.status.toLocaleLowerCase()))
// ).map((v) => ({
// label: v.slice(0, 1).toLocaleUpperCase() + v.slice(1),
// value: v.slice(0, 1).toLocaleUpperCase() + v.slice(1),
// }));
console.log(statusFilterOptions);
return (
<div className="flex items-center justify-between">
<div className="flex flex-1 items-center gap-2 flex-wrap">
<Input
placeholder="Search messages..."
value={
(table.getColumn("messages")?.getFilterValue() as string) ?? ""
}
onChange={(event: any) =>
table.getColumn("messages")?.setFilterValue(event.target.value)
}
className="h-8 w-[150px] lg:w-[250px]"
/>
<Input
placeholder="Search completion..."
value={
(table.getColumn("completion")?.getFilterValue() as string) ?? ""
}
onChange={(event: any) =>
table.getColumn("completion")?.setFilterValue(event.target.value)
}
className="h-8 w-[150px] lg:w-[250px]"
/>
<Input
placeholder="Search evaluation..."
value={
(table.getColumn("evaluation")?.getFilterValue() as string) ?? ""
}
onChange={(event: any) =>
table.getColumn("evaluation")?.setFilterValue(event.target.value)
}
className="h-8 w-[150px] lg:w-[250px]"
/>
{table.getColumn("status") && (
<DataTableFacetedFilter
column={table.getColumn("status")}
title="Status"
options={statusFilterOptions}
/>
)}
{isFiltered && (
<Button
variant="ghost"
onClick={() => table.resetColumnFilters()}
className="h-8 px-2 lg:px-3"
>
Reset
<Cross2Icon className="ml-2 h-4 w-4" />
</Button>
)}
</div>
<DataTableViewOptions table={table} />
</div>
);
}

View File

@ -0,0 +1,59 @@
"use client"
import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu"
import { MixerHorizontalIcon } from "@radix-ui/react-icons"
import { Table } from "@tanstack/react-table"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
} from "@/components/ui/dropdown-menu"
interface DataTableViewOptionsProps<TData> {
table: Table<TData>
}
export function DataTableViewOptions<TData>({
table,
}: DataTableViewOptionsProps<TData>) {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
className="ml-auto hidden h-8 lg:flex"
>
<MixerHorizontalIcon className="mr-2 h-4 w-4" />
View
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[150px]">
<DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
<DropdownMenuSeparator />
{table
.getAllColumns()
.filter(
(column) =>
typeof column.accessorFn !== "undefined" && column.getCanHide()
)
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) => column.toggleVisibility(!!value)}
>
{column.id}
</DropdownMenuCheckboxItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
)
}

View File

@ -0,0 +1,131 @@
"use client";
import * as React from "react";
import {
ColumnDef,
ColumnFiltersState,
SortingState,
VisibilityState,
flexRender,
getCoreRowModel,
getFacetedRowModel,
getFacetedUniqueValues,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { DataTablePagination } from "./data-table-pagination";
import { DataTableToolbar } from "./data-table-toolbar";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import { Organization } from "@/types/organization";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[];
}
export function DataTable<TData, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
const [rowSelection, setRowSelection] = React.useState({});
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[]
);
const [sorting, setSorting] = React.useState<SortingState>([]);
const table = useReactTable({
data,
columns,
state: {
sorting,
columnVisibility,
rowSelection,
columnFilters,
},
enableRowSelection: true,
onRowSelectionChange: setRowSelection,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onColumnVisibilityChange: setColumnVisibility,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFacetedRowModel: getFacetedRowModel(),
getFacetedUniqueValues: getFacetedUniqueValues(),
});
return (
<div className="space-y-4">
<DataTableToolbar table={table} data={data as Organization[]} />
{/* <div className="rounded-md border"> */}
<ScrollArea className="rounded-md border h-[66vh]">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id} colSpan={header.colSpan}>
{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>
<ScrollBar orientation="horizontal" />
</ScrollArea>
{/* </div> */}
<DataTablePagination table={table} />
</div>
);
}

View File

@ -0,0 +1,47 @@
"use client";
import { Heading } from "@/components/ui/heading";
import { Separator } from "@/components/ui/separator";
import React from "react";
import { DataTable } from "./data-table";
import { columns } from "./columns";
import Link from "next/link";
import { Plus } from "lucide-react";
import { buttonVariants } from "@/components/ui/button";
import { Organization } from "@/types/organization";
import { ApiCommunication } from "@/types/api-communication";
const ToolsTable = ({
apiCommunications,
}: {
apiCommunications: ApiCommunication[];
}) => {
return (
<>
<div className="flex items-center justify-between">
<Heading
title={`Api Communications (${apiCommunications.length})`}
description="Manage Api Communications"
/>
<Link
href="#"
className={buttonVariants({
variant: "default",
// size: "sm",
})}
// className="text-xs md:text-sm"
// onClick={() => router.push(`/admin-dashboard/tools/add`)}
>
<Plus className="mr-2 h-4 w-4" />{" "}
<span className="md:hidden">Add</span>{" "}
<span className="hidden md:block">Add ApiCommunication</span>
</Link>
</div>
<Separator />
<DataTable data={apiCommunications} columns={columns} />
{/* <DataTable data={tasks} columns={columns} /> */}
</>
);
};
export default ToolsTable;

View File

@ -3,38 +3,37 @@ import {
ChevronRightIcon,
DoubleArrowLeftIcon,
DoubleArrowRightIcon,
} from "@radix-ui/react-icons"
import { Table } from "@tanstack/react-table"
} from "@radix-ui/react-icons";
import { Table } from "@tanstack/react-table";
import { Button } from "@/components/ui/button"
import {
import { Button } from "@/components/ui/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
} from "@/components/ui/select";
interface DataTablePaginationProps<TData> {
table: Table<TData>
}
interface DataTablePaginationProps<TData> {
table: Table<TData>;
}
export function DataTablePagination<TData>({
export function DataTablePagination<TData>({
table,
}: DataTablePaginationProps<TData>) {
}: DataTablePaginationProps<TData>) {
return (
<div className="flex items-center justify-between px-2">
<div className="flex-1 text-sm text-muted-foreground">
<div className="grid grid-cols-2 md:grid-cols-[1fr_auto_auto] gap-x-6 gap-y-2">
<div className="text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="flex items-center space-x-6 lg:space-x-8">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium hidden md:block">Rows per page</p>
<p className="text-sm font-medium">Rows per page</p>
<Select
value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => {
table.setPageSize(Number(value))
table.setPageSize(Number(value));
}}
>
<SelectTrigger className="h-8 w-[70px]">
@ -49,14 +48,15 @@ import {
</SelectContent>
</Select>
</div>
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
<div className="flex items-center gap-2">
<div className="text-sm font-medium">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
className="h-8 w-8 p-0 flex"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
@ -83,7 +83,7 @@ import {
</Button>
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
className="h-8 w-8 p-0 flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
@ -93,5 +93,5 @@ import {
</div>
</div>
</div>
)
}
);
}

View File

@ -13,13 +13,14 @@ import { useParams, usePathname } from "next/navigation";
import { Separator } from "@/components/ui/separator";
import { Icons } from "./icons";
import { ExpandedItem } from "./sidebar-nav";
import { Fragment } from "react";
import { Fragment, useState } from "react";
import { sideNavItems } from "@/config/nav";
import { siteConfig } from "@/config/site";
import { buttonVariants } from "./ui/button";
import { ScrollArea, ScrollBar } from "./ui/scroll-area";
export default function MobileSidebarNav() {
const [isSheetOpen, setIsSheetOpen] = useState(false)
const params = useParams<{ workspaceId: string }>();
const path = usePathname();
@ -28,7 +29,7 @@ export default function MobileSidebarNav() {
return (
<div className="flex lg:hidden">
<Sheet>
<Sheet open={isSheetOpen} onOpenChange={(v) => setIsSheetOpen(v)}>
<SheetTrigger
className={buttonVariants({ variant: "outline", size: "icon" })}
>
@ -60,6 +61,7 @@ export default function MobileSidebarNav() {
key={link.href + idx}
item={link}
currentPath={"/" + currentPath}
onLinkClick={() => setIsSheetOpen(!isSheetOpen)}
/>
);
})}

View File

@ -3,38 +3,37 @@ import {
ChevronRightIcon,
DoubleArrowLeftIcon,
DoubleArrowRightIcon,
} from "@radix-ui/react-icons"
import { Table } from "@tanstack/react-table"
} from "@radix-ui/react-icons";
import { Table } from "@tanstack/react-table";
import { Button } from "@/components/ui/button"
import {
import { Button } from "@/components/ui/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
} from "@/components/ui/select";
interface DataTablePaginationProps<TData> {
table: Table<TData>
}
interface DataTablePaginationProps<TData> {
table: Table<TData>;
}
export function DataTablePagination<TData>({
export function DataTablePagination<TData>({
table,
}: DataTablePaginationProps<TData>) {
}: DataTablePaginationProps<TData>) {
return (
<div className="flex items-center justify-between px-2">
<div className="flex-1 text-sm text-muted-foreground">
<div className="grid grid-cols-2 md:grid-cols-[1fr_auto_auto] gap-x-6 gap-y-2">
<div className="text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="flex items-center space-x-6 lg:space-x-8">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium hidden md:block">Rows per page</p>
<p className="text-sm font-medium">Rows per page</p>
<Select
value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => {
table.setPageSize(Number(value))
table.setPageSize(Number(value));
}}
>
<SelectTrigger className="h-8 w-[70px]">
@ -49,14 +48,15 @@ import {
</SelectContent>
</Select>
</div>
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
<div className="flex items-center gap-2">
<div className="text-sm font-medium">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
className="h-8 w-8 p-0 flex"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
@ -83,7 +83,7 @@ import {
</Button>
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
className="h-8 w-8 p-0 flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
@ -93,5 +93,5 @@ import {
</div>
</div>
</div>
)
}
);
}

View File

@ -52,19 +52,22 @@ const CollapsedItem = ({
const ExpandedItem = ({
item,
currentPath,
onLinkClick,
}: {
item: NavItem;
currentPath: string;
onLinkClick?: Function;
}) => {
return (
<Link
onClick={() => onLinkClick && onLinkClick()}
href={item.href}
className={cn(
buttonVariants({ variant: "ghost" }),
"justify-start text-muted-foreground",
currentPath === item.href
? "bg-primary text-primary-foreground hover:bg-primary/90 hover:text-primary-foreground"
: "transparent",
: "transparent"
// "soon" === item.badge && "cursor-not-allowed opacity-80"
)}
>

View File

@ -3,38 +3,37 @@ import {
ChevronRightIcon,
DoubleArrowLeftIcon,
DoubleArrowRightIcon,
} from "@radix-ui/react-icons"
import { Table } from "@tanstack/react-table"
} from "@radix-ui/react-icons";
import { Table } from "@tanstack/react-table";
import { Button } from "@/components/ui/button"
import {
import { Button } from "@/components/ui/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
} from "@/components/ui/select";
interface DataTablePaginationProps<TData> {
table: Table<TData>
}
interface DataTablePaginationProps<TData> {
table: Table<TData>;
}
export function DataTablePagination<TData>({
export function DataTablePagination<TData>({
table,
}: DataTablePaginationProps<TData>) {
}: DataTablePaginationProps<TData>) {
return (
<div className="flex items-center justify-between px-2">
<div className="flex-1 text-sm text-muted-foreground">
<div className="grid grid-cols-2 md:grid-cols-[1fr_auto_auto] gap-x-6 gap-y-2">
<div className="text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="flex items-center space-x-6 lg:space-x-8">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium hidden md:block">Rows per page</p>
<p className="text-sm font-medium">Rows per page</p>
<Select
value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => {
table.setPageSize(Number(value))
table.setPageSize(Number(value));
}}
>
<SelectTrigger className="h-8 w-[70px]">
@ -49,14 +48,15 @@ import {
</SelectContent>
</Select>
</div>
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
<div className="flex items-center gap-2">
<div className="text-sm font-medium">
Page {table.getState().pagination.pageIndex + 1} of{" "}
{table.getPageCount()}
</div>
<div className="flex items-center space-x-2">
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
className="h-8 w-8 p-0 flex"
onClick={() => table.setPageIndex(0)}
disabled={!table.getCanPreviousPage()}
>
@ -83,7 +83,7 @@ import {
</Button>
<Button
variant="outline"
className="hidden h-8 w-8 p-0 lg:flex"
className="h-8 w-8 p-0 flex"
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
disabled={!table.getCanNextPage()}
>
@ -93,5 +93,5 @@ import {
</div>
</div>
</div>
)
}
);
}

View File

@ -85,7 +85,7 @@ export const sideNavItems: GroupedNavItems[] = [
icon: Wrench,
},
{
href: "/dashboard/api-communication",
href: "/dashboard/api-communications",
title: "Api Communication",
icon: Cloudy,
},

View File

@ -1,4 +1,5 @@
import { Account } from "@/types/account";
import { ApiCommunication } from "@/types/api-communication";
import { Configuration } from "@/types/configuration";
import { Organization } from "@/types/organization";
import { User } from "@/types/user";
@ -130,3 +131,35 @@ export const dummyConfigurations: Configuration[] = [
created_at: new Date().toISOString(),
},
];
export const dummyApiCommunications: ApiCommunication[] = [
{
id: 1,
messages:
"{&quot;messages&quot;:[{&quot;role&quot;:&quot;system&quot;,&quot;content&quot;:&quot;\n \n You are a professional language detection service.\n You are given a commit message or a commit diff.\n You have to answer with the programming languages and libraries the user input's code is written in.\n You have to ignore comments and strings in variables. \n You can detect up to 3 but you need to be 100% sure.\n \n Always answer in the following json format where &quot;n&quot; is the name of the detected language or library:\n \n [{&quot;n&quot;:&quot;PHP&quot;, {&quot;n&quot;:&quot;Symfony&quot;}] or []\n \n ----------------------------------------\n \n ONLY JSON IS ALLOWED as an answer. No explanation or other text is allowed.\n \n \n&quot;},{&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:&quot;Allow PHP 8\n\ndiff --git a/composer.json b/composer.json\nindex 7889d20..4c6382c 100644\n--- a/composer.json\n+++ b/composer.json\n@@ -13,7 +13,7 @@\n ],\n &quot;minimum-stability&quot;: &quot;dev&quot;,\n &quot;require&quot;: {\n- &quot;php&quot;: &quot;^7.1.3&quot;,\n+ &quot;php&quot;: &quot;>=7.1.3&quot;,\n &quot;doctrine/inflector&quot;: &quot;^1.2&quot;,\n &quot;nikic/php-parser&quot;: &quot;^4.0&quot;,\n &quot;symfony/config&quot;: &quot;^3.4|^4.0|^5.0&quot;,\n&quot;}]}",
completion: '"[{"n":"PHP"}]"',
pt: 287,
ct: 7,
tt: 294,
status: "skill_extraction_success",
type: "skill_extraction",
evaluation:
"Uploaded at: 2023-09-20 00:00:00 - Uploader: jschultz@php.net - Developer: fabien@potencier.org - Status: finished",
created_at: new Date().toISOString(),
},
{
id: 2,
messages:
"{&quot;messages&quot;:[{&quot;role&quot;:&quot;system&quot;,&quot;content&quot;:&quot;\nThis is a training task for a developer who has the following skillMatrix:\n\nskill: PHP - score:7\nskill: Symfony - score: 7\nskill: EasyAdmin - score: 3\n\n===========================================================================\n\n--- task\ntaskTitle: Create a tabbed user administration page in symfony 6 using easy admin\ntaskDescription: \n\n -- index page\n\n - &quot;List&quot; all users that have a Role that matches or is below your own Role\n - &quot;Create&quot; a new user with a Role that matches or is below your own Role \n tab 1\n firstname, lastname, email\n tab 2\n roles with checkboxes\n tab 3\n change password\n - &quot;Edit&quot; a user with a Role that matches or is below your own Role \n tab 1\n form with firstname, lastname, email \n tab 2\n form with roles with checkboxes\n - &quot;Change Password of exiting User&quot;\n form with password and password confirmation \n\n You got 2 hours to finish this task starting from now.\n Document your findings and the challenges you faced. Adhere to the project's coding standards.\n \n===========================================================================\n\nThe user will give you another skill matrix and you need to create a task with a task title for training purposes. \n\nAlways answer in the following json format:\n\n{ &quot;taskTitle&quot;: &quot;&quot;, &quot;taskDescription&quot;: &quot;[15-20 sentences formatted in markdown]&quot; }\n\ntaskDescription should be written in markdown with proper headlines and formatting.\n\nYou may use newlines in the json response values for formatting reasons.\n\nONLY JSON IS ALLOWED as an answer. No explanation or other text is allowed.\n&quot;},{&quot;role&quot;:&quot;user&quot;,&quot;content&quot;:&quot;PHP: 7\nSymfony: 7\n&quot;}]}",
completion:
"&quot;{ &quot;taskTitle&quot;: &quot;Develop a Blogging Platform using Symfony 6&quot;, &quot;taskDescription&quot;: &quot;\n\nYour task is to create a basic blogging platform using Symfony 6. The platform should have the following features:\n\n- `User Registration and Login`: Implement a user registration and login system using Symfony's security component. Users should be able to register with an email and password.\n\n- `Create Blog Post`: Authenticated users should be able to create a new blog post. A blog post should have a title, content, and the date of creation.\n\n- `List Blog Posts`: Create a page to list all blog posts. Each blog post listing should include the title, an excerpt of the content, and the date of creation. The list should be paginated.\n\n- `View Blog Post`: Users should be able to click on a blog post title in the list to view the full blog post. The full blog post view should include the title, full content, and the date of creation.\n\n- `Edit and Delete Blog Post`: Authenticated users should be able to edit and delete their own blog posts.\n\n- `Comments`: Authenticated users should be able to post comments on a blog post.\n\nYou are given 4 hours to complete this task. Document your findings and the challenges you faced. Adhere to the project's coding standards.\n\n&quot; }&quot;",
pt: 364,
ct: 271,
tt: 635,
status: "course_creation_success",
type: "course_creation",
evaluation:
" Uploaded at: 2023-09-20 00:00:00 - Uploader: jschultz@php.net - Developer: fabien@potencier.org - Status: finished",
created_at: new Date().toISOString(),
},
];

View File

@ -0,0 +1,12 @@
export interface ApiCommunication {
id: number;
messages: string;
completion: string;
pt: number;
ct: number;
tt: number;
status: string;
type: string;
evaluation: string;
created_at: string;
}