diff --git a/src/components/TextWithTooltip.tsx b/src/components/TextWithTooltip.tsx new file mode 100644 index 00000000..483c90d3 --- /dev/null +++ b/src/components/TextWithTooltip.tsx @@ -0,0 +1,16 @@ +import { useOverflowCheck } from '@/hooks/useOverflowCheck' +import { Tooltip, Text, TextProps } from '@radix-ui/themes' +import { useRef } from 'react' + +export function TextWithTooltip({ children, ...props }: TextProps) { + const ref = useRef(null) + const isEllipsisActive = useOverflowCheck(ref) + + return ( + + ) +} diff --git a/src/components/WebLogView/Row.tsx b/src/components/WebLogView/Row.tsx index 5b6179f9..ec7c5d6c 100644 --- a/src/components/WebLogView/Row.tsx +++ b/src/components/WebLogView/Row.tsx @@ -14,30 +14,22 @@ export interface RowProps { isSelected?: boolean onSelectRequest: (data: ProxyDataWithMatches) => void filter?: string - className?: string } -export function Row({ - data, - onSelectRequest, - isSelected, - filter, - className, -}: RowProps) { +export function Row({ data, onSelectRequest, isSelected, filter }: RowProps) { return ( <> - onSelectRequest(data)} - className={className} - css={{ - backgroundColor: isSelected ? 'var(--accent-3)' : 'transparent', - '&:hover': { - backgroundColor: isSelected ? 'var(--accent-3)' : 'var(--accent-2)', - }, - }} + - - + + + + + + onSelectRequest(data)} + css={{ + backgroundColor: isSelected ? 'var(--accent-3)' : 'transparent', + '&:hover': { + backgroundColor: isSelected ? 'var(--accent-3)' : 'var(--accent-2)', + }, + }} + > + {children} + + ) +} + +export function MethodCell({ data, isSelected, }: { @@ -57,43 +70,58 @@ export function Cells({ isSelected?: boolean }) { return ( - <> - + - + + + + + ) +} + +export function StatusCell({ data }: { data: ProxyDataWithMatches }) { + return ( + + + - - - - + + + ) +} - - - - - - {getRequestType(data)} - - - - - - - +export function RequestTypeCell({ data }: { data: ProxyDataWithMatches }) { + return {getRequestType(data)} +} + +export function HostCell({ data }: { data: ProxyDataWithMatches }) { + return ( + + + + ) +} + +export function PathCell({ data }: { data: ProxyDataWithMatches }) { + return ( + + + ) } diff --git a/src/components/WebLogView/SearchResults.tsx b/src/components/WebLogView/SearchResults.tsx index 1c58aace..c04d234b 100644 --- a/src/components/WebLogView/SearchResults.tsx +++ b/src/components/WebLogView/SearchResults.tsx @@ -39,12 +39,10 @@ export function SearchResults({ data, onSelectRequest, filter, - colSpan = 5, }: { data: ProxyDataWithMatches onSelectRequest: (data: ProxyDataWithMatches) => void filter?: string - colSpan?: number }) { const { setTab: setRequestTab } = useRequestDetailsTab() const { setTab: setResponseTab } = useResponseDetailsTab() @@ -105,7 +103,7 @@ export function SearchResults({ return ( - + {visibleResults.map((result, excerptIndex) => ( {!recordingError && ( - ( )} /> diff --git a/src/views/Generator/GeneratorTabs/RequestList.utils.tsx b/src/views/Generator/GeneratorTabs/RequestList/RequestList.utils.tsx similarity index 97% rename from src/views/Generator/GeneratorTabs/RequestList.utils.tsx rename to src/views/Generator/GeneratorTabs/RequestList/RequestList.utils.tsx index 4abefca8..8d176019 100644 --- a/src/views/Generator/GeneratorTabs/RequestList.utils.tsx +++ b/src/views/Generator/GeneratorTabs/RequestList/RequestList.utils.tsx @@ -3,7 +3,7 @@ import { ProxyData } from '@/types' import { GlobeIcon } from '@radix-ui/react-icons' import { Button } from '@radix-ui/themes' import { ComponentProps } from 'react' -import { RecordingSelector } from '../RecordingSelector' +import { RecordingSelector } from '../../RecordingSelector' export function validateRecording({ allowlist, diff --git a/src/views/Generator/GeneratorTabs/RequestList/RequestRow.tsx b/src/views/Generator/GeneratorTabs/RequestList/RequestRow.tsx new file mode 100644 index 00000000..642ce0d3 --- /dev/null +++ b/src/views/Generator/GeneratorTabs/RequestList/RequestRow.tsx @@ -0,0 +1,60 @@ +import { HighlightedText } from '@/components/HighlightedText' +import { Table } from '@/components/Table' +import { TextWithTooltip } from '@/components/TextWithTooltip' +import { + HostCell, + MethodCell, + RequestTypeCell, + RowProps, + StatusCell, + TableRow, +} from '@/components/WebLogView' +import { SearchResults } from '@/components/WebLogView/SearchResults' +import { RuleInstance } from '@/types/rules' +import { Flex } from '@radix-ui/themes' +import { RuleBadges } from './RuleBadges' + +export function RequestRow({ + data, + onSelectRequest, + isSelected, + filter, + selectedRuleInstance, +}: RowProps & { selectedRuleInstance?: RuleInstance }) { + return ( + <> + + + + + + + + + + + + + + + + + + + ) +} diff --git a/src/views/Generator/GeneratorTabs/RequestTable.tsx b/src/views/Generator/GeneratorTabs/RequestList/RequestTable.tsx similarity index 85% rename from src/views/Generator/GeneratorTabs/RequestTable.tsx rename to src/views/Generator/GeneratorTabs/RequestList/RequestTable.tsx index dedeb1b5..e1b6221f 100644 --- a/src/views/Generator/GeneratorTabs/RequestTable.tsx +++ b/src/views/Generator/GeneratorTabs/RequestList/RequestTable.tsx @@ -2,14 +2,15 @@ import { Table } from '@/components/Table' import { RequestListProps } from '@/components/WebLogView' import { RequestRow } from './RequestRow' +import { RuleInstance } from '@/types/rules' export function RequestTable({ requests, selectedRequestId, onSelectRequest, filter, - highlightedRequestIds, -}: RequestListProps & { highlightedRequestIds?: string[] }) { + selectedRuleInstance, +}: RequestListProps & { selectedRuleInstance?: RuleInstance }) { return ( @@ -19,7 +20,6 @@ export function RequestTable({ Type Host Path - @@ -30,7 +30,7 @@ export function RequestTable({ isSelected={selectedRequestId === data.id} onSelectRequest={onSelectRequest} filter={filter} - highlightedRequestIds={highlightedRequestIds} + selectedRuleInstance={selectedRuleInstance} /> ))} diff --git a/src/views/Generator/GeneratorTabs/RequestList/RuleBadges.tsx b/src/views/Generator/GeneratorTabs/RequestList/RuleBadges.tsx new file mode 100644 index 00000000..23754243 --- /dev/null +++ b/src/views/Generator/GeneratorTabs/RequestList/RuleBadges.tsx @@ -0,0 +1,73 @@ +import { ProxyData } from '@/types' +import { RuleInstance } from '@/types/rules' +import { Badge, Flex, Strong } from '@radix-ui/themes' +import { useMemo } from 'react' + +export function RuleBadges({ + selectedRuleInstance, + data, +}: { + selectedRuleInstance?: RuleInstance + data: ProxyData +}) { + if (!selectedRuleInstance) { + return null + } + + return ( + + + + + ) +} + +function MatchBadge({ + selectedRuleInstance, + data, +}: { + selectedRuleInstance: RuleInstance + data: ProxyData +}) { + const isMatch = useMemo(() => { + return selectedRuleInstance.state.matchedRequestIds.includes(data.id) + }, [selectedRuleInstance, data.id]) + + if (!isMatch) { + return null + } + + return ( + + Match + + ) +} + +function ExtractorBadge({ + selectedRuleInstance, + data, +}: { + selectedRuleInstance: RuleInstance + data: ProxyData +}) { + const isExtractor = useMemo(() => { + if (selectedRuleInstance.type !== 'correlation') { + return false + } + + return selectedRuleInstance.state.responsesExtracted.some( + (request) => request.id === data.id + ) + }, [selectedRuleInstance, data.id]) + + if (!isExtractor) { + return null + } + + return ( + + Value extracted + + ) +} diff --git a/src/views/Generator/GeneratorTabs/RequestList/index.ts b/src/views/Generator/GeneratorTabs/RequestList/index.ts new file mode 100644 index 00000000..20dcb480 --- /dev/null +++ b/src/views/Generator/GeneratorTabs/RequestList/index.ts @@ -0,0 +1 @@ +export * from './RequestList' diff --git a/src/views/Generator/GeneratorTabs/RequestRow.tsx b/src/views/Generator/GeneratorTabs/RequestRow.tsx deleted file mode 100644 index 536e6125..00000000 --- a/src/views/Generator/GeneratorTabs/RequestRow.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Cells, RowProps } from '@/components/WebLogView' -import { SearchResults } from '@/components/WebLogView/SearchResults' -import { Badge, Flex, Strong, Table } from '@radix-ui/themes' -import { useMemo } from 'react' - -export function RequestRow({ - data, - onSelectRequest, - isSelected, - filter, - highlightedRequestIds, -}: RowProps & { highlightedRequestIds?: string[] }) { - const isMatch = useMemo(() => { - if (!highlightedRequestIds) { - return false - } - - return highlightedRequestIds.includes(data.id) - }, [highlightedRequestIds, data.id]) - return ( - <> - onSelectRequest(data)} - css={{ - backgroundColor: isSelected ? 'var(--accent-3)' : 'transparent', - '&:hover': { - backgroundColor: isSelected ? 'var(--accent-3)' : 'var(--accent-2)', - }, - }} - > - - - {isMatch && ( - - - Match - - - )} - - - - - - ) -} diff --git a/src/views/Generator/RuleEditor/CorrelationEditor.tsx b/src/views/Generator/RuleEditor/CorrelationEditor.tsx index 52ba0d85..1cd4da08 100644 --- a/src/views/Generator/RuleEditor/CorrelationEditor.tsx +++ b/src/views/Generator/RuleEditor/CorrelationEditor.tsx @@ -10,7 +10,7 @@ import { Tooltip, } from '@radix-ui/themes' -import { TestRule } from '@/types/rules' +import { RuleInstance, TestRule } from '@/types/rules' import { FilterField } from './FilterField' import { SelectorField } from './SelectorField' import { Label } from '@/components/Label' @@ -19,7 +19,6 @@ import { FieldGroup } from '@/components/Form' import { ControlledRadioGroup } from '@/components/Form/ControllerRadioGroup' import { InfoCircledIcon } from '@radix-ui/react-icons' import { useApplyRules } from '@/store/hooks/useApplyRules' -import invariant from 'tiny-invariant' const EXTRACTION_MODE_OPTIONS = [ { value: 'single', label: 'First match' }, @@ -29,11 +28,6 @@ const EXTRACTION_MODE_OPTIONS = [ export function CorrelationEditor() { const { selectedRuleInstance } = useApplyRules() - invariant( - selectedRuleInstance?.type === 'correlation', - 'Selected rule instance is not a correlation rule' - ) - const { setValue, watch, @@ -41,7 +35,6 @@ export function CorrelationEditor() { formState: { errors }, } = useFormContext() - const { extractedValue } = selectedRuleInstance.state const replacer = watch('replacer') const isCustomReplacerSelector = !!replacer?.selector @@ -88,19 +81,7 @@ export function CorrelationEditor() { } /> - {extractedValue && ( - - Extracted value:{' '} -
-              {JSON.stringify(extractedValue, null, 2)}
-            
-
- )} - {!extractedValue && ( - - The rule does not match any requests - - )} + @@ -145,5 +126,33 @@ export function CorrelationEditor() { ) } +function ExtractedValue({ + selectedRuleInstance, +}: { + selectedRuleInstance?: RuleInstance +}) { + if (selectedRuleInstance?.type !== 'correlation') { + return null + } + + const extractedValue = selectedRuleInstance?.state?.extractedValue + + if (!extractedValue) { + return ( + + The rule does not match any requests + + ) + } + return ( + + Extracted value:{' '} +
+        {JSON.stringify(extractedValue, null, 2)}
+      
+
+ ) +} + const replacerTooltip = 'By default, the correlation rule will replace all occurrences of the extracted value in the requests. Enable this option to fine tune your selection.'