+
{children}
@@ -33,6 +36,7 @@ export interface AddToQueryHOCProps {
fieldKey: string;
fieldValue: string;
onAddToQuery: (fieldKey: string, fieldValue: string, operator: string) => void;
+ fontSize: FontSize;
children: ReactNode;
}
diff --git a/frontend/src/components/Logs/CopyClipboardHOC.tsx b/frontend/src/components/Logs/CopyClipboardHOC.tsx
index a12208bf77..65cb6fc854 100644
--- a/frontend/src/components/Logs/CopyClipboardHOC.tsx
+++ b/frontend/src/components/Logs/CopyClipboardHOC.tsx
@@ -4,6 +4,7 @@ import { ReactNode, useCallback, useEffect } from 'react';
import { useCopyToClipboard } from 'react-use';
function CopyClipboardHOC({
+ entityKey,
textToCopy,
children,
}: CopyClipboardHOCProps): JSX.Element {
@@ -11,11 +12,15 @@ function CopyClipboardHOC({
const { notifications } = useNotifications();
useEffect(() => {
if (value.value) {
+ const key = entityKey || '';
+
+ const notificationMessage = `${key} copied to clipboard`;
+
notifications.success({
- message: 'Copied to clipboard',
+ message: notificationMessage,
});
}
- }, [value, notifications]);
+ }, [value, notifications, entityKey]);
const onClick = useCallback((): void => {
setCopy(textToCopy);
@@ -34,6 +39,7 @@ function CopyClipboardHOC({
}
interface CopyClipboardHOCProps {
+ entityKey: string | undefined;
textToCopy: string;
children: ReactNode;
}
diff --git a/frontend/src/components/Logs/ListLogView/ListLogView.styles.scss b/frontend/src/components/Logs/ListLogView/ListLogView.styles.scss
index 3caf6a3282..21dcf171ce 100644
--- a/frontend/src/components/Logs/ListLogView/ListLogView.styles.scss
+++ b/frontend/src/components/Logs/ListLogView/ListLogView.styles.scss
@@ -6,6 +6,21 @@
font-weight: 400;
line-height: 18px; /* 128.571% */
letter-spacing: -0.07px;
+
+ &.small {
+ font-size: 11px;
+ line-height: 16px;
+ }
+
+ &.medium {
+ font-size: 13px;
+ line-height: 20px;
+ }
+
+ &.large {
+ font-size: 14px;
+ line-height: 24px;
+ }
}
.log-value {
color: var(--text-vanilla-400, #c0c1c3);
@@ -14,6 +29,21 @@
font-weight: 400;
line-height: 18px; /* 128.571% */
letter-spacing: -0.07px;
+
+ &.small {
+ font-size: 11px;
+ line-height: 16px;
+ }
+
+ &.medium {
+ font-size: 13px;
+ line-height: 20px;
+ }
+
+ &.large {
+ font-size: 14px;
+ line-height: 24px;
+ }
}
.log-line {
display: flex;
@@ -40,6 +70,20 @@
font-weight: 400;
line-height: 18px; /* 128.571% */
letter-spacing: -0.07px;
+ &.small {
+ font-size: 11px;
+ line-height: 16px;
+ }
+
+ &.medium {
+ font-size: 13px;
+ line-height: 20px;
+ }
+
+ &.large {
+ font-size: 14px;
+ line-height: 24px;
+ }
}
.selected-log-value {
@@ -52,12 +96,37 @@
line-height: 18px;
letter-spacing: -0.07px;
font-size: 14px;
+ &.small {
+ font-size: 11px;
+ line-height: 16px;
+ }
+
+ &.medium {
+ font-size: 13px;
+ line-height: 20px;
+ }
+
+ &.large {
+ font-size: 14px;
+ line-height: 24px;
+ }
}
.selected-log-kv {
min-height: 24px;
display: flex;
align-items: center;
+ &.small {
+ min-height: 16px;
+ }
+
+ &.medium {
+ min-height: 20px;
+ }
+
+ &.large {
+ min-height: 24px;
+ }
}
}
diff --git a/frontend/src/components/Logs/ListLogView/index.tsx b/frontend/src/components/Logs/ListLogView/index.tsx
index fa8a2fb608..ed2627552d 100644
--- a/frontend/src/components/Logs/ListLogView/index.tsx
+++ b/frontend/src/components/Logs/ListLogView/index.tsx
@@ -3,8 +3,11 @@ import './ListLogView.styles.scss';
import { blue } from '@ant-design/colors';
import Convert from 'ansi-to-html';
import { Typography } from 'antd';
+import cx from 'classnames';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES } from 'components/LogDetail/constants';
+import { unescapeString } from 'container/LogDetailedView/utils';
+import { FontSize } from 'container/OptionsMenu/types';
import dayjs from 'dayjs';
import dompurify from 'dompurify';
import { useActiveLog } from 'hooks/logs/useActiveLog';
@@ -39,6 +42,7 @@ interface LogFieldProps {
fieldKey: string;
fieldValue: string;
linesPerRow?: number;
+ fontSize: FontSize;
}
type LogSelectedFieldProps = Omit
&
@@ -48,11 +52,12 @@ function LogGeneralField({
fieldKey,
fieldValue,
linesPerRow = 1,
+ fontSize,
}: LogFieldProps): JSX.Element {
const html = useMemo(
() => ({
__html: convert.toHtml(
- dompurify.sanitize(fieldValue, {
+ dompurify.sanitize(unescapeString(fieldValue), {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),
@@ -62,12 +67,12 @@ function LogGeneralField({
return (
-
+
{`${fieldKey} : `}
1 ? linesPerRow : undefined}
/>
@@ -78,6 +83,7 @@ function LogSelectedField({
fieldKey = '',
fieldValue = '',
onAddToQuery,
+ fontSize,
}: LogSelectedFieldProps): JSX.Element {
return (
@@ -85,16 +91,22 @@ function LogSelectedField({
fieldKey={fieldKey}
fieldValue={fieldValue}
onAddToQuery={onAddToQuery}
+ fontSize={fontSize}
>
-
+
{fieldKey}
-
- {': '}
- {fieldValue || "''"}
+
+ {': '}
+
+ {fieldValue || "''"}
+
);
@@ -107,6 +119,7 @@ type ListLogViewProps = {
onAddToQuery: AddToQueryHOCProps['onAddToQuery'];
activeLog?: ILog | null;
linesPerRow: number;
+ fontSize: FontSize;
};
function ListLogView({
@@ -116,6 +129,7 @@ function ListLogView({
onAddToQuery,
activeLog,
linesPerRow,
+ fontSize,
}: ListLogViewProps): JSX.Element {
const flattenLogData = useMemo(() => FlatLogData(logData), [logData]);
@@ -128,6 +142,7 @@ function ListLogView({
onAddToQuery: handleAddToQuery,
onSetActiveLog: handleSetActiveContextLog,
onClearActiveLog: handleClearActiveContextLog,
+ onGroupByAttribute,
} = useActiveLog();
const isDarkMode = useIsDarkMode();
@@ -185,6 +200,7 @@ function ListLogView({
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onClick={handleDetailedView}
+ fontSize={fontSize}
>
-
+
{flattenLogData.stream && (
-
+
)}
-
+
{updatedSelecedFields.map((field) =>
isValidLogField(flattenLogData[field.name] as never) ? (
@@ -212,6 +238,7 @@ function ListLogView({
fieldKey={field.name}
fieldValue={flattenLogData[field.name] as never}
onAddToQuery={onAddToQuery}
+ fontSize={fontSize}
/>
) : null,
)}
@@ -232,6 +259,7 @@ function ListLogView({
onAddToQuery={handleAddToQuery}
selectedTab={VIEW_TYPES.CONTEXT}
onClose={handlerClearActiveContextLog}
+ onGroupByAttribute={onGroupByAttribute}
/>
)}
>
diff --git a/frontend/src/components/Logs/ListLogView/styles.ts b/frontend/src/components/Logs/ListLogView/styles.ts
index 52cc2b20d4..d2a6342c77 100644
--- a/frontend/src/components/Logs/ListLogView/styles.ts
+++ b/frontend/src/components/Logs/ListLogView/styles.ts
@@ -1,21 +1,46 @@
+/* eslint-disable no-nested-ternary */
import { Color } from '@signozhq/design-tokens';
import { Card, Typography } from 'antd';
+import { FontSize } from 'container/OptionsMenu/types';
import styled from 'styled-components';
interface LogTextProps {
linesPerRow?: number;
}
+interface LogContainerProps {
+ fontSize: FontSize;
+}
+
export const Container = styled(Card)<{
$isActiveLog: boolean;
$isDarkMode: boolean;
+ fontSize: FontSize;
}>`
width: 100% !important;
margin-bottom: 0.3rem;
+
+ ${({ fontSize }): string =>
+ fontSize === FontSize.SMALL
+ ? `margin-bottom:0.1rem;`
+ : fontSize === FontSize.MEDIUM
+ ? `margin-bottom: 0.2rem;`
+ : fontSize === FontSize.LARGE
+ ? `margin-bottom:0.3rem;`
+ : ``}
cursor: pointer;
.ant-card-body {
padding: 0.3rem 0.6rem;
+ ${({ fontSize }): string =>
+ fontSize === FontSize.SMALL
+ ? `padding:0.1rem 0.6rem;`
+ : fontSize === FontSize.MEDIUM
+ ? `padding: 0.2rem 0.6rem;`
+ : fontSize === FontSize.LARGE
+ ? `padding:0.3rem 0.6rem;`
+ : ``}
+
${({ $isActiveLog, $isDarkMode }): string =>
$isActiveLog
? `background-color: ${
@@ -38,11 +63,17 @@ export const TextContainer = styled.div`
width: 100%;
`;
-export const LogContainer = styled.div`
+export const LogContainer = styled.div`
margin-left: 0.5rem;
display: flex;
flex-direction: column;
gap: 6px;
+ ${({ fontSize }): string =>
+ fontSize === FontSize.SMALL
+ ? `gap: 2px;`
+ : fontSize === FontSize.MEDIUM
+ ? ` gap:4px;`
+ : `gap:6px;`}
`;
export const LogText = styled.div`
diff --git a/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.styles.scss b/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.styles.scss
index a00c7f6761..61870abc71 100644
--- a/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.styles.scss
+++ b/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.styles.scss
@@ -9,11 +9,24 @@
border-radius: 50px;
background-color: transparent;
+ &.small {
+ min-height: 16px;
+ }
+
+ &.medium {
+ min-height: 20px;
+ }
+
+ &.large {
+ min-height: 24px;
+ }
+
&.INFO {
background-color: var(--bg-slate-400);
}
- &.WARNING, &.WARN {
+ &.WARNING,
+ &.WARN {
background-color: var(--bg-amber-500);
}
diff --git a/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.test.tsx b/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.test.tsx
index d924c27426..06cc9d3ec4 100644
--- a/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.test.tsx
+++ b/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.test.tsx
@@ -1,10 +1,13 @@
import { render } from '@testing-library/react';
+import { FontSize } from 'container/OptionsMenu/types';
import LogStateIndicator from './LogStateIndicator';
describe('LogStateIndicator', () => {
it('renders correctly with default props', () => {
- const { container } = render();
+ const { container } = render(
+ ,
+ );
const indicator = container.firstChild as HTMLElement;
expect(indicator.classList.contains('log-state-indicator')).toBe(true);
expect(indicator.classList.contains('isActive')).toBe(false);
@@ -15,28 +18,30 @@ describe('LogStateIndicator', () => {
});
it('renders correctly when isActive is true', () => {
- const { container } = render();
+ const { container } = render(
+ ,
+ );
const indicator = container.firstChild as HTMLElement;
expect(indicator.classList.contains('isActive')).toBe(true);
});
it('renders correctly with different types', () => {
const { container: containerInfo } = render(
- ,
+ ,
);
expect(containerInfo.querySelector('.line')?.classList.contains('INFO')).toBe(
true,
);
const { container: containerWarning } = render(
- ,
+ ,
);
expect(
containerWarning.querySelector('.line')?.classList.contains('WARNING'),
).toBe(true);
const { container: containerError } = render(
- ,
+ ,
);
expect(
containerError.querySelector('.line')?.classList.contains('ERROR'),
diff --git a/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.tsx b/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.tsx
index ebad7bd116..b9afa5b7a2 100644
--- a/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.tsx
+++ b/frontend/src/components/Logs/LogStateIndicator/LogStateIndicator.tsx
@@ -1,6 +1,7 @@
import './LogStateIndicator.styles.scss';
import cx from 'classnames';
+import { FontSize } from 'container/OptionsMenu/types';
export const SEVERITY_TEXT_TYPE = {
TRACE: 'TRACE',
@@ -44,13 +45,15 @@ export const LogType = {
function LogStateIndicator({
type,
isActive,
+ fontSize,
}: {
type: string;
+ fontSize: FontSize;
isActive?: boolean;
}): JSX.Element {
return (
);
}
diff --git a/frontend/src/components/Logs/RawLogView/index.tsx b/frontend/src/components/Logs/RawLogView/index.tsx
index d1ae19fe99..2cda9c7247 100644
--- a/frontend/src/components/Logs/RawLogView/index.tsx
+++ b/frontend/src/components/Logs/RawLogView/index.tsx
@@ -4,6 +4,7 @@ import Convert from 'ansi-to-html';
import { DrawerProps } from 'antd';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES, VIEWS } from 'components/LogDetail/constants';
+import { unescapeString } from 'container/LogDetailedView/utils';
import LogsExplorerContext from 'container/LogsExplorerContext';
import dayjs from 'dayjs';
import dompurify from 'dompurify';
@@ -39,6 +40,7 @@ function RawLogView({
linesPerRow,
isTextOverflowEllipsisDisabled,
selectedFields = [],
+ fontSize,
}: RawLogViewProps): JSX.Element {
const { isHighlighted, isLogsExplorerPage, onLogCopy } = useCopyLogLink(
data.id,
@@ -54,6 +56,7 @@ function RawLogView({
onSetActiveLog,
onClearActiveLog,
onAddToQuery,
+ onGroupByAttribute,
} = useActiveLog();
const [hasActionButtons, setHasActionButtons] = useState(false);
@@ -143,7 +146,9 @@ function RawLogView({
const html = useMemo(
() => ({
__html: convert.toHtml(
- dompurify.sanitize(text, { FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS] }),
+ dompurify.sanitize(unescapeString(text), {
+ FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
+ }),
),
}),
[text],
@@ -160,6 +165,7 @@ function RawLogView({
$isActiveLog={isActiveLog}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
+ fontSize={fontSize}
>
@@ -199,6 +207,7 @@ function RawLogView({
onClose={handleCloseLogDetail}
onAddToQuery={onAddToQuery}
onClickActionItem={onAddToQuery}
+ onGroupByAttribute={onGroupByAttribute}
/>
)}
diff --git a/frontend/src/components/Logs/RawLogView/styles.ts b/frontend/src/components/Logs/RawLogView/styles.ts
index d86de435c2..d464f35910 100644
--- a/frontend/src/components/Logs/RawLogView/styles.ts
+++ b/frontend/src/components/Logs/RawLogView/styles.ts
@@ -1,6 +1,8 @@
+/* eslint-disable no-nested-ternary */
import { blue } from '@ant-design/colors';
import { Color } from '@signozhq/design-tokens';
import { Col, Row, Space } from 'antd';
+import { FontSize } from 'container/OptionsMenu/types';
import styled from 'styled-components';
import { getActiveLogBackground, getDefaultLogBackground } from 'utils/logs';
@@ -11,6 +13,7 @@ export const RawLogViewContainer = styled(Row)<{
$isReadOnly?: boolean;
$isActiveLog?: boolean;
$isHightlightedLog: boolean;
+ fontSize: FontSize;
}>`
position: relative;
width: 100%;
@@ -22,6 +25,13 @@ export const RawLogViewContainer = styled(Row)<{
.log-state-indicator {
margin: 4px 0;
+
+ ${({ fontSize }): string =>
+ fontSize === FontSize.SMALL
+ ? `margin: 1px 0;`
+ : fontSize === FontSize.MEDIUM
+ ? `margin: 1px 0;`
+ : `margin: 2px 0;`}
}
${({ $isActiveLog }): string => getActiveLogBackground($isActiveLog)}
@@ -50,8 +60,8 @@ export const RawLogContent = styled.div`
margin-bottom: 0;
font-family: 'SF Mono', monospace;
font-family: 'Geist Mono';
- font-size: 13px;
- font-weight: 400;
+ letter-spacing: -0.07px;
+ padding: 4px;
text-align: left;
color: ${({ $isDarkMode }): string =>
$isDarkMode ? Color.BG_VANILLA_400 : Color.BG_INK_400};
@@ -66,9 +76,15 @@ export const RawLogContent = styled.div`
line-clamp: ${linesPerRow};
-webkit-box-orient: vertical;`};
+ font-size: 13px;
+ font-weight: 400;
line-height: 24px;
- letter-spacing: -0.07px;
- padding: 4px;
+ ${({ fontSize }): string =>
+ fontSize === FontSize.SMALL
+ ? `font-size:11px; line-height:16px; padding:1px;`
+ : fontSize === FontSize.MEDIUM
+ ? `font-size:13px; line-height:20px; padding:1px;`
+ : `font-size:14px; line-height:24px; padding:2px;`}
cursor: ${({ $isActiveLog, $isReadOnly }): string =>
$isActiveLog || $isReadOnly ? 'initial' : 'pointer'};
diff --git a/frontend/src/components/Logs/RawLogView/types.ts b/frontend/src/components/Logs/RawLogView/types.ts
index a9c85c2ad6..ed73725dcc 100644
--- a/frontend/src/components/Logs/RawLogView/types.ts
+++ b/frontend/src/components/Logs/RawLogView/types.ts
@@ -1,3 +1,4 @@
+import { FontSize } from 'container/OptionsMenu/types';
import { IField } from 'types/api/logs/fields';
import { ILog } from 'types/api/logs/log';
@@ -7,11 +8,13 @@ export interface RawLogViewProps {
isTextOverflowEllipsisDisabled?: boolean;
data: ILog;
linesPerRow: number;
+ fontSize: FontSize;
selectedFields?: IField[];
}
export interface RawLogContentProps {
linesPerRow: number;
+ fontSize: FontSize;
$isReadOnly?: boolean;
$isActiveLog?: boolean;
$isDarkMode?: boolean;
diff --git a/frontend/src/components/Logs/TableView/styles.ts b/frontend/src/components/Logs/TableView/styles.ts
index 9213021971..a79db04a76 100644
--- a/frontend/src/components/Logs/TableView/styles.ts
+++ b/frontend/src/components/Logs/TableView/styles.ts
@@ -1,7 +1,10 @@
+/* eslint-disable no-nested-ternary */
+import { FontSize } from 'container/OptionsMenu/types';
import styled from 'styled-components';
interface TableBodyContentProps {
linesPerRow: number;
+ fontSize: FontSize;
isDarkMode?: boolean;
}
@@ -20,4 +23,10 @@ export const TableBodyContent = styled.div`
-webkit-line-clamp: ${(props): number => props.linesPerRow};
line-clamp: ${(props): number => props.linesPerRow};
-webkit-box-orient: vertical;
+ ${({ fontSize }): string =>
+ fontSize === FontSize.SMALL
+ ? `font-size:11px; line-height:16px;`
+ : fontSize === FontSize.MEDIUM
+ ? `font-size:13px; line-height:20px;`
+ : `font-size:14px; line-height:24px;`}
`;
diff --git a/frontend/src/components/Logs/TableView/types.ts b/frontend/src/components/Logs/TableView/types.ts
index 36a796ac0f..b2d3670dd8 100644
--- a/frontend/src/components/Logs/TableView/types.ts
+++ b/frontend/src/components/Logs/TableView/types.ts
@@ -1,4 +1,5 @@
import { ColumnsType, ColumnType } from 'antd/es/table';
+import { FontSize } from 'container/OptionsMenu/types';
import { IField } from 'types/api/logs/fields';
import { ILog } from 'types/api/logs/log';
@@ -10,6 +11,7 @@ export type LogsTableViewProps = {
logs: ILog[];
fields: IField[];
linesPerRow: number;
+ fontSize: FontSize;
onClickExpand?: (log: ILog) => void;
};
diff --git a/frontend/src/components/Logs/TableView/useTableView.styles.scss b/frontend/src/components/Logs/TableView/useTableView.styles.scss
index 3723ecc705..9592d0ae12 100644
--- a/frontend/src/components/Logs/TableView/useTableView.styles.scss
+++ b/frontend/src/components/Logs/TableView/useTableView.styles.scss
@@ -5,6 +5,21 @@
font-weight: 400;
line-height: 18px; /* 128.571% */
letter-spacing: -0.07px;
+
+ &.small {
+ font-size: 11px;
+ line-height: 16px;
+ }
+
+ &.medium {
+ font-size: 13px;
+ line-height: 20px;
+ }
+
+ &.large {
+ font-size: 14px;
+ line-height: 24px;
+ }
}
.table-timestamp {
@@ -25,3 +40,21 @@
color: var(--bg-slate-400);
}
}
+
+.paragraph {
+ padding: 0px !important;
+ &.small {
+ font-size: 11px !important;
+ line-height: 16px !important;
+ }
+
+ &.medium {
+ font-size: 13px !important;
+ line-height: 20px !important;
+ }
+
+ &.large {
+ font-size: 14px !important;
+ line-height: 24px !important;
+ }
+}
diff --git a/frontend/src/components/Logs/TableView/useTableView.tsx b/frontend/src/components/Logs/TableView/useTableView.tsx
index fd37132110..43b4ba2628 100644
--- a/frontend/src/components/Logs/TableView/useTableView.tsx
+++ b/frontend/src/components/Logs/TableView/useTableView.tsx
@@ -3,6 +3,8 @@ import './useTableView.styles.scss';
import Convert from 'ansi-to-html';
import { Typography } from 'antd';
import { ColumnsType } from 'antd/es/table';
+import cx from 'classnames';
+import { unescapeString } from 'container/LogDetailedView/utils';
import dayjs from 'dayjs';
import dompurify from 'dompurify';
import { useIsDarkMode } from 'hooks/useDarkMode';
@@ -31,6 +33,7 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
logs,
fields,
linesPerRow,
+ fontSize,
appendTo = 'center',
activeContextLog,
activeLog,
@@ -57,7 +60,10 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
: getDefaultCellStyle(isDarkMode),
},
children: (
-
+
{field}
),
@@ -87,8 +93,9 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
isActive={
activeLog?.id === item.id || activeContextLog?.id === item.id
}
+ fontSize={fontSize}
/>
-
+
{date}
@@ -109,11 +116,12 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
@@ -130,6 +138,7 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
linesPerRow,
activeLog?.id,
activeContextLog?.id,
+ fontSize,
]);
return { columns, dataSource: flattenLogData };
diff --git a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss
index af325a2d25..070d440781 100644
--- a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss
+++ b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss
@@ -17,17 +17,126 @@
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(20px);
+ .font-size-dropdown {
+ display: flex;
+ flex-direction: column;
+
+ .back-btn {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 12px;
+ border: none !important;
+ box-shadow: none !important;
+
+ .icon {
+ flex-shrink: 0;
+ }
+ .text {
+ color: var(--bg-vanilla-400);
+ font-family: Inter;
+ font-size: 13px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 20px; /* 142.857% */
+ letter-spacing: 0.14px;
+ }
+ }
+
+ .back-btn:hover {
+ background-color: unset !important;
+ }
+
+ .content {
+ display: flex;
+ flex-direction: column;
+ .option-btn {
+ display: flex;
+ align-items: center;
+ padding: 12px;
+ border: none !important;
+ box-shadow: none !important;
+ justify-content: space-between;
+
+ .icon {
+ flex-shrink: 0;
+ }
+ .text {
+ color: var(--bg-vanilla-400);
+ font-family: Inter;
+ font-size: 13px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal; /* 142.857% */
+ letter-spacing: 0.14px;
+ text-transform: capitalize;
+ }
+
+ .text:hover {
+ color: var(--bg-vanilla-300);
+ }
+ }
+
+ .option-btn:hover {
+ background-color: unset !important;
+ }
+ }
+ }
+
+ .font-size-container {
+ padding: 12px;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+
+ .title {
+ color: var(--bg-slate-50);
+ font-family: Inter;
+ font-size: 11px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: 18px; /* 163.636% */
+ letter-spacing: 0.88px;
+ text-transform: uppercase;
+ }
+
+ .value {
+ display: flex;
+ height: 20px;
+ padding: 4px 0px;
+ justify-content: space-between;
+ align-items: center;
+ border: none !important;
+ .font-value {
+ color: var(--bg-vanilla-400);
+ font-family: Inter;
+ font-size: 13px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: 0.14px;
+ text-transform: capitalize;
+ }
+ .icon {
+ }
+ }
+
+ .value:hover {
+ background-color: unset !important;
+ }
+ }
+
.menu-container {
padding: 12px;
.title {
font-family: Inter;
font-size: 11px;
- font-weight: 600;
+ font-weight: 500;
line-height: 18px;
letter-spacing: 0.08em;
text-align: left;
- color: #52575c;
+ color: var(--bg-slate-50);
}
.menu-items {
@@ -65,11 +174,11 @@
padding: 12px;
.title {
- color: #52575c;
+ color: var(--bg-slate-50);
font-family: Inter;
font-size: 11px;
font-style: normal;
- font-weight: 600;
+ font-weight: 500;
line-height: 18px; /* 163.636% */
letter-spacing: 0.88px;
text-transform: uppercase;
@@ -149,11 +258,11 @@
}
.title {
- color: #52575c;
+ color: var(--bg-slate-50);
font-family: Inter;
font-size: 11px;
font-style: normal;
- font-weight: 600;
+ font-weight: 500;
line-height: 18px; /* 163.636% */
letter-spacing: 0.88px;
text-transform: uppercase;
@@ -299,6 +408,38 @@
box-shadow: 4px 10px 16px 2px rgba(255, 255, 255, 0.2);
+ .font-size-dropdown {
+ .back-btn {
+ .text {
+ color: var(--bg-ink-400);
+ }
+ }
+
+ .content {
+ .option-btn {
+ .text {
+ color: var(--bg-ink-400);
+ }
+
+ .text:hover {
+ color: var(--bg-ink-300);
+ }
+ }
+ }
+ }
+
+ .font-size-container {
+ .title {
+ color: var(--bg-ink-100);
+ }
+
+ .value {
+ .font-value {
+ color: var(--bg-ink-400);
+ }
+ }
+ }
+
.horizontal-line {
background: var(--bg-vanilla-300);
}
diff --git a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx
index 3a42e9a0b0..527c77c6af 100644
--- a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx
+++ b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx
@@ -3,12 +3,12 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
import './LogsFormatOptionsMenu.styles.scss';
-import { Divider, Input, InputNumber, Tooltip } from 'antd';
+import { Button, Divider, Input, InputNumber, Tooltip, Typography } from 'antd';
import cx from 'classnames';
import { LogViewMode } from 'container/LogsTable';
-import { OptionsMenuConfig } from 'container/OptionsMenu/types';
+import { FontSize, OptionsMenuConfig } from 'container/OptionsMenu/types';
import useDebouncedFn from 'hooks/useDebouncedFunction';
-import { Check, Minus, Plus, X } from 'lucide-react';
+import { Check, ChevronLeft, ChevronRight, Minus, Plus, X } from 'lucide-react';
import { useCallback, useEffect, useState } from 'react';
interface LogsFormatOptionsMenuProps {
@@ -24,10 +24,16 @@ export default function LogsFormatOptionsMenu({
selectedOptionFormat,
config,
}: LogsFormatOptionsMenuProps): JSX.Element {
- const { maxLines, format, addColumn } = config;
+ const { maxLines, format, addColumn, fontSize } = config;
const [selectedItem, setSelectedItem] = useState(selectedOptionFormat);
const maxLinesNumber = (maxLines?.value as number) || 1;
const [maxLinesPerRow, setMaxLinesPerRow] = useState
(maxLinesNumber);
+ const [fontSizeValue, setFontSizeValue] = useState(
+ fontSize?.value || FontSize.SMALL,
+ );
+ const [isFontSizeOptionsOpen, setIsFontSizeOptionsOpen] = useState(
+ false,
+ );
const [addNewColumn, setAddNewColumn] = useState(false);
@@ -88,6 +94,12 @@ export default function LogsFormatOptionsMenu({
}
}, [maxLinesPerRow]);
+ useEffect(() => {
+ if (fontSizeValue && config && config.fontSize?.onChange) {
+ config.fontSize.onChange(fontSizeValue);
+ }
+ }, [fontSizeValue]);
+
return (
-
-
{title}
-
-
- {items.map(
- (item: any): JSX.Element => (
-
handleMenuItemClick(item.key)}
- >
-
- {item.label}
-
- {selectedItem === item.key && }
-
-
- ),
- )}
+ {isFontSizeOptionsOpen ? (
+
+
+
+
+
+
+
+
-
-
- {selectedItem && (
+ ) : (
<>
- <>
-
-
-
max lines per row
-
-
-
-
-
-
- >
+
+
Font Size
+
+
+
+
+
{title}
-
- {!addNewColumn &&
}
+
+ {items.map(
+ (item: any): JSX.Element => (
+
handleMenuItemClick(item.key)}
+ >
+
+ {item.label}
- {addNewColumn && (
-
-
- {' '}
- columns
- {' '}
-
-
-
-
- )}
-
-
- {!addNewColumn && (
-
- )}
-
-
- {addColumn?.value?.map(({ key, id }) => (
-
-
-
- {key}
-
+ {selectedItem === item.key && }
-
addColumn.onRemove(id as string)}
- />
- ))}
-
-
- {addColumn?.isFetching && (
-
Loading ...
- )}
-
- {addNewColumn &&
- addColumn &&
- addColumn.value.length > 0 &&
- addColumn.options &&
- addColumn?.options?.length > 0 && (
-
- )}
-
- {addNewColumn && (
-
- {addColumn?.options?.map(({ label, value }) => (
-
{
- eve.stopPropagation();
-
- if (addColumn && addColumn?.onSelect) {
- addColumn?.onSelect(value, { label, disabled: false });
- }
- }}
- >
-
-
- {label}
-
-
-
- ))}
-
+ ),
)}
+
+ {selectedItem && (
+ <>
+ <>
+
+
+
max lines per row
+
+
+
+
+
+
+ >
+
+
+ {!addNewColumn &&
}
+
+ {addNewColumn && (
+
+
+ {' '}
+ columns
+ {' '}
+
+
+
+
+ )}
+
+
+ {!addNewColumn && (
+
+ )}
+
+
+ {addColumn?.value?.map(({ key, id }) => (
+
+
+
+ {key}
+
+
+
addColumn.onRemove(id as string)}
+ />
+
+ ))}
+
+
+ {addColumn?.isFetching && (
+
Loading ...
+ )}
+
+ {addNewColumn &&
+ addColumn &&
+ addColumn.value.length > 0 &&
+ addColumn.options &&
+ addColumn?.options?.length > 0 && (
+
+ )}
+
+ {addNewColumn && (
+
+ {addColumn?.options?.map(({ label, value }) => (
+
{
+ eve.stopPropagation();
+
+ if (addColumn && addColumn?.onSelect) {
+ addColumn?.onSelect(value, { label, disabled: false });
+ }
+ }}
+ >
+
+
+ {label}
+
+
+
+ ))}
+
+ )}
+
+
+ >
+ )}
>
)}
diff --git a/frontend/src/constants/env.ts b/frontend/src/constants/env.ts
index 2c5230dfcc..cf75739eff 100644
--- a/frontend/src/constants/env.ts
+++ b/frontend/src/constants/env.ts
@@ -3,4 +3,5 @@ export const ENVIRONMENT = {
process?.env?.FRONTEND_API_ENDPOINT ||
process?.env?.GITPOD_WORKSPACE_URL?.replace('://', '://8080-') ||
'',
+ wsURL: process?.env?.WEBSOCKET_API_ENDPOINT || '',
};
diff --git a/frontend/src/constants/query.ts b/frontend/src/constants/query.ts
index 9b731ca089..3ee0a39634 100644
--- a/frontend/src/constants/query.ts
+++ b/frontend/src/constants/query.ts
@@ -32,4 +32,8 @@ export enum QueryParams {
relativeTime = 'relativeTime',
alertType = 'alertType',
ruleId = 'ruleId',
+ consumerGrp = 'consumerGrp',
+ topic = 'topic',
+ partition = 'partition',
+ selectedTimelineQuery = 'selectedTimelineQuery',
}
diff --git a/frontend/src/constants/queryBuilder.ts b/frontend/src/constants/queryBuilder.ts
index 7b7b464b3e..5fe7112796 100644
--- a/frontend/src/constants/queryBuilder.ts
+++ b/frontend/src/constants/queryBuilder.ts
@@ -52,7 +52,7 @@ export const selectValueDivider = '__';
export const baseAutoCompleteIdKeysOrder: (keyof Omit<
BaseAutocompleteData,
- 'id' | 'isJSON'
+ 'id' | 'isJSON' | 'isIndexed'
>)[] = ['key', 'dataType', 'type', 'isColumn'];
export const autocompleteType: Record
= {
@@ -71,6 +71,7 @@ export const alphabet: string[] = alpha.map((str) => String.fromCharCode(str));
export enum QueryBuilderKeys {
GET_AGGREGATE_ATTRIBUTE = 'GET_AGGREGATE_ATTRIBUTE',
GET_AGGREGATE_KEYS = 'GET_AGGREGATE_KEYS',
+ GET_ATTRIBUTE_SUGGESTIONS = 'GET_ATTRIBUTE_SUGGESTIONS',
}
export const mapOfOperators = {
diff --git a/frontend/src/constants/reactQueryKeys.ts b/frontend/src/constants/reactQueryKeys.ts
index 63fc205d81..52ae235ef6 100644
--- a/frontend/src/constants/reactQueryKeys.ts
+++ b/frontend/src/constants/reactQueryKeys.ts
@@ -8,4 +8,5 @@ export const REACT_QUERY_KEY = {
GET_FEATURES_FLAGS: 'GET_FEATURES_FLAGS',
DELETE_DASHBOARD: 'DELETE_DASHBOARD',
LOGS_PIPELINE_PREVIEW: 'LOGS_PIPELINE_PREVIEW',
+ GET_CONSUMER_LAG_DETAILS: 'GET_CONSUMER_LAG_DETAILS',
};
diff --git a/frontend/src/constants/routes.ts b/frontend/src/constants/routes.ts
index ef73184a86..8f76cd0386 100644
--- a/frontend/src/constants/routes.ts
+++ b/frontend/src/constants/routes.ts
@@ -54,6 +54,8 @@ const ROUTES = {
WORKSPACE_LOCKED: '/workspace-locked',
SHORTCUTS: '/shortcuts',
INTEGRATIONS: '/integrations',
+ MESSAGING_QUEUES: '/messaging-queues',
+ MESSAGING_QUEUES_DETAIL: '/messaging-queues/detail',
} as const;
export default ROUTES;
diff --git a/frontend/src/constants/shortcuts/globalShortcuts.ts b/frontend/src/constants/shortcuts/globalShortcuts.ts
index 81420fc830..4ab7752fac 100644
--- a/frontend/src/constants/shortcuts/globalShortcuts.ts
+++ b/frontend/src/constants/shortcuts/globalShortcuts.ts
@@ -9,6 +9,7 @@ export const GlobalShortcuts = {
NavigateToDashboards: 'd+shift',
NavigateToAlerts: 'a+shift',
NavigateToExceptions: 'e+shift',
+ NavigateToMessagingQueues: 'm+shift',
};
export const GlobalShortcutsName = {
@@ -19,6 +20,7 @@ export const GlobalShortcutsName = {
NavigateToDashboards: 'shift+d',
NavigateToAlerts: 'shift+a',
NavigateToExceptions: 'shift+e',
+ NavigateToMessagingQueues: 'shift+m',
};
export const GlobalShortcutsDescription = {
@@ -29,4 +31,5 @@ export const GlobalShortcutsDescription = {
NavigateToDashboards: 'Navigate to dashboards page',
NavigateToAlerts: 'Navigate to alerts page',
NavigateToExceptions: 'Navigate to Exceptions page',
+ NavigateToMessagingQueues: 'Navigate to Messaging Queues page',
};
diff --git a/frontend/src/constants/shortcuts/logsExplorerShortcuts.ts b/frontend/src/constants/shortcuts/logsExplorerShortcuts.ts
index 33c2b4061f..68331a4c2d 100644
--- a/frontend/src/constants/shortcuts/logsExplorerShortcuts.ts
+++ b/frontend/src/constants/shortcuts/logsExplorerShortcuts.ts
@@ -4,6 +4,7 @@ const userOS = getUserOperatingSystem();
export const LogsExplorerShortcuts = {
StageAndRunQuery: 'enter+meta',
FocusTheSearchBar: 's',
+ ShowAllFilters: '/+meta',
};
export const LogsExplorerShortcutsName = {
@@ -11,9 +12,11 @@ export const LogsExplorerShortcutsName = {
userOS === UserOperatingSystem.MACOS ? 'cmd' : 'ctrl'
}+enter`,
FocusTheSearchBar: 's',
+ ShowAllFilters: `${userOS === UserOperatingSystem.MACOS ? 'cmd' : 'ctrl'}+/`,
};
export const LogsExplorerShortcutsDescription = {
StageAndRunQuery: 'Stage and Run the current query',
FocusTheSearchBar: 'Shift the focus to the last query filter bar',
+ ShowAllFilters: 'Toggle all filters in the filters dropdown',
};
diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx
index 0f267976d2..e821e67104 100644
--- a/frontend/src/container/AppLayout/index.tsx
+++ b/frontend/src/container/AppLayout/index.tsx
@@ -47,6 +47,7 @@ import {
UPDATE_LATEST_VERSION_ERROR,
} from 'types/actions/app';
import AppReducer from 'types/reducer/app';
+import { isCloudUser } from 'utils/app';
import { getFormattedDate, getRemainingDays } from 'utils/timeUtils';
import { ChildrenContainer, Layout, LayoutContent } from './styles';
@@ -71,7 +72,14 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
const isPremiumChatSupportEnabled =
useFeatureFlags(FeatureKeys.PREMIUM_SUPPORT)?.active || false;
+ const isChatSupportEnabled =
+ useFeatureFlags(FeatureKeys.CHAT_SUPPORT)?.active || false;
+
+ const isCloudUserVal = isCloudUser();
+
const showAddCreditCardModal =
+ isChatSupportEnabled &&
+ isCloudUserVal &&
!isPremiumChatSupportEnabled &&
!licenseData?.payload?.trialConvertedToSubscription;
@@ -241,6 +249,9 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
const isTracesView = (): boolean =>
routeKey === 'TRACES_EXPLORER' || routeKey === 'TRACES_SAVE_VIEWS';
+ const isMessagingQueues = (): boolean =>
+ routeKey === 'MESSAGING_QUEUES' || routeKey === 'MESSAGING_QUEUES_DETAIL';
+
const isDashboardListView = (): boolean => routeKey === 'ALL_DASHBOARD';
const isDashboardView = (): boolean => {
/**
@@ -329,7 +340,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
isTracesView() ||
isDashboardView() ||
isDashboardWidgetView() ||
- isDashboardListView()
+ isDashboardListView() ||
+ isMessagingQueues()
? 0
: '0 1rem',
}}
diff --git a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx
index 138694058e..44378e602b 100644
--- a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx
+++ b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx
@@ -33,6 +33,7 @@ import useErrorNotification from 'hooks/useErrorNotification';
import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
import { useNotifications } from 'hooks/useNotifications';
import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery';
+import { cloneDeep } from 'lodash-es';
import {
Check,
ConciergeBell,
@@ -56,7 +57,7 @@ import { useHistory } from 'react-router-dom';
import { AppState } from 'store/reducers';
import { Dashboard } from 'types/api/dashboard/getAll';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
-import { DataSource } from 'types/common/queryBuilder';
+import { DataSource, StringOperators } from 'types/common/queryBuilder';
import AppReducer from 'types/reducer/app';
import { USER_ROLES } from 'types/roles';
@@ -120,6 +121,21 @@ function ExplorerOptions({
const { role } = useSelector((state) => state.app);
+ const handleConditionalQueryModification = useCallback((): string => {
+ if (
+ query?.builder?.queryData?.[0]?.aggregateOperator !== StringOperators.NOOP
+ ) {
+ return JSON.stringify(query);
+ }
+
+ // Modify aggregateOperator to count, as noop is not supported in alerts
+ const modifiedQuery = cloneDeep(query);
+
+ modifiedQuery.builder.queryData[0].aggregateOperator = StringOperators.COUNT;
+
+ return JSON.stringify(modifiedQuery);
+ }, [query]);
+
const onCreateAlertsHandler = useCallback(() => {
if (sourcepage === DataSource.TRACES) {
logEvent('Traces Explorer: Create alert', {
@@ -130,13 +146,16 @@ function ExplorerOptions({
panelType,
});
}
+
+ const stringifiedQuery = handleConditionalQueryModification();
+
history.push(
`${ROUTES.ALERTS_NEW}?${QueryParams.compositeQuery}=${encodeURIComponent(
- JSON.stringify(query),
+ stringifiedQuery,
)}`,
);
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [history, query]);
+ }, [handleConditionalQueryModification, history]);
const onCancel = (value: boolean) => (): void => {
onModalToggle(value);
@@ -482,6 +501,7 @@ function ExplorerOptions({
shape="circle"
onClick={hideToolbar}
icon={}
+ data-testid="hide-toolbar"
/>
@@ -511,6 +531,7 @@ function ExplorerOptions({
icon={
}
onClick={onSaveHandler}
disabled={isSaveViewLoading}
+ data-testid="save-view-btn"
>
Save this view
,
diff --git a/frontend/src/container/ExplorerOptions/ExplorerOptionsHideArea.tsx b/frontend/src/container/ExplorerOptions/ExplorerOptionsHideArea.tsx
index a420c25ecc..efdaef1cd1 100644
--- a/frontend/src/container/ExplorerOptions/ExplorerOptionsHideArea.tsx
+++ b/frontend/src/container/ExplorerOptions/ExplorerOptionsHideArea.tsx
@@ -65,6 +65,7 @@ function ExplorerOptionsHideArea({
// style={{ alignSelf: 'center', marginRight: 'calc(10% - 20px)' }}
className="explorer-show-btn"
onClick={handleShowExplorerOption}
+ data-testid="show-explorer-option"
>
diff --git a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx
index d0e16857be..b76c7c9f73 100644
--- a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx
+++ b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx
@@ -47,6 +47,7 @@ function WidgetGraphComponent({
setRequestData,
onClickHandler,
onDragSelect,
+ customTooltipElement,
}: WidgetGraphComponentProps): JSX.Element {
const [deleteModal, setDeleteModal] = useState(false);
const [hovered, setHovered] = useState(false);
@@ -335,6 +336,7 @@ function WidgetGraphComponent({
onClickHandler={onClickHandler}
onDragSelect={onDragSelect}
tableProcessedDataRef={tableProcessedDataRef}
+ customTooltipElement={customTooltipElement}
/>
)}
diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx
index 8e34e32879..d7d2e729cb 100644
--- a/frontend/src/container/GridCardLayout/GridCard/index.tsx
+++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx
@@ -33,6 +33,7 @@ function GridCardGraph({
version,
onClickHandler,
onDragSelect,
+ customTooltipElement,
}: GridCardGraphProps): JSX.Element {
const dispatch = useDispatch();
const [errorMessage, setErrorMessage] = useState
();
@@ -215,6 +216,7 @@ function GridCardGraph({
setRequestData={setRequestData}
onClickHandler={onClickHandler}
onDragSelect={onDragSelect}
+ customTooltipElement={customTooltipElement}
/>
)}
diff --git a/frontend/src/container/GridCardLayout/GridCard/types.ts b/frontend/src/container/GridCardLayout/GridCard/types.ts
index 1235a26440..d0edede5a1 100644
--- a/frontend/src/container/GridCardLayout/GridCard/types.ts
+++ b/frontend/src/container/GridCardLayout/GridCard/types.ts
@@ -31,6 +31,7 @@ export interface WidgetGraphComponentProps {
setRequestData?: Dispatch
>;
onClickHandler?: OnClickPluginOpts['onClick'];
onDragSelect: (start: number, end: number) => void;
+ customTooltipElement?: HTMLDivElement;
}
export interface GridCardGraphProps {
@@ -42,6 +43,7 @@ export interface GridCardGraphProps {
variables?: Dashboard['data']['variables'];
version?: string;
onDragSelect: (start: number, end: number) => void;
+ customTooltipElement?: HTMLDivElement;
}
export interface GetGraphVisibilityStateOnLegendClickProps {
diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.tsx b/frontend/src/container/GridCardLayout/GridCardLayout.tsx
index 75fde3ce7a..a96599b127 100644
--- a/frontend/src/container/GridCardLayout/GridCardLayout.tsx
+++ b/frontend/src/container/GridCardLayout/GridCardLayout.tsx
@@ -194,7 +194,7 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
urlQuery.set(QueryParams.startTime, startTimestamp.toString());
urlQuery.set(QueryParams.endTime, endTimestamp.toString());
const generatedUrl = `${pathname}?${urlQuery.toString()}`;
- history.replace(generatedUrl);
+ history.push(generatedUrl);
if (startTimestamp !== endTimestamp) {
dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
diff --git a/frontend/src/container/ListOfDashboard/DashboardList.styles.scss b/frontend/src/container/ListOfDashboard/DashboardList.styles.scss
index 7fff109d2a..cf9ec283d2 100644
--- a/frontend/src/container/ListOfDashboard/DashboardList.styles.scss
+++ b/frontend/src/container/ListOfDashboard/DashboardList.styles.scss
@@ -590,6 +590,8 @@
}
.new-dashboard-menu {
+ width: 200px;
+
.create-dashboard-menu-item {
display: flex;
align-items: center;
@@ -1067,7 +1069,7 @@
color: var(--bg-ink-500);
}
.subtitle {
- color: var(--bg-vanilla-400);
+ color: var(--bg-ink-300);
}
.ant-table-row {
@@ -1087,6 +1089,10 @@
.dashboard-title {
color: var(--bg-slate-300);
+
+ .title {
+ color: var(--bg-ink-500);
+ }
}
.title-with-action {
diff --git a/frontend/src/container/ListOfDashboard/DashboardsList.tsx b/frontend/src/container/ListOfDashboard/DashboardsList.tsx
index 8de34fdaf1..421c7e31c4 100644
--- a/frontend/src/container/ListOfDashboard/DashboardsList.tsx
+++ b/frontend/src/container/ListOfDashboard/DashboardsList.tsx
@@ -45,6 +45,8 @@ import {
Ellipsis,
EllipsisVertical,
Expand,
+ ExternalLink,
+ Github,
HdmiPort,
LayoutGrid,
Link2,
@@ -53,6 +55,8 @@ import {
RotateCw,
Search,
} from 'lucide-react';
+// #TODO: lucide will be removing brand icons like Github in future, in that case we can use simple icons
+// see more: https://github.com/lucide-icons/lucide/issues/94
import { handleContactSupport } from 'pages/Integrations/utils';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import {
@@ -600,6 +604,28 @@ function DashboardsList(): JSX.Element {
),
key: '1',
},
+ {
+ label: (
+
+
+
+ View templates
+
+
+
+
+ ),
+ key: '2',
+ },
];
if (createNewDashboard) {
diff --git a/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx b/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx
index 9bf1db2051..62767e6799 100644
--- a/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx
+++ b/frontend/src/container/ListOfDashboard/ImportJSON/index.tsx
@@ -4,7 +4,15 @@ import { red } from '@ant-design/colors';
import { ExclamationCircleTwoTone } from '@ant-design/icons';
import MEditor, { Monaco } from '@monaco-editor/react';
import { Color } from '@signozhq/design-tokens';
-import { Button, Modal, Space, Typography, Upload, UploadProps } from 'antd';
+import {
+ Button,
+ Flex,
+ Modal,
+ Space,
+ Typography,
+ Upload,
+ UploadProps,
+} from 'antd';
import logEvent from 'api/common/logEvent';
import createDashboard from 'api/dashboard/create';
import ROUTES from 'constants/routes';
@@ -13,7 +21,9 @@ import { MESSAGE } from 'hooks/useFeatureFlag';
import { useNotifications } from 'hooks/useNotifications';
import { getUpdatedLayout } from 'lib/dashboard/getUpdatedLayout';
import history from 'lib/history';
-import { MonitorDot, MoveRight, X } from 'lucide-react';
+import { ExternalLink, Github, MonitorDot, MoveRight, X } from 'lucide-react';
+// #TODO: Lucide will be removing brand icons like GitHub in the future. In that case, we can use Simple Icons. https://simpleicons.org/
+// See more: https://github.com/lucide-icons/lucide/issues/94
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { generatePath } from 'react-router-dom';
@@ -174,27 +184,43 @@ function ImportJSON({
)}
-
false}
- action="none"
- data={jsonData}
- >
- }
- onClick={(): void => {
- logEvent('Dashboard List: Upload JSON file clicked', {});
- }}
+
+ false}
+ action="none"
+ data={jsonData}
>
- {' '}
- {t('upload_json_file')}
-
-
+ }
+ onClick={(): void => {
+ logEvent('Dashboard List: Upload JSON file clicked', {});
+ }}
+ >
+ {' '}
+ {t('upload_json_file')}
+
+
+
+ }
+ >
+ {t('view_template')}
+
+
+
+
);
}
@@ -75,12 +77,14 @@ function LiveLogsList({ logs }: LiveLogsListProps): JSX.Element {
linesPerRow={options.maxLines}
onAddToQuery={onAddToQuery}
onSetActiveLog={onSetActiveLog}
+ fontSize={options.fontSize}
/>
);
},
[
onAddToQuery,
onSetActiveLog,
+ options.fontSize,
options.format,
options.maxLines,
selectedFields,
@@ -123,6 +127,7 @@ function LiveLogsList({ logs }: LiveLogsListProps): JSX.Element {
logs,
fields: selectedFields,
linesPerRow: options.maxLines,
+ fontSize: options.fontSize,
appendTo: 'end',
activeLogIndex,
}}
@@ -147,6 +152,7 @@ function LiveLogsList({ logs }: LiveLogsListProps): JSX.Element {
log={activeLog}
onClose={onClearActiveLog}
onAddToQuery={onAddToQuery}
+ onGroupByAttribute={onGroupByAttribute}
onClickActionItem={onAddToQuery}
/>
>
diff --git a/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx b/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx
index d5c9f68547..39b55d21a0 100644
--- a/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx
+++ b/frontend/src/container/LogDetailedView/ContextView/ContextLogRenderer.tsx
@@ -3,12 +3,17 @@ import './ContextLogRenderer.styles.scss';
import { Skeleton } from 'antd';
import RawLogView from 'components/Logs/RawLogView';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
+import { LOCALSTORAGE } from 'constants/localStorage';
import ShowButton from 'container/LogsContextList/ShowButton';
+import { useOptionsMenu } from 'container/OptionsMenu';
+import { FontSize } from 'container/OptionsMenu/types';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
-import { useCallback, useEffect, useState } from 'react';
+import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
+import { useCallback, useEffect, useMemo, useState } from 'react';
import { Virtuoso } from 'react-virtuoso';
import { ILog } from 'types/api/logs/log';
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
+import { DataSource, StringOperators } from 'types/common/queryBuilder';
import { useContextLogData } from './useContextLogData';
@@ -22,6 +27,20 @@ function ContextLogRenderer({
const [afterLogPage, setAfterLogPage] = useState
(1);
const [logs, setLogs] = useState([log]);
+ const { initialDataSource, stagedQuery } = useQueryBuilder();
+
+ const listQuery = useMemo(() => {
+ if (!stagedQuery || stagedQuery.builder.queryData.length < 1) return null;
+
+ return stagedQuery.builder.queryData.find((item) => !item.disabled) || null;
+ }, [stagedQuery]);
+
+ const { options } = useOptionsMenu({
+ storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
+ dataSource: initialDataSource || DataSource.METRICS,
+ aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
+ });
+
const {
logs: previousLogs,
isFetching: isPreviousLogsFetching,
@@ -34,6 +53,7 @@ function ContextLogRenderer({
order: ORDERBY_FILTERS.ASC,
page: prevLogPage,
setPage: setPrevLogPage,
+ fontSize: options.fontSize,
});
const {
@@ -48,6 +68,7 @@ function ContextLogRenderer({
order: ORDERBY_FILTERS.DESC,
page: afterLogPage,
setPage: setAfterLogPage,
+ fontSize: options.fontSize,
});
useEffect(() => {
@@ -65,6 +86,19 @@ function ContextLogRenderer({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [filters]);
+ const lengthMultipier = useMemo(() => {
+ switch (options.fontSize) {
+ case FontSize.SMALL:
+ return 24;
+ case FontSize.MEDIUM:
+ return 28;
+ case FontSize.LARGE:
+ return 32;
+ default:
+ return 32;
+ }
+ }, [options.fontSize]);
+
const getItemContent = useCallback(
(_: number, logTorender: ILog): JSX.Element => (
),
- [log.id],
+ [log.id, options.fontSize],
);
return (
@@ -101,7 +136,7 @@ function ContextLogRenderer({
initialTopMostItemIndex={0}
data={logs}
itemContent={getItemContent}
- style={{ height: `calc(${logs.length} * 32px)` }}
+ style={{ height: `calc(${logs.length} * ${lengthMultipier}px)` }}
/>
{isAfterLogsFetching && (
diff --git a/frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts b/frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts
index 91c7fdf3f8..3d07ea0af9 100644
--- a/frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts
+++ b/frontend/src/container/LogDetailedView/ContextView/useContextLogData.ts
@@ -4,9 +4,11 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
import {
getOrderByTimestamp,
INITIAL_PAGE_SIZE,
+ INITIAL_PAGE_SIZE_SMALL_FONT,
LOGS_MORE_PAGE_SIZE,
} from 'container/LogsContextList/configs';
import { getRequestData } from 'container/LogsContextList/utils';
+import { FontSize } from 'container/OptionsMenu/types';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
import {
@@ -30,6 +32,7 @@ export const useContextLogData = ({
filters,
page,
setPage,
+ fontSize,
}: {
log: ILog;
query: Query;
@@ -38,6 +41,7 @@ export const useContextLogData = ({
filters: TagFilter | null;
page: number;
setPage: Dispatch>;
+ fontSize?: FontSize;
}): {
logs: ILog[];
handleShowNextLines: () => void;
@@ -54,9 +58,14 @@ export const useContextLogData = ({
const logsMorePageSize = useMemo(() => (page - 1) * LOGS_MORE_PAGE_SIZE, [
page,
]);
+
+ const initialPageSize =
+ fontSize && fontSize === FontSize.SMALL
+ ? INITIAL_PAGE_SIZE_SMALL_FONT
+ : INITIAL_PAGE_SIZE;
const pageSize = useMemo(
- () => (page <= 1 ? INITIAL_PAGE_SIZE : logsMorePageSize + INITIAL_PAGE_SIZE),
- [page, logsMorePageSize],
+ () => (page <= 1 ? initialPageSize : logsMorePageSize + initialPageSize),
+ [page, initialPageSize, logsMorePageSize],
);
const isDisabledFetch = useMemo(() => logs.length < pageSize, [
logs.length,
@@ -77,8 +86,16 @@ export const useContextLogData = ({
log: lastLog,
orderByTimestamp,
page,
+ pageSize: initialPageSize,
}),
- [currentStagedQueryData, query, lastLog, orderByTimestamp, page],
+ [
+ currentStagedQueryData,
+ query,
+ lastLog,
+ orderByTimestamp,
+ page,
+ initialPageSize,
+ ],
);
const [requestData, setRequestData] = useState(
diff --git a/frontend/src/container/LogDetailedView/FieldRenderer.styles.scss b/frontend/src/container/LogDetailedView/FieldRenderer.styles.scss
index 7e43e1caf3..96d72cad81 100644
--- a/frontend/src/container/LogDetailedView/FieldRenderer.styles.scss
+++ b/frontend/src/container/LogDetailedView/FieldRenderer.styles.scss
@@ -18,6 +18,7 @@
.tags {
display: flex;
- gap: 8;
+ flex-wrap: wrap;
+ gap: 8px;
}
}
diff --git a/frontend/src/container/LogDetailedView/LogContext.tsx b/frontend/src/container/LogDetailedView/LogContext.tsx
index 90d4a6e5bb..6997c65171 100644
--- a/frontend/src/container/LogDetailedView/LogContext.tsx
+++ b/frontend/src/container/LogDetailedView/LogContext.tsx
@@ -2,6 +2,7 @@ import './LogContext.styles.scss';
import RawLogView from 'components/Logs/RawLogView';
import LogsContextList from 'container/LogsContextList';
+import { FontSize } from 'container/OptionsMenu/types';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import { ILog } from 'types/api/logs/log';
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
@@ -37,6 +38,7 @@ function LogContext({
isTextOverflowEllipsisDisabled={false}
data={log}
linesPerRow={1}
+ fontSize={FontSize.SMALL}
/>
Promise;
}
type Props = OverviewProps &
@@ -39,6 +46,7 @@ function Overview({
onClickActionItem,
isListViewPanel = false,
selectedOptions,
+ onGroupByAttribute,
listViewPanelSelectedFields,
}: Props): JSX.Element {
const [isWrapWord, setIsWrapWord] = useState(true);
@@ -117,7 +125,7 @@ function Overview({
children: (
{}}
@@ -204,6 +212,7 @@ function Overview({
logData={logData}
onAddToQuery={onAddToQuery}
fieldSearchInput={fieldSearchInput}
+ onGroupByAttribute={onGroupByAttribute}
onClickActionItem={onClickActionItem}
isListViewPanel={isListViewPanel}
selectedOptions={selectedOptions}
@@ -222,6 +231,7 @@ function Overview({
Overview.defaultProps = {
isListViewPanel: false,
listViewPanelSelectedFields: null,
+ onGroupByAttribute: undefined,
};
export default Overview;
diff --git a/frontend/src/container/LogDetailedView/TableView.styles.scss b/frontend/src/container/LogDetailedView/TableView.styles.scss
index 2f092dc04d..322a5cd638 100644
--- a/frontend/src/container/LogDetailedView/TableView.styles.scss
+++ b/frontend/src/container/LogDetailedView/TableView.styles.scss
@@ -11,7 +11,7 @@
top: 50%;
right: 16px;
transform: translateY(-50%);
- gap: 8px;
+ gap: 4px;
}
}
}
@@ -76,8 +76,10 @@
box-shadow: none;
border-radius: 2px;
background: var(--bg-slate-400);
-
- height: 24px;
+ padding: 2px 3px;
+ gap: 3px;
+ height: 18px;
+ width: 20px;
}
}
}
diff --git a/frontend/src/container/LogDetailedView/TableView.tsx b/frontend/src/container/LogDetailedView/TableView.tsx
index 58b3779eda..591109ac3c 100644
--- a/frontend/src/container/LogDetailedView/TableView.tsx
+++ b/frontend/src/container/LogDetailedView/TableView.tsx
@@ -4,23 +4,21 @@ import './TableView.styles.scss';
import { LinkOutlined } from '@ant-design/icons';
import { Color } from '@signozhq/design-tokens';
-import { Button, Space, Spin, Tooltip, Tree, Typography } from 'antd';
+import { Button, Space, Tooltip, Typography } from 'antd';
import { ColumnsType } from 'antd/es/table';
import cx from 'classnames';
import AddToQueryHOC, {
AddToQueryHOCProps,
} from 'components/Logs/AddToQueryHOC';
-import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
import { ResizeTable } from 'components/ResizeTable';
import { OPERATORS } from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
-import { OptionsQuery } from 'container/OptionsMenu/types';
+import { FontSize, OptionsQuery } from 'container/OptionsMenu/types';
import { useIsDarkMode } from 'hooks/useDarkMode';
import history from 'lib/history';
import { fieldSearchFilter } from 'lib/logs/fieldSearch';
import { removeJSONStringifyQuotes } from 'lib/removeJSONStringifyQuotes';
-import { isEmpty } from 'lodash-es';
-import { ArrowDownToDot, ArrowUpFromDot, Pin } from 'lucide-react';
+import { Pin } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
import { useDispatch } from 'react-redux';
import { generatePath } from 'react-router-dom';
@@ -29,17 +27,12 @@ import AppActions from 'types/actions';
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs';
import { IField } from 'types/api/logs/fields';
import { ILog } from 'types/api/logs/log';
+import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { ActionItemProps } from './ActionItem';
import FieldRenderer from './FieldRenderer';
-import {
- filterKeyForField,
- findKeyPath,
- flattenObject,
- jsonToDataNodes,
- recursiveParseJSON,
- removeEscapeCharacters,
-} from './utils';
+import { TableViewActions } from './TableView/TableViewActions';
+import { filterKeyForField, findKeyPath, flattenObject } from './utils';
// Fields which should be restricted from adding it to query
const RESTRICTED_FIELDS = ['timestamp'];
@@ -50,6 +43,11 @@ interface TableViewProps {
selectedOptions: OptionsQuery;
isListViewPanel?: boolean;
listViewPanelSelectedFields?: IField[] | null;
+ onGroupByAttribute?: (
+ fieldKey: string,
+ isJSON?: boolean,
+ dataType?: DataTypes,
+ ) => Promise;
}
type Props = TableViewProps &
@@ -63,6 +61,7 @@ function TableView({
onClickActionItem,
isListViewPanel = false,
selectedOptions,
+ onGroupByAttribute,
listViewPanelSelectedFields,
}: Props): JSX.Element | null {
const dispatch = useDispatch>();
@@ -256,6 +255,7 @@ function TableView({
fieldKey={fieldFilterKey}
fieldValue={flattenLogData[field]}
onAddToQuery={onAddToQuery}
+ fontSize={FontSize.SMALL}
>
{renderedField}
@@ -270,75 +270,17 @@ function TableView({
width: 70,
ellipsis: false,
className: 'value-field-container attribute-value',
- render: (fieldData: Record, record): JSX.Element => {
- const textToCopy = fieldData.value.slice(1, -1);
-
- if (record.field === 'body') {
- const parsedBody = recursiveParseJSON(fieldData.value);
- if (!isEmpty(parsedBody)) {
- return (
-
- );
- }
- }
-
- const fieldFilterKey = filterKeyForField(fieldData.field);
-
- return (
-
-
-
- {removeEscapeCharacters(fieldData.value)}
-
-
-
- {!isListViewPanel && (
-
-
-
- ) : (
-
- )
- }
- onClick={onClickHandler(
- OPERATORS.IN,
- fieldFilterKey,
- fieldData.value,
- )}
- />
-
-
-
- ) : (
-
- )
- }
- onClick={onClickHandler(
- OPERATORS.NIN,
- fieldFilterKey,
- fieldData.value,
- )}
- />
-
-
- )}
-
- );
- },
+ render: (fieldData: Record, record): JSX.Element => (
+
+ ),
},
];
function sortPinnedAttributes(
@@ -379,9 +321,10 @@ function TableView({
TableView.defaultProps = {
isListViewPanel: false,
listViewPanelSelectedFields: null,
+ onGroupByAttribute: undefined,
};
-interface DataType {
+export interface DataType {
key: string;
field: string;
value: string;
diff --git a/frontend/src/container/LogDetailedView/TableView/TableViewActions.styles.scss b/frontend/src/container/LogDetailedView/TableView/TableViewActions.styles.scss
new file mode 100644
index 0000000000..f5a45ef416
--- /dev/null
+++ b/frontend/src/container/LogDetailedView/TableView/TableViewActions.styles.scss
@@ -0,0 +1,61 @@
+.open-popover {
+ &.value-field {
+ .action-btn {
+ display: flex !important;
+ position: absolute !important;
+ top: 50% !important;
+ right: 16px !important;
+ transform: translateY(-50%) !important;
+ gap: 4px !important;
+ }
+ }
+}
+
+.table-view-actions-content {
+ .ant-popover-inner {
+ border-radius: 4px;
+ border: 1px solid var(--bg-slate-400);
+ background: linear-gradient(
+ 139deg,
+ rgba(18, 19, 23, 0.8) 0%,
+ rgba(18, 19, 23, 0.9) 98.68%
+ );
+ box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
+ backdrop-filter: blur(20px);
+ padding: 0px;
+ .group-by-clause {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ color: var(--bg-vanilla-400);
+ font-family: Inter;
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ letter-spacing: 0.14px;
+ padding: 12px 18px 12px 14px;
+
+ .ant-btn-icon {
+ margin-inline-end: 0px;
+ }
+ }
+
+ .group-by-clause:hover {
+ background-color: unset !important;
+ }
+ }
+}
+
+.lightMode {
+ .table-view-actions-content {
+ .ant-popover-inner {
+ border: 1px solid var(--bg-vanilla-400);
+ background: var(--bg-vanilla-100) !important;
+
+ .group-by-clause {
+ color: var(--bg-ink-400);
+ }
+ }
+ }
+}
diff --git a/frontend/src/container/LogDetailedView/TableView/TableViewActions.tsx b/frontend/src/container/LogDetailedView/TableView/TableViewActions.tsx
new file mode 100644
index 0000000000..63912ffa82
--- /dev/null
+++ b/frontend/src/container/LogDetailedView/TableView/TableViewActions.tsx
@@ -0,0 +1,185 @@
+import './TableViewActions.styles.scss';
+
+import { Color } from '@signozhq/design-tokens';
+import Convert from 'ansi-to-html';
+import { Button, Popover, Spin, Tooltip, Tree } from 'antd';
+import GroupByIcon from 'assets/CustomIcons/GroupByIcon';
+import cx from 'classnames';
+import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
+import { OPERATORS } from 'constants/queryBuilder';
+import ROUTES from 'constants/routes';
+import dompurify from 'dompurify';
+import { isEmpty } from 'lodash-es';
+import { ArrowDownToDot, ArrowUpFromDot, Ellipsis } from 'lucide-react';
+import { useMemo, useState } from 'react';
+import { useLocation } from 'react-router-dom';
+import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
+import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';
+
+import { DataType } from '../TableView';
+import {
+ filterKeyForField,
+ jsonToDataNodes,
+ recursiveParseJSON,
+ removeEscapeCharacters,
+ unescapeString,
+} from '../utils';
+
+interface ITableViewActionsProps {
+ fieldData: Record;
+ record: DataType;
+ isListViewPanel: boolean;
+ isfilterInLoading: boolean;
+ isfilterOutLoading: boolean;
+ onGroupByAttribute?: (
+ fieldKey: string,
+ isJSON?: boolean,
+ dataType?: DataTypes,
+ ) => Promise;
+ onClickHandler: (
+ operator: string,
+ fieldKey: string,
+ fieldValue: string,
+ ) => () => void;
+}
+
+const convert = new Convert();
+
+export function TableViewActions(
+ props: ITableViewActionsProps,
+): React.ReactElement {
+ const {
+ fieldData,
+ record,
+ isListViewPanel,
+ isfilterInLoading,
+ isfilterOutLoading,
+ onClickHandler,
+ onGroupByAttribute,
+ } = props;
+
+ const { pathname } = useLocation();
+
+ // there is no option for where clause in old logs explorer and live logs page
+ const isOldLogsExplorerOrLiveLogsPage = useMemo(
+ () => pathname === ROUTES.OLD_LOGS_EXPLORER || pathname === ROUTES.LIVE_LOGS,
+ [pathname],
+ );
+
+ const [isOpen, setIsOpen] = useState(false);
+ const textToCopy = fieldData.value;
+
+ if (record.field === 'body') {
+ const parsedBody = recursiveParseJSON(fieldData.value);
+ if (!isEmpty(parsedBody)) {
+ return (
+
+ );
+ }
+ }
+ const bodyHtml =
+ record.field === 'body'
+ ? {
+ __html: convert.toHtml(
+ dompurify.sanitize(unescapeString(record.value), {
+ FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
+ }),
+ ),
+ }
+ : { __html: '' };
+
+ const fieldFilterKey = filterKeyForField(fieldData.field);
+
+ return (
+
+ {record.field === 'body' ? (
+
+
+
+ ) : (
+
+
+ {removeEscapeCharacters(fieldData.value)}
+
+
+ )}
+
+ {!isListViewPanel && (
+
+
+
+ ) : (
+
+ )
+ }
+ onClick={onClickHandler(OPERATORS.IN, fieldFilterKey, fieldData.value)}
+ />
+
+
+
+ ) : (
+
+ )
+ }
+ onClick={onClickHandler(OPERATORS.NIN, fieldFilterKey, fieldData.value)}
+ />
+
+ {!isOldLogsExplorerOrLiveLogsPage && (
+
+ }
+ onClick={(): Promise | void =>
+ onGroupByAttribute?.(fieldFilterKey)
+ }
+ >
+ Group By Attribute
+
+
+ }
+ rootClassName="table-view-actions-content"
+ trigger="hover"
+ placement="bottomLeft"
+ >
+ }
+ className="filter-btn periscope-btn"
+ />
+
+ )}
+
+ )}
+
+ );
+}
+
+TableViewActions.defaultProps = {
+ onGroupByAttribute: undefined,
+};
diff --git a/frontend/src/container/LogDetailedView/utils.tsx b/frontend/src/container/LogDetailedView/utils.tsx
index 8418e13893..766bb8b5bf 100644
--- a/frontend/src/container/LogDetailedView/utils.tsx
+++ b/frontend/src/container/LogDetailedView/utils.tsx
@@ -250,19 +250,37 @@ export const getDataTypes = (value: unknown): DataTypes => {
return determineType(value);
};
+// now we do not want to render colors everywhere like in tooltip and monaco editor hence we remove such codes to make
+// the log line readable
export const removeEscapeCharacters = (str: string): string =>
- str.replace(/\\([ntfr'"\\])/g, (_: string, char: string) => {
- const escapeMap: Record = {
- n: '\n',
- t: '\t',
- f: '\f',
- r: '\r',
- "'": "'",
- '"': '"',
- '\\': '\\',
- };
- return escapeMap[char as keyof typeof escapeMap];
- });
+ str
+ .replace(/\\x1[bB][[0-9;]*m/g, '')
+ .replace(/\\u001[bB][[0-9;]*m/g, '')
+ .replace(/\\x[0-9A-Fa-f]{2}/g, '')
+ .replace(/\\u[0-9A-Fa-f]{4}/g, '')
+ .replace(/\\[btnfrv0'"\\]/g, '');
+
+// we need to remove the escape from the escaped characters as some recievers like file log escape the unicode escape characters.
+// example: Log [\u001B[32;1mThis is bright green\u001B[0m] is being sent as [\\u001B[32;1mThis is bright green\\u001B[0m]
+//
+// so we need to remove this escapes to render the color properly
+export const unescapeString = (str: string): string =>
+ str
+ .replace(/\\n/g, '\n') // Replaces escaped newlines
+ .replace(/\\r/g, '\r') // Replaces escaped carriage returns
+ .replace(/\\t/g, '\t') // Replaces escaped tabs
+ .replace(/\\b/g, '\b') // Replaces escaped backspaces
+ .replace(/\\f/g, '\f') // Replaces escaped form feeds
+ .replace(/\\v/g, '\v') // Replaces escaped vertical tabs
+ .replace(/\\'/g, "'") // Replaces escaped single quotes
+ .replace(/\\"/g, '"') // Replaces escaped double quotes
+ .replace(/\\\\/g, '\\') // Replaces escaped backslashes
+ .replace(/\\x([0-9A-Fa-f]{2})/g, (_, hex) =>
+ String.fromCharCode(parseInt(hex, 16)),
+ ) // Replaces hexadecimal escape sequences
+ .replace(/\\u([0-9A-Fa-f]{4})/g, (_, hex) =>
+ String.fromCharCode(parseInt(hex, 16)),
+ ); // Replaces Unicode escape sequences
export function removeExtraSpaces(input: string): string {
return input.replace(/\s+/g, ' ').trim();
diff --git a/frontend/src/container/LogsContextList/configs.ts b/frontend/src/container/LogsContextList/configs.ts
index baa3b39420..9b70dfc5be 100644
--- a/frontend/src/container/LogsContextList/configs.ts
+++ b/frontend/src/container/LogsContextList/configs.ts
@@ -1,6 +1,7 @@
import { OrderByPayload } from 'types/api/queryBuilder/queryBuilderData';
export const INITIAL_PAGE_SIZE = 10;
+export const INITIAL_PAGE_SIZE_SMALL_FONT = 12;
export const LOGS_MORE_PAGE_SIZE = 10;
export const getOrderByTimestamp = (order: string): OrderByPayload => ({
diff --git a/frontend/src/container/LogsContextList/index.tsx b/frontend/src/container/LogsContextList/index.tsx
index 270291a33e..d215386c03 100644
--- a/frontend/src/container/LogsContextList/index.tsx
+++ b/frontend/src/container/LogsContextList/index.tsx
@@ -5,6 +5,7 @@ import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import Spinner from 'components/Spinner';
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import { PANEL_TYPES } from 'constants/queryBuilder';
+import { FontSize } from 'container/OptionsMenu/types';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
import { useIsDarkMode } from 'hooks/useDarkMode';
@@ -167,6 +168,7 @@ function LogsContextList({
key={log.id}
data={log}
linesPerRow={1}
+ fontSize={FontSize.SMALL}
/>
),
[],
diff --git a/frontend/src/container/LogsExplorerContext/index.tsx b/frontend/src/container/LogsExplorerContext/index.tsx
index d62cdb274b..32075097e5 100644
--- a/frontend/src/container/LogsExplorerContext/index.tsx
+++ b/frontend/src/container/LogsExplorerContext/index.tsx
@@ -2,6 +2,7 @@ import { EditFilled } from '@ant-design/icons';
import { Modal, Typography } from 'antd';
import RawLogView from 'components/Logs/RawLogView';
import LogsContextList from 'container/LogsContextList';
+import { FontSize } from 'container/OptionsMenu/types';
import { ORDERBY_FILTERS } from 'container/QueryBuilder/filters/OrderByFilter/config';
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
import { useIsDarkMode } from 'hooks/useDarkMode';
@@ -99,6 +100,7 @@ function LogsExplorerContext({
isTextOverflowEllipsisDisabled
data={log}
linesPerRow={1}
+ fontSize={FontSize.SMALL}
/>
void;
logs: ILog[];
hasActions: boolean;
+ fontSize: FontSize;
}
export default function TableRow({
@@ -33,6 +35,7 @@ export default function TableRow({
handleSetActiveContextLog,
logs,
hasActions,
+ fontSize,
}: TableRowProps): JSX.Element {
const isDarkMode = useIsDarkMode();
@@ -78,6 +81,7 @@ export default function TableRow({
$isDragColumn={false}
$isDarkMode={isDarkMode}
key={column.key}
+ fontSize={fontSize}
>
{cloneElement(children, props)}
diff --git a/frontend/src/container/LogsExplorerList/InfinityTableView/config.ts b/frontend/src/container/LogsExplorerList/InfinityTableView/config.ts
index ec16ba1024..c235cbd5e7 100644
--- a/frontend/src/container/LogsExplorerList/InfinityTableView/config.ts
+++ b/frontend/src/container/LogsExplorerList/InfinityTableView/config.ts
@@ -1,3 +1,5 @@
+/* eslint-disable no-nested-ternary */
+import { FontSize } from 'container/OptionsMenu/types';
import { CSSProperties } from 'react';
export const infinityDefaultStyles: CSSProperties = {
@@ -5,3 +7,16 @@ export const infinityDefaultStyles: CSSProperties = {
overflowX: 'scroll',
marginTop: '15px',
};
+
+export function getInfinityDefaultStyles(fontSize: FontSize): CSSProperties {
+ return {
+ width: '100%',
+ overflowX: 'scroll',
+ marginTop:
+ fontSize === FontSize.SMALL
+ ? '10px'
+ : fontSize === FontSize.MEDIUM
+ ? '12px'
+ : '15px',
+ };
+}
diff --git a/frontend/src/container/LogsExplorerList/InfinityTableView/index.tsx b/frontend/src/container/LogsExplorerList/InfinityTableView/index.tsx
index 2875575162..fe2d2ba1d4 100644
--- a/frontend/src/container/LogsExplorerList/InfinityTableView/index.tsx
+++ b/frontend/src/container/LogsExplorerList/InfinityTableView/index.tsx
@@ -15,7 +15,7 @@ import {
} from 'react-virtuoso';
import { ILog } from 'types/api/logs/log';
-import { infinityDefaultStyles } from './config';
+import { getInfinityDefaultStyles } from './config';
import { LogsCustomTable } from './LogsCustomTable';
import { TableHeaderCellStyled, TableRowStyled } from './styles';
import TableRow from './TableRow';
@@ -59,6 +59,7 @@ const InfinityTable = forwardRef(
onSetActiveLog,
onClearActiveLog,
onAddToQuery,
+ onGroupByAttribute,
} = useActiveLog();
const { dataSource, columns } = useTableView({
@@ -95,9 +96,15 @@ const InfinityTable = forwardRef(
handleSetActiveContextLog={handleSetActiveContextLog}
logs={tableViewProps.logs}
hasActions
+ fontSize={tableViewProps.fontSize}
/>
),
- [handleSetActiveContextLog, tableColumns, tableViewProps.logs],
+ [
+ handleSetActiveContextLog,
+ tableColumns,
+ tableViewProps.fontSize,
+ tableViewProps.logs,
+ ],
);
const tableHeader = useCallback(
@@ -112,6 +119,7 @@ const InfinityTable = forwardRef(
$isDarkMode={isDarkMode}
$isDragColumn={isDragColumn}
key={column.key}
+ fontSize={tableViewProps?.fontSize}
// eslint-disable-next-line react/jsx-props-no-spreading
{...(isDragColumn && { className: 'dragHandler' })}
>
@@ -121,7 +129,7 @@ const InfinityTable = forwardRef(
})}
),
- [tableColumns, isDarkMode],
+ [tableColumns, isDarkMode, tableViewProps?.fontSize],
);
const handleClickExpand = (index: number): void => {
@@ -137,7 +145,7 @@ const InfinityTable = forwardRef(
initialTopMostItemIndex={
tableViewProps.activeLogIndex !== -1 ? tableViewProps.activeLogIndex : 0
}
- style={infinityDefaultStyles}
+ style={getInfinityDefaultStyles(tableViewProps.fontSize)}
data={dataSource}
components={{
// eslint-disable-next-line react/jsx-props-no-spreading
@@ -165,6 +173,7 @@ const InfinityTable = forwardRef(
onClose={handleClearActiveContextLog}
onAddToQuery={handleAddToQuery}
selectedTab={VIEW_TYPES.CONTEXT}
+ onGroupByAttribute={onGroupByAttribute}
/>
)}
(
onClose={onClearActiveLog}
onAddToQuery={onAddToQuery}
onClickActionItem={onAddToQuery}
+ onGroupByAttribute={onGroupByAttribute}
/>
>
);
diff --git a/frontend/src/container/LogsExplorerList/InfinityTableView/styles.ts b/frontend/src/container/LogsExplorerList/InfinityTableView/styles.ts
index e2719dbc3f..89c9592dd4 100644
--- a/frontend/src/container/LogsExplorerList/InfinityTableView/styles.ts
+++ b/frontend/src/container/LogsExplorerList/InfinityTableView/styles.ts
@@ -1,5 +1,7 @@
+/* eslint-disable no-nested-ternary */
import { Color } from '@signozhq/design-tokens';
import { themeColors } from 'constants/theme';
+import { FontSize } from 'container/OptionsMenu/types';
import styled from 'styled-components';
import { getActiveLogBackground } from 'utils/logs';
@@ -7,6 +9,7 @@ interface TableHeaderCellStyledProps {
$isDragColumn: boolean;
$isDarkMode: boolean;
$isTimestamp?: boolean;
+ fontSize?: FontSize;
}
export const TableStyled = styled.table`
@@ -15,6 +18,14 @@ export const TableStyled = styled.table`
export const TableCellStyled = styled.td`
padding: 0.5rem;
+ ${({ fontSize }): string =>
+ fontSize === FontSize.SMALL
+ ? `padding:0.3rem;`
+ : fontSize === FontSize.MEDIUM
+ ? `padding:0.4rem;`
+ : fontSize === FontSize.LARGE
+ ? `padding:0.5rem;`
+ : ``}
background-color: ${(props): string =>
props.$isDarkMode ? 'inherit' : themeColors.whiteCream};
@@ -33,7 +44,7 @@ export const TableRowStyled = styled.tr<{
? `background-color: ${
$isDarkMode ? Color.BG_SLATE_500 : Color.BG_VANILLA_300
} !important`
- : ''}
+ : ''};
}
cursor: pointer;
@@ -66,9 +77,17 @@ export const TableHeaderCellStyled = styled.th`
line-height: 18px;
letter-spacing: -0.07px;
background: ${(props): string => (props.$isDarkMode ? '#0b0c0d' : '#fdfdfd')};
- ${({ $isTimestamp }): string => ($isTimestamp ? 'padding-left: 24px;' : '')}
${({ $isDragColumn }): string => ($isDragColumn ? 'cursor: col-resize;' : '')}
+ ${({ fontSize }): string =>
+ fontSize === FontSize.SMALL
+ ? `font-size:11px; line-height:16px; padding: 0.1rem;`
+ : fontSize === FontSize.MEDIUM
+ ? `font-size:13px; line-height:20px; padding:0.3rem;`
+ : fontSize === FontSize.LARGE
+ ? `font-size:14px; line-height:24px; padding: 0.5rem;`
+ : ``};
+ ${({ $isTimestamp }): string => ($isTimestamp ? 'padding-left: 24px;' : '')}
color: ${(props): string =>
props.$isDarkMode ? 'var(--bg-vanilla-100, #fff)' : themeColors.bckgGrey};
`;
diff --git a/frontend/src/container/LogsExplorerList/index.tsx b/frontend/src/container/LogsExplorerList/index.tsx
index 760f3fea30..dd97916ce6 100644
--- a/frontend/src/container/LogsExplorerList/index.tsx
+++ b/frontend/src/container/LogsExplorerList/index.tsx
@@ -14,6 +14,7 @@ import EmptyLogsSearch from 'container/EmptyLogsSearch/EmptyLogsSearch';
import LogsError from 'container/LogsError/LogsError';
import { LogsLoading } from 'container/LogsLoading/LogsLoading';
import { useOptionsMenu } from 'container/OptionsMenu';
+import { FontSize } from 'container/OptionsMenu/types';
import { useActiveLog } from 'hooks/logs/useActiveLog';
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
@@ -50,6 +51,7 @@ function LogsExplorerList({
activeLog,
onClearActiveLog,
onAddToQuery,
+ onGroupByAttribute,
onSetActiveLog,
} = useActiveLog();
@@ -79,6 +81,7 @@ function LogsExplorerList({
data={log}
linesPerRow={options.maxLines}
selectedFields={selectedFields}
+ fontSize={options.fontSize}
/>
);
}
@@ -91,6 +94,7 @@ function LogsExplorerList({
onAddToQuery={onAddToQuery}
onSetActiveLog={onSetActiveLog}
activeLog={activeLog}
+ fontSize={options.fontSize}
linesPerRow={options.maxLines}
/>
);
@@ -99,6 +103,7 @@ function LogsExplorerList({
activeLog,
onAddToQuery,
onSetActiveLog,
+ options.fontSize,
options.format,
options.maxLines,
selectedFields,
@@ -121,6 +126,7 @@ function LogsExplorerList({
logs,
fields: selectedFields,
linesPerRow: options.maxLines,
+ fontSize: options.fontSize,
appendTo: 'end',
activeLogIndex,
}}
@@ -129,13 +135,27 @@ function LogsExplorerList({
);
}
+ function getMarginTop(): string {
+ switch (options.fontSize) {
+ case FontSize.SMALL:
+ return '10px';
+ case FontSize.MEDIUM:
+ return '12px';
+ case FontSize.LARGE:
+ return '15px';
+ default:
+ return '15px';
+ }
+ }
+
return (
>
diff --git a/frontend/src/container/LogsExplorerViews/LogsExplorerViews.styles.scss b/frontend/src/container/LogsExplorerViews/LogsExplorerViews.styles.scss
index a6142d195c..3821e7025c 100644
--- a/frontend/src/container/LogsExplorerViews/LogsExplorerViews.styles.scss
+++ b/frontend/src/container/LogsExplorerViews/LogsExplorerViews.styles.scss
@@ -80,6 +80,36 @@
position: relative;
}
}
+ .query-stats {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ .rows {
+ color: var(--bg-vanilla-400);
+ font-family: 'Geist Mono';
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 18px; /* 150% */
+ letter-spacing: 0.36px;
+ }
+
+ .divider {
+ width: 1px;
+ height: 14px;
+ background: #242834;
+ }
+
+ .time {
+ color: var(--bg-vanilla-400);
+ font-family: 'Geist Mono';
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 18px; /* 150% */
+ letter-spacing: 0.36px;
+ }
+ }
}
.logs-actions-container {
@@ -149,6 +179,15 @@
background: var(--bg-robin-400);
}
}
+ .query-stats {
+ .rows {
+ color: var(--bg-ink-400);
+ }
+
+ .time {
+ color: var(--bg-ink-400);
+ }
+ }
}
}
}
diff --git a/frontend/src/container/LogsExplorerViews/QueryStatus.styles.scss b/frontend/src/container/LogsExplorerViews/QueryStatus.styles.scss
new file mode 100644
index 0000000000..d6f9579113
--- /dev/null
+++ b/frontend/src/container/LogsExplorerViews/QueryStatus.styles.scss
@@ -0,0 +1,4 @@
+.query-status {
+ display: flex;
+ align-items: center;
+}
diff --git a/frontend/src/container/LogsExplorerViews/QueryStatus.tsx b/frontend/src/container/LogsExplorerViews/QueryStatus.tsx
new file mode 100644
index 0000000000..628b3d5fc1
--- /dev/null
+++ b/frontend/src/container/LogsExplorerViews/QueryStatus.tsx
@@ -0,0 +1,42 @@
+import './QueryStatus.styles.scss';
+
+import { LoadingOutlined } from '@ant-design/icons';
+import { Color } from '@signozhq/design-tokens';
+import { Spin } from 'antd';
+import { CircleCheck } from 'lucide-react';
+import React, { useMemo } from 'react';
+
+interface IQueryStatusProps {
+ loading: boolean;
+ error: boolean;
+ success: boolean;
+}
+
+export default function QueryStatus(
+ props: IQueryStatusProps,
+): React.ReactElement {
+ const { loading, error, success } = props;
+
+ const content = useMemo((): React.ReactElement => {
+ if (loading) {
+ return } />;
+ }
+ if (error) {
+ return (
+
+ );
+ }
+ if (success) {
+ return (
+
+ );
+ }
+ return ;
+ }, [error, loading, success]);
+ return {content}
;
+}
diff --git a/frontend/src/container/LogsExplorerViews/index.tsx b/frontend/src/container/LogsExplorerViews/index.tsx
index 6318e1da71..06a39a4da4 100644
--- a/frontend/src/container/LogsExplorerViews/index.tsx
+++ b/frontend/src/container/LogsExplorerViews/index.tsx
@@ -1,8 +1,10 @@
/* eslint-disable sonarjs/cognitive-complexity */
import './LogsExplorerViews.styles.scss';
-import { Button } from 'antd';
+import { Button, Typography } from 'antd';
+import { getQueryStats, WsDataEvent } from 'api/common/getQueryStats';
import logEvent from 'api/common/logEvent';
+import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import { LOCALSTORAGE } from 'constants/localStorage';
@@ -48,7 +50,15 @@ import {
} from 'lodash-es';
import { Sliders } from 'lucide-react';
import { SELECTED_VIEWS } from 'pages/LogsExplorer/utils';
-import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import {
+ memo,
+ MutableRefObject,
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from 'react';
import { useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { AppState } from 'store/reducers';
@@ -69,12 +79,20 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { generateExportToDashboardLink } from 'utils/dashboard/generateExportToDashboardLink';
import { v4 } from 'uuid';
+import QueryStatus from './QueryStatus';
+
function LogsExplorerViews({
selectedView,
showFrequencyChart,
+ setIsLoadingQueries,
+ listQueryKeyRef,
+ chartQueryKeyRef,
}: {
selectedView: SELECTED_VIEWS;
showFrequencyChart: boolean;
+ setIsLoadingQueries: React.Dispatch>;
+ listQueryKeyRef: MutableRefObject;
+ chartQueryKeyRef: MutableRefObject;
}): JSX.Element {
const { notifications } = useNotifications();
const history = useHistory();
@@ -82,14 +100,14 @@ function LogsExplorerViews({
// this is to respect the panel type present in the URL rather than defaulting it to list always.
const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.LIST);
- const { activeLogId, timeRange, onTimeRangeChange } = useCopyLogLink();
+ const { activeLogId, onTimeRangeChange } = useCopyLogLink();
const { queryData: pageSize } = useUrlQueryData(
QueryParams.pageSize,
DEFAULT_PER_PAGE_VALUE,
);
- const { minTime } = useSelector(
+ const { minTime, maxTime } = useSelector(
(state) => state.globalTime,
);
@@ -116,6 +134,8 @@ function LogsExplorerViews({
const [logs, setLogs] = useState([]);
const [requestData, setRequestData] = useState(null);
const [showFormatMenuItems, setShowFormatMenuItems] = useState(false);
+ const [queryId, setQueryId] = useState(v4());
+ const [queryStats, setQueryStats] = useState();
const handleAxisError = useAxiosError();
@@ -214,9 +234,18 @@ function LogsExplorerViews({
{
enabled: !!listChartQuery && panelType === PANEL_TYPES.LIST,
},
+ {},
+ undefined,
+ chartQueryKeyRef,
);
- const { data, isLoading, isFetching, isError } = useGetExplorerQueryRange(
+ const {
+ data,
+ isLoading,
+ isFetching,
+ isError,
+ isSuccess,
+ } = useGetExplorerQueryRange(
requestData,
panelType,
DEFAULT_ENTITY_VERSION,
@@ -225,13 +254,18 @@ function LogsExplorerViews({
enabled: !isLimit && !!requestData,
},
{
- ...(timeRange &&
- activeLogId &&
+ ...(activeLogId &&
!logs.length && {
- start: timeRange.start,
- end: timeRange.end,
+ start: minTime,
+ end: maxTime,
}),
},
+ undefined,
+ listQueryKeyRef,
+ {
+ ...(!isEmpty(queryId) &&
+ selectedPanelType !== PANEL_TYPES.LIST && { 'X-SIGNOZ-QUERY-ID': queryId }),
+ },
);
const getRequestData = useCallback(
@@ -318,6 +352,23 @@ function LogsExplorerViews({
],
);
+ useEffect(() => {
+ setQueryId(v4());
+ }, [data]);
+
+ useEffect(() => {
+ if (
+ !isEmpty(queryId) &&
+ (isLoading || isFetching) &&
+ selectedPanelType !== PANEL_TYPES.LIST
+ ) {
+ setQueryStats(undefined);
+ setTimeout(() => {
+ getQueryStats({ queryId, setData: setQueryStats });
+ }, 500);
+ }
+ }, [queryId, isLoading, isFetching, selectedPanelType]);
+
const logEventCalledRef = useRef(false);
useEffect(() => {
if (!logEventCalledRef.current && !isUndefined(data?.payload)) {
@@ -469,7 +520,7 @@ function LogsExplorerViews({
setLogs(newLogs);
onTimeRangeChange({
start: currentParams?.start,
- end: timeRange?.end || currentParams?.end,
+ end: currentParams?.end,
pageSize: newLogs.length,
});
}
@@ -486,8 +537,7 @@ function LogsExplorerViews({
filters: listQuery?.filters || initialFilters,
page: 1,
log: null,
- pageSize:
- timeRange?.pageSize && activeLogId ? timeRange?.pageSize : pageSize,
+ pageSize,
});
setLogs([]);
@@ -502,7 +552,6 @@ function LogsExplorerViews({
listQuery,
pageSize,
minTime,
- timeRange,
activeLogId,
onTimeRangeChange,
panelType,
@@ -569,6 +618,25 @@ function LogsExplorerViews({
},
});
+ useEffect(() => {
+ if (
+ isLoading ||
+ isFetching ||
+ isLoadingListChartData ||
+ isFetchingListChartData
+ ) {
+ setIsLoadingQueries(true);
+ } else {
+ setIsLoadingQueries(false);
+ }
+ }, [
+ isLoading,
+ isFetching,
+ isFetchingListChartData,
+ isLoadingListChartData,
+ setIsLoadingQueries,
+ ]);
+
const flattenLogData = useMemo(
() =>
logs.map((log) => {
@@ -665,6 +733,30 @@ function LogsExplorerViews({
)}
+ {(selectedPanelType === PANEL_TYPES.TIME_SERIES ||
+ selectedPanelType === PANEL_TYPES.TABLE) && (
+
+
+ {queryStats?.read_rows && (
+
+ {getYAxisFormattedValue(queryStats.read_rows?.toString(), 'short')}{' '}
+ rows
+
+ )}
+ {queryStats?.elapsed_ms && (
+ <>
+
+
+ {getYAxisFormattedValue(queryStats?.elapsed_ms?.toString(), 'ms')}
+
+ >
+ )}
+
+ )}
diff --git a/frontend/src/container/LogsExplorerViews/tests/LogsExplorerViews.test.tsx b/frontend/src/container/LogsExplorerViews/tests/LogsExplorerViews.test.tsx
index a1ae308efd..8262d6f9bc 100644
--- a/frontend/src/container/LogsExplorerViews/tests/LogsExplorerViews.test.tsx
+++ b/frontend/src/container/LogsExplorerViews/tests/LogsExplorerViews.test.tsx
@@ -46,6 +46,10 @@ jest.mock(
},
);
+jest.mock('api/common/getQueryStats', () => ({
+ getQueryStats: jest.fn(),
+}));
+
jest.mock('constants/panelTypes', () => ({
AVAILABLE_EXPORT_PANEL_TYPES: ['graph', 'table'],
}));
@@ -79,6 +83,9 @@ const renderer = (): RenderResult =>
{}}
+ listQueryKeyRef={{ current: {} }}
+ chartQueryKeyRef={{ current: {} }}
/>
diff --git a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx
index 3835386cd3..a7598b5c76 100644
--- a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx
+++ b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx
@@ -108,6 +108,7 @@ function LogsPanelComponent({
onSetActiveLog,
onClearActiveLog,
onAddToQuery,
+ onGroupByAttribute,
} = useActiveLog();
const handleRow = useCallback(
@@ -244,6 +245,7 @@ function LogsPanelComponent({
onClose={onClearActiveLog}
onAddToQuery={onAddToQuery}
onClickActionItem={onAddToQuery}
+ onGroupByAttribute={onGroupByAttribute}
isListViewPanel
listViewPanelSelectedFields={widget?.selectedLogFields}
/>
diff --git a/frontend/src/container/LogsTable/index.tsx b/frontend/src/container/LogsTable/index.tsx
index 6b20986d1f..577533adcf 100644
--- a/frontend/src/container/LogsTable/index.tsx
+++ b/frontend/src/container/LogsTable/index.tsx
@@ -10,11 +10,14 @@ import LogsTableView from 'components/Logs/TableView';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import Spinner from 'components/Spinner';
import { CARD_BODY_STYLE } from 'constants/card';
+import { LOCALSTORAGE } from 'constants/localStorage';
+import { useOptionsMenu } from 'container/OptionsMenu';
import { useActiveLog } from 'hooks/logs/useActiveLog';
import { memo, useCallback, useMemo } from 'react';
import { useSelector } from 'react-redux';
import { Virtuoso } from 'react-virtuoso';
import { AppState } from 'store/reducers';
+import { DataSource, StringOperators } from 'types/common/queryBuilder';
// interfaces
import { ILogsReducer } from 'types/reducer/logs';
@@ -35,6 +38,7 @@ function LogsTable(props: LogsTableProps): JSX.Element {
activeLog,
onClearActiveLog,
onAddToQuery,
+ onGroupByAttribute,
onSetActiveLog,
} = useActiveLog();
@@ -55,6 +59,14 @@ function LogsTable(props: LogsTableProps): JSX.Element {
liveTail,
]);
+ const { options } = useOptionsMenu({
+ storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
+ // this component will alwyays be called on old logs explorer page itself!
+ dataSource: DataSource.LOGS,
+ // and we do not have table / timeseries aggregated views in the old logs explorer!
+ aggregateOperator: StringOperators.NOOP,
+ });
+
const getItemContent = useCallback(
(index: number): JSX.Element => {
const log = logs[index];
@@ -66,6 +78,7 @@ function LogsTable(props: LogsTableProps): JSX.Element {
data={log}
linesPerRow={linesPerRow}
selectedFields={selected}
+ fontSize={options.fontSize}
/>
);
}
@@ -78,10 +91,19 @@ function LogsTable(props: LogsTableProps): JSX.Element {
linesPerRow={linesPerRow}
onAddToQuery={onAddToQuery}
onSetActiveLog={onSetActiveLog}
+ fontSize={options.fontSize}
/>
);
},
- [logs, viewMode, selected, onAddToQuery, onSetActiveLog, linesPerRow],
+ [
+ logs,
+ viewMode,
+ selected,
+ linesPerRow,
+ onAddToQuery,
+ onSetActiveLog,
+ options.fontSize,
+ ],
);
const renderContent = useMemo(() => {
@@ -92,6 +114,7 @@ function LogsTable(props: LogsTableProps): JSX.Element {
logs={logs}
fields={selected}
linesPerRow={linesPerRow}
+ fontSize={options.fontSize}
/>
);
}
@@ -103,7 +126,15 @@ function LogsTable(props: LogsTableProps): JSX.Element {
);
- }, [getItemContent, linesPerRow, logs, onSetActiveLog, selected, viewMode]);
+ }, [
+ getItemContent,
+ linesPerRow,
+ logs,
+ onSetActiveLog,
+ options.fontSize,
+ selected,
+ viewMode,
+ ]);
if (isLoading) {
return ;
@@ -126,6 +157,7 @@ function LogsTable(props: LogsTableProps): JSX.Element {
selectedTab={VIEW_TYPES.OVERVIEW}
log={activeLog}
onClose={onClearActiveLog}
+ onGroupByAttribute={onGroupByAttribute}
onAddToQuery={onAddToQuery}
onClickActionItem={onAddToQuery}
/>
diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx
index 3ee49ddc9a..da7bbbfc60 100644
--- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx
@@ -1,6 +1,7 @@
import { Col } from 'antd';
import logEvent from 'api/common/logEvent';
import { ENTITY_VERSION_V4 } from 'constants/app';
+import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder';
import Graph from 'container/GridCardLayout/GridCard';
import {
@@ -12,8 +13,12 @@ import {
convertRawQueriesToTraceSelectedTags,
resourceAttributesToTagFilterItems,
} from 'hooks/useResourceAttribute/utils';
-import { useEffect, useMemo, useRef, useState } from 'react';
-import { useParams } from 'react-router-dom';
+import useUrlQuery from 'hooks/useUrlQuery';
+import history from 'lib/history';
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import { useDispatch } from 'react-redux';
+import { useLocation, useParams } from 'react-router-dom';
+import { UpdateTimeInterval } from 'store/actions';
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
import { EQueryType } from 'types/common/dashboard';
import { v4 as uuid } from 'uuid';
@@ -37,6 +42,26 @@ function DBCall(): JSX.Element {
const servicename = decodeURIComponent(encodedServiceName);
const [selectedTimeStamp, setSelectedTimeStamp] = useState(0);
const { queries } = useResourceAttribute();
+ const urlQuery = useUrlQuery();
+ const { pathname } = useLocation();
+ const dispatch = useDispatch();
+
+ const onDragSelect = useCallback(
+ (start: number, end: number) => {
+ const startTimestamp = Math.trunc(start);
+ const endTimestamp = Math.trunc(end);
+
+ urlQuery.set(QueryParams.startTime, startTimestamp.toString());
+ urlQuery.set(QueryParams.endTime, endTimestamp.toString());
+ const generatedUrl = `${pathname}?${urlQuery.toString()}`;
+ history.push(generatedUrl);
+
+ if (startTimestamp !== endTimestamp) {
+ dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
+ }
+ },
+ [dispatch, pathname, urlQuery],
+ );
const tagFilterItems: TagFilterItem[] = useMemo(
() =>
@@ -150,6 +175,7 @@ function DBCall(): JSX.Element {
'database_call_rps',
);
}}
+ onDragSelect={onDragSelect}
version={ENTITY_VERSION_V4}
/>
@@ -185,6 +211,7 @@ function DBCall(): JSX.Element {
'database_call_avg_duration',
);
}}
+ onDragSelect={onDragSelect}
version={ENTITY_VERSION_V4}
/>
diff --git a/frontend/src/container/MetricsApplication/Tabs/External.tsx b/frontend/src/container/MetricsApplication/Tabs/External.tsx
index 69abd6696d..5ba3d3df6c 100644
--- a/frontend/src/container/MetricsApplication/Tabs/External.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/External.tsx
@@ -1,6 +1,7 @@
import { Col } from 'antd';
import logEvent from 'api/common/logEvent';
import { ENTITY_VERSION_V4 } from 'constants/app';
+import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder';
import Graph from 'container/GridCardLayout/GridCard';
import {
@@ -14,8 +15,12 @@ import {
convertRawQueriesToTraceSelectedTags,
resourceAttributesToTagFilterItems,
} from 'hooks/useResourceAttribute/utils';
-import { useEffect, useMemo, useRef, useState } from 'react';
-import { useParams } from 'react-router-dom';
+import useUrlQuery from 'hooks/useUrlQuery';
+import history from 'lib/history';
+import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import { useDispatch } from 'react-redux';
+import { useLocation, useParams } from 'react-router-dom';
+import { UpdateTimeInterval } from 'store/actions';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { EQueryType } from 'types/common/dashboard';
import { v4 as uuid } from 'uuid';
@@ -40,6 +45,27 @@ function External(): JSX.Element {
const servicename = decodeURIComponent(encodedServiceName);
const { queries } = useResourceAttribute();
+ const urlQuery = useUrlQuery();
+ const { pathname } = useLocation();
+ const dispatch = useDispatch();
+
+ const onDragSelect = useCallback(
+ (start: number, end: number) => {
+ const startTimestamp = Math.trunc(start);
+ const endTimestamp = Math.trunc(end);
+
+ urlQuery.set(QueryParams.startTime, startTimestamp.toString());
+ urlQuery.set(QueryParams.endTime, endTimestamp.toString());
+ const generatedUrl = `${pathname}?${urlQuery.toString()}`;
+ history.push(generatedUrl);
+
+ if (startTimestamp !== endTimestamp) {
+ dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
+ }
+ },
+ [dispatch, pathname, urlQuery],
+ );
+
const tagFilterItems = useMemo(
() =>
handleNonInQueryRange(resourceAttributesToTagFilterItems(queries)) || [],
@@ -214,6 +240,7 @@ function External(): JSX.Element {
'external_call_error_percentage',
);
}}
+ onDragSelect={onDragSelect}
version={ENTITY_VERSION_V4}
/>
@@ -249,6 +276,7 @@ function External(): JSX.Element {
'external_call_duration',
);
}}
+ onDragSelect={onDragSelect}
version={ENTITY_VERSION_V4}
/>
@@ -285,6 +313,7 @@ function External(): JSX.Element {
'external_call_rps_by_address',
)
}
+ onDragSelect={onDragSelect}
version={ENTITY_VERSION_V4}
/>
@@ -320,6 +349,7 @@ function External(): JSX.Element {
'external_call_duration_by_address',
);
}}
+ onDragSelect={onDragSelect}
version={ENTITY_VERSION_V4}
/>
diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx
index 916c3435b5..241395b23a 100644
--- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx
+++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx
@@ -185,7 +185,7 @@ function Application(): JSX.Element {
urlQuery.set(QueryParams.startTime, startTimestamp.toString());
urlQuery.set(QueryParams.endTime, endTimestamp.toString());
const generatedUrl = `${pathname}?${urlQuery.toString()}`;
- history.replace(generatedUrl);
+ history.push(generatedUrl);
if (startTimestamp !== endTimestamp) {
dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
diff --git a/frontend/src/container/MySettings/index.tsx b/frontend/src/container/MySettings/index.tsx
index 234911796e..de6c12eb53 100644
--- a/frontend/src/container/MySettings/index.tsx
+++ b/frontend/src/container/MySettings/index.tsx
@@ -26,7 +26,9 @@ function MySettings(): JSX.Element {
label: (
Light{' '}
- Beta
+
+ Beta
+
),
value: 'light',
diff --git a/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx b/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx
index 3129d76c8e..c1275ff115 100644
--- a/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx
+++ b/frontend/src/container/OnboardingContainer/OnboardingContainer.tsx
@@ -247,8 +247,7 @@ export default function Onboarding(): JSX.Element {
const handleNext = (): void => {
if (activeStep <= 3) {
- handleNextStep();
- history.replace(moduleRouteMap[selectedModule.id as ModulesMap]);
+ history.push(moduleRouteMap[selectedModule.id as ModulesMap]);
}
};
@@ -258,6 +257,13 @@ export default function Onboarding(): JSX.Element {
updateSelectedDataSource(null);
};
+ const handleBackNavigation = (): void => {
+ setCurrent(0);
+ setActiveStep(1);
+ setSelectedModule(useCases.APM);
+ resetProgress();
+ };
+
useEffect(() => {
const { pathname } = location;
@@ -277,9 +283,11 @@ export default function Onboarding(): JSX.Element {
} else if (pathname === ROUTES.GET_STARTED_AZURE_MONITORING) {
handleModuleSelect(useCases.AzureMonitoring);
handleNextStep();
+ } else {
+ handleBackNavigation();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
+ }, [location.pathname]);
const [form] = Form.useForm();
const [
diff --git a/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx b/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx
index 138aac775c..f2f7028bdc 100644
--- a/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx
+++ b/frontend/src/container/OnboardingContainer/Steps/DataSource/DataSource.tsx
@@ -131,6 +131,11 @@ export default function DataSource(): JSX.Element {
};
const goToIntegrationsPage = (): void => {
+ logEvent('Onboarding V2: Go to integrations', {
+ module: selectedModule?.id,
+ dataSource: selectedDataSource?.name,
+ framework: selectedFramework,
+ });
history.push(ROUTES.INTEGRATIONS);
};
diff --git a/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx b/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx
index ae74930d57..f9efa061f7 100644
--- a/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx
+++ b/frontend/src/container/OnboardingContainer/common/ModuleStepsContainer/ModuleStepsContainer.tsx
@@ -11,13 +11,15 @@ import {
} from '@ant-design/icons';
import { Button, Space, Steps, Typography } from 'antd';
import logEvent from 'api/common/logEvent';
+import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
+import { onboardingHelpMessage } from 'components/LaunchChatSupport/util';
import ROUTES from 'constants/routes';
import { stepsMap } from 'container/OnboardingContainer/constants/stepsConfig';
import { DataSourceType } from 'container/OnboardingContainer/Steps/DataSource/DataSource';
import { hasFrameworks } from 'container/OnboardingContainer/utils/dataSourceUtils';
import history from 'lib/history';
import { isEmpty, isNull } from 'lodash-es';
-import { HelpCircle, UserPlus } from 'lucide-react';
+import { UserPlus } from 'lucide-react';
import { SetStateAction, useState } from 'react';
import { useOnboardingContext } from '../../context/OnboardingContext';
@@ -381,31 +383,6 @@ export default function ModuleStepsContainer({
history.push('/');
};
- const handleFacingIssuesClick = (): void => {
- logEvent('Onboarding V2: Facing Issues Sending Data to SigNoz', {
- dataSource: selectedDataSource?.id,
- framework: selectedFramework,
- environment: selectedEnvironment,
- module: activeStep?.module?.id,
- step: activeStep?.step?.id,
- });
-
- const message = `Hi Team,
-
-I am facing issues sending data to SigNoz. Here are my application details
-
-Data Source: ${selectedDataSource?.name}
-Framework:
-Environment:
-Module: ${activeStep?.module?.id}
-
-Thanks
-`;
- if (window.Intercom) {
- window.Intercom('showNewMessage', message);
- }
- };
-
return (
@@ -493,19 +470,26 @@ Thanks
>
Back
-
}>
{current < lastStepIndex ? 'Continue to next step' : 'Done'}
-
- }
- >
- Facing issues sending data to SigNoz?
-
+
diff --git a/frontend/src/container/OptionsMenu/constants.ts b/frontend/src/container/OptionsMenu/constants.ts
index 2db02f85b8..7b591cd4c5 100644
--- a/frontend/src/container/OptionsMenu/constants.ts
+++ b/frontend/src/container/OptionsMenu/constants.ts
@@ -1,6 +1,6 @@
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
-import { OptionsQuery } from './types';
+import { FontSize, OptionsQuery } from './types';
export const URL_OPTIONS = 'options';
@@ -8,6 +8,7 @@ export const defaultOptionsQuery: OptionsQuery = {
selectColumns: [],
maxLines: 2,
format: 'list',
+ fontSize: FontSize.SMALL,
};
export const defaultTraceSelectedColumns = [
@@ -18,6 +19,7 @@ export const defaultTraceSelectedColumns = [
isColumn: true,
isJSON: false,
id: 'serviceName--string--tag--true',
+ isIndexed: false,
},
{
key: 'name',
@@ -26,6 +28,7 @@ export const defaultTraceSelectedColumns = [
isColumn: true,
isJSON: false,
id: 'name--string--tag--true',
+ isIndexed: false,
},
{
key: 'durationNano',
@@ -34,6 +37,7 @@ export const defaultTraceSelectedColumns = [
isColumn: true,
isJSON: false,
id: 'durationNano--float64--tag--true',
+ isIndexed: false,
},
{
key: 'httpMethod',
@@ -42,6 +46,7 @@ export const defaultTraceSelectedColumns = [
isColumn: true,
isJSON: false,
id: 'httpMethod--string--tag--true',
+ isIndexed: false,
},
{
key: 'responseStatusCode',
@@ -50,5 +55,6 @@ export const defaultTraceSelectedColumns = [
isColumn: true,
isJSON: false,
id: 'responseStatusCode--string--tag--true',
+ isIndexed: false,
},
];
diff --git a/frontend/src/container/OptionsMenu/types.ts b/frontend/src/container/OptionsMenu/types.ts
index 57b81364d6..2c57d66b28 100644
--- a/frontend/src/container/OptionsMenu/types.ts
+++ b/frontend/src/container/OptionsMenu/types.ts
@@ -2,10 +2,21 @@ import { InputNumberProps, RadioProps, SelectProps } from 'antd';
import { LogViewMode } from 'container/LogsTable';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
+export enum FontSize {
+ SMALL = 'small',
+ MEDIUM = 'medium',
+ LARGE = 'large',
+}
+
+interface FontSizeProps {
+ value: FontSize;
+ onChange: (val: FontSize) => void;
+}
export interface OptionsQuery {
selectColumns: BaseAutocompleteData[];
maxLines: number;
format: LogViewMode;
+ fontSize: FontSize;
}
export interface InitialOptions
@@ -18,6 +29,7 @@ export type OptionsMenuConfig = {
onChange: (value: LogViewMode) => void;
};
maxLines?: Pick
;
+ fontSize?: FontSizeProps;
addColumn?: Pick<
SelectProps,
'options' | 'onSelect' | 'onFocus' | 'onSearch' | 'onBlur'
diff --git a/frontend/src/container/OptionsMenu/useOptionsMenu.ts b/frontend/src/container/OptionsMenu/useOptionsMenu.ts
index 97fbbbb006..7b3cfce035 100644
--- a/frontend/src/container/OptionsMenu/useOptionsMenu.ts
+++ b/frontend/src/container/OptionsMenu/useOptionsMenu.ts
@@ -7,6 +7,10 @@ import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
import useDebounce from 'hooks/useDebounce';
import { useNotifications } from 'hooks/useNotifications';
import useUrlQueryData from 'hooks/useUrlQueryData';
+import {
+ AllTraceFilterKeys,
+ AllTraceFilterKeyValue,
+} from 'pages/TracesExplorer/Filter/filterUtils';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useQueries } from 'react-query';
import { ErrorResponse, SuccessResponse } from 'types/api';
@@ -21,7 +25,12 @@ import {
defaultTraceSelectedColumns,
URL_OPTIONS,
} from './constants';
-import { InitialOptions, OptionsMenuConfig, OptionsQuery } from './types';
+import {
+ FontSize,
+ InitialOptions,
+ OptionsMenuConfig,
+ OptionsQuery,
+} from './types';
import { getOptionsFromKeys } from './utils';
interface UseOptionsMenuProps {
@@ -106,15 +115,40 @@ const useOptionsMenu = ({
[] as BaseAutocompleteData[],
);
- return (
- (initialOptions.selectColumns
- ?.map((column) => attributesData.find(({ key }) => key === column))
- .filter(Boolean) as BaseAutocompleteData[]) || []
- );
+ let initialSelected = initialOptions.selectColumns
+ ?.map((column) => attributesData.find(({ key }) => key === column))
+ .filter(Boolean) as BaseAutocompleteData[];
+
+ if (dataSource === DataSource.TRACES) {
+ initialSelected = initialSelected
+ ?.map((col) => {
+ if (col && Object.keys(AllTraceFilterKeyValue).includes(col?.key)) {
+ const metaData = defaultTraceSelectedColumns.find(
+ (coln) => coln.key === (col.key as AllTraceFilterKeys),
+ );
+
+ return {
+ ...metaData,
+ key: metaData?.key,
+ dataType: metaData?.dataType,
+ type: metaData?.type,
+ isColumn: metaData?.isColumn,
+ isJSON: metaData?.isJSON,
+ id: metaData?.id,
+ };
+ }
+ return col;
+ })
+ .filter(Boolean) as BaseAutocompleteData[];
+ }
+
+ return initialSelected || [];
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [
isFetchedInitialAttributes,
initialOptions?.selectColumns,
initialAttributesResult,
+ dataSource,
]);
const {
@@ -248,6 +282,17 @@ const useOptionsMenu = ({
},
[handleRedirectWithOptionsData, optionsQueryData],
);
+ const handleFontSizeChange = useCallback(
+ (value: FontSize) => {
+ const optionsData: OptionsQuery = {
+ ...optionsQueryData,
+ fontSize: value,
+ };
+
+ handleRedirectWithOptionsData(optionsData);
+ },
+ [handleRedirectWithOptionsData, optionsQueryData],
+ );
const handleSearchAttribute = useCallback((value: string) => {
setSearchText(value);
@@ -282,18 +327,24 @@ const useOptionsMenu = ({
value: optionsQueryData.maxLines || defaultOptionsQuery.maxLines,
onChange: handleMaxLinesChange,
},
+ fontSize: {
+ value: optionsQueryData?.fontSize || defaultOptionsQuery.fontSize,
+ onChange: handleFontSizeChange,
+ },
}),
[
- optionsFromAttributeKeys,
- optionsQueryData?.maxLines,
- optionsQueryData?.format,
- optionsQueryData?.selectColumns,
isSearchedAttributesFetching,
- handleSearchAttribute,
+ optionsQueryData?.selectColumns,
+ optionsQueryData.format,
+ optionsQueryData.maxLines,
+ optionsQueryData?.fontSize,
+ optionsFromAttributeKeys,
handleSelectColumns,
handleRemoveSelectedColumn,
+ handleSearchAttribute,
handleFormatChange,
handleMaxLinesChange,
+ handleFontSizeChange,
],
);
diff --git a/frontend/src/container/PanelWrapper/PanelWrapper.tsx b/frontend/src/container/PanelWrapper/PanelWrapper.tsx
index fdc20f7d30..ed105b3948 100644
--- a/frontend/src/container/PanelWrapper/PanelWrapper.tsx
+++ b/frontend/src/container/PanelWrapper/PanelWrapper.tsx
@@ -15,6 +15,7 @@ function PanelWrapper({
onDragSelect,
selectedGraph,
tableProcessedDataRef,
+ customTooltipElement,
}: PanelWrapperProps): JSX.Element {
const Component = PanelTypeVsPanelWrapper[
selectedGraph || widget.panelTypes
@@ -37,6 +38,7 @@ function PanelWrapper({
onDragSelect={onDragSelect}
selectedGraph={selectedGraph}
tableProcessedDataRef={tableProcessedDataRef}
+ customTooltipElement={customTooltipElement}
/>
);
}
diff --git a/frontend/src/container/PanelWrapper/UplotPanelWrapper.tsx b/frontend/src/container/PanelWrapper/UplotPanelWrapper.tsx
index dc404adabb..ff231b563a 100644
--- a/frontend/src/container/PanelWrapper/UplotPanelWrapper.tsx
+++ b/frontend/src/container/PanelWrapper/UplotPanelWrapper.tsx
@@ -30,6 +30,7 @@ function UplotPanelWrapper({
onClickHandler,
onDragSelect,
selectedGraph,
+ customTooltipElement,
}: PanelWrapperProps): JSX.Element {
const { toScrollWidgetId, setToScrollWidgetId } = useDashboard();
const isDarkMode = useIsDarkMode();
@@ -126,6 +127,7 @@ function UplotPanelWrapper({
stackBarChart: widget?.stackedBarChart,
hiddenGraph,
setHiddenGraph,
+ customTooltipElement,
}),
[
widget?.id,
@@ -147,6 +149,7 @@ function UplotPanelWrapper({
selectedGraph,
currentQuery,
hiddenGraph,
+ customTooltipElement,
],
);
diff --git a/frontend/src/container/PanelWrapper/panelWrapper.types.ts b/frontend/src/container/PanelWrapper/panelWrapper.types.ts
index 6fd993f377..7d5e3122e8 100644
--- a/frontend/src/container/PanelWrapper/panelWrapper.types.ts
+++ b/frontend/src/container/PanelWrapper/panelWrapper.types.ts
@@ -23,6 +23,7 @@ export type PanelWrapperProps = {
onDragSelect: (start: number, end: number) => void;
selectedGraph?: PANEL_TYPES;
tableProcessedDataRef?: React.MutableRefObject;
+ customTooltipElement?: HTMLDivElement;
};
export type TooltipData = {
diff --git a/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/index.tsx b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/index.tsx
index f7d3af3a88..5bbe6de737 100644
--- a/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/index.tsx
+++ b/frontend/src/container/PipelinePage/PipelineListsView/Preview/components/LogsList/index.tsx
@@ -13,6 +13,7 @@ function LogsList({ logs }: LogsListProps): JSX.Element {
onSetActiveLog,
onClearActiveLog,
onAddToQuery,
+ onGroupByAttribute,
} = useActiveLog();
const makeLogDetailsHandler = (log: ILog) => (): void => onSetActiveLog(log);
@@ -42,6 +43,7 @@ function LogsList({ logs }: LogsListProps): JSX.Element {
onClose={onClearActiveLog}
onAddToQuery={onAddToQuery}
onClickActionItem={onAddToQuery}
+ onGroupByAttribute={onGroupByAttribute}
/>
);
diff --git a/frontend/src/container/QueryBuilder/components/ToolbarActions/RightToolbarActions.tsx b/frontend/src/container/QueryBuilder/components/ToolbarActions/RightToolbarActions.tsx
index aea205d569..f5106dffbe 100644
--- a/frontend/src/container/QueryBuilder/components/ToolbarActions/RightToolbarActions.tsx
+++ b/frontend/src/container/QueryBuilder/components/ToolbarActions/RightToolbarActions.tsx
@@ -3,18 +3,27 @@ import './ToolbarActions.styles.scss';
import { Button } from 'antd';
import { LogsExplorerShortcuts } from 'constants/shortcuts/logsExplorerShortcuts';
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
-import { Play } from 'lucide-react';
-import { useEffect } from 'react';
+import { Play, X } from 'lucide-react';
+import { MutableRefObject, useEffect } from 'react';
+import { useQueryClient } from 'react-query';
interface RightToolbarActionsProps {
onStageRunQuery: () => void;
+ isLoadingQueries?: boolean;
+ listQueryKeyRef?: MutableRefObject