160 lines
4.3 KiB
Svelte
160 lines
4.3 KiB
Svelte
<script lang="ts">
|
|
import {
|
|
type ColumnDef,
|
|
type ColumnFiltersState,
|
|
type FilterFn,
|
|
getCoreRowModel,
|
|
getFilteredRowModel,
|
|
getPaginationRowModel,
|
|
getSortedRowModel,
|
|
type GlobalFilterTableState,
|
|
type PaginationState,
|
|
type SortingState
|
|
} from '@tanstack/table-core';
|
|
import { rankItem } from '@tanstack/match-sorter-utils';
|
|
import { createSvelteTable, FlexRender } from '$lib/components/ui/data-table/index';
|
|
import * as Table from '$lib/components/ui/table/index';
|
|
import Button from '$lib/components/ui/button/button.svelte';
|
|
import Input from '$lib/components/ui/input/input.svelte';
|
|
import { SearchIcon } from '@lucide/svelte/icons';
|
|
import type { AdminUser } from '$lib/core/admin/adminTypes';
|
|
|
|
let { data, columns }: { data: AdminUser[]; columns: ColumnDef<AdminUser>[] } = $props();
|
|
|
|
let pagination = $state<PaginationState>({ pageIndex: 0, pageSize: 10 });
|
|
let sorting = $state<SortingState>([]);
|
|
let columnFilter = $state<ColumnFiltersState>([]);
|
|
let globalFilter = $state<GlobalFilterTableState>();
|
|
|
|
const fuzzyFilter: FilterFn<AdminUser> = (row, columnId, value, addMeta) => {
|
|
const itemRank = rankItem(row.getValue(columnId), value);
|
|
addMeta({ itemRank });
|
|
return itemRank.passed;
|
|
};
|
|
|
|
const table = createSvelteTable({
|
|
get data() {
|
|
return data;
|
|
},
|
|
columns,
|
|
state: {
|
|
get pagination() {
|
|
return pagination;
|
|
},
|
|
get sorting() {
|
|
return sorting;
|
|
},
|
|
get globalFilter() {
|
|
return globalFilter;
|
|
}
|
|
},
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
getSortedRowModel: getSortedRowModel(),
|
|
getFilteredRowModel: getFilteredRowModel(),
|
|
filterFns: {
|
|
fuzzy: fuzzyFilter
|
|
},
|
|
globalFilterFn: fuzzyFilter,
|
|
onSortingChange: (updater) => {
|
|
if (typeof updater === 'function') {
|
|
sorting = updater(sorting);
|
|
} else {
|
|
sorting = updater;
|
|
}
|
|
},
|
|
onPaginationChange: (updater) => {
|
|
if (typeof updater === 'function') {
|
|
pagination = updater(pagination);
|
|
} else {
|
|
pagination = updater;
|
|
}
|
|
},
|
|
onColumnFiltersChange: (updater) => {
|
|
if (typeof updater === 'function') {
|
|
columnFilter = updater(columnFilter);
|
|
} else {
|
|
columnFilter = updater;
|
|
}
|
|
},
|
|
onGlobalFilterChange: (updater) => {
|
|
if (typeof updater === 'function') {
|
|
globalFilter = updater(globalFilter);
|
|
} else {
|
|
globalFilter = updater;
|
|
}
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<div>
|
|
<div class="flex items-center gap-2 py-4">
|
|
<SearchIcon class="h-5 w-5 text-gray-500" />
|
|
<Input
|
|
type="text"
|
|
placeholder="Search by name or email..."
|
|
class="max-w-sm"
|
|
onchange={(e) => {
|
|
table.setGlobalFilter(e.currentTarget.value);
|
|
}}
|
|
oninput={(e) => {
|
|
table.setGlobalFilter(e.currentTarget.value);
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<div class="rounded-md border">
|
|
<Table.Root>
|
|
<Table.Header>
|
|
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
|
|
<Table.Row>
|
|
{#each headerGroup.headers as header (header.id)}
|
|
<Table.Head colspan={header.colSpan}>
|
|
{#if !header.isPlaceholder}
|
|
<FlexRender
|
|
content={header.column.columnDef.header}
|
|
context={header.getContext()}
|
|
/>
|
|
{/if}
|
|
</Table.Head>
|
|
{/each}
|
|
</Table.Row>
|
|
{/each}
|
|
</Table.Header>
|
|
<Table.Body>
|
|
{#each table.getRowModel().rows as row (row.id)}
|
|
<Table.Row data-state={row.getIsSelected() && 'selected'}>
|
|
{#each row.getVisibleCells() as cell (cell.id)}
|
|
<Table.Cell>
|
|
<FlexRender content={cell.column.columnDef.cell} context={cell.getContext()} />
|
|
</Table.Cell>
|
|
{/each}
|
|
</Table.Row>
|
|
{:else}
|
|
<Table.Row>
|
|
<Table.Cell colspan={columns.length} class="h-24 text-center">No users found.</Table.Cell>
|
|
</Table.Row>
|
|
{/each}
|
|
</Table.Body>
|
|
</Table.Root>
|
|
<div class="mx-4 flex items-center justify-between py-4">
|
|
<span class="text-muted-foreground text-sm">
|
|
{table.getFilteredRowModel().rows.length} user(s) total
|
|
</span>
|
|
<div class="flex items-center space-x-2">
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onclick={() => table.previousPage()}
|
|
disabled={!table.getCanPreviousPage()}>Previous</Button
|
|
>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onclick={() => table.nextPage()}
|
|
disabled={!table.getCanNextPage()}>Next</Button
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|