Skip to content

Commit

Permalink
feat: CallList component and update usage (#431)
Browse files Browse the repository at this point in the history
  • Loading branch information
wesbillman authored Oct 3, 2023
1 parent d4d5455 commit dee120e
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 95 deletions.
96 changes: 96 additions & 0 deletions console/client/src/features/calls/CallList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import React from 'react'
import { CallEvent } from '../../protos/xyz/block/ftl/v1/console/console_pb'
import { SidePanelContext } from '../../providers/side-panel-provider'
import { formatDuration, formatTimestampShort } from '../../utils'
import { TimelineCallDetails } from '../timeline/details/TimelineCallDetails'
import { verbRefString } from '../verbs/verb.utils'

interface Props {
calls: CallEvent[] | undefined
}

export const CallList = ({ calls }: Props) => {
const { openPanel, closePanel } = React.useContext(SidePanelContext)
const [selectedCall, setSelectedCall] = React.useState<CallEvent | undefined>()

const handleCallClicked = (call: CallEvent) => {
if (selectedCall?.equals(call)) {
setSelectedCall(undefined)
closePanel()
return
}
setSelectedCall(call)
openPanel(<TimelineCallDetails timestamp={call.timeStamp!} call={call} />)
}

return (
<div className='flex flex-col h-full relative'>
<div className='border border-gray-100 dark:border-slate-700 rounded h-full absolute inset-0'>
<table className={`w-full table-fixed text-gray-600 dark:text-gray-300`}>
<thead>
<tr className='flex text-xs'>
<th className='p-1 text-left border-b w-40 border-gray-100 dark:border-slate-700 flex-none sticky top-0 z-10'>
Date
</th>
<th className='p-1 text-left border-b w-14 border-gray-100 dark:border-slate-700 flex-none sticky top-0 z-10'>
Dur.
</th>
<th className='p-1 text-left border-b w-40 border-gray-100 dark:border-slate-700 flex-none sticky top-0 z-10'>
Source
</th>
<th className='p-1 text-left border-b w-40 border-gray-100 dark:border-slate-700 flex-none sticky top-0 z-10'>
Destination
</th>
<th className='p-1 text-left border-b border-gray-100 dark:border-slate-700 flex-1 flex-grow sticky top-0 z-10'>
Request
</th>
<th className='p-1 text-left border-b border-gray-100 dark:border-slate-700 flex-1 flex-grow sticky top-0 z-10'>
Response
</th>
<th className='p-1 text-left border-b border-gray-100 dark:border-slate-700 flex-1 flex-grow sticky top-0 z-10'>
Error
</th>
</tr>
</thead>
</table>
<div className='overflow-y-auto h-[calc(100%-1.5rem)]'>
<table className={`w-full table-fixed text-gray-600 dark:text-gray-300`}>
<tbody className='text-xs'>
{calls?.map((call, index) => (
<tr
key={`${index}-${call.timeStamp?.toDate().toUTCString()}`}
className={`border-b border-gray-100 dark:border-slate-700 font-roboto-mono ${
selectedCall?.equals(call) ? 'bg-indigo-50 dark:bg-slate-700' : ''
} relative flex cursor-pointer hover:bg-indigo-50 dark:hover:bg-slate-700`}
onClick={() => handleCallClicked(call)}
>
<td className='p-1 w-40 items-center flex-none text-gray-400 dark:text-gray-400'>
{formatTimestampShort(call.timeStamp)}
</td>
<td className='p-1 w-14 items-center flex-none text-gray-400 dark:text-gray-400 truncate'>
{formatDuration(call.duration)}
</td>
<td className='p-1 w-40 flex-none text-indigo-500 dark:text-indigo-300'>
{call.sourceVerbRef && verbRefString(call.sourceVerbRef)}
</td>
<td className='p-1 w-40 flex-none text-indigo-500 dark:text-indigo-300'>
{call.destinationVerbRef && verbRefString(call.destinationVerbRef)}
</td>
<td className='p-1 flex-1 flex-grow truncate' title={call.request}>
{call.request}
</td>
<td className='p-1 flex-1 flex-grow truncate' title={call.response}>
{call.response}
</td>
<td className='p-1 flex-1 flex-grow truncate' title={call.error}>
{call.error}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
)
}
84 changes: 5 additions & 79 deletions console/client/src/features/modules/ModulePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,15 @@ import { Card } from '../../components/Card'
import { PageHeader } from '../../components/PageHeader'
import { CallEvent, Module } from '../../protos/xyz/block/ftl/v1/console/console_pb'
import { modulesContext } from '../../providers/modules-provider'
import { SidePanelContext } from '../../providers/side-panel-provider'
import { getCalls } from '../../services/console.service'
import { formatTimestampShort } from '../../utils/date.utils'
import { TimelineCallDetails } from '../timeline/details/TimelineCallDetails'
import { verbRefString } from '../verbs/verb.utils'
import { CallList } from '../calls/CallList'

export const ModulePage = () => {
const navigate = useNavigate()
const { moduleName } = useParams()
const { openPanel, closePanel } = React.useContext(SidePanelContext)
const modules = React.useContext(modulesContext)
const [module, setModule] = React.useState<Module | undefined>()
const [calls, setCalls] = React.useState<CallEvent[] | undefined>()
const [selectedCall, setSelectedCall] = React.useState<CallEvent | undefined>()

React.useEffect(() => {
if (modules) {
Expand All @@ -37,20 +32,10 @@ export const ModulePage = () => {
fetchCalls()
}, [module])

const handleCallClicked = (call: CallEvent) => {
if (selectedCall?.equals(call)) {
setSelectedCall(undefined)
closePanel()
return
}
setSelectedCall(call)
openPanel(<TimelineCallDetails timestamp={call.timeStamp!} call={call} />)
}

return (
<>
<div className='flex-1 flex flex-col h-full'>
<div className='flex-1'>
<div className='flex flex-col h-full'>
<div className='flex-1 flex flex-col h-full'>
<PageHeader
icon={<Square3Stack3DIcon />}
title={module?.name || ''}
Expand All @@ -70,67 +55,8 @@ export const ModulePage = () => {
))}
</div>
</div>
</div>
<div className='flex-1 relative m-4'>
<div className='border border-gray-100 dark:border-slate-700 rounded h-full absolute inset-0'>
<table className={`w-full table-fixed text-gray-600 dark:text-gray-300`}>
<thead>
<tr className='flex text-xs'>
<th className='p-1 text-left border-b w-40 border-gray-100 dark:border-slate-700 flex-none sticky top-0 z-10'>
Date
</th>
<th className='p-1 text-left border-b w-40 border-gray-100 dark:border-slate-700 flex-none sticky top-0 z-10'>
Source
</th>
<th className='p-1 text-left border-b w-40 border-gray-100 dark:border-slate-700 flex-none sticky top-0 z-10'>
Destination
</th>
<th className='p-1 text-left border-b border-gray-100 dark:border-slate-700 flex-1 flex-grow sticky top-0 z-10'>
Request
</th>
<th className='p-1 text-left border-b border-gray-100 dark:border-slate-700 flex-1 flex-grow sticky top-0 z-10'>
Response
</th>
<th className='p-1 text-left border-b border-gray-100 dark:border-slate-700 flex-1 flex-grow sticky top-0 z-10'>
Error
</th>
</tr>
</thead>
</table>
<div className='overflow-y-auto h-[calc(100%-2rem)]'>
<table className={`w-full table-fixed text-gray-600 dark:text-gray-300`}>
<tbody className='text-xs'>
{calls?.map((call, index) => (
<tr
key={`${index}-${call.timeStamp?.toDate().toUTCString()}`}
className={`border-b border-gray-100 dark:border-slate-700 font-roboto-mono ${
selectedCall?.equals(call) ? 'bg-indigo-50 dark:bg-slate-700' : ''
} relative flex cursor-pointer hover:bg-indigo-50 dark:hover:bg-slate-700`}
onClick={() => handleCallClicked(call)}
>
<td className='p-1 w-40 items-center flex-none text-gray-400 dark:text-gray-400'>
{formatTimestampShort(call.timeStamp)}
</td>
<td className='p-1 w-40 flex-none text-indigo-500 dark:text-indigo-300'>
{call.sourceVerbRef && verbRefString(call.sourceVerbRef)}
</td>
<td className='p-1 w-40 flex-none text-indigo-500 dark:text-indigo-300'>
{call.destinationVerbRef && verbRefString(call.destinationVerbRef)}
</td>
<td className='p-1 flex-1 flex-grow truncate' title={call.request}>
{call.request}
</td>
<td className='p-1 flex-1 flex-grow truncate' title={call.response}>
{call.response}
</td>
<td className='p-1 flex-1 flex-grow truncate' title={call.error}>
{call.error}
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className='flex-1 h-1/2 m-4'>
<CallList calls={calls} />
</div>
</div>
</div>
Expand Down
4 changes: 0 additions & 4 deletions console/client/src/features/timeline/TimelinePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ export const TimelinePage = () => {
// if we're loading a specific event, we don't want to tail.
setSelectedTimeRange(TIME_RANGES['5m'])
setIsTimelinePaused(true)
} else {
// Reset to initial state if there's no 'id' query parameter
setSelectedTimeRange(TIME_RANGES['tail'])
setIsTimelinePaused(false)
}
}, [searchParams])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const TimelineCallDetails = ({ timestamp, call }: Props) => {
return
}
const calls = await getRequestCalls(selectedCall.requestName)
setRequestCalls(calls)
setRequestCalls(calls.reverse())
}

fetchRequestCalls()
Expand Down
57 changes: 46 additions & 11 deletions console/client/src/features/verbs/VerbPage.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import { Square3Stack3DIcon } from '@heroicons/react/24/outline'
import React from 'react'
import { useParams } from 'react-router-dom'
import { CodeBlock } from '../../components/CodeBlock'
import { PageHeader } from '../../components/PageHeader'
import { Module, Verb } from '../../protos/xyz/block/ftl/v1/console/console_pb'
import { CallEvent, Module, Verb } from '../../protos/xyz/block/ftl/v1/console/console_pb'
import { modulesContext } from '../../providers/modules-provider'
import { getCalls } from '../../services/console.service'
import { CallList } from '../calls/CallList'
import { buildVerbSchema } from './verb.utils'

export const VerbPage = () => {
const { moduleName, verbName } = useParams()
const modules = React.useContext(modulesContext)
const [module, setModule] = React.useState<Module | undefined>()
const [verb, setVerb] = React.useState<Verb | undefined>()
const [calls, setCalls] = React.useState<CallEvent[] | undefined>()

const callData =
module?.data.filter((data) => [verb?.verb?.request?.name, verb?.verb?.response?.name].includes(data.data?.name)) ??
[]

React.useEffect(() => {
if (modules) {
Expand All @@ -20,18 +29,44 @@ export const VerbPage = () => {
}
}, [modules, moduleName])

React.useEffect(() => {
if (!module) return

const fetchCalls = async () => {
const calls = await getCalls(module.name, verb?.verb?.name)
setCalls(calls)
}
fetchCalls()
}, [module])

return (
<>
<PageHeader
icon={<Square3Stack3DIcon />}
title={verb?.verb?.name || ''}
breadcrumbs={[
{ label: 'Modules', link: '/modules' },
{ label: module?.name || '', link: `/modules/${module?.name}` },
]}
/>
<div className='m-4'>
<h1>Verb: {verb?.verb?.name}</h1>
<div className='flex flex-col h-full'>
<div className='flex-1 flex flex-col h-full'>
<PageHeader
icon={<Square3Stack3DIcon />}
title={verb?.verb?.name || ''}
breadcrumbs={[
{ label: 'Modules', link: '/modules' },
{ label: module?.name || '', link: `/modules/${module?.name}` },
]}
/>
<div className='flex-1 h-1/2 overflow-y-auto mx-4 mt-4'>
{verb?.verb?.request?.toJsonString() && (
<CodeBlock
code={buildVerbSchema(
verb?.schema,
callData.map((d) => d.schema),
)}
language='json'
/>
)}
</div>

<div className='flex-1 h-1/2 m-4'>
<CallList calls={calls} />
</div>
</div>
</div>
</>
)
Expand Down

0 comments on commit dee120e

Please sign in to comment.