checkpoint/backup-fail-server
This commit is contained in:
@@ -32,7 +32,7 @@ export function createColumns(onSuccess?: () => void): ColumnDef<Doda>[] {
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
Headers: 'Acciones',
|
||||
header: 'Acciones',
|
||||
cell: ({ row }) => {
|
||||
return renderComponent(DataTableActions, {
|
||||
item: row.original,
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
<script lang="ts" generics="TData, TValue">
|
||||
import { type ColumnDef, getCoreRowModel, type RowSelectionState } from '@tanstack/table-core';
|
||||
import { createSvelteTable, FlexRender } from '$lib/components/ui/data-table/index.js';
|
||||
import * as Table from '$lib/components/ui/table/index.js';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
type DataTableProps<TData, TValue> = {
|
||||
columns: ColumnDef<TData, TValue>[];
|
||||
data: TData[];
|
||||
pageCount: number;
|
||||
totalItems: number;
|
||||
selectedId?: number | null;
|
||||
onRowClick?: (row: TData) => void;
|
||||
};
|
||||
|
||||
let {
|
||||
data,
|
||||
columns,
|
||||
pageCount,
|
||||
totalItems,
|
||||
selectedId = null,
|
||||
onRowClick
|
||||
}: DataTableProps<TData, TValue> = $props();
|
||||
|
||||
const table = createSvelteTable({
|
||||
get data() {
|
||||
return data;
|
||||
},
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getRowId: (row: any) => row.id?.toString(),
|
||||
state: {
|
||||
get rowSelection() {
|
||||
return selectedId ? { [selectedId]: true } : {};
|
||||
}
|
||||
},
|
||||
enableRowSelection: true,
|
||||
enableMultiRowSelection: false,
|
||||
manualPagination: true,
|
||||
pageCount: pageCount
|
||||
});
|
||||
|
||||
function handlePageChange(newPage: number) {
|
||||
const url = new URL($page.url);
|
||||
url.searchParams.set('page', newPage.toString());
|
||||
goto(url);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-4">
|
||||
<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 class="whitespace-nowrap">
|
||||
{#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'}
|
||||
onclick={() => onRowClick?.(row.original)}
|
||||
class="cursor-pointer hover:bg-muted/50 transition-colors {row.getIsSelected()
|
||||
? 'bg-primary/10'
|
||||
: ''}"
|
||||
>
|
||||
{#each row.getVisibleCells() as cell (cell.id)}
|
||||
<Table.Cell class="whitespace-nowrap">
|
||||
<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 hay resultados.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
</div>
|
||||
|
||||
<!-- Paginación -->
|
||||
<div class="flex items-center justify-end space-x-2 py-4">
|
||||
<div class="flex-1 text-sm text-muted-foreground">
|
||||
Total: {totalItems}
|
||||
</div>
|
||||
<div class="space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onclick={() => handlePageChange(Number($page.url.searchParams.get('page') || 1) - 1)}
|
||||
disabled={Number($page.url.searchParams.get('page') || 1) <= 1}
|
||||
>
|
||||
Anterior
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onclick={() => handlePageChange(Number($page.url.searchParams.get('page') || 1) + 1)}
|
||||
disabled={Number($page.url.searchParams.get('page') || 1) >= pageCount}
|
||||
>
|
||||
Siguiente
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -25,9 +25,12 @@
|
||||
'input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]):not([role="tab"])'
|
||||
);
|
||||
|
||||
// Find the first one that is actually visible
|
||||
// Find the first one that is actually visible AND not in the sidebar
|
||||
const firstVisible = Array.from(focusables).find((el) => {
|
||||
const htmlEl = el as HTMLElement;
|
||||
// Exclude sidebar elements
|
||||
if (htmlEl.closest('[data-sidebar="sidebar"]')) return false;
|
||||
|
||||
return !!(htmlEl.offsetWidth || htmlEl.offsetHeight || htmlEl.getClientRects().length);
|
||||
}) as HTMLElement;
|
||||
|
||||
@@ -60,6 +63,9 @@
|
||||
const elements = document.querySelectorAll('button, [role="tab"], a');
|
||||
const target = Array.from(elements).find((el) => {
|
||||
const text = el.textContent?.trim() || '';
|
||||
// Exclude sidebar
|
||||
if (el.closest('[data-sidebar="sidebar"]')) return false;
|
||||
|
||||
return text.toLowerCase().includes(keyword.toLowerCase());
|
||||
}) as HTMLElement;
|
||||
|
||||
@@ -276,6 +282,9 @@
|
||||
const target = event.target as HTMLElement;
|
||||
if (!target) return;
|
||||
|
||||
// Exclude sidebar from any magic scrolling
|
||||
if (target.closest('[data-sidebar="sidebar"]')) return;
|
||||
|
||||
// Check if it's an interactive element we care about
|
||||
const isInteractive =
|
||||
['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON', 'A'].includes(target.tagName) ||
|
||||
|
||||
Reference in New Issue
Block a user