Transactions table: Show/hide columns
This commit is contained in:
parent
3795e46d4f
commit
f0f062468d
|
|
@ -1,5 +1,5 @@
|
|||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import { ArrowDown, ArrowUpDown, MoreHorizontal, Pencil, Trash2 } from 'lucide-react';
|
||||
import { ArrowDown, MoreHorizontal } from 'lucide-react';
|
||||
import { format, parseISO } from 'date-fns';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
|
@ -36,6 +36,7 @@ export function createTransactionColumns({
|
|||
return [
|
||||
{
|
||||
accessorKey: 'transaction_date',
|
||||
meta: { label: 'Date' },
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
|
|
@ -56,9 +57,11 @@ export function createTransactionColumns({
|
|||
</div>
|
||||
);
|
||||
},
|
||||
enableHiding: true,
|
||||
},
|
||||
{
|
||||
accessorKey: 'category_id',
|
||||
meta: { label: 'Category' },
|
||||
header: 'Category',
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
|
|
@ -74,6 +77,7 @@ export function createTransactionColumns({
|
|||
},
|
||||
{
|
||||
accessorKey: 'decryptedDescription',
|
||||
meta: { label: 'Description' },
|
||||
header: 'Description',
|
||||
cell: ({ row }) => {
|
||||
const transaction = row.original;
|
||||
|
|
@ -90,6 +94,7 @@ export function createTransactionColumns({
|
|||
},
|
||||
{
|
||||
accessorKey: 'bank',
|
||||
meta: { label: 'Bank' },
|
||||
header: 'Bank',
|
||||
cell: ({ row }) => {
|
||||
const bank = row.original.bank;
|
||||
|
|
@ -109,15 +114,28 @@ export function createTransactionColumns({
|
|||
},
|
||||
{
|
||||
accessorKey: 'account',
|
||||
meta: { label: 'Account' },
|
||||
header: 'Account',
|
||||
cell: ({ row }) => {
|
||||
const account = row.original.account;
|
||||
return <div>{account?.name || 'N/A'}</div>;
|
||||
if (!account) {
|
||||
return <div>N/A</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<EncryptedText
|
||||
encryptedText={account.name}
|
||||
iv={account.name_iv}
|
||||
length={{ min: 5, max: 10 }}
|
||||
className="max-w-[100px] truncate"
|
||||
/>
|
||||
);
|
||||
},
|
||||
enableHiding: true,
|
||||
},
|
||||
{
|
||||
accessorKey: 'amount',
|
||||
meta: { label: 'Amount' },
|
||||
header: () => {
|
||||
return (
|
||||
<div className="w-full text-right">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useState } from 'react';
|
||||
import { type ReactNode, useState } from 'react';
|
||||
import * as Icons from 'lucide-react';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
|
|
@ -22,6 +22,7 @@ interface TransactionFiltersProps {
|
|||
categories: Category[];
|
||||
accounts: Account[];
|
||||
isKeySet: boolean;
|
||||
actions?: ReactNode;
|
||||
}
|
||||
|
||||
export function TransactionFilters({
|
||||
|
|
@ -30,6 +31,7 @@ export function TransactionFilters({
|
|||
categories,
|
||||
accounts,
|
||||
isKeySet,
|
||||
actions,
|
||||
}: TransactionFiltersProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const UncategorizedIcon = resolveIconComponent('CircleHelp');
|
||||
|
|
@ -284,6 +286,11 @@ export function TransactionFilters({
|
|||
Clear
|
||||
</Button>
|
||||
)}
|
||||
{actions ? (
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
{actions}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { type Table as TableType } from '@tanstack/react-table';
|
||||
import { SlidersHorizontal } from 'lucide-react';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
|
||||
interface DataTableViewOptionsProps<TData> {
|
||||
table: TableType<TData>;
|
||||
}
|
||||
|
||||
export function DataTableViewOptions<TData>({
|
||||
table,
|
||||
}: DataTableViewOptionsProps<TData>) {
|
||||
const columns = table
|
||||
.getAllLeafColumns()
|
||||
.filter((column) => column.getCanHide());
|
||||
|
||||
const visibilityStates = columns.map((col) => col.getIsVisible()).join(',');
|
||||
const [, forceUpdate] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
forceUpdate((prev) => prev + 1);
|
||||
}, [visibilityStates]);
|
||||
|
||||
if (columns.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="sm">
|
||||
Columns
|
||||
<SlidersHorizontal className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56">
|
||||
{columns.map((column) => {
|
||||
const label: string =
|
||||
typeof column.columnDef.header === 'string'
|
||||
? column.columnDef.header
|
||||
: ((column.columnDef.meta as Record<string, unknown>)?.label as string) ?? column.id.replace(/_/g, ' ');
|
||||
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={column.id}
|
||||
className="capitalize"
|
||||
checked={column.getIsVisible()}
|
||||
onCheckedChange={(value) =>
|
||||
column.toggleVisibility(!!value)
|
||||
}
|
||||
>
|
||||
{label}
|
||||
</DropdownMenuCheckboxItem>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
|
|
@ -44,6 +44,8 @@ export function DataTable<TData, TValue>({
|
|||
virtualRows.length > 0
|
||||
? totalSize - virtualRows[virtualRows.length - 1].end
|
||||
: 0;
|
||||
const visibleColumnCount =
|
||||
table.getVisibleLeafColumns().length || columns.length || 1;
|
||||
|
||||
return (
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
|
|
@ -76,7 +78,7 @@ export function DataTable<TData, TValue>({
|
|||
{paddingTop > 0 && (
|
||||
<TableRow className="border-none hover:bg-transparent">
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
colSpan={visibleColumnCount}
|
||||
style={{ height: paddingTop }}
|
||||
/>
|
||||
</TableRow>
|
||||
|
|
@ -110,7 +112,7 @@ export function DataTable<TData, TValue>({
|
|||
{paddingBottom > 0 && (
|
||||
<TableRow className="border-none hover:bg-transparent">
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
colSpan={visibleColumnCount}
|
||||
style={{ height: paddingBottom }}
|
||||
/>
|
||||
</TableRow>
|
||||
|
|
@ -119,7 +121,7 @@ export function DataTable<TData, TValue>({
|
|||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
colSpan={visibleColumnCount}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
{emptyMessage}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import AppSidebarLayout from '@/layouts/app/app-sidebar-layout';
|
|||
import { DataTable } from '@/components/ui/data-table';
|
||||
import HeadingSmall from '@/components/heading-small';
|
||||
import { DataTablePagination } from '@/components/ui/data-table-pagination';
|
||||
import { DataTableViewOptions } from '@/components/ui/data-table-view-options';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { TransactionFilters } from '@/components/transactions/transaction-filters';
|
||||
import { EditTransactionDialog } from '@/components/transactions/edit-transaction-dialog';
|
||||
|
|
@ -452,6 +453,7 @@ export default function Transactions({ categories, accounts, banks }: Props) {
|
|||
categories={categories}
|
||||
accounts={accounts}
|
||||
isKeySet={isKeySet}
|
||||
actions={<DataTableViewOptions table={table} />}
|
||||
/>
|
||||
|
||||
{isLoading ? (
|
||||
|
|
|
|||
Loading…
Reference in New Issue