diff --git a/packages/bruno-app/src/components/ResponsePane/RunnerTimeline/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/RunnerTimeline/StyledWrapper.js
index 020d5bd91..4b7cb28a7 100644
--- a/packages/bruno-app/src/components/ResponsePane/RunnerTimeline/StyledWrapper.js
+++ b/packages/bruno-app/src/components/ResponsePane/RunnerTimeline/StyledWrapper.js
@@ -1,11 +1,109 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
+ .timeline-event {
+ padding: 8px 0 0 0;
+ cursor: pointer;
+ }
+
+ .timeline-event-content {
+ border-radius: 4px;
+ padding: 12px;
+ margin-top: 0.5rem;
+ }
+
+ .timeline-event-header {
+ color: ${(props) => props.theme.text};
+ }
+
+ .method-label {
+ font-weight: 600;
+ }
+
+ .status-code {
+ font-weight: 600;
+ }
+
+ .url-text {
+ color: ${(props) => props.theme.colors.text.muted};
+ font-size: 0.875rem;
+ margin-top: 0.25rem;
+ }
+
+ .timestamp {
+ color: ${(props) => props.theme.colors.text.muted};
+ font-size: 0.875rem;
+ }
+
+ .meta-info {
+ color: ${(props) => props.theme.colors.text.muted};
+ font-size: 0.875rem;
+ }
+
+ .oauth-section {
+ .oauth-header {
+ display: flex;
+ align-items: center;
+ color: ${(props) => props.theme.text};
+ font-weight: 600;
+
+ span {
+ margin-left: 0.5rem;
+ }
+ }
+ }
+
+ .tabs-switcher {
+ border-bottom: 1px solid ${(props) => props.theme.modal.input.border};
+ margin-bottom: 16px;
+
+ button {
+ position: relative;
+ padding: 8px 16px;
+ color: ${(props) => props.theme.colors.text.muted};
+
+ &.active {
+ color: ${(props) => props.theme.tabs.active.color};
+ &:after {
+ content: '';
+ position: absolute;
+ bottom: -1px;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background: ${(props) => props.theme.tabs.active.border};
+ }
+ }
+ }
+ }
+
+ .network-logs {
+ background: ${(props) => props.theme.codemirror.bg};
+ color: ${(props) => props.theme.text};
+ border-radius: 4px;
+ }
+
+ .oauth-request-item-content {
+ border-radius: 4px;
+ margin-top: 0.5rem;
+ }
+
+ .collapsible-section {
+ margin-bottom: 12px;
+
+ .section-header {
+ cursor: pointer;
+ &:hover {
+ opacity: 0.8;
+ }
+ }
+ }
+
.line {
white-space: pre-line;
word-wrap: break-word;
word-break: break-all;
- font-family: Inter, sans-serif !important;
+ font-family: ${(props) => props.theme.font || 'Inter, sans-serif'} !important;
.arrow {
opacity: 0.5;
@@ -19,6 +117,35 @@ const StyledWrapper = styled.div`
color: ${(props) => props.theme.colors.text.purple};
}
}
+
+ .request-label {
+ font-size: 0.75rem;
+ padding: 2px 6px;
+ border-radius: 3px;
+ margin-left: 8px;
+ background: ${(props) => props.theme.requestTabs.bg};
+ }
+
+ table {
+ width: 100%;
+ border-collapse: collapse;
+ font-weight: 600;
+ table-layout: fixed;
+
+ thead,
+ td {
+ border: 1px solid ${(props) => props.theme.table.border};
+ }
+
+ thead {
+ color: ${(props) => props.theme.table.thead.color};
+ font-size: 0.8125rem;
+ user-select: none;
+ }
+ td {
+ padding: 6px 10px;
+ }
+ }
`;
export default StyledWrapper;
diff --git a/packages/bruno-app/src/components/ResponsePane/RunnerTimeline/index.js b/packages/bruno-app/src/components/ResponsePane/RunnerTimeline/index.js
index 592e0641b..4fac7ed6d 100644
--- a/packages/bruno-app/src/components/ResponsePane/RunnerTimeline/index.js
+++ b/packages/bruno-app/src/components/ResponsePane/RunnerTimeline/index.js
@@ -1,14 +1,10 @@
-import React from 'react';
+import React, { useMemo } from 'react';
import forOwn from 'lodash/forOwn';
-import { safeStringifyJSON } from 'utils/common';
import StyledWrapper from './StyledWrapper';
+import TimelineItem from '../Timeline/TimelineItem';
-const RunnerTimeline = ({ request, response }) => {
+const RunnerTimeline = ({ request = {}, response = {}, item, collection }) => {
const requestHeaders = [];
- const responseHeaders = typeof response.headers === 'object' ? Object.entries(response.headers) : [];
-
- request = request || {};
- response = response || {};
forOwn(request.headers, (value, key) => {
requestHeaders.push({
@@ -17,43 +13,56 @@ const RunnerTimeline = ({ request, response }) => {
});
});
- let requestData = typeof request?.data === "string" ? request?.data : safeStringifyJSON(request?.data, true);
+ const oauth2Events = useMemo(
+ () =>
+ collection?.timeline?.filter(
+ (event) => event.type === 'oauth2' && event.itemUid === item.uid
+ ) || [],
+ [collection?.timeline, item.uid]
+ );
return (
- {'>'} {request.method} {request.url}
-
- {requestHeaders.map((h) => {
- return (
-
- {'>'} {h.name}: {h.value}
-
- );
- })}
-
- {requestData ? (
-
- {'>'} data{' '}
-
- ) : null}
- {requestData}
-
- {'<'} {response.status} - {response.statusText}
-
-
- {responseHeaders.map((h) => {
- return (
-
- {'<'} {h[0]}: {h[1]}
-
- );
- })}
-
[oauth2.0]: null} -
[{new Date(timestamp).toISOString()}]
+ {!hideTimestamp && (
+ <>
+ [{new Date(timestamp).toISOString()}]
+
+