Files
supabase/apps/studio/components/interfaces/AuditLogs/AuditLogsSelectionHeader.tsx
Joshen Lim e3fec137fe Joshenlim/fe 4466 audit logs update organization logs as well (#50935)
## 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 -->
2026-09-29 17:41:54 +08:00

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>
)
}