Files
David Camacho Cateura 1a483ab255 feat: Show all partner audit logs fields (#49305)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Changes to the audit logs UI

## What is the current behavior?

Partner related fields in the audit logs are not shown

## What is the new behavior?

- Shows all partner related fields in the audit logs
- Also uses the new fields to compute the user name



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Audit log entries now display partner names, installation IDs, user
emails, and user IDs when available.
* Partner identity and email are shown when standard actor details are
unavailable.
  * Partner names are consistently formatted for clearer display.
* Entries without partner information continue to display cleanly
without blank or confusing actor details.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-08-20 14:39:38 +02:00

155 lines
6.1 KiB
TypeScript

import dayjs from 'dayjs'
import { Input, SidePanel, TextArea } from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import {
FormSection,
FormSectionContent,
FormSectionLabel,
} from '@/components/ui/Forms/FormSection'
import {
TIMESTAMP_MICROS_PER_MS,
type AuditLog,
} from '@/data/organizations/organization-audit-logs-query'
interface LogDetailsPanelProps {
selectedLog?: AuditLog
onClose: () => void
}
export const LogDetailsPanel = ({ selectedLog, onClose }: LogDetailsPanelProps) => {
const timestamp = selectedLog
? dayjs(selectedLog.timestamp / TIMESTAMP_MICROS_PER_MS).format('DD MMM YYYY, HH:mm:ss')
: ''
const timestampWithTz = selectedLog
? dayjs(selectedLog.timestamp / TIMESTAMP_MICROS_PER_MS).format('DD MMM YYYY, HH:mm:ss (ZZ)')
: ''
return (
<SidePanel
size="large"
header={selectedLog ? `"${selectedLog.action.name}" on ${timestamp}` : ''}
visible={selectedLog !== undefined}
onCancel={onClose}
cancelText="Close"
>
<FormSection header={<FormSectionLabel>General</FormSectionLabel>}>
<FormSectionContent loading={false}>
<FormItemLayout label="Occurred at" description={timestampWithTz} isReactForm={false}>
<Input
readOnly
size="small"
value={
selectedLog
? dayjs(selectedLog.timestamp / TIMESTAMP_MICROS_PER_MS).toISOString()
: ''
}
/>
</FormItemLayout>
<FormItemLayout label="Request ID" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.request_id ?? ''} />
</FormItemLayout>
{selectedLog?.organization_slug && (
<FormItemLayout label="Organization" isReactForm={false}>
<Input readOnly size="small" value={selectedLog.organization_slug} />
</FormItemLayout>
)}
{selectedLog?.project_ref && (
<FormItemLayout label="Project ref" isReactForm={false}>
<Input readOnly size="small" value={selectedLog.project_ref} />
</FormItemLayout>
)}
</FormSectionContent>
</FormSection>
<SidePanel.Separator />
<FormSection header={<FormSectionLabel>Actor</FormSectionLabel>}>
<FormSectionContent loading={false}>
<FormItemLayout label="Token type" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.actor.token_type ?? ''} />
</FormItemLayout>
{selectedLog?.actor.email && (
<FormItemLayout label="Email" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.actor.email ?? ''} />
</FormItemLayout>
)}
{selectedLog?.actor.user_id && (
<FormItemLayout label="User ID" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.actor.user_id ?? ''} />
</FormItemLayout>
)}
{selectedLog?.actor.ip && (
<FormItemLayout label="IP address" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.actor.ip ?? ''} />
</FormItemLayout>
)}
{selectedLog?.actor.oauth_app_name && (
<FormItemLayout label="OAuth app" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.actor.oauth_app_name ?? ''} />
</FormItemLayout>
)}
{selectedLog?.actor.app_name && (
<FormItemLayout label="App" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.actor.app_name ?? ''} />
</FormItemLayout>
)}
{selectedLog?.actor.partner && (
<FormItemLayout label="Partner" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.actor.partner ?? ''} />
</FormItemLayout>
)}
{selectedLog?.actor.partner_installation_id && (
<FormItemLayout label="Partner installation ID" isReactForm={false}>
<Input
readOnly
size="small"
value={selectedLog?.actor.partner_installation_id ?? ''}
/>
</FormItemLayout>
)}
{selectedLog?.actor.partner_user_email && (
<FormItemLayout label="Partner user email" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.actor.partner_user_email ?? ''} />
</FormItemLayout>
)}
{selectedLog?.actor.partner_user_id && (
<FormItemLayout label="Partner user ID" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.actor.partner_user_id ?? ''} />
</FormItemLayout>
)}
</FormSectionContent>
</FormSection>
<SidePanel.Separator />
<FormSection header={<FormSectionLabel>Action</FormSectionLabel>}>
<FormSectionContent loading={false}>
<FormItemLayout label="Name" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.action.name ?? ''} />
</FormItemLayout>
<FormItemLayout label="Method" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.action.method ?? ''} />
</FormItemLayout>
<FormItemLayout label="Route" isReactForm={false}>
<Input readOnly size="small" value={selectedLog?.action.route ?? ''} />
</FormItemLayout>
<FormItemLayout label="Status" isReactForm={false}>
<Input readOnly size="small" value={String(selectedLog?.action.status ?? '')} />
</FormItemLayout>
{selectedLog?.action.metadata && (
<FormItemLayout label="Metadata" isReactForm={false}>
<TextArea
readOnly
rows={5}
className="font-mono input-xs"
value={JSON.stringify(selectedLog.action.metadata, null, 2)}
/>
</FormItemLayout>
)}
</FormSectionContent>
</FormSection>
</SidePanel>
)
}