diff --git a/components.json b/components.json new file mode 100644 index 00000000..8b9622e5 --- /dev/null +++ b/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/styles/globals.css", + "baseColor": "neutral", + "cssVariables": false, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 29e2a35a..9f3dc6db 100644 --- a/package.json +++ b/package.json @@ -14,12 +14,14 @@ "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", "@firebase/util": "^1.9.7", - "@headlessui/react": "^1.7.13", + "@headlessui/react": "^2.1.2", "@mdi/js": "^7.1.96", "@mdi/react": "^1.6.1", "@next/font": "13.1.6", "@paypal/paypal-js": "^7.1.0", "@paypal/react-paypal-js": "^8.1.3", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-popover": "^1.1.1", "@react-pdf/renderer": "^3.1.14", "@react-spring/web": "^9.7.4", "@tanstack/react-table": "^8.10.1", @@ -30,7 +32,8 @@ "axios": "^1.3.5", "bcrypt": "^5.1.1", "chart.js": "^4.2.1", - "clsx": "^1.2.1", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", "countries-list": "^3.0.1", "country-codes-list": "^1.6.11", "currency-symbol-map": "^5.1.0", @@ -48,7 +51,7 @@ "lodash": "^4.17.21", "moment": "^2.29.4", "moment-timezone": "^0.5.44", - "next": "13.1.6", + "next": "^14.2.5", "nodemailer": "^6.9.5", "nodemailer-express-handlebars": "^6.1.0", "primeicons": "^6.0.1", @@ -77,7 +80,9 @@ "short-unique-id": "5.0.2", "stripe": "^13.10.0", "swr": "^2.1.3", + "tailwind-merge": "^2.5.2", "tailwind-scrollbar-hide": "^1.1.7", + "tailwindcss-animate": "^1.0.7", "typescript": "4.9.5", "use-file-picker": "^2.1.0", "uuid": "^9.0.0", diff --git a/src/components/List.tsx b/src/components/List.tsx index 19781bf4..0e76b663 100644 --- a/src/components/List.tsx +++ b/src/components/List.tsx @@ -1,10 +1,11 @@ -import {Column, flexRender, getCoreRowModel, useReactTable} from "@tanstack/react-table"; +import {Column, flexRender, getCoreRowModel, getSortedRowModel, useReactTable} from "@tanstack/react-table"; export default function List({data, columns}: {data: T[]; columns: any[]}) { const table = useReactTable({ data, columns: columns, getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), }); return ( @@ -13,8 +14,22 @@ export default function List({data, columns}: {data: T[]; columns: any[]}) { {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( - - {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} + + {header.isPlaceholder ? null : ( + <> +
+ {flexRender(header.column.columnDef.header, header.getContext())} + {{ + asc: " 🔼", + desc: " 🔽", + }[header.column.getIsSorted() as string] ?? null} +
+ + )} ))} diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx new file mode 100644 index 00000000..7bc4061e --- /dev/null +++ b/src/components/ui/popover.tsx @@ -0,0 +1,31 @@ +import * as React from "react" +import * as PopoverPrimitive from "@radix-ui/react-popover" + +import { cn } from "@/lib/utils" + +const Popover = PopoverPrimitive.Root + +const PopoverTrigger = PopoverPrimitive.Trigger + +const PopoverAnchor = PopoverPrimitive.Anchor + +const PopoverContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + + + +)) +PopoverContent.displayName = PopoverPrimitive.Content.displayName + +export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } diff --git a/src/dashboards/MasterCorporate.tsx b/src/dashboards/MasterCorporate.tsx index 2f661040..0f00de7b 100644 --- a/src/dashboards/MasterCorporate.tsx +++ b/src/dashboards/MasterCorporate.tsx @@ -21,6 +21,7 @@ import { BsArrowRepeat, BsPlus, BsPersonFillGear, + BsFilter, } from "react-icons/bs"; import UserCard from "@/components/UserCard"; import useGroups from "@/hooks/useGroups"; @@ -44,10 +45,11 @@ import {createColumn, createColumnHelper} from "@tanstack/react-table"; import List from "@/components/List"; import {getUserCorporate} from "@/utils/groups"; import {getCorporateUser, getUserCompanyName} from "@/resources/user"; -import {Switch} from "@headlessui/react"; import Checkbox from "@/components/Low/Checkbox"; import {uniq, uniqBy} from "lodash"; import Select from "@/components/Low/Select"; +import {Menu, MenuButton, MenuItem, MenuItems} from "@headlessui/react"; +import {Popover, PopoverContent, PopoverTrigger} from "@/components/ui/popover"; interface Props { user: MasterCorporateUser; @@ -62,8 +64,15 @@ const StudentPerformanceList = ({items, stats, users, groups}: {items: StudentPe "id", ), ); + const [availableGroups] = useState( + uniqBy( + items.map((x) => x.group), + "id", + ), + ); const [selectedCorporate, setSelectedCorporate] = useState(null); + const [selectedGroup, setSelectedGroup] = useState(null); const columnHelper = createColumnHelper(); @@ -184,40 +193,80 @@ const StudentPerformanceList = ({items, stats, users, groups}: {items: StudentPe const filterUsers = (data: StudentPerformanceItem[]) => { console.log(data, selectedCorporate); const filterByCorporate = (item: StudentPerformanceItem) => item.corporate?.id === selectedCorporate?.id; + const filterByGroup = (item: StudentPerformanceItem) => item.group?.id === selectedGroup?.id; const filters: ((item: StudentPerformanceItem) => boolean)[] = []; if (selectedCorporate !== null) filters.push(filterByCorporate); + if (selectedGroup !== null) filters.push(filterByGroup); return filters.reduce((d, f) => d.filter(f), data); }; return (
-
- ({ + value: x?.id || "N/A", + label: x?.corporateInformation?.companyInformation?.name || x?.name || "N/A", + }))} + isClearable + value={ + selectedCorporate === null + ? null + : { + value: selectedCorporate?.id || "N/A", + label: + selectedCorporate?.corporateInformation?.companyInformation?.name || + selectedCorporate?.name || + "N/A", + } + } + placeholder="Select a Corporate..." + onChange={(value) => + !value + ? setSelectedCorporate(null) + : setSelectedCorporate( + value.value === "N/A" ? undefined : availableCorporates.find((x) => x?.id === value.value), + ) + } + /> +