Path: blob/1.0-develop/resources/scripts/components/elements/activity/ActivityLogEntry.tsx
10288 views
import React from 'react';1import { Link } from 'react-router-dom';2import Tooltip from '@/components/elements/tooltip/Tooltip';3import Translate from '@/components/elements/Translate';4import { format, formatDistanceToNowStrict } from 'date-fns';5import { ActivityLog } from '@definitions/user';6import ActivityLogMetaButton from '@/components/elements/activity/ActivityLogMetaButton';7import { FolderOpenIcon, TerminalIcon } from '@heroicons/react/solid';8import classNames from 'classnames';9import style from './style.module.css';10import Avatar from '@/components/Avatar';11import useLocationHash from '@/plugins/useLocationHash';12import { getObjectKeys, isObject } from '@/lib/objects';1314interface Props {15activity: ActivityLog;16children?: React.ReactNode;17}1819function wrapProperties(value: unknown): any {20if (value === null || typeof value === 'string' || typeof value === 'number') {21return `<strong>${String(value)}</strong>`;22}2324if (isObject(value)) {25return getObjectKeys(value).reduce((obj, key) => {26if (key === 'count' || (typeof key === 'string' && key.endsWith('_count'))) {27return { ...obj, [key]: value[key] };28}29return { ...obj, [key]: wrapProperties(value[key]) };30}, {} as Record<string, unknown>);31}3233if (Array.isArray(value)) {34return value.map(wrapProperties);35}3637return value;38}3940export default ({ activity, children }: Props) => {41const { pathTo } = useLocationHash();42const actor = activity.relationships.actor;43const properties = wrapProperties(activity.properties);4445return (46<div className={'grid grid-cols-10 py-4 border-b-2 border-gray-800 last:rounded-b last:border-0 group'}>47<div className={'hidden sm:flex sm:col-span-1 items-center justify-center select-none'}>48<div className={'flex items-center w-10 h-10 rounded-full bg-gray-600 overflow-hidden'}>49<Avatar name={actor?.uuid || 'system'} />50</div>51</div>52<div className={'col-span-10 sm:col-span-9 flex'}>53<div className={'flex-1 px-4 sm:px-0'}>54<div className={'flex items-center text-gray-50'}>55<Tooltip placement={'top'} content={actor?.email || 'System User'}>56<span>{actor?.username || 'System'}</span>57</Tooltip>58<span className={'text-gray-400'}> — </span>59<Link60to={`#${pathTo({ event: activity.event })}`}61className={'transition-colors duration-75 active:text-cyan-400 hover:text-cyan-400'}62>63{activity.event}64</Link>65<div className={classNames(style.icons, 'group-hover:text-gray-300')}>66{activity.isApi && (67<Tooltip placement={'top'} content={'Using API Key'}>68<TerminalIcon />69</Tooltip>70)}71{activity.event.startsWith('server:sftp.') && (72<Tooltip placement={'top'} content={'Using SFTP'}>73<FolderOpenIcon />74</Tooltip>75)}76{children}77</div>78</div>79<p className={style.description}>80<Translate ns={'activity'} values={properties} i18nKey={activity.event.replace(':', '.')} />81</p>82<div className={'mt-1 flex items-center text-sm'}>83{activity.ip && (84<span>85{activity.ip}86<span className={'text-gray-400'}> | </span>87</span>88)}89<Tooltip placement={'right'} content={format(activity.timestamp, 'MMM do, yyyy H:mm:ss')}>90<span>{formatDistanceToNowStrict(activity.timestamp, { addSuffix: true })}</span>91</Tooltip>92</div>93</div>94{activity.hasAdditionalMetadata && <ActivityLogMetaButton meta={activity.properties} />}95</div>96</div>97);98};99100101