diff --git a/src/app/(dashboard)/dashboard/configurations/page.tsx b/src/app/(dashboard)/dashboard/configurations/page.tsx new file mode 100644 index 0000000..01bec97 --- /dev/null +++ b/src/app/(dashboard)/dashboard/configurations/page.tsx @@ -0,0 +1,21 @@ +import Breadcrumb from "@/components/breadcrumb"; +import ToolsTable from "@/components/configurations-table/tools-table"; +import { dummyAccounts, dummyConfigurations } from "@/constants/data"; +import { Account } from "@/types/account"; +import { Configuration } from "@/types/configuration"; +import React from "react"; + +const breadcrumbItems = [ + { title: "Dashboard", link: "#" }, + { title: "Configurations" }, +]; +const Page = () => { + return ( +
+ + +
+ ); +}; + +export default Page; diff --git a/src/components/accounts-table/columns.tsx b/src/components/accounts-table/columns.tsx index d77570d..3b81e59 100644 --- a/src/components/accounts-table/columns.tsx +++ b/src/components/accounts-table/columns.tsx @@ -1,7 +1,6 @@ "use client"; import { DataTableColumnHeader } from "./data-table-column-header"; import { CellAction } from "./cell-action"; - import { ColumnDef } from "@tanstack/react-table"; import { HoverCard, @@ -9,7 +8,6 @@ import { HoverCardTrigger, } from "@/components/ui/hover-card"; import { Checkbox } from "@/components/ui/checkbox"; -import { Organization } from "@/types/organization"; import { formatDate } from "@/lib/format-date"; import { Account } from "@/types/account"; @@ -39,64 +37,6 @@ export const columns: ColumnDef[] = [ enableHiding: false, }, - // { - // accessorKey: "industry", - // header: ({ column }) => ( - // - // ), - // cell: ({ row }) => { - // return ( - //
- // - // - //

- // {row.getValue("industry")} - //

- //
- // - // {/* */} - // {row.getValue("industry")} - // {/* */} - // {/* */} - // - //
- //
- // ); - // }, - // filterFn: (row, id, value) => { - // return value.includes(row.getValue(id)); - // }, - // }, - // { - // accessorKey: "contactPerson", - // header: ({ column }) => ( - // - // ), - // cell: ({ row }) => { - // return ( - //
- // - // - //

{row.getValue("contactPerson")}

- // {/*

- // {row.getValue("contactPerson")} - //

*/} - //
- // - // {/* */} - // {row.getValue("contactPerson")} - // {/* */} - // {/* */} - // - //
- //
- // ); - // }, - // }, { accessorKey: "email", header: ({ column }) => ( @@ -125,6 +65,34 @@ export const columns: ColumnDef[] = [ ); }, }, + { + accessorKey: "balance", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ( +
+ + +

{row.getValue("balance")}

+ {/*

+ {row.getValue("balance")} +

*/} +
+ + {/* */} + {row.getValue("balance")} + {/* */} + {/* */} + +
+
+ ); + }, + }, { accessorKey: "organization", diff --git a/src/components/configurations-table/cell-action.tsx b/src/components/configurations-table/cell-action.tsx new file mode 100644 index 0000000..96a0b37 --- /dev/null +++ b/src/components/configurations-table/cell-action.tsx @@ -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 { Configuration } from "@/types/configuration"; + +import { BookCheck, Edit, MoreHorizontal, Trash } from "lucide-react"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; + +interface CellActionProps { + data: Configuration; +} + +export const CellAction: React.FC = ({ data }) => { + const [loading, setLoading] = useState(false); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const router = useRouter(); + const { toast } = useToast(); + + const onDeleteConfirm = async () => {}; + + const handleUpdateStatus = async () => {}; + + return ( + <> + setIsDeleteModalOpen(false)} + onConfirm={onDeleteConfirm} + loading={loading} + /> + + + + + + Actions + + + // router.push(`#`) + // } + > + Update + + setIsDeleteModalOpen(true)} + > + Delete + + + + + ); +}; diff --git a/src/components/configurations-table/columns.tsx b/src/components/configurations-table/columns.tsx new file mode 100644 index 0000000..32645f1 --- /dev/null +++ b/src/components/configurations-table/columns.tsx @@ -0,0 +1,117 @@ +"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 { formatDate } from "@/lib/format-date"; +import { Configuration } from "@/types/configuration"; + +export const columns: ColumnDef[] = [ + { + id: "select", + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + className="translate-y-[2px]" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="Select row" + className="translate-y-[2px]" + /> + ), + enableSorting: false, + enableHiding: false, + }, + + { + accessorKey: "name", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ( +
+ + +

{row.getValue("name")}

+ {/*

+ {row.getValue("name")} +

*/} +
+ + {/* */} + {row.getValue("name")} + {/* */} + {/* */} + +
+
+ ); + }, + }, + + { + accessorKey: "value", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ( +
+ + + {/*

+ {row.getValue("email")} +

*/} +

{row.getValue("value")}

+
+ + {/* */} + {row.getValue("value")} + {/* */} + {/* */} + +
+
+ ); + }, + }, + + { + accessorKey: "created_at", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return

{formatDate(row.getValue("created_at"))}

; + }, + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)); + }, + }, + + { + id: "actions", + header: ({ column }) => ( + + ), + cell: ({ row }) => , + }, +]; diff --git a/src/components/configurations-table/data-table-column-header.tsx b/src/components/configurations-table/data-table-column-header.tsx new file mode 100644 index 0000000..85df45e --- /dev/null +++ b/src/components/configurations-table/data-table-column-header.tsx @@ -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 + extends React.HTMLAttributes { + column: Column + title: string + } + + export function DataTableColumnHeader({ + column, + title, + className, + }: DataTableColumnHeaderProps) { + if (!column.getCanSort()) { + return
{title}
+ } + + + return ( +
+ + + + + + column.toggleSorting(false)}> + + Asc + + column.toggleSorting(true)}> + + Desc + + + column.toggleVisibility(false)}> + + Hide + + + +
+ ) + } \ No newline at end of file diff --git a/src/components/configurations-table/data-table-faceted-filter.tsx b/src/components/configurations-table/data-table-faceted-filter.tsx new file mode 100644 index 0000000..a5efee2 --- /dev/null +++ b/src/components/configurations-table/data-table-faceted-filter.tsx @@ -0,0 +1,147 @@ +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 { + column?: Column + title?: string + options: { + label: string + value: string + icon?: React.ComponentType<{ className?: string }> + }[] +} + +export function DataTableFacetedFilter({ + column, + title, + options, +}: DataTableFacetedFilterProps) { + const facets = column?.getFacetedUniqueValues() + const selectedValues = new Set(column?.getFilterValue() as string[]) + + return ( + + + + + + + + + No results found. + + {options.map((option) => { + const isSelected = selectedValues.has(option.value) + return ( + { + if (isSelected) { + selectedValues.delete(option.value) + } else { + selectedValues.add(option.value) + } + const filterValues = Array.from(selectedValues) + column?.setFilterValue( + filterValues.length ? filterValues : undefined + ) + }} + > +
+ +
+ {option.icon && ( + + )} + {option.label} + {facets?.get(option.value) && ( + + {facets.get(option.value)} + + )} +
+ ) + })} +
+ {selectedValues.size > 0 && ( + <> + + + column?.setFilterValue(undefined)} + className="justify-center text-center" + > + Clear filters + + + + )} +
+
+
+
+ ) +} \ No newline at end of file diff --git a/src/components/configurations-table/data-table-pagination.tsx b/src/components/configurations-table/data-table-pagination.tsx new file mode 100644 index 0000000..aec3adc --- /dev/null +++ b/src/components/configurations-table/data-table-pagination.tsx @@ -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 { + table: Table + } + + export function DataTablePagination({ + table, + }: DataTablePaginationProps) { + return ( +
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "} + {table.getFilteredRowModel().rows.length} row(s) selected. +
+
+
+

Rows per page

+ +
+
+ Page {table.getState().pagination.pageIndex + 1} of{" "} + {table.getPageCount()} +
+
+ + + + +
+
+
+ ) + } \ No newline at end of file diff --git a/src/components/configurations-table/data-table-toolbar.tsx b/src/components/configurations-table/data-table-toolbar.tsx new file mode 100644 index 0000000..b7faed2 --- /dev/null +++ b/src/components/configurations-table/data-table-toolbar.tsx @@ -0,0 +1,76 @@ +"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 { userFilterLabels } from "@/constants/data"; +import { Organization } from "@/types/organization"; + +interface DataTableToolbarProps { + table: Table; + data: Organization[]; +} + +export function DataTableToolbar({ + table, + data, +}: DataTableToolbarProps) { + const isFiltered = table.getState().columnFilters.length > 0; + + // const industryFilterOptions = Array.from( + // new Set(data.map((d) => d.industry.toLocaleLowerCase())) + // ).map((v) => ({ + // label: v.slice(0, 1).toLocaleUpperCase() + v.slice(1), + // value: v.slice(0, 1).toLocaleUpperCase() + v.slice(1), + // })); + + return ( +
+
+ + table.getColumn("name")?.setFilterValue(event.target.value) + } + className="h-8 w-[150px] lg:w-[250px]" + /> + {/* + table.getColumn("value")?.setFilterValue(event.target.value) + } + className="h-8 w-[150px] lg:w-[250px]" + /> */} + + {/* + {table.getColumn("industry") && ( + + )} */} + + {/* {isFiltered && ( + + )} */} +
+ +
+ ); +} diff --git a/src/components/configurations-table/data-table-view-options.tsx b/src/components/configurations-table/data-table-view-options.tsx new file mode 100644 index 0000000..6d56347 --- /dev/null +++ b/src/components/configurations-table/data-table-view-options.tsx @@ -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 { + table: Table +} + +export function DataTableViewOptions({ + table, +}: DataTableViewOptionsProps) { + return ( + + + + + + Toggle columns + + {table + .getAllColumns() + .filter( + (column) => + typeof column.accessorFn !== "undefined" && column.getCanHide() + ) + .map((column) => { + return ( + column.toggleVisibility(!!value)} + > + {column.id} + + ) + })} + + + ) +} \ No newline at end of file diff --git a/src/components/configurations-table/data-table.tsx b/src/components/configurations-table/data-table.tsx new file mode 100644 index 0000000..5879ceb --- /dev/null +++ b/src/components/configurations-table/data-table.tsx @@ -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 { + columns: ColumnDef[]; + data: TData[]; +} + +export function DataTable({ + columns, + data, +}: DataTableProps) { + const [rowSelection, setRowSelection] = React.useState({}); + const [columnVisibility, setColumnVisibility] = + React.useState({}); + const [columnFilters, setColumnFilters] = React.useState( + [] + ); + const [sorting, setSorting] = React.useState([]); + + 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 ( +
+ + {/*
*/} + + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+ +
+ {/*
*/} + +
+ ); +} diff --git a/src/components/configurations-table/tools-table.tsx b/src/components/configurations-table/tools-table.tsx new file mode 100644 index 0000000..02fab29 --- /dev/null +++ b/src/components/configurations-table/tools-table.tsx @@ -0,0 +1,45 @@ +"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 { Account } from "@/types/account"; +import { Configuration } from "@/types/configuration"; + +const ToolsTable = ({ + configurations, +}: { + configurations: Configuration[]; +}) => { + return ( + <> +
+ + router.push(`/admin-dashboard/tools/add`)} + > + Add Configuration + +
+ + + {/* */} + + ); +}; + +export default ToolsTable; diff --git a/src/constants/data.ts b/src/constants/data.ts index bb16813..eb52743 100644 --- a/src/constants/data.ts +++ b/src/constants/data.ts @@ -1,3 +1,5 @@ +import { Account } from "@/types/account"; +import { Configuration } from "@/types/configuration"; import { Organization } from "@/types/organization"; import { User } from "@/types/user"; import { UserRound, UserRoundCheck } from "lucide-react"; @@ -69,10 +71,11 @@ export const dummyOrganizations: Organization[] = [ }, ]; -export const dummyAccounts = [ +export const dummyAccounts: Account[] = [ { id: 1, email: "john@gmail.com", + balance: 1000, organization: "XYZ Tech Inc.", accountHistory: "None", created_at: new Date().toISOString(), @@ -80,6 +83,7 @@ export const dummyAccounts = [ { id: 2, email: "sarah@example.com", + balance: 0, organization: "ABC Solutions", accountHistory: "None", created_at: new Date().toISOString(), @@ -87,8 +91,42 @@ export const dummyAccounts = [ { id: 3, email: "michael@example.com", + balance: 1000, organization: "Acme Corporation", accountHistory: "None", created_at: new Date().toISOString(), }, ]; + +export const dummyConfigurations: Configuration[] = [ + { + id: 1, + name: "process_limit", + value: 20, + created_at: new Date().toISOString(), + }, + { + id: 2, + name: "process_timeout", + value: 600, + created_at: new Date().toISOString(), + }, + { + id: 3, + name: "number_of_courses_to_create", + value: 5, + created_at: new Date().toISOString(), + }, + { + id: 4, + name: "max_retries", + value: 2, + created_at: new Date().toISOString(), + }, + { + id: 5, + name: "max_token_per_evaluation", + value: 1000000, + created_at: new Date().toISOString(), + }, +]; diff --git a/src/types/account.ts b/src/types/account.ts index 85142b8..a71fe2a 100644 --- a/src/types/account.ts +++ b/src/types/account.ts @@ -1,7 +1,7 @@ export interface Account { id: number; email: string; - balance: string; + balance: number; organization: string; accountHistory: string; created_at: string; diff --git a/src/types/configuration.ts b/src/types/configuration.ts new file mode 100644 index 0000000..6eaef96 --- /dev/null +++ b/src/types/configuration.ts @@ -0,0 +1,6 @@ +export interface Configuration { + id: number; + name: string; + value: number; + created_at: string; +}