mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 09:55:06 +03:00
## Context Follows up from [this PR](https://github.com/supabase/supabase/pull/50799) - updates the Audit Logs UI for organization audit logs. Just differs from Account audit logs slightly with added "Actor" + "Target" column Reuses the same UI components from account audit logs so quite a bit of code clean up 🙂 <img width="1451" height="955" alt="image" src="https://github.com/user-attachments/assets/a1002cee-917f-4d9a-b977-3fab8ecd2d13" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Organization audit logs now use a sortable table with row selection and a resizable details panel. * Actor details show a member’s username when available, otherwise the actor’s email; a dash appears when neither is available. * Logs can be filtered by users and projects, with separate messages for no logs and no matching results. * A refresh control and loading indicators help show audit-log updates. * **Bug Fixes** * Organization project lists stop loading when pagination totals are unavailable. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
61 lines
2.1 KiB
TypeScript
61 lines
2.1 KiB
TypeScript
import type { Table } from '@tanstack/react-table'
|
|
import { AnimatePresence, motion } from 'framer-motion'
|
|
import { Copy, X } from 'lucide-react'
|
|
import { toast } from 'sonner'
|
|
import { copyToClipboard } from 'ui'
|
|
|
|
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
|
|
import type { AuditLog } from '@/data/organizations/organization-audit-logs-query'
|
|
|
|
interface AuditLogsSelectionHeaderProps {
|
|
table: Table<AuditLog>
|
|
}
|
|
|
|
export const AuditLogsSelectionHeader = ({ table }: AuditLogsSelectionHeaderProps) => {
|
|
const selectedRows = table.getSelectedRowModel().rows.map((row) => row.original)
|
|
const hasSelection = selectedRows.length > 0
|
|
|
|
const handleCopy = () => {
|
|
const text = JSON.stringify(selectedRows, null, 2)
|
|
copyToClipboard(text, () => {
|
|
toast.success(`Copied ${selectedRows.length} log${selectedRows.length !== 1 ? 's' : ''}`)
|
|
})
|
|
}
|
|
|
|
return (
|
|
<AnimatePresence>
|
|
{hasSelection && (
|
|
<motion.div
|
|
initial={{ height: 0, opacity: 0 }}
|
|
animate={{ height: '40px', opacity: 1 }}
|
|
exit={{ height: 0, opacity: 0 }}
|
|
transition={{ type: 'spring', stiffness: 420, damping: 30, mass: 0.4 }}
|
|
className="px-10 flex items-center justify-between border-b bg-surface-75 w-full overflow-hidden absolute top-0 left-0 right-0 z-20"
|
|
>
|
|
<p className="text-xs">
|
|
{selectedRows.length} row{selectedRows.length > 1 ? 's' : ''} selected
|
|
</p>
|
|
|
|
<div className="flex items-center justify-center gap-x-2">
|
|
<ButtonTooltip
|
|
size="tiny"
|
|
icon={<Copy size={12} />}
|
|
className="w-7"
|
|
onClick={handleCopy}
|
|
tooltip={{ content: { side: 'bottom', text: 'Copy selected logs' } }}
|
|
/>
|
|
<ButtonTooltip
|
|
size="tiny"
|
|
variant="text"
|
|
icon={<X />}
|
|
className="px-1"
|
|
onClick={() => table.resetRowSelection()}
|
|
tooltip={{ content: { side: 'bottom', text: 'Clear selection' } }}
|
|
/>
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
)
|
|
}
|