({ entry }: AuditLogRowProps)
| 166 | } |
| 167 | |
| 168 | function AuditLogRow({ entry }: AuditLogRowProps) { |
| 169 | const [expanded, setExpanded] = useState(false) |
| 170 | const timestamp = formatDateTime(new Date(entry.createdAt)) |
| 171 | const metadataEntries = getMetadataEntries(entry.metadata) |
| 172 | |
| 173 | return ( |
| 174 | <div |
| 175 | className={cn( |
| 176 | 'rounded-md transition-colors', |
| 177 | 'hover-hover:bg-[var(--surface-2)]', |
| 178 | expanded && 'bg-[var(--surface-2)]' |
| 179 | )} |
| 180 | > |
| 181 | <button |
| 182 | type='button' |
| 183 | className='flex w-full items-center gap-3 px-3 py-2 text-left' |
| 184 | onClick={() => setExpanded(!expanded)} |
| 185 | > |
| 186 | <span className='w-[160px] flex-shrink-0 text-[var(--text-secondary)] text-small'> |
| 187 | {timestamp} |
| 188 | </span> |
| 189 | <span className='w-[180px] flex-shrink-0'> |
| 190 | <ActionBadge action={entry.action} /> |
| 191 | </span> |
| 192 | <span className='min-w-0 flex-1 truncate text-[var(--text-primary)] text-small'> |
| 193 | {entry.description || entry.resourceName || entry.resourceId || '-'} |
| 194 | </span> |
| 195 | <span className='flex w-[160px] flex-shrink-0 items-center justify-end gap-1.5 text-[var(--text-secondary)] text-small'> |
| 196 | <span className='min-w-0 truncate'> |
| 197 | {entry.actorEmail || entry.actorName || 'System'} |
| 198 | </span> |
| 199 | <ChevronDown |
| 200 | className={cn( |
| 201 | 'size-[14px] flex-shrink-0 text-[var(--text-muted)] transition-transform duration-200', |
| 202 | expanded && 'rotate-180' |
| 203 | )} |
| 204 | /> |
| 205 | </span> |
| 206 | </button> |
| 207 | {expanded && ( |
| 208 | <div className='px-3 pb-2'> |
| 209 | <div className='flex flex-col gap-1.5 rounded-lg border border-[var(--border-1)] bg-[var(--surface-3)] p-3 text-small'> |
| 210 | <div className='flex gap-2'> |
| 211 | <span className='w-[100px] flex-shrink-0 text-[var(--text-muted)]'>Resource</span> |
| 212 | <span className='text-[var(--text-primary)]'> |
| 213 | {formatResourceType(entry.resourceType)} |
| 214 | {entry.resourceId && ( |
| 215 | <span className='ml-1 text-[var(--text-muted)]'>({entry.resourceId})</span> |
| 216 | )} |
| 217 | </span> |
| 218 | </div> |
| 219 | {entry.resourceName && ( |
| 220 | <div className='flex gap-2'> |
| 221 | <span className='w-[100px] flex-shrink-0 text-[var(--text-muted)]'>Name</span> |
| 222 | <span className='text-[var(--text-primary)]'>{entry.resourceName}</span> |
| 223 | </div> |
| 224 | )} |
| 225 | <div className='flex gap-2'> |
nothing calls this directly
no test coverage detected