Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
new: recordings admin page (#7980)
Browse files Browse the repository at this point in the history
* new: recordings route

* feat: recordings table

* feat: remove recording

* feat: recording resources drawer

* chore: remove grid in recordings index

* fix recording page not loading

---------

Co-authored-by: HexaField <joshfield999@gmail.com>
  • Loading branch information
aditya-mitra and HexaField authored May 23, 2023
1 parent ad8cf29 commit 751cb6d
Show file tree
Hide file tree
Showing 14 changed files with 436 additions and 12 deletions.
5 changes: 5 additions & 0 deletions packages/client-core/i18n/en/admin.json
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,11 @@
"minutes": "minutes",
"download": "Download",
"confirmPodDelete": "Do you want to delete pod"
},
"recording": {
"recording": "Recording",
"confirmRecordingDelete": "Do you want to delete recording",
"recordingFiles": "Recordings Files for:"
}
}
}
3 changes: 2 additions & 1 deletion packages/client-core/i18n/en/user.json
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@
"avatars": "Avatars",
"benchmarking": "Benchmarking",
"bots": "Bots",
"server": "Server"
"server": "Server",
"recordings": "Recordings"
}
},
"oauth": {
Expand Down
3 changes: 2 additions & 1 deletion packages/client-core/src/admin/adminRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ const AdminRoutes = () => {
routes: false,
projects: false,
settings: false,
server: false
server: false,
recording: false
}
const scopes = admin?.scopes?.value || []

Expand Down
6 changes: 4 additions & 2 deletions packages/client-core/src/admin/allowedRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Invites from './components/Invite'
import Locations from './components/Location'
import Party from './components/Party'
import Projects from './components/Project'
import Recordings from './components/Recordings'
import Resources from './components/Resources'
import RoutesComp from './components/Routes'
import Server from './components/Server'
Expand All @@ -22,7 +23,7 @@ const availableRoutes = [
{ route: '/avatars', key: 'globalAvatars', component: Avatars, props: {} },
{ route: '/benchmarking', key: 'benchmarking', component: Benchmarking, props: {} },
{ route: '/groups', key: 'groups', component: Groups, props: {} },
{ route: '/instance', key: 'instance', component: Instance, props: {} },
{ route: '/instance', key: 'instance', component: Recordings, props: {} },
{ route: '/invites', key: 'invite', component: Invites, props: {} },
{ route: '/locations', key: 'location', component: Locations, props: {} },
{ route: '/routes', key: 'routes', component: RoutesComp, props: {} },
Expand All @@ -32,7 +33,8 @@ const availableRoutes = [
{ route: '/server', key: 'server', component: Server, props: {} },
{ route: '/settings', key: 'settings', component: Setting, props: {} },
{ route: '/resources', key: 'static_resource', component: Resources, props: {} },
{ route: '/users', key: 'user', component: Users, props: {} }
{ route: '/users', key: 'user', component: Users, props: {} },
{ route: '/recordings', key: 'recording', component: Recordings, props: {} }
]

const AllowedRoutes = ({ allowedRoutes }) => {
Expand Down
26 changes: 26 additions & 0 deletions packages/client-core/src/admin/common/variables/recording.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export interface RecordingColumn {
id: 'id' | 'user' | 'ended' | 'schema' | 'view' | 'action'
label: string
minWidth?: number
align?: 'right'
}

export const recordingColumns: RecordingColumn[] = [
{ id: 'id', label: 'Recording ID' },
{ id: 'user', label: 'User' },
{ id: 'ended', label: 'Ended' },
{
id: 'schema',
label: 'Schema'
},
{
id: 'view',
label: 'View',
align: 'right'
},
{
id: 'action',
label: 'Action',
align: 'right'
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { useHookstate } from '@hookstate/core'
import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'

import { RecordingResult } from '@etherealengine/common/src/interfaces/Recording'
import {
AssetSelectionChangePropsType,
AssetsPreviewPanel
} from '@etherealengine/editor/src/components/assets/AssetsPreviewPanel'
import FileBrowserContentPanel from '@etherealengine/editor/src/components/assets/FileBrowserContentPanel'
import { getMutableState } from '@etherealengine/hyperflux'
import Box from '@etherealengine/ui/src/primitives/mui/Box'
import Container from '@etherealengine/ui/src/primitives/mui/Container'
import DialogTitle from '@etherealengine/ui/src/primitives/mui/DialogTitle'

import DrawerView from '../../common/DrawerView'
import { AdminSingleRecordingService, AdminSingleRecordingState } from '../../services/RecordingService'
import styles from '../../styles/admin.module.scss'

interface Props {
open: boolean
selectedRecordingId: RecordingResult['id'] | null
onClose: () => void
}

const RecordingFilesDrawer = ({ open, onClose, selectedRecordingId }: Props) => {
const assetsPreviewPanelRef = React.useRef()

const { t } = useTranslation()
const adminSingleRecording = useHookstate(getMutableState(AdminSingleRecordingState))

const onSelectionChanged = (props: AssetSelectionChangePropsType) => {
;(assetsPreviewPanelRef.current as any)?.onSelectionChanged?.(props)
}

useEffect(() => {
if (selectedRecordingId) {
AdminSingleRecordingService.fetchSingleAdminRecording(selectedRecordingId)
}
}, [selectedRecordingId])

return (
<DrawerView open={open} onClose={onClose}>
<Container maxWidth="sm" className={styles.mt20}>
<>
<DialogTitle className={styles.textAlign}>
{`${t('admin:components.recording.recordingFiles')} ${adminSingleRecording.recording.value?.id}`}
</DialogTitle>

<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Box sx={{ flexGrow: 1, minHeight: 150 }}>
{selectedRecordingId && (
<FileBrowserContentPanel
disableDnD
selectedFile={selectedRecordingId}
onSelectionChanged={onSelectionChanged}
folderName="recordings"
/>
)}
</Box>
<Box sx={{ flexGrow: 1 }}>
<AssetsPreviewPanel ref={assetsPreviewPanelRef} />
</Box>
</Box>
</>
</Container>
</DrawerView>
)
}

export default RecordingFilesDrawer
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { useHookstate } from '@hookstate/core'
import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'

import { RecordingResult } from '@etherealengine/common/src/interfaces/Recording'
import { getMutableState } from '@etherealengine/hyperflux'
import Box from '@etherealengine/ui/src/primitives/mui/Box'
import Icon from '@etherealengine/ui/src/primitives/mui/Icon'
import IconButton from '@etherealengine/ui/src/primitives/mui/IconButton'

import ConfirmDialog from '../../../common/components/ConfirmDialog'
import TableComponent from '../../common/Table'
import { recordingColumns } from '../../common/variables/recording'
import { AdminRecordingService, AdminRecordingState, RECORDING_PAGE_LIMIT } from '../../services/RecordingService'
import styles from '../../styles/admin.module.scss'
import RecordingFilesDrawer from './RecordingsDrawer'

const RecordingsTable = () => {
const page = useHookstate(0)
const rowsPerPage = useHookstate(RECORDING_PAGE_LIMIT)
const fieldOrder = useHookstate('asc')
const sortField = useHookstate('createdAt')
const openConfirm = useHookstate(false)
const currentRecordingId = useHookstate<string | null>(null)
const recordingResourcesDrawerOpen = useHookstate<boolean>(false)
const { t } = useTranslation()

const handlePageChange = (_event: unknown, newPage: number) => {
page.set(newPage)
}
const handleRowsPerPageChange = (event: React.ChangeEvent<HTMLInputElement>) => {
rowsPerPage.set(+event.target.value)
page.set(0)
}

const adminRecordingsState = useHookstate(getMutableState(AdminRecordingState))

useEffect(() => {
if (adminRecordingsState.updateNeeded.value) {
AdminRecordingService.fetchAdminRecordings(null, page.value, sortField.value, fieldOrder.value, rowsPerPage.value)
}
}, [page.value, sortField.value, fieldOrder.value, rowsPerPage.value, adminRecordingsState.updateNeeded.value])

const handleSubmitRemove = () => {
if (currentRecordingId.value) {
AdminRecordingService.removeRecording(currentRecordingId.value)
openConfirm.set(false)
currentRecordingId.set(null)
}
}

const createData = (el: RecordingResult, id: string, user: string, ended: boolean, schema: string) => ({
el,
id,
user,
ended: ended ? t('admin:components.common.yes') : t('admin:components.common.no'),
schema,
view: (
<IconButton
className={styles.iconButton}
name="view"
onClick={() => {
currentRecordingId.set(id)
recordingResourcesDrawerOpen.set(true)
}}
icon={<Icon type="Visibility" />}
/>
),
action: (
<IconButton
className={styles.iconButton}
name="remove"
onClick={() => {
currentRecordingId.set(el.id)
openConfirm.set(true)
}}
icon={<Icon type="Cancel" />}
/>
)
})

const rows = adminRecordingsState.recordings.value.map((val) =>
createData(val, val.id, val['user.name'], val.ended, val.schema)
)

return (
<Box>
<TableComponent
allowSort={false}
fieldOrder={fieldOrder.value}
setSortField={sortField.set}
setFieldOrder={fieldOrder.set}
rows={rows}
column={recordingColumns}
page={page.value}
rowsPerPage={rowsPerPage.value}
count={adminRecordingsState.total.value}
handlePageChange={handlePageChange}
handleRowsPerPageChange={handleRowsPerPageChange}
/>
<ConfirmDialog
open={openConfirm.value}
description={`${t('admin:components.recording.confirmRecordingDelete')} '${currentRecordingId.value}'?`}
onClose={() => openConfirm.set(false)}
onSubmit={handleSubmitRemove}
/>
<RecordingFilesDrawer
open={recordingResourcesDrawerOpen.value}
selectedRecordingId={currentRecordingId.value}
onClose={() => {
recordingResourcesDrawerOpen.set(false)
currentRecordingId.set(null)
}}
/>
</Box>
)
}

export default RecordingsTable
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react'

import RecordingsTable from './RecordingsTable'

const Recordings = () => {
return <RecordingsTable />
}

export default Recordings
Loading

0 comments on commit 751cb6d

Please sign in to comment.