From 52543a5d040437c0e8fcbcf3bd8aadc3e3484b28 Mon Sep 17 00:00:00 2001 From: Michael Mainguy Date: Tue, 19 Aug 2025 08:29:03 -0500 Subject: [PATCH] Refactor Request Breakdown into modular components MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract breakdown sections into separate components for better maintainability: - ResourceTypeBreakdown: handles resource type analysis and visualization - StatusCodeBreakdown: handles status code analysis and visualization - HostnameBreakdown: handles hostname analysis with toggle functionality - RequestDataSummary: statistics cards component (previously extracted) - BreakdownTableHeader: shared table header component (previously extracted) Main RequestBreakdown component reduced from ~420 to ~90 lines with improved separation of concerns. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/components/RequestBreakdown.tsx | 370 +----------------- .../RequestBreakdown/BreakdownTableHeader.tsx | 23 ++ .../RequestBreakdown/HostnameBreakdown.tsx | 149 +++++++ .../RequestBreakdown/RequestDataSummary.tsx | 57 +++ .../ResourceTypeBreakdown.tsx | 122 ++++++ .../RequestBreakdown/StatusCodeBreakdown.tsx | 122 ++++++ 6 files changed, 488 insertions(+), 355 deletions(-) create mode 100644 src/components/RequestBreakdown/BreakdownTableHeader.tsx create mode 100644 src/components/RequestBreakdown/HostnameBreakdown.tsx create mode 100644 src/components/RequestBreakdown/RequestDataSummary.tsx create mode 100644 src/components/RequestBreakdown/ResourceTypeBreakdown.tsx create mode 100644 src/components/RequestBreakdown/StatusCodeBreakdown.tsx diff --git a/src/components/RequestBreakdown.tsx b/src/components/RequestBreakdown.tsx index 90c4add..6ede23a 100644 --- a/src/components/RequestBreakdown.tsx +++ b/src/components/RequestBreakdown.tsx @@ -1,11 +1,14 @@ -import { useMemo, useState } from 'react' +import { useMemo } from 'react' import { useDatabaseTraceData } from '../hooks/useDatabaseTraceData' import { processHTTPRequests } from './httprequestviewer/lib/httpRequestProcessor' import { addRequestPostProcessing } from './httprequestviewer/lib/requestPostProcessor' import { analyzeCDN, analyzeQueueReason } from './httprequestviewer/lib/analysisUtils' import { assignConnectionNumbers } from './httprequestviewer/lib/connectionUtils' import sortRequests from './httprequestviewer/lib/sortRequests' -import type { HTTPRequest } from './httprequestviewer/types/httpRequest' +import RequestDataSummary from './RequestBreakdown/RequestDataSummary' +import ResourceTypeBreakdown from './RequestBreakdown/ResourceTypeBreakdown' +import StatusCodeBreakdown from './RequestBreakdown/StatusCodeBreakdown' +import HostnameBreakdown from './RequestBreakdown/HostnameBreakdown' import styles from './RequestBreakdown.module.css' interface RequestBreakdownProps { @@ -21,20 +24,9 @@ interface BreakdownStats { cacheHitRate: number } -interface CategoryBreakdown { - name: string - count: number - percentage: number - totalSize: number - sizePercentage: number - totalResponseTime: number - responseTimePercentage: number - averageResponseTime: number -} const RequestBreakdown: React.FC = ({ traceId }) => { const { traceData, loading, error } = useDatabaseTraceData(traceId) - const [showAllHostnames, setShowAllHostnames] = useState(false) const httpRequests = useMemo(() => { if (!traceData) return [] @@ -85,143 +77,6 @@ const RequestBreakdown: React.FC = ({ traceId }) => { } }, [httpRequests]) - const resourceTypeBreakdown: CategoryBreakdown[] = useMemo(() => { - const typeMap = new Map() - - httpRequests.forEach(req => { - const type = req.resourceType || 'unknown' - if (!typeMap.has(type)) { - typeMap.set(type, []) - } - typeMap.get(type)!.push(req) - }) - - // Calculate total size and response time across all requests for percentage calculations - const totalAllSize = httpRequests.reduce((sum, req) => { - return sum + (req.contentLength || req.encodedDataLength || 0) - }, 0) - - const totalAllResponseTime = httpRequests.reduce((sum, req) => { - return sum + (req.timing.totalResponseTime || 0) - }, 0) - - return Array.from(typeMap.entries()).map(([type, requests]) => { - const totalSize = requests.reduce((sum, req) => { - return sum + (req.contentLength || req.encodedDataLength || 0) - }, 0) - - const totalResponseTime = requests.reduce((sum, req) => { - return sum + (req.timing.totalResponseTime || 0) - }, 0) - - return { - name: type, - count: requests.length, - percentage: (requests.length / httpRequests.length) * 100, - totalSize, - sizePercentage: totalAllSize > 0 ? (totalSize / totalAllSize) * 100 : 0, - totalResponseTime, - responseTimePercentage: totalAllResponseTime > 0 ? (totalResponseTime / totalAllResponseTime) * 100 : 0, - averageResponseTime: totalResponseTime / requests.length - } - }).sort((a, b) => b.count - a.count) - }, [httpRequests]) - - const statusCodeBreakdown: CategoryBreakdown[] = useMemo(() => { - const statusMap = new Map() - - httpRequests.forEach(req => { - const status = req.statusCode ? Math.floor(req.statusCode / 100) + 'xx' : 'Unknown' - if (!statusMap.has(status)) { - statusMap.set(status, []) - } - statusMap.get(status)!.push(req) - }) - - // Calculate total size and response time across all requests for percentage calculations - const totalAllSize = httpRequests.reduce((sum, req) => { - return sum + (req.contentLength || req.encodedDataLength || 0) - }, 0) - - const totalAllResponseTime = httpRequests.reduce((sum, req) => { - return sum + (req.timing.totalResponseTime || 0) - }, 0) - - return Array.from(statusMap.entries()).map(([status, requests]) => { - const totalSize = requests.reduce((sum, req) => { - return sum + (req.contentLength || req.encodedDataLength || 0) - }, 0) - - const totalResponseTime = requests.reduce((sum, req) => { - return sum + (req.timing.totalResponseTime || 0) - }, 0) - - return { - name: status, - count: requests.length, - percentage: (requests.length / httpRequests.length) * 100, - totalSize, - sizePercentage: totalAllSize > 0 ? (totalSize / totalAllSize) * 100 : 0, - totalResponseTime, - responseTimePercentage: totalAllResponseTime > 0 ? (totalResponseTime / totalAllResponseTime) * 100 : 0, - averageResponseTime: totalResponseTime / requests.length - } - }).sort((a, b) => b.count - a.count) - }, [httpRequests]) - - const hostnameBreakdown: CategoryBreakdown[] = useMemo(() => { - const hostMap = new Map() - - httpRequests.forEach(req => { - const hostname = req.hostname || 'unknown' - if (!hostMap.has(hostname)) { - hostMap.set(hostname, []) - } - hostMap.get(hostname)!.push(req) - }) - - // Calculate total size and response time across all requests for percentage calculations - const totalAllSize = httpRequests.reduce((sum, req) => { - return sum + (req.contentLength || req.encodedDataLength || 0) - }, 0) - - const totalAllResponseTime = httpRequests.reduce((sum, req) => { - return sum + (req.timing.totalResponseTime || 0) - }, 0) - - return Array.from(hostMap.entries()).map(([hostname, requests]) => { - const totalSize = requests.reduce((sum, req) => { - return sum + (req.contentLength || req.encodedDataLength || 0) - }, 0) - - const totalResponseTime = requests.reduce((sum, req) => { - return sum + (req.timing.totalResponseTime || 0) - }, 0) - - return { - name: hostname, - count: requests.length, - percentage: (requests.length / httpRequests.length) * 100, - totalSize, - sizePercentage: totalAllSize > 0 ? (totalSize / totalAllSize) * 100 : 0, - totalResponseTime, - responseTimePercentage: totalAllResponseTime > 0 ? (totalResponseTime / totalAllResponseTime) * 100 : 0, - averageResponseTime: totalResponseTime / requests.length - } - }).sort((a, b) => b.count - a.count) - }, [httpRequests]) - - const formatSize = (bytes: number): string => { - if (bytes < 1024) return `${bytes} B` - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` - return `${(bytes / 1024 / 1024).toFixed(1)} MB` - } - - const formatDuration = (microseconds: number): string => { - if (microseconds < 1000) return `${microseconds.toFixed(0)} μs` - if (microseconds < 1000000) return `${(microseconds / 1000).toFixed(1)} ms` - return `${(microseconds / 1000000).toFixed(2)} s` - } if (loading) { return ( @@ -247,212 +102,17 @@ const RequestBreakdown: React.FC = ({ traceId }) => {

Request Data Breakdown

{/* Overall Stats */} -
-
-

Total Requests

-
{overallStats.totalRequests}
-
-
-

Total Size

-
{formatSize(overallStats.totalSize)}
-
-
-

Average Response Time

-
{formatDuration(overallStats.averageResponseTime)}
-
-
-

Success Rate

-
{overallStats.successRate.toFixed(1)}%
-
-
-

Cache Hit Rate

-
{overallStats.cacheHitRate.toFixed(1)}%
-
-
+ - {/* Resource Type Breakdown */} -
-

By Resource Type

-
-
- Type - Count - Count % - Total Size - Size % - Total Response Time - Response Time % - Avg Response Time -
- {resourceTypeBreakdown.map(item => ( -
- {item.name} - {item.count} -
- {item.percentage.toFixed(1)}% -
-
-
-
- {formatSize(item.totalSize)} -
- {item.sizePercentage.toFixed(1)}% -
-
-
-
- {formatDuration(item.totalResponseTime)} -
- {item.responseTimePercentage.toFixed(1)}% -
-
-
-
- {formatDuration(item.averageResponseTime)} -
- ))} -
-
- - {/* Status Code Breakdown */} -
-

By Status Code

-
-
- Status - Count - Count % - Total Size - Size % - Total Response Time - Response Time % - Avg Response Time -
- {statusCodeBreakdown.map(item => ( -
- {item.name} - {item.count} -
- {item.percentage.toFixed(1)}% -
-
-
-
- {formatSize(item.totalSize)} -
- {item.sizePercentage.toFixed(1)}% -
-
-
-
- {formatDuration(item.totalResponseTime)} -
- {item.responseTimePercentage.toFixed(1)}% -
-
-
-
- {formatDuration(item.averageResponseTime)} -
- ))} -
-
- - {/* Hostname Breakdown */} -
-

By Hostname

-
-
- Hostname - Count - Count % - Total Size - Size % - Total Response Time - Response Time % - Avg Response Time -
- {(showAllHostnames ? hostnameBreakdown : hostnameBreakdown.slice(0, 10)).map(item => ( -
- {item.name} - {item.count} -
- {item.percentage.toFixed(1)}% -
-
-
-
- {formatSize(item.totalSize)} -
- {item.sizePercentage.toFixed(1)}% -
-
-
-
- {formatDuration(item.totalResponseTime)} -
- {item.responseTimePercentage.toFixed(1)}% -
-
-
-
- {formatDuration(item.averageResponseTime)} -
- ))} -
- {hostnameBreakdown.length > 10 && ( -
- {showAllHostnames ? ( - <> - Showing all {hostnameBreakdown.length} hostnames - - - ) : ( - <> - Showing top 10 of {hostnameBreakdown.length} hostnames - - - )} -
- )} -
+ + + ) } diff --git a/src/components/RequestBreakdown/BreakdownTableHeader.tsx b/src/components/RequestBreakdown/BreakdownTableHeader.tsx new file mode 100644 index 0000000..3240e38 --- /dev/null +++ b/src/components/RequestBreakdown/BreakdownTableHeader.tsx @@ -0,0 +1,23 @@ +import React from 'react' +import styles from '../RequestBreakdown.module.css' + +interface BreakdownTableHeaderProps { + categoryLabel: string +} + +const BreakdownTableHeader: React.FC = ({ categoryLabel }) => { + return ( +
+ {categoryLabel} + Request Count + Request Count % + Total Size + Total Size % + Total Response Time + Total Response Time % + Avg Response Time +
+ ) +} + +export default BreakdownTableHeader \ No newline at end of file diff --git a/src/components/RequestBreakdown/HostnameBreakdown.tsx b/src/components/RequestBreakdown/HostnameBreakdown.tsx new file mode 100644 index 0000000..670688e --- /dev/null +++ b/src/components/RequestBreakdown/HostnameBreakdown.tsx @@ -0,0 +1,149 @@ +import React, { useMemo, useState } from 'react' +import type { HTTPRequest } from '../httprequestviewer/types/httpRequest' +import BreakdownTableHeader from './BreakdownTableHeader' +import styles from '../RequestBreakdown.module.css' + +interface CategoryBreakdown { + name: string + count: number + percentage: number + totalSize: number + sizePercentage: number + totalResponseTime: number + responseTimePercentage: number + averageResponseTime: number +} + +interface HostnameBreakdownProps { + httpRequests: HTTPRequest[] +} + +const HostnameBreakdown: React.FC = ({ httpRequests }) => { + const [showAllHostnames, setShowAllHostnames] = useState(false) + + const formatSize = (bytes: number): string => { + if (bytes < 1024) return `${bytes} B` + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` + return `${(bytes / 1024 / 1024).toFixed(1)} MB` + } + + const formatDuration = (microseconds: number): string => { + if (microseconds < 1000) return `${microseconds.toFixed(0)} μs` + if (microseconds < 1000000) return `${(microseconds / 1000).toFixed(1)} ms` + return `${(microseconds / 1000000).toFixed(2)} s` + } + + const hostnameBreakdown: CategoryBreakdown[] = useMemo(() => { + const hostMap = new Map() + + httpRequests.forEach(req => { + const hostname = req.hostname || 'unknown' + if (!hostMap.has(hostname)) { + hostMap.set(hostname, []) + } + hostMap.get(hostname)!.push(req) + }) + + // Calculate total size and response time across all requests for percentage calculations + const totalAllSize = httpRequests.reduce((sum, req) => { + return sum + (req.contentLength || req.encodedDataLength || 0) + }, 0) + + const totalAllResponseTime = httpRequests.reduce((sum, req) => { + return sum + (req.timing.totalResponseTime || 0) + }, 0) + + return Array.from(hostMap.entries()).map(([hostname, requests]) => { + const totalSize = requests.reduce((sum, req) => { + return sum + (req.contentLength || req.encodedDataLength || 0) + }, 0) + + const totalResponseTime = requests.reduce((sum, req) => { + return sum + (req.timing.totalResponseTime || 0) + }, 0) + + return { + name: hostname, + count: requests.length, + percentage: (requests.length / httpRequests.length) * 100, + totalSize, + sizePercentage: totalAllSize > 0 ? (totalSize / totalAllSize) * 100 : 0, + totalResponseTime, + responseTimePercentage: totalAllResponseTime > 0 ? (totalResponseTime / totalAllResponseTime) * 100 : 0, + averageResponseTime: totalResponseTime / requests.length + } + }).sort((a, b) => b.count - a.count) + }, [httpRequests]) + + return ( +
+

By Hostname

+
+ + {(showAllHostnames ? hostnameBreakdown : hostnameBreakdown.slice(0, 10)).map(item => ( +
+ {item.name} + {item.count} +
+ {item.percentage.toFixed(1)}% +
+
+
+
+ {formatSize(item.totalSize)} +
+ {item.sizePercentage.toFixed(1)}% +
+
+
+
+ {formatDuration(item.totalResponseTime)} +
+ {item.responseTimePercentage.toFixed(1)}% +
+
+
+
+ {formatDuration(item.averageResponseTime)} +
+ ))} +
+ {hostnameBreakdown.length > 10 && ( +
+ {showAllHostnames ? ( + <> + Showing all {hostnameBreakdown.length} hostnames + + + ) : ( + <> + Showing top 10 of {hostnameBreakdown.length} hostnames + + + )} +
+ )} +
+ ) +} + +export default HostnameBreakdown \ No newline at end of file diff --git a/src/components/RequestBreakdown/RequestDataSummary.tsx b/src/components/RequestBreakdown/RequestDataSummary.tsx new file mode 100644 index 0000000..cb36be3 --- /dev/null +++ b/src/components/RequestBreakdown/RequestDataSummary.tsx @@ -0,0 +1,57 @@ +import React from 'react' +import styles from '../RequestBreakdown.module.css' + +interface RequestDataSummaryProps { + totalRequests: number + totalSize: number + averageResponseTime: number + successRate: number + cacheHitRate: number +} + +const RequestDataSummary: React.FC = ({ + totalRequests, + totalSize, + averageResponseTime, + successRate, + cacheHitRate +}) => { + const formatSize = (bytes: number): string => { + if (bytes < 1024) return `${bytes} B` + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` + return `${(bytes / 1024 / 1024).toFixed(1)} MB` + } + + const formatDuration = (microseconds: number): string => { + if (microseconds < 1000) return `${microseconds.toFixed(0)} μs` + if (microseconds < 1000000) return `${(microseconds / 1000).toFixed(1)} ms` + return `${(microseconds / 1000000).toFixed(2)} s` + } + + return ( +
+
+

Total Requests

+
{totalRequests}
+
+
+

Total Size

+
{formatSize(totalSize)}
+
+
+

Average Response Time

+
{formatDuration(averageResponseTime)}
+
+
+

Success Rate

+
{successRate.toFixed(1)}%
+
+
+

Cache Hit Rate

+
{cacheHitRate.toFixed(1)}%
+
+
+ ) +} + +export default RequestDataSummary \ No newline at end of file diff --git a/src/components/RequestBreakdown/ResourceTypeBreakdown.tsx b/src/components/RequestBreakdown/ResourceTypeBreakdown.tsx new file mode 100644 index 0000000..f6b114d --- /dev/null +++ b/src/components/RequestBreakdown/ResourceTypeBreakdown.tsx @@ -0,0 +1,122 @@ +import React, { useMemo } from 'react' +import type { HTTPRequest } from '../httprequestviewer/types/httpRequest' +import BreakdownTableHeader from './BreakdownTableHeader' +import styles from '../RequestBreakdown.module.css' + +interface CategoryBreakdown { + name: string + count: number + percentage: number + totalSize: number + sizePercentage: number + totalResponseTime: number + responseTimePercentage: number + averageResponseTime: number +} + +interface ResourceTypeBreakdownProps { + httpRequests: HTTPRequest[] +} + +const ResourceTypeBreakdown: React.FC = ({ httpRequests }) => { + const formatSize = (bytes: number): string => { + if (bytes < 1024) return `${bytes} B` + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` + return `${(bytes / 1024 / 1024).toFixed(1)} MB` + } + + const formatDuration = (microseconds: number): string => { + if (microseconds < 1000) return `${microseconds.toFixed(0)} μs` + if (microseconds < 1000000) return `${(microseconds / 1000).toFixed(1)} ms` + return `${(microseconds / 1000000).toFixed(2)} s` + } + + const resourceTypeBreakdown: CategoryBreakdown[] = useMemo(() => { + const typeMap = new Map() + + httpRequests.forEach(req => { + const type = req.resourceType || 'unknown' + if (!typeMap.has(type)) { + typeMap.set(type, []) + } + typeMap.get(type)!.push(req) + }) + + // Calculate total size and response time across all requests for percentage calculations + const totalAllSize = httpRequests.reduce((sum, req) => { + return sum + (req.contentLength || req.encodedDataLength || 0) + }, 0) + + const totalAllResponseTime = httpRequests.reduce((sum, req) => { + return sum + (req.timing.totalResponseTime || 0) + }, 0) + + return Array.from(typeMap.entries()).map(([type, requests]) => { + const totalSize = requests.reduce((sum, req) => { + return sum + (req.contentLength || req.encodedDataLength || 0) + }, 0) + + const totalResponseTime = requests.reduce((sum, req) => { + return sum + (req.timing.totalResponseTime || 0) + }, 0) + + return { + name: type, + count: requests.length, + percentage: (requests.length / httpRequests.length) * 100, + totalSize, + sizePercentage: totalAllSize > 0 ? (totalSize / totalAllSize) * 100 : 0, + totalResponseTime, + responseTimePercentage: totalAllResponseTime > 0 ? (totalResponseTime / totalAllResponseTime) * 100 : 0, + averageResponseTime: totalResponseTime / requests.length + } + }).sort((a, b) => b.count - a.count) + }, [httpRequests]) + + return ( +
+

By Resource Type

+
+ + {resourceTypeBreakdown.map(item => ( +
+ {item.name} + {item.count} +
+ {item.percentage.toFixed(1)}% +
+
+
+
+ {formatSize(item.totalSize)} +
+ {item.sizePercentage.toFixed(1)}% +
+
+
+
+ {formatDuration(item.totalResponseTime)} +
+ {item.responseTimePercentage.toFixed(1)}% +
+
+
+
+ {formatDuration(item.averageResponseTime)} +
+ ))} +
+
+ ) +} + +export default ResourceTypeBreakdown \ No newline at end of file diff --git a/src/components/RequestBreakdown/StatusCodeBreakdown.tsx b/src/components/RequestBreakdown/StatusCodeBreakdown.tsx new file mode 100644 index 0000000..eb887b3 --- /dev/null +++ b/src/components/RequestBreakdown/StatusCodeBreakdown.tsx @@ -0,0 +1,122 @@ +import React, { useMemo } from 'react' +import type { HTTPRequest } from '../httprequestviewer/types/httpRequest' +import BreakdownTableHeader from './BreakdownTableHeader' +import styles from '../RequestBreakdown.module.css' + +interface CategoryBreakdown { + name: string + count: number + percentage: number + totalSize: number + sizePercentage: number + totalResponseTime: number + responseTimePercentage: number + averageResponseTime: number +} + +interface StatusCodeBreakdownProps { + httpRequests: HTTPRequest[] +} + +const StatusCodeBreakdown: React.FC = ({ httpRequests }) => { + const formatSize = (bytes: number): string => { + if (bytes < 1024) return `${bytes} B` + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` + return `${(bytes / 1024 / 1024).toFixed(1)} MB` + } + + const formatDuration = (microseconds: number): string => { + if (microseconds < 1000) return `${microseconds.toFixed(0)} μs` + if (microseconds < 1000000) return `${(microseconds / 1000).toFixed(1)} ms` + return `${(microseconds / 1000000).toFixed(2)} s` + } + + const statusCodeBreakdown: CategoryBreakdown[] = useMemo(() => { + const statusMap = new Map() + + httpRequests.forEach(req => { + const status = req.statusCode ? Math.floor(req.statusCode / 100) + 'xx' : 'Unknown' + if (!statusMap.has(status)) { + statusMap.set(status, []) + } + statusMap.get(status)!.push(req) + }) + + // Calculate total size and response time across all requests for percentage calculations + const totalAllSize = httpRequests.reduce((sum, req) => { + return sum + (req.contentLength || req.encodedDataLength || 0) + }, 0) + + const totalAllResponseTime = httpRequests.reduce((sum, req) => { + return sum + (req.timing.totalResponseTime || 0) + }, 0) + + return Array.from(statusMap.entries()).map(([status, requests]) => { + const totalSize = requests.reduce((sum, req) => { + return sum + (req.contentLength || req.encodedDataLength || 0) + }, 0) + + const totalResponseTime = requests.reduce((sum, req) => { + return sum + (req.timing.totalResponseTime || 0) + }, 0) + + return { + name: status, + count: requests.length, + percentage: (requests.length / httpRequests.length) * 100, + totalSize, + sizePercentage: totalAllSize > 0 ? (totalSize / totalAllSize) * 100 : 0, + totalResponseTime, + responseTimePercentage: totalAllResponseTime > 0 ? (totalResponseTime / totalAllResponseTime) * 100 : 0, + averageResponseTime: totalResponseTime / requests.length + } + }).sort((a, b) => b.count - a.count) + }, [httpRequests]) + + return ( +
+

By Status Code

+
+ + {statusCodeBreakdown.map(item => ( +
+ {item.name} + {item.count} +
+ {item.percentage.toFixed(1)}% +
+
+
+
+ {formatSize(item.totalSize)} +
+ {item.sizePercentage.toFixed(1)}% +
+
+
+
+ {formatDuration(item.totalResponseTime)} +
+ {item.responseTimePercentage.toFixed(1)}% +
+
+
+
+ {formatDuration(item.averageResponseTime)} +
+ ))} +
+
+ ) +} + +export default StatusCodeBreakdown \ No newline at end of file