-
Notifications
You must be signed in to change notification settings - Fork 607
fix: merge issues #3422
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: merge issues #3422
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,207 @@ | ||||||||||||||||||||||||||||
| "use client"; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||
| type ColumnDef, | ||||||||||||||||||||||||||||
| type ColumnFiltersState, | ||||||||||||||||||||||||||||
| type SortingState, | ||||||||||||||||||||||||||||
| type VisibilityState, | ||||||||||||||||||||||||||||
| flexRender, | ||||||||||||||||||||||||||||
| getCoreRowModel, | ||||||||||||||||||||||||||||
| getFilteredRowModel, | ||||||||||||||||||||||||||||
| getPaginationRowModel, | ||||||||||||||||||||||||||||
| getSortedRowModel, | ||||||||||||||||||||||||||||
| useReactTable, | ||||||||||||||||||||||||||||
| } from "@tanstack/react-table"; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||
| Table, | ||||||||||||||||||||||||||||
| TableBody, | ||||||||||||||||||||||||||||
| TableCell, | ||||||||||||||||||||||||||||
| TableHead, | ||||||||||||||||||||||||||||
| TableHeader, | ||||||||||||||||||||||||||||
| TableRow, | ||||||||||||||||||||||||||||
| } from "@/components/ui/table"; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||
| Dialog, | ||||||||||||||||||||||||||||
| DialogContent, | ||||||||||||||||||||||||||||
| DialogDescription, | ||||||||||||||||||||||||||||
| DialogFooter, | ||||||||||||||||||||||||||||
| DialogHeader, | ||||||||||||||||||||||||||||
| DialogTitle, | ||||||||||||||||||||||||||||
| DialogTrigger, | ||||||||||||||||||||||||||||
| } from "@/components/ui/dialog"; | ||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||
| DropdownMenu, | ||||||||||||||||||||||||||||
| DropdownMenuCheckboxItem, | ||||||||||||||||||||||||||||
| DropdownMenuContent, | ||||||||||||||||||||||||||||
| DropdownMenuTrigger, | ||||||||||||||||||||||||||||
| } from "@/components/ui/dropdown-menu"; | ||||||||||||||||||||||||||||
| import { toast } from "@/components/ui/toaster"; | ||||||||||||||||||||||||||||
| import { trpc } from "@/lib/trpc/client"; | ||||||||||||||||||||||||||||
| import { Button, Input } from "@unkey/ui"; | ||||||||||||||||||||||||||||
| import { useRouter } from "next/navigation"; | ||||||||||||||||||||||||||||
| import { useState } from "react"; | ||||||||||||||||||||||||||||
| interface DataTableProps<TData, TValue> { | ||||||||||||||||||||||||||||
| columns: ColumnDef<TData, TValue>[]; | ||||||||||||||||||||||||||||
| data: TData[]; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export function DataTable<TData, TValue>({ data, columns }: DataTableProps<TData, TValue>) { | ||||||||||||||||||||||||||||
| const [sorting, setSorting] = useState<SortingState>([]); | ||||||||||||||||||||||||||||
| const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]); | ||||||||||||||||||||||||||||
| const [rowSelection, setRowSelection] = useState({}); | ||||||||||||||||||||||||||||
| const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({}); | ||||||||||||||||||||||||||||
| const router = useRouter(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const deleteKey = trpc.settings.rootKeys.delete.useMutation({ | ||||||||||||||||||||||||||||
| onSuccess: (_data, variables) => { | ||||||||||||||||||||||||||||
| setRowSelection({}); | ||||||||||||||||||||||||||||
| toast.success( | ||||||||||||||||||||||||||||
| variables.keyIds.length > 1 | ||||||||||||||||||||||||||||
| ? `All ${variables.keyIds.length} keys were deleted` | ||||||||||||||||||||||||||||
| : "Key deleted", | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| router.refresh(); | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| onError: (err, variables) => { | ||||||||||||||||||||||||||||
| router.refresh(); | ||||||||||||||||||||||||||||
| console.error(err); | ||||||||||||||||||||||||||||
| toast.error(`Could not delete key ${JSON.stringify(variables)}`); | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
| const table = useReactTable({ | ||||||||||||||||||||||||||||
| data, | ||||||||||||||||||||||||||||
| columns, | ||||||||||||||||||||||||||||
| getCoreRowModel: getCoreRowModel(), | ||||||||||||||||||||||||||||
| getPaginationRowModel: getPaginationRowModel(), | ||||||||||||||||||||||||||||
| onSortingChange: setSorting, | ||||||||||||||||||||||||||||
| onColumnVisibilityChange: setColumnVisibility, | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| getSortedRowModel: getSortedRowModel(), | ||||||||||||||||||||||||||||
| state: { | ||||||||||||||||||||||||||||
| sorting, | ||||||||||||||||||||||||||||
| columnFilters, | ||||||||||||||||||||||||||||
| rowSelection, | ||||||||||||||||||||||||||||
| columnVisibility, | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| onColumnFiltersChange: setColumnFilters, | ||||||||||||||||||||||||||||
| getFilteredRowModel: getFilteredRowModel(), | ||||||||||||||||||||||||||||
| onRowSelectionChange: setRowSelection, | ||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||
| <div className="flex flex-wrap items-center justify-between space-x-0 space-y-4 md:space-x-4 sm:space-y-0 sm:flex-nowrap"> | ||||||||||||||||||||||||||||
| {Object.values(rowSelection).length > 0 ? ( | ||||||||||||||||||||||||||||
| <Dialog> | ||||||||||||||||||||||||||||
| <DialogTrigger asChild> | ||||||||||||||||||||||||||||
| <Button className="min-w-full md:min-w-min ">Revoke selected keys</Button> | ||||||||||||||||||||||||||||
| </DialogTrigger> | ||||||||||||||||||||||||||||
| <DialogContent className="sm:max-w-[425px]"> | ||||||||||||||||||||||||||||
| <DialogHeader> | ||||||||||||||||||||||||||||
| <DialogTitle>Revoke {Object.keys(rowSelection).length} keys</DialogTitle> | ||||||||||||||||||||||||||||
| <DialogDescription className="text-alert"> | ||||||||||||||||||||||||||||
| This action can not be undone. Your root key(s) will no longer be able to create | ||||||||||||||||||||||||||||
| resources. | ||||||||||||||||||||||||||||
| </DialogDescription> | ||||||||||||||||||||||||||||
| </DialogHeader> | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| <DialogFooter> | ||||||||||||||||||||||||||||
| <Button | ||||||||||||||||||||||||||||
| disabled={deleteKey.isLoading} | ||||||||||||||||||||||||||||
| onClick={() => { | ||||||||||||||||||||||||||||
| const keyIds = table | ||||||||||||||||||||||||||||
| .getSelectedRowModel() | ||||||||||||||||||||||||||||
| // @ts-ignore | ||||||||||||||||||||||||||||
| .rows.map((row) => row.original.id as string); | ||||||||||||||||||||||||||||
| deleteKey.mutate({ keyIds }); | ||||||||||||||||||||||||||||
|
Comment on lines
+115
to
+119
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove @ts-ignore and fix the type issue properly. The - const keyIds = table
- .getSelectedRowModel()
- // @ts-ignore
- .rows.map((row) => row.original.id as string);
+ const keyIds = table
+ .getSelectedRowModel()
+ .rows.map((row) => (row.original as { id: string }).id);Alternatively, ensure the generic type interface BaseRowData {
id: string;
}
export function DataTable<TData extends BaseRowData, TValue>({ data, columns }: DataTableProps<TData, TValue>) {🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||
| loading={deleteKey.isLoading} | ||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||
| Delete permanently | ||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||
| </DialogFooter> | ||||||||||||||||||||||||||||
| </DialogContent> | ||||||||||||||||||||||||||||
| </Dialog> | ||||||||||||||||||||||||||||
| ) : null} | ||||||||||||||||||||||||||||
| <Input | ||||||||||||||||||||||||||||
| placeholder="Filter keys" | ||||||||||||||||||||||||||||
| value={(table.getColumn("start")?.getFilterValue() as string) ?? ""} | ||||||||||||||||||||||||||||
| onChange={(event) => table.getColumn("start")?.setFilterValue(event.target.value)} | ||||||||||||||||||||||||||||
| className="max-w-sm md:max-w-2xl" | ||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||
|
Comment on lines
+129
to
+134
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Add proper ARIA labels for better accessibility. The filter input should have proper labeling for screen readers. <Input
placeholder="Filter keys"
+ aria-label="Filter root keys"
value={(table.getColumn("start")?.getFilterValue() as string) ?? ""}
onChange={(event) => table.getColumn("start")?.setFilterValue(event.target.value)}
className="max-w-sm md:max-w-2xl"
/>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| <DropdownMenu> | ||||||||||||||||||||||||||||
| <DropdownMenuTrigger> | ||||||||||||||||||||||||||||
| <Button className="min-w-full mt-4 ml-0 md:min-w-min md:mt-0 md:ml-auto"> | ||||||||||||||||||||||||||||
| Columns | ||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||
| </DropdownMenuTrigger> | ||||||||||||||||||||||||||||
| <DropdownMenuContent align="end"> | ||||||||||||||||||||||||||||
| {table | ||||||||||||||||||||||||||||
| .getAllColumns() | ||||||||||||||||||||||||||||
| .filter((column) => column.getCanHide()) | ||||||||||||||||||||||||||||
| .map((column) => { | ||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||
| <DropdownMenuCheckboxItem | ||||||||||||||||||||||||||||
| key={column.id} | ||||||||||||||||||||||||||||
| className="capitalize" | ||||||||||||||||||||||||||||
| checked={column.getIsVisible()} | ||||||||||||||||||||||||||||
| onCheckedChange={(value) => column.toggleVisibility(!!value)} | ||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||
| {column.id} | ||||||||||||||||||||||||||||
| </DropdownMenuCheckboxItem> | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| })} | ||||||||||||||||||||||||||||
| </DropdownMenuContent> | ||||||||||||||||||||||||||||
| </DropdownMenu> | ||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||
| <Table className="mt-4"> | ||||||||||||||||||||||||||||
| <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 className="flex items-center justify-end py-4 space-x-2"> | ||||||||||||||||||||||||||||
| <Button onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}> | ||||||||||||||||||||||||||||
| Previous | ||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||
| <Button onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}> | ||||||||||||||||||||||||||||
| Next | ||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Maintain consistency in toast notifications.
The error toast here uses
toast.error(), while inindex.tsxit usestoast(). Use consistent toast methods across components.Or update both files to use the semantic
toast.error()method for error cases.📝 Committable suggestion
🤖 Prompt for AI Agents