diff --git a/console/client/src/features/calls/CallList.tsx b/console/client/src/features/calls/CallList.tsx new file mode 100644 index 0000000000..9a2f0f44cc --- /dev/null +++ b/console/client/src/features/calls/CallList.tsx @@ -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() + + const handleCallClicked = (call: CallEvent) => { + if (selectedCall?.equals(call)) { + setSelectedCall(undefined) + closePanel() + return + } + setSelectedCall(call) + openPanel() + } + + return ( +
+
+ + + + + + + + + + + + +
+ Date + + Dur. + + Source + + Destination + + Request + + Response + + Error +
+
+ + + {calls?.map((call, index) => ( + handleCallClicked(call)} + > + + + + + + + + + ))} + +
+ {formatTimestampShort(call.timeStamp)} + + {formatDuration(call.duration)} + + {call.sourceVerbRef && verbRefString(call.sourceVerbRef)} + + {call.destinationVerbRef && verbRefString(call.destinationVerbRef)} + + {call.request} + + {call.response} + + {call.error} +
+
+
+
+ ) +} diff --git a/console/client/src/features/modules/ModulePage.tsx b/console/client/src/features/modules/ModulePage.tsx index 7e52340908..8af9a2ff25 100644 --- a/console/client/src/features/modules/ModulePage.tsx +++ b/console/client/src/features/modules/ModulePage.tsx @@ -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() const [calls, setCalls] = React.useState() - const [selectedCall, setSelectedCall] = React.useState() React.useEffect(() => { if (modules) { @@ -37,20 +32,10 @@ export const ModulePage = () => { fetchCalls() }, [module]) - const handleCallClicked = (call: CallEvent) => { - if (selectedCall?.equals(call)) { - setSelectedCall(undefined) - closePanel() - return - } - setSelectedCall(call) - openPanel() - } - return ( <> -
-
+
+
} title={module?.name || ''} @@ -70,67 +55,8 @@ export const ModulePage = () => { ))}
-
-
-
- - - - - - - - - - - -
- Date - - Source - - Destination - - Request - - Response - - Error -
-
- - - {calls?.map((call, index) => ( - handleCallClicked(call)} - > - - - - - - - - ))} - -
- {formatTimestampShort(call.timeStamp)} - - {call.sourceVerbRef && verbRefString(call.sourceVerbRef)} - - {call.destinationVerbRef && verbRefString(call.destinationVerbRef)} - - {call.request} - - {call.response} - - {call.error} -
-
+
+
diff --git a/console/client/src/features/timeline/TimelinePage.tsx b/console/client/src/features/timeline/TimelinePage.tsx index f0d1af3766..bdd583924c 100644 --- a/console/client/src/features/timeline/TimelinePage.tsx +++ b/console/client/src/features/timeline/TimelinePage.tsx @@ -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]) diff --git a/console/client/src/features/timeline/details/TimelineCallDetails.tsx b/console/client/src/features/timeline/details/TimelineCallDetails.tsx index f907b88837..55e2f65f25 100644 --- a/console/client/src/features/timeline/details/TimelineCallDetails.tsx +++ b/console/client/src/features/timeline/details/TimelineCallDetails.tsx @@ -34,7 +34,7 @@ export const TimelineCallDetails = ({ timestamp, call }: Props) => { return } const calls = await getRequestCalls(selectedCall.requestName) - setRequestCalls(calls) + setRequestCalls(calls.reverse()) } fetchRequestCalls() diff --git a/console/client/src/features/verbs/VerbPage.tsx b/console/client/src/features/verbs/VerbPage.tsx index 7767f6b0fb..d5b4cad70c 100644 --- a/console/client/src/features/verbs/VerbPage.tsx +++ b/console/client/src/features/verbs/VerbPage.tsx @@ -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() const [verb, setVerb] = React.useState() + const [calls, setCalls] = React.useState() + + const callData = + module?.data.filter((data) => [verb?.verb?.request?.name, verb?.verb?.response?.name].includes(data.data?.name)) ?? + [] React.useEffect(() => { if (modules) { @@ -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 ( <> - } - title={verb?.verb?.name || ''} - breadcrumbs={[ - { label: 'Modules', link: '/modules' }, - { label: module?.name || '', link: `/modules/${module?.name}` }, - ]} - /> -
-

Verb: {verb?.verb?.name}

+
+
+ } + title={verb?.verb?.name || ''} + breadcrumbs={[ + { label: 'Modules', link: '/modules' }, + { label: module?.name || '', link: `/modules/${module?.name}` }, + ]} + /> +
+ {verb?.verb?.request?.toJsonString() && ( + d.schema), + )} + language='json' + /> + )} +
+ +
+ +
+
)