diff --git a/src/app/(dashboard)/dashboard/commits/page.tsx b/src/app/(dashboard)/dashboard/commits/page.tsx
new file mode 100644
index 0000000..9bfcdc1
--- /dev/null
+++ b/src/app/(dashboard)/dashboard/commits/page.tsx
@@ -0,0 +1,20 @@
+import Breadcrumb from "@/components/breadcrumb";
+import ToolsTable from "@/components/commits-table/tools-table";
+import { dummyCommits } from "@/constants/data";
+import { Commit } from "@/types/commit";
+import React from "react";
+
+const breadcrumbItems = [
+ { title: "Dashboard", link: "#" },
+ { title: "Commits" },
+];
+const Page = () => {
+ return (
+
+
+
+
+ );
+};
+
+export default Page;
diff --git a/src/components/commits-table/cell-action.tsx b/src/components/commits-table/cell-action.tsx
new file mode 100644
index 0000000..5ccdcaf
--- /dev/null
+++ b/src/components/commits-table/cell-action.tsx
@@ -0,0 +1,72 @@
+"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 { Commit } from "@/types/commit";
+import { Criteria } from "@/types/criteria";
+import { Evaluation } from "@/types/evaluation";
+import { EvaluationStart } from "@/types/evaluation-start";
+import { Skill } from "@/types/skill";
+
+import { Edit, GitCommit, MoreHorizontal, Trash } from "lucide-react";
+import { useRouter } from "next/navigation";
+import { useState } from "react";
+
+interface CellActionProps {
+ data: Commit;
+}
+
+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(`#`)
+ // }
+ >
+ Edit
+
+ setIsDeleteModalOpen(true)}
+ >
+ Delete
+
+
+
+ >
+ );
+};
diff --git a/src/components/commits-table/columns.tsx b/src/components/commits-table/columns.tsx
new file mode 100644
index 0000000..0bed911
--- /dev/null
+++ b/src/components/commits-table/columns.tsx
@@ -0,0 +1,145 @@
+"use client";
+import { DataTableColumnHeader } from "./data-table-column-header";
+import { CellAction } from "./cell-action";
+import { ColumnDef } from "@tanstack/react-table";
+import { Checkbox } from "@/components/ui/checkbox";
+import { formatDate } from "@/lib/format-date";
+import { Evaluation } from "@/types/evaluation";
+import { Commit } from "@/types/commit";
+import {
+ HoverCard,
+ HoverCardContent,
+ HoverCardTrigger,
+} from "../ui/hover-card";
+import { ScrollArea, ScrollBar } from "../ui/scroll-area";
+
+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: "hash",
+ header: ({ column }) => (
+
+ ),
+ cell: ({ row }) => {
+ return (
+
+
+
+
+ {row.getValue("hash")}
+
+
+
+
+ {row.getValue("hash")}
+
+
+
+
+
+ );
+ },
+ },
+
+ {
+ accessorKey: "status",
+ header: ({ column }) => (
+
+ ),
+ cell: ({ row }) => {
+ return {row.getValue("status")}
;
+ },
+ filterFn: (row, id, value) => {
+ return value.includes(row.getValue(id));
+ },
+ },
+
+ {
+ accessorKey: "retryCount",
+ header: ({ column }) => (
+
+ ),
+ cell: ({ row }) => {
+ return {row.getValue("retryCount")}
;
+ },
+ },
+
+ {
+ accessorKey: "type",
+ header: ({ column }) => (
+
+ ),
+ cell: ({ row }) => {
+ return {row.getValue("type")}
;
+ },
+ },
+ {
+ accessorKey: "evaluation",
+ header: ({ column }) => (
+
+ ),
+ cell: ({ row }) => {
+ return (
+
+
+
+
+ {row.getValue("evaluation")}
+
+
+
+
+ {row.getValue("evaluation")}
+
+
+
+
+ );
+ },
+ },
+
+ {
+ accessorKey: "commitDate",
+ header: ({ column }) => (
+
+ ),
+ cell: ({ row }) => {
+ return {formatDate(row.getValue("commitDate"))}
;
+ },
+ filterFn: (row, id, value) => {
+ return value.includes(row.getValue(id));
+ },
+ },
+
+ {
+ id: "actions",
+ header: ({ column }) => (
+
+ ),
+ cell: ({ row }) => ,
+ },
+];
diff --git a/src/components/commits-table/data-table-column-header.tsx b/src/components/commits-table/data-table-column-header.tsx
new file mode 100644
index 0000000..85df45e
--- /dev/null
+++ b/src/components/commits-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/commits-table/data-table-faceted-filter.tsx b/src/components/commits-table/data-table-faceted-filter.tsx
new file mode 100644
index 0000000..a5efee2
--- /dev/null
+++ b/src/components/commits-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/commits-table/data-table-pagination.tsx b/src/components/commits-table/data-table-pagination.tsx
new file mode 100644
index 0000000..3c5a1d9
--- /dev/null
+++ b/src/components/commits-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()}
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/commits-table/data-table-toolbar.tsx b/src/components/commits-table/data-table-toolbar.tsx
new file mode 100644
index 0000000..adbed42
--- /dev/null
+++ b/src/components/commits-table/data-table-toolbar.tsx
@@ -0,0 +1,78 @@
+"use client";
+
+import { Table } from "@tanstack/react-table";
+import { Input } from "@/components/ui/input";
+import { DataTableViewOptions } from "./data-table-view-options";
+import { DataTableFacetedFilter } from "./data-table-faceted-filter";
+import { Button } from "../ui/button";
+import { Cross2Icon } from "@radix-ui/react-icons";
+import { Commit } from "@/types/commit";
+
+interface DataTableToolbarProps {
+ table: Table;
+ data: Commit[];
+}
+
+export function DataTableToolbar({
+ table,
+ data,
+}: DataTableToolbarProps) {
+ const isFiltered = table.getState().columnFilters.length > 0;
+
+ const statusFilterOptions = Array.from(
+ new Set(data.map((d) => d.status))
+ ).map((v) => ({
+ label: v,
+ value: v,
+ }));
+
+ return (
+
+ );
+}
diff --git a/src/components/commits-table/data-table-view-options.tsx b/src/components/commits-table/data-table-view-options.tsx
new file mode 100644
index 0000000..6d56347
--- /dev/null
+++ b/src/components/commits-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/commits-table/data-table.tsx b/src/components/commits-table/data-table.tsx
new file mode 100644
index 0000000..82c09da
--- /dev/null
+++ b/src/components/commits-table/data-table.tsx
@@ -0,0 +1,133 @@
+"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 { Skill } from "@/types/skill";
+import { Evaluation } from "@/types/evaluation";
+import { Commit } from "@/types/commit";
+
+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/commits-table/tools-table.tsx b/src/components/commits-table/tools-table.tsx
new file mode 100644
index 0000000..dd80345
--- /dev/null
+++ b/src/components/commits-table/tools-table.tsx
@@ -0,0 +1,39 @@
+"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 { Commit } from "@/types/commit";
+
+const ToolsTable = ({ commitsData }: { commitsData: Commit[] }) => {
+ return (
+ <>
+
+
+
router.push(`/admin-dashboard/tools/add`)}
+ >
+
Add Commit
+
+
+
+
+ >
+ );
+};
+
+export default ToolsTable;
diff --git a/src/components/evaluations-table/columns.tsx b/src/components/evaluations-table/columns.tsx
index d8da10a..2197f47 100644
--- a/src/components/evaluations-table/columns.tsx
+++ b/src/components/evaluations-table/columns.tsx
@@ -40,6 +40,9 @@ export const columns: ColumnDef[] = [
cell: ({ row }) => {
return {row.getValue("status")}
;
},
+ filterFn: (row, id, value) => {
+ return value.includes(row.getValue(id));
+ },
},
{
diff --git a/src/constants/data.ts b/src/constants/data.ts
index f0e54cf..cb5ec26 100644
--- a/src/constants/data.ts
+++ b/src/constants/data.ts
@@ -1,5 +1,6 @@
import { Account } from "@/types/account";
import { ApiCommunication } from "@/types/api-communication";
+import { Commit } from "@/types/commit";
import { Configuration } from "@/types/configuration";
import { Criteria } from "@/types/criteria";
import { Evaluation } from "@/types/evaluation";
@@ -296,3 +297,17 @@ export const dummyEvaluations: Evaluation[] = [
uploadTime: new Date().toISOString(),
},
];
+
+export const dummyCommits: Commit[] = [
+ {
+ id: 1,
+ hash: "9998211595b05c7f8555e4a129d7792bce7a8dd6_1",
+ status: "skills_extracted",
+ retryCount: 0,
+ type: "code",
+ evaluation:
+ "Uploaded at: 2023-09-20 00:00:00 - Uploader: jschultz@php.net - Developer: fabien@potencier.org - Status: finished",
+ commitDate: new Date().toISOString(),
+ skills: "Skill: PHP - Score: 0",
+ },
+];
diff --git a/src/types/commit.ts b/src/types/commit.ts
new file mode 100644
index 0000000..9c7151f
--- /dev/null
+++ b/src/types/commit.ts
@@ -0,0 +1,10 @@
+export interface Commit {
+ id: number;
+ hash: string;
+ status: string;
+ retryCount: number;
+ type: string;
+ evaluation: string;
+ commitDate: string;
+ skills: string;
+}
\ No newline at end of file