mirror of
https://github.com/hyperdxio/hyperdx
synced 2026-04-21 13:37:15 +00:00
Refactor: Remove bootstrap, adopt semantic tokens, and improve Mantine UI usage (#1347)
This commit is contained in:
parent
a7e150c825
commit
af6a8d0dac
118 changed files with 2828 additions and 2070 deletions
5
.changeset/semantic-color-tokens.md
Normal file
5
.changeset/semantic-color-tokens.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@hyperdx/app": minor
|
||||
---
|
||||
|
||||
feat: Remove `bootstrap`, `react-bootstrap` and unused `react-bootstrap-range-slider`, adopt semantic tokens, and improve Mantine UI usage
|
||||
|
|
@ -50,7 +50,6 @@
|
|||
"@uiw/codemirror-themes": "^4.23.3",
|
||||
"@uiw/react-codemirror": "^4.23.3",
|
||||
"@xyflow/react": "^12.9.0",
|
||||
"bootstrap": "^5.1.3",
|
||||
"chrono-node": "^2.7.8",
|
||||
"classnames": "^2.3.1",
|
||||
"crypto-js": "^4.2.0",
|
||||
|
|
@ -76,8 +75,6 @@
|
|||
"nuqs": "^1.17.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"react": "18.3.1",
|
||||
"react-bootstrap": "^2.4.0",
|
||||
"react-bootstrap-range-slider": "^3.0.8",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "18.3.1",
|
||||
"react-error-boundary": "^3.1.4",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import Head from 'next/head';
|
|||
import { NextAdapter } from 'next-query-params';
|
||||
import randomUUID from 'crypto-randomuuid';
|
||||
import { enableMapSet } from 'immer';
|
||||
import SSRProvider from 'react-bootstrap/SSRProvider';
|
||||
import { QueryParamProvider } from 'use-query-params';
|
||||
import HyperDX from '@hyperdx/browser';
|
||||
import { ColorSchemeScript } from '@mantine/core';
|
||||
|
|
@ -96,13 +95,11 @@ export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
|||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.className =
|
||||
userPreferences.theme === 'dark' ? 'hdx-theme-dark' : 'hdx-theme-light';
|
||||
// TODO: Remove after migration to Mantine
|
||||
document.body.style.fontFamily = userPreferences.font
|
||||
? `"${userPreferences.font}", sans-serif`
|
||||
: '"IBM Plex Mono"';
|
||||
}, [userPreferences.theme, userPreferences.font]);
|
||||
}, [userPreferences.font]);
|
||||
|
||||
const getLayout = Component.getLayout ?? (page => page);
|
||||
|
||||
|
|
@ -117,23 +114,26 @@ export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
|
|||
/>
|
||||
<meta name="theme-color" content="#25292e"></meta>
|
||||
<meta name="google" content="notranslate" />
|
||||
<ColorSchemeScript forceColorScheme="dark" />
|
||||
<ColorSchemeScript
|
||||
forceColorScheme={userPreferences.theme === 'dark' ? 'dark' : 'light'}
|
||||
/>
|
||||
</Head>
|
||||
|
||||
<SSRProvider>
|
||||
<HDXQueryParamProvider>
|
||||
<QueryParamProvider adapter={NextAdapter}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ThemeWrapper fontFamily={userPreferences.font}>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
{confirmModal}
|
||||
</ThemeWrapper>
|
||||
<ReactQueryDevtools initialIsOpen={true} />
|
||||
{background}
|
||||
</QueryClientProvider>
|
||||
</QueryParamProvider>
|
||||
</HDXQueryParamProvider>
|
||||
</SSRProvider>
|
||||
<HDXQueryParamProvider>
|
||||
<QueryParamProvider adapter={NextAdapter}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ThemeWrapper
|
||||
fontFamily={userPreferences.font}
|
||||
colorScheme={userPreferences.theme === 'dark' ? 'dark' : 'light'}
|
||||
>
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
{confirmModal}
|
||||
</ThemeWrapper>
|
||||
<ReactQueryDevtools initialIsOpen={true} />
|
||||
{background}
|
||||
</QueryClientProvider>
|
||||
</QueryParamProvider>
|
||||
</HDXQueryParamProvider>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ function AlertHistoryCardList({ history }: { history: AlertHistory[] }) {
|
|||
return (
|
||||
<div className={styles.historyCardWrapper}>
|
||||
{paddingItems.map((_, index) => (
|
||||
<Tooltip label="No data" color="dark" withArrow key={index}>
|
||||
<Tooltip label="No data" withArrow key={index}>
|
||||
<div className={styles.historyCard} />
|
||||
</Tooltip>
|
||||
))}
|
||||
|
|
@ -84,7 +84,7 @@ function AlertDetails({ alert }: { alert: AlertsPageItem }) {
|
|||
{alert.dashboard?.name}
|
||||
{tileName ? (
|
||||
<>
|
||||
<i className="bi bi-chevron-right fs-8 mx-1 text-slate-400" />
|
||||
<i className="bi bi-chevron-right fs-8 mx-1 " />
|
||||
{tileName}
|
||||
</>
|
||||
) : null}
|
||||
|
|
@ -123,7 +123,7 @@ function AlertDetails({ alert }: { alert: AlertsPageItem }) {
|
|||
<>
|
||||
If value is {alert.thresholdType === 'above' ? 'over' : 'under'}{' '}
|
||||
<span className="fw-bold">{alert.threshold}</span>
|
||||
<span className="text-slate-400">·</span>
|
||||
<span>·</span>
|
||||
</>
|
||||
);
|
||||
}, [alert]);
|
||||
|
|
@ -172,16 +172,16 @@ function AlertDetails({ alert }: { alert: AlertsPageItem }) {
|
|||
className={styles.alertLink}
|
||||
title={linkTitle}
|
||||
>
|
||||
<i className={`bi ${alertIcon} text-slate-200 me-2 fs-8`} />
|
||||
<i className={`bi ${alertIcon} me-2 fs-8`} />
|
||||
{alertName}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="text-slate-400 fs-8 d-flex gap-2">
|
||||
<div className="fs-8 d-flex gap-2">
|
||||
{alertType}
|
||||
{notificationMethod}
|
||||
{alert.createdBy && (
|
||||
<>
|
||||
<span className="text-slate-400">·</span>
|
||||
<span>·</span>
|
||||
<span>
|
||||
Created by {alert.createdBy.name || alert.createdBy.email}
|
||||
</span>
|
||||
|
|
@ -219,7 +219,7 @@ function AlertCardList({ alerts }: { alerts: AlertsPageItem[] }) {
|
|||
<i className="bi bi-check-lg"></i> OK
|
||||
</div>
|
||||
{okData.length === 0 && (
|
||||
<div className="text-center text-slate-400 my-4 fs-8">No alerts</div>
|
||||
<div className="text-center my-4 fs-8">No alerts</div>
|
||||
)}
|
||||
{okData.map((alert, index) => (
|
||||
<AlertDetails key={index} alert={alert} />
|
||||
|
|
@ -243,7 +243,7 @@ export default function AlertsPage() {
|
|||
<div className="my-4">
|
||||
<Container maw={1500}>
|
||||
<Alert
|
||||
icon={<i className="bi bi-info-circle-fill text-slate-400" />}
|
||||
icon={<i className="bi bi-info-circle-fill " />}
|
||||
color="gray"
|
||||
py="xs"
|
||||
mt="md"
|
||||
|
|
@ -259,19 +259,15 @@ export default function AlertsPage() {
|
|||
from dashboard charts and saved searches.
|
||||
</Alert>
|
||||
{isLoading ? (
|
||||
<div className="text-center text-slate-400 my-4 fs-8">
|
||||
Loading...
|
||||
</div>
|
||||
<div className="text-center my-4 fs-8">Loading...</div>
|
||||
) : isError ? (
|
||||
<div className="text-center text-slate-400 my-4 fs-8">Error</div>
|
||||
<div className="text-center my-4 fs-8">Error</div>
|
||||
) : alerts?.length ? (
|
||||
<>
|
||||
<AlertCardList alerts={alerts} />
|
||||
</>
|
||||
) : (
|
||||
<div className="text-center text-slate-400 my-4 fs-8">
|
||||
No alerts created yet
|
||||
</div>
|
||||
<div className="text-center my-4 fs-8">No alerts created yet</div>
|
||||
)}
|
||||
</Container>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -33,10 +33,8 @@ export const AppNavContext = React.createContext<{
|
|||
|
||||
export const AppNavCloudBanner = () => {
|
||||
return (
|
||||
<div className="my-3 bg-hdx-dark rounded p-2 text-center">
|
||||
<span className="text-slate-300 fs-8">
|
||||
Ready to deploy on ClickHouse Cloud?
|
||||
</span>
|
||||
<div className="my-3 bg-muted rounded p-2 text-center">
|
||||
<span className="fs-8">Ready to deploy on ClickHouse Cloud?</span>
|
||||
<div className="mt-2 mb-2">
|
||||
<Link
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack/getting-started#deploy-with-clickhouse-cloud"
|
||||
|
|
@ -84,16 +82,9 @@ export const AppNavUserMenu = ({
|
|||
<Menu.Target>
|
||||
<Paper
|
||||
data-testid="user-menu-trigger"
|
||||
m="sm"
|
||||
mt={8}
|
||||
px={8}
|
||||
py={4}
|
||||
radius="md"
|
||||
{...(isCollapsed && {
|
||||
p: 2,
|
||||
bg: 'transparent',
|
||||
className={cx(styles.userMenuTrigger, {
|
||||
[styles.userMenuTriggerCollapsed]: isCollapsed,
|
||||
})}
|
||||
className={styles.appNavMenu}
|
||||
>
|
||||
<Group gap="xs" wrap="nowrap" miw={0}>
|
||||
<Avatar size="sm" radius="xl" color="green">
|
||||
|
|
@ -141,7 +132,7 @@ export const AppNavUserMenu = ({
|
|||
</Text>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Icon name="chevron-right" className="fs-8 text-slate-400" />
|
||||
<Icon name="chevron-right" className="fs-8 " />
|
||||
</>
|
||||
)}
|
||||
</Group>
|
||||
|
|
@ -213,21 +204,13 @@ export const AppNavHelpMenu = ({
|
|||
] = useDisclosure(false);
|
||||
|
||||
// const isTeamHasNoData = useIsTeamHasNoData();
|
||||
const size = 28;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Paper
|
||||
mb={8}
|
||||
ml="sm"
|
||||
withBorder
|
||||
w={size}
|
||||
h={size}
|
||||
radius="xl"
|
||||
{...(isCollapsed && {
|
||||
ml: 'sm',
|
||||
className={cx(styles.helpMenuTrigger, {
|
||||
[styles.helpMenuTriggerCollapsed]: isCollapsed,
|
||||
})}
|
||||
className={styles.appNavMenu}
|
||||
>
|
||||
<Menu
|
||||
withArrow
|
||||
|
|
@ -237,12 +220,7 @@ export const AppNavHelpMenu = ({
|
|||
>
|
||||
<Menu.Target>
|
||||
<UnstyledButton data-testid="help-menu-trigger" w="100%">
|
||||
<Group
|
||||
align="center"
|
||||
justify="center"
|
||||
h={size}
|
||||
className="text-slate-200 "
|
||||
>
|
||||
<Group align="center" justify="center" h={28}>
|
||||
<Icon name="question-lg" />
|
||||
</Group>
|
||||
</UnstyledButton>
|
||||
|
|
@ -251,7 +229,7 @@ export const AppNavHelpMenu = ({
|
|||
<Menu.Label>
|
||||
Help{' '}
|
||||
{version && (
|
||||
<Text size="xs" c="gray.7" component="span">
|
||||
<Text size="xs" component="span">
|
||||
v{version}
|
||||
</Text>
|
||||
)}
|
||||
|
|
@ -295,7 +273,7 @@ export const AppNavHelpMenu = ({
|
|||
export const AppNavLink = ({
|
||||
className,
|
||||
label,
|
||||
iconName,
|
||||
icon,
|
||||
href,
|
||||
isExpanded,
|
||||
onToggle,
|
||||
|
|
@ -303,7 +281,7 @@ export const AppNavLink = ({
|
|||
}: {
|
||||
className?: string;
|
||||
label: React.ReactNode;
|
||||
iconName: string;
|
||||
icon: React.ReactNode;
|
||||
href: string;
|
||||
isExpanded?: boolean;
|
||||
onToggle?: () => void;
|
||||
|
|
@ -320,37 +298,26 @@ export const AppNavLink = ({
|
|||
data-testid={testId}
|
||||
href={href}
|
||||
className={cx(
|
||||
styles.listLink,
|
||||
{ [styles.listLinkActive]: pathname?.includes(href) },
|
||||
className,
|
||||
'text-decoration-none d-flex justify-content-between align-items-center fs-7 text-muted-hover',
|
||||
{ 'fw-600 text-success': pathname?.includes(href) },
|
||||
)}
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
>
|
||||
<span>
|
||||
<i className={`bi ${iconName} pe-2 text-slate-300`} />{' '}
|
||||
{!isCollapsed && (
|
||||
<span>
|
||||
{label}
|
||||
{isBeta && (
|
||||
<Badge
|
||||
size="xs"
|
||||
ms="xs"
|
||||
color="gray.4"
|
||||
autoContrast
|
||||
radius="sm"
|
||||
className="align-text-bottom"
|
||||
>
|
||||
Beta
|
||||
</Badge>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
<span style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<span className={styles.linkIcon}>{icon}</span>
|
||||
{!isCollapsed && <span>{label}</span>}
|
||||
</span>
|
||||
</Link>
|
||||
{!isCollapsed && isBeta && (
|
||||
<Badge size="xs" radius="sm" color="gray" style={{ marginRight: 8 }}>
|
||||
Beta
|
||||
</Badge>
|
||||
)}
|
||||
{!isCollapsed && onToggle && (
|
||||
<ActionIcon
|
||||
data-testid={`${testId}-toggle`}
|
||||
variant="subtle"
|
||||
color="dark.2"
|
||||
size="sm"
|
||||
onClick={onToggle}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
import HyperDX from '@hyperdx/browser';
|
||||
import { AlertState } from '@hyperdx/common-utils/dist/types';
|
||||
import {
|
||||
ActionIcon,
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
|
|
@ -24,6 +25,16 @@ import {
|
|||
ScrollArea,
|
||||
} from '@mantine/core';
|
||||
import { useDisclosure, useLocalStorage } from '@mantine/hooks';
|
||||
import {
|
||||
IconBell,
|
||||
IconChartDots,
|
||||
IconDeviceLaptop,
|
||||
IconLayoutGrid,
|
||||
IconLayoutSidebarLeftCollapse,
|
||||
IconSettings,
|
||||
IconSitemap,
|
||||
IconTable,
|
||||
} from '@tabler/icons-react';
|
||||
|
||||
import {
|
||||
useCreateDashboard,
|
||||
|
|
@ -66,10 +77,10 @@ function NewDashboardButton() {
|
|||
<Button
|
||||
data-testid="create-dashboard-button"
|
||||
variant="transparent"
|
||||
color="var(--color-text)"
|
||||
py="0px"
|
||||
px="sm"
|
||||
fw={400}
|
||||
color="gray.2"
|
||||
>
|
||||
<span className="pe-2">+</span> Create Dashboard
|
||||
</Button>
|
||||
|
|
@ -81,10 +92,10 @@ function NewDashboardButton() {
|
|||
<Button
|
||||
data-testid="create-dashboard-button"
|
||||
variant="transparent"
|
||||
color="var(--color-text)"
|
||||
py="0px"
|
||||
px="sm"
|
||||
fw={400}
|
||||
color="gray.2"
|
||||
onClick={() =>
|
||||
createDashboard.mutate(
|
||||
{
|
||||
|
|
@ -144,7 +155,7 @@ function SearchInput({
|
|||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={e => onChange(e.currentTarget.value)}
|
||||
leftSection={<i className="bi bi-search fs-8 ps-1 text-slate-400" />}
|
||||
leftSection={<i className="bi bi-search fs-8 ps-1 " />}
|
||||
onKeyDown={handleKeyDown}
|
||||
rightSection={
|
||||
value ? (
|
||||
|
|
@ -461,8 +472,8 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
key={savedSearch.id}
|
||||
tabIndex={0}
|
||||
className={cx(
|
||||
styles.listLink,
|
||||
savedSearch.id === query.savedSearchId && styles.listLinkActive,
|
||||
styles.nestedLink,
|
||||
savedSearch.id === query.savedSearchId && styles.nestedLinkActive,
|
||||
)}
|
||||
title={savedSearch.name}
|
||||
draggable
|
||||
|
|
@ -524,8 +535,8 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
href={`/dashboards/${dashboard.id}`}
|
||||
key={dashboard.id}
|
||||
tabIndex={0}
|
||||
className={cx(styles.listLink, {
|
||||
[styles.listLinkActive]: dashboard.id === query.dashboardId,
|
||||
className={cx(styles.nestedLink, {
|
||||
[styles.nestedLinkActive]: dashboard.id === query.dashboardId,
|
||||
})}
|
||||
draggable
|
||||
data-dashboardid={dashboard.id}
|
||||
|
|
@ -606,7 +617,6 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
<Badge
|
||||
size="xs"
|
||||
color="gray"
|
||||
bg="gray.8"
|
||||
variant="light"
|
||||
fw="normal"
|
||||
title="Showing time in UTC"
|
||||
|
|
@ -617,19 +627,16 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
</Group>
|
||||
)}
|
||||
</Link>
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="gray.4"
|
||||
p={isCollapsed ? '0px' : '8px'}
|
||||
h="32px"
|
||||
size="md"
|
||||
<ActionIcon
|
||||
variant="transparent"
|
||||
size="sm"
|
||||
className={isCollapsed ? 'mt-4' : ''}
|
||||
style={{ marginRight: -4 }}
|
||||
style={{ marginRight: -4, marginLeft: -4 }}
|
||||
title="Collapse/Expand Navigation"
|
||||
onClick={() => setIsPreferCollapsed((v: boolean) => !v)}
|
||||
>
|
||||
<i className="bi bi-layout-sidebar"></i>
|
||||
</Button>
|
||||
<IconLayoutSidebarLeftCollapse size={16} />
|
||||
</ActionIcon>
|
||||
</div>
|
||||
</div>
|
||||
<ScrollArea
|
||||
|
|
@ -647,7 +654,7 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
<div className="mt-2">
|
||||
<AppNavLink
|
||||
label="Search"
|
||||
iconName="bi-layout-text-sidebar-reverse"
|
||||
icon={<IconTable size={16} />}
|
||||
href="/search"
|
||||
className={cx({
|
||||
'text-success fw-600':
|
||||
|
|
@ -667,13 +674,7 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
<Collapse in={isSearchExpanded}>
|
||||
<div className={styles.list}>
|
||||
{isLogViewsLoading ? (
|
||||
<Loader
|
||||
color="gray.7"
|
||||
variant="dots"
|
||||
mx="md"
|
||||
my="xs"
|
||||
size="sm"
|
||||
/>
|
||||
<Loader variant="dots" mx="md" my="xs" size="sm" />
|
||||
) : (
|
||||
!IS_LOCAL_MODE && (
|
||||
<>
|
||||
|
|
@ -719,28 +720,32 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
<AppNavLink
|
||||
label="Chart Explorer"
|
||||
href="/chart"
|
||||
iconName="bi-graph-up"
|
||||
icon={<IconChartDots size={16} />}
|
||||
/>
|
||||
{!IS_LOCAL_MODE && (
|
||||
<AppNavLink label="Alerts" href="/alerts" iconName="bi-bell" />
|
||||
<AppNavLink
|
||||
label="Alerts"
|
||||
href="/alerts"
|
||||
icon={<IconBell size={16} />}
|
||||
/>
|
||||
)}
|
||||
<AppNavLink
|
||||
label="Client Sessions"
|
||||
href="/sessions"
|
||||
iconName="bi-laptop"
|
||||
icon={<IconDeviceLaptop size={16} />}
|
||||
/>
|
||||
|
||||
<AppNavLink
|
||||
label="Service Map"
|
||||
href="/service-map"
|
||||
iconName="bi-diagram-2-fill"
|
||||
icon={<IconSitemap size={16} />}
|
||||
isBeta
|
||||
/>
|
||||
|
||||
<AppNavLink
|
||||
label="Dashboards"
|
||||
href="/dashboards"
|
||||
iconName="bi-grid-1x2"
|
||||
icon={<IconLayoutGrid size={16} />}
|
||||
isExpanded={isDashboardsExpanded}
|
||||
onToggle={() => setIsDashboardExpanded(!isDashboardsExpanded)}
|
||||
/>
|
||||
|
|
@ -751,13 +756,7 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
<NewDashboardButton />
|
||||
|
||||
{isDashboardsLoading ? (
|
||||
<Loader
|
||||
color="gray.7"
|
||||
variant="dots"
|
||||
mx="md"
|
||||
my="xs"
|
||||
size="sm"
|
||||
/>
|
||||
<Loader variant="dots" mx="md" my="xs" size="sm" />
|
||||
) : (
|
||||
!IS_LOCAL_MODE && (
|
||||
<>
|
||||
|
|
@ -811,8 +810,8 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
<Link
|
||||
href={`/clickhouse`}
|
||||
tabIndex={0}
|
||||
className={cx(styles.listLink, {
|
||||
[styles.listLinkActive]:
|
||||
className={cx(styles.nestedLink, {
|
||||
[styles.nestedLinkActive]:
|
||||
pathname.startsWith('/clickhouse'),
|
||||
})}
|
||||
>
|
||||
|
|
@ -821,8 +820,8 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
<Link
|
||||
href={`/services`}
|
||||
tabIndex={0}
|
||||
className={cx(styles.listLink, {
|
||||
[styles.listLinkActive]:
|
||||
className={cx(styles.nestedLink, {
|
||||
[styles.nestedLinkActive]:
|
||||
pathname.startsWith('/services'),
|
||||
})}
|
||||
>
|
||||
|
|
@ -832,8 +831,8 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
<Link
|
||||
href={`/kubernetes`}
|
||||
tabIndex={0}
|
||||
className={cx(styles.listLink, {
|
||||
[styles.listLinkActive]:
|
||||
className={cx(styles.nestedLink, {
|
||||
[styles.nestedLinkActive]:
|
||||
pathname.startsWith('/kubernetes'),
|
||||
})}
|
||||
data-testid="k8s-dashboard-nav-link"
|
||||
|
|
@ -851,7 +850,7 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
<AppNavLink
|
||||
label="Team Settings"
|
||||
href="/team"
|
||||
iconName="bi-gear"
|
||||
icon={<IconSettings size={16} />}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
|
@ -871,11 +870,9 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
|
|||
</ScrollArea>
|
||||
|
||||
<div
|
||||
className={styles.bottomSection}
|
||||
style={{
|
||||
width: navWidth,
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<AppNavHelpMenu
|
||||
|
|
|
|||
|
|
@ -124,10 +124,7 @@ export default function AuthPage({ action }: { action: 'register' | 'login' }) {
|
|||
<LandingHeader activeKey={`/${action}`} fixed />
|
||||
<div className="d-flex justify-content-center align-items-center vh-100">
|
||||
<div style={{ width: '26rem' }}>
|
||||
<div
|
||||
className="text-center mb-2 fs-5 text-slate-300"
|
||||
style={{ marginTop: -30 }}
|
||||
>
|
||||
<div className="text-center mb-2 fs-5 " style={{ marginTop: -30 }}>
|
||||
{config.IS_OSS && isRegister
|
||||
? 'Setup '
|
||||
: isRegister
|
||||
|
|
@ -136,7 +133,7 @@ export default function AuthPage({ action }: { action: 'register' | 'login' }) {
|
|||
<span className="text-success fw-bold">HyperDX</span>
|
||||
</div>
|
||||
{action === 'login' && (
|
||||
<div className="text-center mb-2 text-slate-300">Welcome back!</div>
|
||||
<div className="text-center mb-2 ">Welcome back!</div>
|
||||
)}
|
||||
{isRegister && config.IS_OSS === true && (
|
||||
<div className="text-center mb-2 text-muted">
|
||||
|
|
@ -187,7 +184,7 @@ export default function AuthPage({ action }: { action: 'register' | 'login' }) {
|
|||
placeholder="Confirm Password"
|
||||
{...form.confirmPassword}
|
||||
/>
|
||||
<Notification color="gray.7" withCloseButton={false}>
|
||||
<Notification withCloseButton={false}>
|
||||
<PasswordCheck password={currentPassword} />
|
||||
</Notification>
|
||||
</>
|
||||
|
|
@ -243,19 +240,13 @@ export default function AuthPage({ action }: { action: 'register' | 'login' }) {
|
|||
)}
|
||||
|
||||
{isRegister && config.IS_OSS === false && (
|
||||
<div
|
||||
data-test-id="login-link"
|
||||
className="text-center fs-8 text-slate-400"
|
||||
>
|
||||
<div data-test-id="login-link" className="text-center fs-8 ">
|
||||
Already have an account? <Link href="/login">Log in</Link>{' '}
|
||||
instead.
|
||||
</div>
|
||||
)}
|
||||
{action === 'login' && config.IS_OSS === false && (
|
||||
<div
|
||||
data-test-id="register-link"
|
||||
className="text-center fs-8 text-slate-400"
|
||||
>
|
||||
<div data-test-id="register-link" className="text-center fs-8 ">
|
||||
Don{"'"}t have an account yet?{' '}
|
||||
<Link href="/register">Register</Link> instead.
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import Fuse from 'fuse.js';
|
||||
import { OverlayTrigger } from 'react-bootstrap';
|
||||
import { Textarea, UnstyledButton } from '@mantine/core';
|
||||
import { Popover, Textarea, UnstyledButton } from '@mantine/core';
|
||||
|
||||
import { useQueryHistory } from '@/utils';
|
||||
|
||||
|
|
@ -131,210 +130,198 @@ export default function AutocompleteInput({
|
|||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<OverlayTrigger
|
||||
rootClose
|
||||
onToggle={opened => {
|
||||
// if opened is transitioning to false, but input is focused, ignore it
|
||||
if (!opened && isSearchInputFocused) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsInputDropdownOpen(opened);
|
||||
<Popover
|
||||
opened={isInputDropdownOpen}
|
||||
onChange={setIsInputDropdownOpen}
|
||||
position="bottom-start"
|
||||
offset={8}
|
||||
width="target"
|
||||
withinPortal
|
||||
closeOnClickOutside
|
||||
closeOnEscape
|
||||
styles={{
|
||||
dropdown: {
|
||||
maxWidth:
|
||||
(inputRef.current?.clientWidth || 0) > 300
|
||||
? inputRef.current?.clientWidth
|
||||
: 720,
|
||||
width: '100%',
|
||||
zIndex,
|
||||
},
|
||||
}}
|
||||
show={isInputDropdownOpen}
|
||||
placement="bottom-start"
|
||||
delay={{ show: 0, hide: 0 }}
|
||||
overlay={({ style, ...props }) => (
|
||||
<div
|
||||
className="bg-body border border-dark rounded"
|
||||
style={{
|
||||
...style,
|
||||
maxWidth:
|
||||
(inputRef.current?.clientWidth || 0) > 300
|
||||
? inputRef.current?.clientWidth
|
||||
: 720,
|
||||
width: '100%',
|
||||
zIndex,
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{aboveSuggestions != null && (
|
||||
<div className="d-flex p-2 flex-wrap px-3">{aboveSuggestions}</div>
|
||||
)}
|
||||
<div>
|
||||
{suggestedProperties.length > 0 && (
|
||||
<div className="border-top border-dark fs-8 py-2">
|
||||
<div className="d-flex justify-content-between px-3 mb-2">
|
||||
<div className="me-2 text-light">{suggestionsHeader}</div>
|
||||
{suggestedProperties.length > suggestionsLimit && (
|
||||
<div className="text-muted">
|
||||
(Showing Top {suggestionsLimit})
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{suggestedProperties
|
||||
.slice(0, suggestionsLimit)
|
||||
.map(({ value, label }, i) => (
|
||||
<div
|
||||
className={`py-2 px-3 ${
|
||||
selectedAutocompleteIndex === i ? 'bg-hdx-dark' : ''
|
||||
}`}
|
||||
role="button"
|
||||
key={value}
|
||||
onMouseOver={() => {
|
||||
setSelectedAutocompleteIndex(i);
|
||||
}}
|
||||
onClick={() => {
|
||||
onAcceptSuggestion(value);
|
||||
}}
|
||||
>
|
||||
<span className="me-1">{label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{belowSuggestions != null && (
|
||||
<div className="border-top border-dark bg-body px-3 pt-2 pb-1 mt-2 fs-8 d-flex align-items-center text-muted flex-wrap">
|
||||
{belowSuggestions}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
{showSearchHistory && (
|
||||
<div className="border-top border-dark fs-8 py-2">
|
||||
<div className="text-muted fs-8 fw-bold me-1 px-3">
|
||||
Search History:
|
||||
</div>
|
||||
{queryHistoryList.map(({ value, label }, i) => {
|
||||
return (
|
||||
<UnstyledButton
|
||||
className={`d-block w-100 text-start text-muted fw-normal px-3 py-2 fs-8 ${
|
||||
selectedQueryHistoryIndex === i ? 'bg-hdx-dark' : ''
|
||||
}`}
|
||||
key={value}
|
||||
onMouseOver={() => setSelectedQueryHistoryIndex(i)}
|
||||
onClick={() => onSelectSearchHistory(value)}
|
||||
>
|
||||
<span className="me-1 text-truncate">{label}</span>
|
||||
</UnstyledButton>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
popperConfig={{
|
||||
modifiers: [
|
||||
{
|
||||
name: 'offset',
|
||||
options: {
|
||||
offset: [0, 8],
|
||||
},
|
||||
},
|
||||
],
|
||||
}}
|
||||
trigger={[]}
|
||||
>
|
||||
<Textarea
|
||||
ref={inputRef}
|
||||
placeholder={placeholder}
|
||||
className="fs-8"
|
||||
value={value}
|
||||
size={size}
|
||||
autosize
|
||||
minRows={1}
|
||||
maxRows={4}
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
resize: 'none',
|
||||
}}
|
||||
data-testid={dataTestId}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
onFocus={() => {
|
||||
setSelectedAutocompleteIndex(-1);
|
||||
setSelectedQueryHistoryIndex(-1);
|
||||
setIsSearchInputFocused(true);
|
||||
}}
|
||||
onBlur={() => {
|
||||
setSelectedAutocompleteIndex(-1);
|
||||
setSelectedQueryHistoryIndex(-1);
|
||||
setIsSearchInputFocused(false);
|
||||
}}
|
||||
onKeyDown={e => {
|
||||
if (e.key === 'Escape' && e.target instanceof HTMLTextAreaElement) {
|
||||
e.target.blur();
|
||||
}
|
||||
|
||||
// Autocomplete Navigation/Acceptance Keys
|
||||
if (e.key === 'Tab' && e.target instanceof HTMLTextAreaElement) {
|
||||
if (
|
||||
suggestedProperties.length > 0 &&
|
||||
selectedAutocompleteIndex < suggestedProperties.length &&
|
||||
selectedAutocompleteIndex >= 0
|
||||
) {
|
||||
<Popover.Target>
|
||||
<Textarea
|
||||
ref={inputRef}
|
||||
placeholder={placeholder}
|
||||
className="fs-8"
|
||||
value={value}
|
||||
size={size}
|
||||
autosize
|
||||
minRows={1}
|
||||
maxRows={4}
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
resize: 'none',
|
||||
}}
|
||||
data-testid={dataTestId}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
onFocus={() => {
|
||||
setSelectedAutocompleteIndex(-1);
|
||||
setSelectedQueryHistoryIndex(-1);
|
||||
setIsSearchInputFocused(true);
|
||||
}}
|
||||
onBlur={() => {
|
||||
setSelectedAutocompleteIndex(-1);
|
||||
setSelectedQueryHistoryIndex(-1);
|
||||
setIsSearchInputFocused(false);
|
||||
}}
|
||||
onKeyDown={e => {
|
||||
if (e.key === 'Escape' && e.target instanceof HTMLTextAreaElement) {
|
||||
e.preventDefault();
|
||||
onAcceptSuggestion(
|
||||
suggestedProperties[selectedAutocompleteIndex].value,
|
||||
);
|
||||
setIsInputDropdownOpen(false);
|
||||
e.target.blur();
|
||||
}
|
||||
}
|
||||
if (e.key === 'Enter' && e.target instanceof HTMLTextAreaElement) {
|
||||
if (
|
||||
suggestedProperties.length > 0 &&
|
||||
selectedAutocompleteIndex < suggestedProperties.length &&
|
||||
selectedAutocompleteIndex >= 0
|
||||
) {
|
||||
e.preventDefault();
|
||||
onAcceptSuggestion(
|
||||
suggestedProperties[selectedAutocompleteIndex].value,
|
||||
);
|
||||
} else {
|
||||
// Allow shift+enter to still create new lines
|
||||
if (!e.shiftKey) {
|
||||
|
||||
// Autocomplete Navigation/Acceptance Keys
|
||||
if (e.key === 'Tab' && e.target instanceof HTMLTextAreaElement) {
|
||||
if (
|
||||
suggestedProperties.length > 0 &&
|
||||
selectedAutocompleteIndex < suggestedProperties.length &&
|
||||
selectedAutocompleteIndex >= 0
|
||||
) {
|
||||
e.preventDefault();
|
||||
if (queryHistoryType && value) {
|
||||
setQueryHistory(value);
|
||||
}
|
||||
onSubmit?.();
|
||||
onAcceptSuggestion(
|
||||
suggestedProperties[selectedAutocompleteIndex].value,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
e.key === 'ArrowDown' &&
|
||||
e.target instanceof HTMLTextAreaElement
|
||||
) {
|
||||
if (suggestedProperties.length > 0) {
|
||||
setSelectedAutocompleteIndex(
|
||||
Math.min(
|
||||
selectedAutocompleteIndex + 1,
|
||||
suggestedProperties.length - 1,
|
||||
suggestionsLimit - 1,
|
||||
),
|
||||
);
|
||||
if (e.key === 'Enter' && e.target instanceof HTMLTextAreaElement) {
|
||||
if (
|
||||
suggestedProperties.length > 0 &&
|
||||
selectedAutocompleteIndex < suggestedProperties.length &&
|
||||
selectedAutocompleteIndex >= 0
|
||||
) {
|
||||
e.preventDefault();
|
||||
onAcceptSuggestion(
|
||||
suggestedProperties[selectedAutocompleteIndex].value,
|
||||
);
|
||||
} else {
|
||||
// Allow shift+enter to still create new lines
|
||||
if (!e.shiftKey) {
|
||||
e.preventDefault();
|
||||
if (queryHistoryType && value) {
|
||||
setQueryHistory(value);
|
||||
}
|
||||
onSubmit?.();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e.key === 'ArrowUp' && e.target instanceof HTMLTextAreaElement) {
|
||||
if (suggestedProperties.length > 0) {
|
||||
setSelectedAutocompleteIndex(
|
||||
Math.max(selectedAutocompleteIndex - 1, 0),
|
||||
);
|
||||
if (
|
||||
e.key === 'ArrowDown' &&
|
||||
e.target instanceof HTMLTextAreaElement
|
||||
) {
|
||||
if (suggestedProperties.length > 0) {
|
||||
setSelectedAutocompleteIndex(
|
||||
Math.min(
|
||||
selectedAutocompleteIndex + 1,
|
||||
suggestedProperties.length - 1,
|
||||
suggestionsLimit - 1,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
if (
|
||||
e.key === 'ArrowUp' &&
|
||||
e.target instanceof HTMLTextAreaElement
|
||||
) {
|
||||
if (suggestedProperties.length > 0) {
|
||||
setSelectedAutocompleteIndex(
|
||||
Math.max(selectedAutocompleteIndex - 1, 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
}}
|
||||
rightSectionWidth={ref.current?.clientWidth ?? 'auto'}
|
||||
rightSection={
|
||||
language != null && onLanguageChange != null ? (
|
||||
<div ref={ref}>
|
||||
<InputLanguageSwitch
|
||||
showHotkey={showHotkey && isSearchInputFocused}
|
||||
language={language}
|
||||
onLanguageChange={onLanguageChange}
|
||||
/>
|
||||
</div>
|
||||
) : undefined
|
||||
}
|
||||
}}
|
||||
rightSectionWidth={ref.current?.clientWidth ?? 'auto'}
|
||||
rightSection={
|
||||
language != null && onLanguageChange != null ? (
|
||||
<div ref={ref}>
|
||||
<InputLanguageSwitch
|
||||
showHotkey={showHotkey && isSearchInputFocused}
|
||||
language={language}
|
||||
onLanguageChange={onLanguageChange}
|
||||
/>
|
||||
/>
|
||||
</Popover.Target>
|
||||
<Popover.Dropdown className="p-0">
|
||||
{aboveSuggestions != null && (
|
||||
<div className="d-flex p-2 flex-wrap px-3">{aboveSuggestions}</div>
|
||||
)}
|
||||
<div>
|
||||
{suggestedProperties.length > 0 && (
|
||||
<div className="border-top border-dark fs-8 py-2">
|
||||
<div className="d-flex justify-content-between px-3 mb-2">
|
||||
<div className="me-2 text-light">{suggestionsHeader}</div>
|
||||
{suggestedProperties.length > suggestionsLimit && (
|
||||
<div className="text-muted">
|
||||
(Showing Top {suggestionsLimit})
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{suggestedProperties
|
||||
.slice(0, suggestionsLimit)
|
||||
.map(({ value, label }, i) => (
|
||||
<div
|
||||
className={`py-2 px-3 ${
|
||||
selectedAutocompleteIndex === i ? 'bg-muted' : ''
|
||||
}`}
|
||||
role="button"
|
||||
key={value}
|
||||
onMouseOver={() => {
|
||||
setSelectedAutocompleteIndex(i);
|
||||
}}
|
||||
onClick={() => {
|
||||
onAcceptSuggestion(value);
|
||||
}}
|
||||
>
|
||||
<span className="me-1">{label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</OverlayTrigger>
|
||||
)}
|
||||
</div>
|
||||
{belowSuggestions != null && (
|
||||
<div className="border-top px-3 pt-2 pb-1 fs-8 d-flex align-items-center text-muted flex-wrap">
|
||||
{belowSuggestions}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
{showSearchHistory && (
|
||||
<div className="border-top border-dark fs-8 py-2">
|
||||
<div className="text-muted fs-8 fw-bold me-1 px-3">
|
||||
Search History:
|
||||
</div>
|
||||
{queryHistoryList.map(({ value, label }, i) => {
|
||||
return (
|
||||
<UnstyledButton
|
||||
className={`d-block w-100 text-start text-muted fw-normal px-3 py-2 fs-8 ${
|
||||
selectedQueryHistoryIndex === i ? 'bg-muted' : ''
|
||||
}`}
|
||||
key={value}
|
||||
onMouseOver={() => setSelectedQueryHistoryIndex(i)}
|
||||
onClick={() => onSelectSearchHistory(value)}
|
||||
>
|
||||
<span className="me-1 text-truncate">{label}</span>
|
||||
</UnstyledButton>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Popover.Dropdown>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -235,9 +235,7 @@ function BenchmarkPage() {
|
|||
<Grid>
|
||||
<Grid.Col span={6}>
|
||||
<Stack>
|
||||
<Text size="lg" c="gray.4">
|
||||
Query 1
|
||||
</Text>
|
||||
<Text size="lg">Query 1</Text>
|
||||
<ConnectionSelectControlled
|
||||
control={control}
|
||||
name="connections.0"
|
||||
|
|
@ -247,9 +245,7 @@ function BenchmarkPage() {
|
|||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<Stack>
|
||||
<Text size="lg" c="gray.4">
|
||||
Query 2
|
||||
</Text>
|
||||
<Text size="lg">Query 2</Text>
|
||||
<ConnectionSelectControlled
|
||||
control={control}
|
||||
name="connections.1"
|
||||
|
|
@ -270,12 +266,8 @@ function BenchmarkPage() {
|
|||
<Grid mt="md">
|
||||
<Grid.Col span={12}>
|
||||
<Stack>
|
||||
<Text size="lg" c="gray.2">
|
||||
Query Estimate & Indexes
|
||||
</Text>
|
||||
<Text c="gray.4" size="sm">
|
||||
Index utilization of your query
|
||||
</Text>
|
||||
<Text size="lg">Query Estimate & Indexes</Text>
|
||||
<Text size="sm">Index utilization of your query</Text>
|
||||
</Stack>
|
||||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ function InfrastructureTab({
|
|||
<Grid mt="md">
|
||||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ minHeight: 400 }}>
|
||||
<Text size="sm" c="gray.4" mb="sm">
|
||||
<Text size="sm" mb="sm">
|
||||
CPU Usage (Cores)
|
||||
</Text>
|
||||
<DBTimeChart
|
||||
|
|
@ -86,7 +86,7 @@ function InfrastructureTab({
|
|||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ minHeight: 400 }}>
|
||||
<Text size="sm" c="gray.4" mb="sm">
|
||||
<Text size="sm" mb="sm">
|
||||
Memory Usage
|
||||
</Text>
|
||||
<DBTimeChart
|
||||
|
|
@ -112,7 +112,7 @@ function InfrastructureTab({
|
|||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ minHeight: 400 }}>
|
||||
<Text size="sm" c="gray.4" mb="sm">
|
||||
<Text size="sm" mb="sm">
|
||||
Disk
|
||||
</Text>
|
||||
<DBTimeChart
|
||||
|
|
@ -146,7 +146,7 @@ function InfrastructureTab({
|
|||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ minHeight: 400 }}>
|
||||
<Text size="sm" c="gray.4" mb="sm">
|
||||
<Text size="sm" mb="sm">
|
||||
S3 Requests
|
||||
</Text>
|
||||
<DBTimeChart
|
||||
|
|
@ -198,10 +198,10 @@ function InfrastructureTab({
|
|||
</Grid.Col>
|
||||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ minHeight: 400 }}>
|
||||
<Text size="sm" c="gray.4" mb="xs">
|
||||
<Text size="sm" mb="xs">
|
||||
Network
|
||||
</Text>
|
||||
<Text size="xs" c="gray.4" mb="sm">
|
||||
<Text size="xs" mb="sm">
|
||||
Network activity for the entire machine, not only Clickhouse.
|
||||
</Text>
|
||||
<DBTimeChart
|
||||
|
|
@ -249,7 +249,7 @@ function InsertsTab({
|
|||
<Grid.Col span={12}>
|
||||
<ChartBox style={{ minHeight: 400 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
<Text size="sm">
|
||||
Insert{' '}
|
||||
{insertsBy === 'queries'
|
||||
? 'Queries'
|
||||
|
|
@ -323,9 +323,7 @@ function InsertsTab({
|
|||
<Grid.Col span={12}>
|
||||
<ChartBox style={{ minHeight: 200, height: 200 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
Max Active Parts per Partition
|
||||
</Text>
|
||||
<Text size="sm">Max Active Parts per Partition</Text>
|
||||
</Group>
|
||||
<DBTimeChart
|
||||
config={{
|
||||
|
|
@ -355,10 +353,10 @@ function InsertsTab({
|
|||
</Grid.Col>
|
||||
<Grid.Col span={12}>
|
||||
<ChartBox style={{ height: 400 }}>
|
||||
<Text size="sm" c="gray.4" mb="sm">
|
||||
<Text size="sm" mb="sm">
|
||||
Active Parts Per Partition
|
||||
</Text>
|
||||
<Text size="xs" c="gray.4" mb="md">
|
||||
<Text size="xs" mb="md">
|
||||
Recommended to stay under 300, ClickHouse will automatically
|
||||
throttle inserts after 1,000 parts per partition and stop inserts at
|
||||
3,000 parts per partition.
|
||||
|
|
@ -499,9 +497,7 @@ function ClickhousePage() {
|
|||
<OnboardingModal requireSource={false} />
|
||||
<Group justify="space-between">
|
||||
<Group>
|
||||
<Text c="gray.4" size="xl">
|
||||
Clickhouse Dashboard
|
||||
</Text>
|
||||
<Text size="xl">Clickhouse Dashboard</Text>
|
||||
<ConnectionSelectControlled
|
||||
control={control}
|
||||
name="connection"
|
||||
|
|
@ -557,7 +553,7 @@ function ClickhousePage() {
|
|||
{/* <Grid.Col span={12}>
|
||||
<ChartBox style={{ minHeight: 300, height: 300 }}>
|
||||
<Group justify="space-between" align="center" mb="md">
|
||||
<Text size="sm" c="gray.4" ms="xs">
|
||||
<Text size="sm" ms="xs">
|
||||
Select P95 Query Latency
|
||||
</Text>
|
||||
<SegmentedControl
|
||||
|
|
@ -600,14 +596,13 @@ function ClickhousePage() {
|
|||
<Grid.Col span={12}>
|
||||
<ChartBox style={{ height: 250 }}>
|
||||
<Flex justify="space-between" align="center">
|
||||
<Text size="sm" c="gray.4" ms="xs">
|
||||
<Text size="sm" ms="xs">
|
||||
Query Latency
|
||||
</Text>
|
||||
{latencyFilter.latencyMin != null ||
|
||||
latencyFilter.latencyMax != null ? (
|
||||
<Button
|
||||
size="xs"
|
||||
color="gray.4"
|
||||
variant="subtle"
|
||||
onClick={() => {
|
||||
// Clears the min/max latency filters that are used to filter the query results
|
||||
|
|
@ -657,7 +652,7 @@ function ClickhousePage() {
|
|||
</Grid.Col>
|
||||
<Grid.Col span={12}>
|
||||
<ChartBox style={{ height: 400 }}>
|
||||
<Text size="sm" c="gray.4" mb="md">
|
||||
<Text size="sm" mb="md">
|
||||
Query Count by Table
|
||||
</Text>
|
||||
|
||||
|
|
@ -698,7 +693,7 @@ function ClickhousePage() {
|
|||
</Grid.Col>
|
||||
<Grid.Col span={12}>
|
||||
<ChartBox style={{ height: 400 }}>
|
||||
<Text size="sm" c="gray.4" mb="md">
|
||||
<Text size="sm" mb="md">
|
||||
Most Time Consuming Query Patterns
|
||||
</Text>
|
||||
<DBTableChart
|
||||
|
|
@ -756,7 +751,7 @@ function ClickhousePage() {
|
|||
</Grid.Col>
|
||||
<Grid.Col span={12}>
|
||||
<ChartBox style={{ height: 400 }}>
|
||||
<Text size="sm" c="gray.4" mb="md">
|
||||
<Text size="sm" mb="md">
|
||||
Slowest Queries
|
||||
</Text>
|
||||
<DBSqlRowTable
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useState } from 'react';
|
||||
import cx from 'classnames';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import { Button } from '@mantine/core';
|
||||
|
||||
export default function Clipboard({
|
||||
text,
|
||||
|
|
@ -23,8 +23,11 @@ export default function Clipboard({
|
|||
}}
|
||||
>
|
||||
<Button
|
||||
variant="link"
|
||||
className={cx('px-0 text-decoration-none fs-7', className)}
|
||||
variant="default"
|
||||
p={0}
|
||||
className={cx('text-decoration-none', className)}
|
||||
size="xs"
|
||||
fullWidth
|
||||
>
|
||||
{children({ isCopied })}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -119,14 +119,14 @@ function AIAssistant({
|
|||
return (
|
||||
<Box mb="sm">
|
||||
<Alert
|
||||
color="dark.3"
|
||||
color="dark"
|
||||
icon={<i className="bi bi-info-circle" />}
|
||||
variant="outline"
|
||||
withCloseButton
|
||||
onClose={() => setAlertDismissed(true)}
|
||||
p="xxs"
|
||||
>
|
||||
<Text size="xs" c="dark.2" pt="2px">
|
||||
<Text size="xs" pt="2px">
|
||||
New AI Assistant available, enable with configuring the{' '}
|
||||
<code>ANTHROPIC_API_KEY</code> environment variable on the HyperDX
|
||||
server.
|
||||
|
|
@ -223,7 +223,7 @@ function DBChartExplorerPage() {
|
|||
);
|
||||
|
||||
return (
|
||||
<Box data-testid="chart-explorer-page" p="sm" className="bg-hdx-dark">
|
||||
<Box data-testid="chart-explorer-page" p="sm">
|
||||
<Head>
|
||||
<title>Chart Explorer - HyperDX</title>
|
||||
</Head>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import dynamic from 'next/dynamic';
|
|||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import { filter } from 'lodash';
|
||||
import { Container } from 'react-bootstrap';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { StringParam, useQueryParam } from 'use-query-params';
|
||||
import { z } from 'zod';
|
||||
|
|
@ -13,6 +12,7 @@ import { DashboardTemplateSchema } from '@hyperdx/common-utils/dist/types';
|
|||
import {
|
||||
Button,
|
||||
Collapse,
|
||||
Container,
|
||||
Group,
|
||||
Input,
|
||||
Stack,
|
||||
|
|
@ -110,21 +110,21 @@ function FileSelection({
|
|||
<Dropzone.Accept>
|
||||
<IconUpload
|
||||
size={52}
|
||||
color="var(--mantine-color-green-4)"
|
||||
color="var(--color-text-success)"
|
||||
stroke={1.5}
|
||||
/>
|
||||
</Dropzone.Accept>
|
||||
<Dropzone.Reject>
|
||||
<IconX
|
||||
size={52}
|
||||
color="var(--mantine-color-red-6)"
|
||||
color="var(--color-text-danger)"
|
||||
stroke={1.5}
|
||||
/>
|
||||
</Dropzone.Reject>
|
||||
<Dropzone.Idle>
|
||||
<IconFile
|
||||
size={52}
|
||||
color="var(--mantine-color-dimmed)"
|
||||
color="var(--color-text-muted)"
|
||||
stroke={1.5}
|
||||
/>
|
||||
</Dropzone.Idle>
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ import {
|
|||
} from '@mantine/core';
|
||||
import { useHover } from '@mantine/hooks';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { IconFilterEdit } from '@tabler/icons-react';
|
||||
import { IconFilterEdit, IconPlayerPlay } from '@tabler/icons-react';
|
||||
|
||||
import { ContactSupportText } from '@/components/ContactSupportText';
|
||||
import EditTimeChartForm from '@/components/DBEditTimeChartForm';
|
||||
|
|
@ -212,7 +212,7 @@ const Tile = forwardRef(
|
|||
return (
|
||||
<div
|
||||
data-testid={`dashboard-tile-${chart.id}`}
|
||||
className={`p-2 ${className} d-flex flex-column ${
|
||||
className={`p-2 ${className} d-flex flex-column bg-muted rounded ${
|
||||
isHighlighed && 'dashboard-chart-highlighted'
|
||||
}`}
|
||||
id={`chart-${chart.id}`}
|
||||
|
|
@ -221,9 +221,6 @@ const Tile = forwardRef(
|
|||
key={chart.id}
|
||||
ref={ref}
|
||||
style={{
|
||||
background:
|
||||
'linear-gradient(180deg, rgba(250,250,250,0.018) 0%, rgba(250,250,250,0.008) 100%)',
|
||||
borderRadius: 2,
|
||||
...style,
|
||||
}}
|
||||
onMouseDown={onMouseDown}
|
||||
|
|
@ -231,7 +228,7 @@ const Tile = forwardRef(
|
|||
onTouchEnd={onTouchEnd}
|
||||
>
|
||||
<div className="d-flex justify-content-between align-items-center mb-2 cursor-grab">
|
||||
<Text size="sm" c="gray.2" ms="xs">
|
||||
<Text size="sm" ms="xs">
|
||||
{chart.config.name}
|
||||
</Text>
|
||||
{hovered ? (
|
||||
|
|
@ -241,15 +238,13 @@ const Tile = forwardRef(
|
|||
size={5}
|
||||
zIndex={1}
|
||||
color={alertIndicatorColor}
|
||||
label={
|
||||
!alert && <span className="text-slate-400 fs-8">+</span>
|
||||
}
|
||||
label={!alert && <span className="fs-8">+</span>}
|
||||
mr={4}
|
||||
>
|
||||
<Button
|
||||
data-testid={`tile-alerts-button-${chart.id}`}
|
||||
variant="subtle"
|
||||
color="gray.4"
|
||||
color="gray"
|
||||
size="xxs"
|
||||
onClick={onEditClick}
|
||||
title="Alerts"
|
||||
|
|
@ -262,7 +257,7 @@ const Tile = forwardRef(
|
|||
<Button
|
||||
data-testid={`tile-duplicate-button-${chart.id}`}
|
||||
variant="subtle"
|
||||
color="gray.4"
|
||||
color="gray"
|
||||
size="xxs"
|
||||
onClick={onDuplicateClick}
|
||||
title="Duplicate"
|
||||
|
|
@ -272,8 +267,8 @@ const Tile = forwardRef(
|
|||
<Button
|
||||
data-testid={`tile-edit-button-${chart.id}`}
|
||||
variant="subtle"
|
||||
color="gray"
|
||||
size="xxs"
|
||||
color="gray.4"
|
||||
onClick={onEditClick}
|
||||
title="Edit"
|
||||
>
|
||||
|
|
@ -282,8 +277,8 @@ const Tile = forwardRef(
|
|||
<Button
|
||||
data-testid={`tile-delete-button-${chart.id}`}
|
||||
variant="subtle"
|
||||
color="gray"
|
||||
size="xxs"
|
||||
color="gray.4"
|
||||
onClick={onDeleteClick}
|
||||
title="Delete"
|
||||
>
|
||||
|
|
@ -481,7 +476,6 @@ function DashboardName({
|
|||
ms="xs"
|
||||
variant="subtle"
|
||||
size="xs"
|
||||
color="gray.4"
|
||||
onClick={() => setEditing(true)}
|
||||
>
|
||||
<i className="bi bi-pencil"></i>
|
||||
|
|
@ -882,7 +876,7 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
|
|||
{IS_LOCAL_MODE === false && isLocalDashboard && isLocalDashboardEmpty && (
|
||||
<Paper my="lg" p="md">
|
||||
<Flex justify="space-between" align="center">
|
||||
<Text c="gray.4" size="sm">
|
||||
<Text size="sm">
|
||||
This is a temporary dashboard and can not be saved.
|
||||
</Text>
|
||||
<Button
|
||||
|
|
@ -917,8 +911,7 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
|
|||
onChange={handleUpdateTags}
|
||||
>
|
||||
<Button
|
||||
variant="outline"
|
||||
color="dark.2"
|
||||
variant="default"
|
||||
px="xs"
|
||||
size="xs"
|
||||
style={{ flexShrink: 0 }}
|
||||
|
|
@ -932,7 +925,7 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
|
|||
{!isLocalDashboard /* local dashboards cant be "deleted" */ && (
|
||||
<Menu width={250}>
|
||||
<Menu.Target>
|
||||
<Button variant="outline" color="dark.2" px="xs" size="xs">
|
||||
<Button variant="default" px="xs" size="xs">
|
||||
<i className="bi bi-three-dots-vertical" />
|
||||
</Button>
|
||||
</Menu.Target>
|
||||
|
|
@ -993,7 +986,7 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
|
|||
</Menu>
|
||||
)}
|
||||
</Group>
|
||||
{/* <Button variant="outline" color="gray.4" size="sm">
|
||||
{/* <Button variant="outline" size="sm">
|
||||
Save
|
||||
</Button> */}
|
||||
</Flex>
|
||||
|
|
@ -1058,10 +1051,9 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
|
|||
>
|
||||
<Button
|
||||
onClick={() => setIsLive(prev => !prev)}
|
||||
color={isLive ? 'green' : 'gray'}
|
||||
mr={6}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
variant={isLive ? 'filled' : 'default'}
|
||||
title={isLive ? 'Disable auto-refresh' : 'Enable auto-refresh'}
|
||||
>
|
||||
Live
|
||||
|
|
@ -1072,9 +1064,8 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
|
|||
onClick={refresh}
|
||||
loading={manualRefreshCooloff}
|
||||
disabled={manualRefreshCooloff}
|
||||
color="gray"
|
||||
mr={6}
|
||||
variant="outline"
|
||||
variant="default"
|
||||
title="Refresh dashboard"
|
||||
px="xs"
|
||||
>
|
||||
|
|
@ -1083,8 +1074,7 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
|
|||
</Tooltip>
|
||||
<Tooltip withArrow label="Edit Filters" fz="xs" color="gray">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray"
|
||||
variant="default"
|
||||
px="xs"
|
||||
mr={6}
|
||||
onClick={() => setShowFiltersModal(true)}
|
||||
|
|
@ -1092,8 +1082,12 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
|
|||
<IconFilterEdit strokeWidth={1} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Button variant="outline" type="submit" color="green">
|
||||
<i className="bi bi-play"></i>
|
||||
<Button
|
||||
data-testid="search-submit-button"
|
||||
variant="outline"
|
||||
type="submit"
|
||||
>
|
||||
<IconPlayerPlay size={16} />
|
||||
</Button>
|
||||
</Flex>
|
||||
<DashboardFilters
|
||||
|
|
@ -1157,7 +1151,7 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
|
|||
data-testid="add-new-tile-button"
|
||||
variant="outline"
|
||||
mt="sm"
|
||||
color={dashboard?.tiles.length === 0 ? 'green' : 'dark.3'}
|
||||
color={dashboard?.tiles.length === 0 ? 'green' : 'gray'}
|
||||
fw={400}
|
||||
onClick={onAddTile}
|
||||
w="100%"
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ import {
|
|||
useDocumentVisibility,
|
||||
} from '@mantine/hooks';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { IconPlayerPlay } from '@tabler/icons-react';
|
||||
import { useIsFetching } from '@tanstack/react-query';
|
||||
import { SortingState } from '@tanstack/react-table';
|
||||
import CodeMirror from '@uiw/react-codemirror';
|
||||
|
|
@ -177,7 +178,7 @@ function SearchNumRows({
|
|||
|
||||
const numRows = data?.[0]?.rows;
|
||||
return (
|
||||
<Text size="xs" c="gray.4" mb={4}>
|
||||
<Text size="xs" mb={4}>
|
||||
{isLoading
|
||||
? 'Scanned Rows ...'
|
||||
: error || !numRows
|
||||
|
|
@ -307,44 +308,36 @@ function SaveSearchModal({
|
|||
<Stack>
|
||||
{chartConfig != null ? (
|
||||
<Card withBorder>
|
||||
<Text c="gray.4" size="xs" mb="xs">
|
||||
<Text size="xs" mb="xs">
|
||||
SELECT
|
||||
</Text>
|
||||
<Text
|
||||
mb="sm"
|
||||
size="xs"
|
||||
c="gray.2"
|
||||
>{`${chartConfig.select}`}</Text>
|
||||
<Text c="gray.4" size="xs" mb="xs">
|
||||
<Text mb="sm" size="xs">{`${chartConfig.select}`}</Text>
|
||||
<Text size="xs" mb="xs">
|
||||
FROM
|
||||
</Text>
|
||||
<Text mb="sm" size="xs" c="gray.2">
|
||||
<Text mb="sm" size="xs">
|
||||
{chartConfig?.from.databaseName}.{chartConfig?.from.tableName}
|
||||
</Text>
|
||||
<Text c="gray.4" size="xs" mb="xs">
|
||||
<Text size="xs" mb="xs">
|
||||
WHERE
|
||||
</Text>
|
||||
{chartConfig.where ? (
|
||||
<Text size="xs" c="gray.2">
|
||||
{chartConfig.where}
|
||||
</Text>
|
||||
<Text size="xs">{chartConfig.where}</Text>
|
||||
) : (
|
||||
<Text size="xxs" c="gray.4" fs="italic">
|
||||
<Text size="xxs" fs="italic">
|
||||
None
|
||||
</Text>
|
||||
)}
|
||||
<Text c="gray.4" size="xs" mb="xs" mt="sm">
|
||||
<Text size="xs" mb="xs" mt="sm">
|
||||
ORDER BY
|
||||
</Text>
|
||||
<Text size="xs" c="gray.2">
|
||||
{chartConfig.orderBy}
|
||||
</Text>
|
||||
<Text size="xs">{chartConfig.orderBy}</Text>
|
||||
</Card>
|
||||
) : (
|
||||
<Text c="gray.4">Loading Chart Config...</Text>
|
||||
<Text>Loading Chart Config...</Text>
|
||||
)}
|
||||
<Box>
|
||||
<Text c="gray.4" size="xs" mb="xs">
|
||||
<Text size="xs" mb="xs">
|
||||
Name
|
||||
</Text>
|
||||
<InputControlled
|
||||
|
|
@ -355,7 +348,7 @@ function SaveSearchModal({
|
|||
/>
|
||||
</Box>
|
||||
<Box mb="sm">
|
||||
<Text c="gray.4" size="xs" mb="xs">
|
||||
<Text size="xs" mb="xs">
|
||||
Tags
|
||||
</Text>
|
||||
<Group gap="xs" align="center" mb="xs">
|
||||
|
|
@ -1312,7 +1305,6 @@ function DBSearchPage() {
|
|||
<ActionIcon
|
||||
data-testid="source-settings-menu"
|
||||
variant="subtle"
|
||||
color="dark.2"
|
||||
size="sm"
|
||||
title="Edit Source"
|
||||
>
|
||||
|
|
@ -1381,9 +1373,7 @@ function DBSearchPage() {
|
|||
{!savedSearchId ? (
|
||||
<Button
|
||||
data-testid="save-search-button"
|
||||
variant="outline"
|
||||
color="dark.2"
|
||||
px="xs"
|
||||
variant="default"
|
||||
size="xs"
|
||||
onClick={onSaveSearch}
|
||||
style={{ flexShrink: 0 }}
|
||||
|
|
@ -1393,9 +1383,7 @@ function DBSearchPage() {
|
|||
) : (
|
||||
<Button
|
||||
data-testid="update-search-button"
|
||||
variant="outline"
|
||||
color="dark.2"
|
||||
px="xs"
|
||||
variant="default"
|
||||
size="xs"
|
||||
onClick={() => {
|
||||
setSaveSearchModalState('update');
|
||||
|
|
@ -1408,9 +1396,7 @@ function DBSearchPage() {
|
|||
{!IS_LOCAL_MODE && (
|
||||
<Button
|
||||
data-testid="alerts-button"
|
||||
variant="outline"
|
||||
color="dark.2"
|
||||
px="xs"
|
||||
variant="default"
|
||||
size="xs"
|
||||
onClick={openAlertModal}
|
||||
style={{ flexShrink: 0 }}
|
||||
|
|
@ -1427,8 +1413,7 @@ function DBSearchPage() {
|
|||
>
|
||||
<Button
|
||||
data-testid="tags-button"
|
||||
variant="outline"
|
||||
color="dark.2"
|
||||
variant="default"
|
||||
px="xs"
|
||||
size="xs"
|
||||
style={{ flexShrink: 0 }}
|
||||
|
|
@ -1551,9 +1536,9 @@ function DBSearchPage() {
|
|||
data-testid="search-submit-button"
|
||||
variant="outline"
|
||||
type="submit"
|
||||
color={formState.isDirty ? 'green' : 'gray.4'}
|
||||
color={formState.isDirty ? 'var(--color-text-success)' : 'gray'}
|
||||
>
|
||||
<i className="bi bi-play"></i>
|
||||
<IconPlayerPlay size={16} />
|
||||
</Button>
|
||||
</Flex>
|
||||
</form>
|
||||
|
|
@ -1570,12 +1555,12 @@ function DBSearchPage() {
|
|||
<Flex
|
||||
direction="column"
|
||||
style={{ overflow: 'hidden', height: '100%' }}
|
||||
className="bg-hdx-dark"
|
||||
className="bg-body"
|
||||
>
|
||||
{!queryReady ? (
|
||||
<Paper shadow="xs" p="xl" h="100%">
|
||||
<Center mih={100} h="100%">
|
||||
<Text size="sm" c="gray.4">
|
||||
<Text size="sm">
|
||||
Please start by selecting a database, table, and timestamp
|
||||
column above to view data.
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ const SavedSearchAlertFormSchema = z
|
|||
.passthrough();
|
||||
|
||||
const CHANNEL_ICONS = {
|
||||
webhook: <i className="bi bi-slack fs-7 text-slate-400" />,
|
||||
webhook: <i className="bi bi-slack fs-7 " />,
|
||||
};
|
||||
|
||||
const AlertForm = ({
|
||||
|
|
@ -106,7 +106,7 @@ const AlertForm = ({
|
|||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Stack gap="xs">
|
||||
<Paper px="md" py="sm" bg="dark.6" radius="xs">
|
||||
<Paper px="md" py="sm" radius="xs">
|
||||
<Text size="xxs" opacity={0.5}>
|
||||
Trigger
|
||||
</Text>
|
||||
|
|
@ -158,7 +158,7 @@ const AlertForm = ({
|
|||
size="xs"
|
||||
/>
|
||||
</Paper>
|
||||
<Paper px="md" py="sm" bg="dark.6" radius="xs">
|
||||
<Paper px="md" py="sm" radius="xs">
|
||||
<Text size="xxs" opacity={0.5} mb={4}>
|
||||
Send to
|
||||
</Text>
|
||||
|
|
@ -166,8 +166,8 @@ const AlertForm = ({
|
|||
</Paper>
|
||||
{groupBy && thresholdType === AlertThresholdType.BELOW && (
|
||||
<MantineAlert
|
||||
icon={<i className="bi bi-info-circle-fill text-slate-400" />}
|
||||
bg="dark.6"
|
||||
icon={<i className="bi bi-info-circle-fill " />}
|
||||
bg="dark"
|
||||
py="xs"
|
||||
>
|
||||
<Text size="sm" opacity={0.7}>
|
||||
|
|
@ -202,7 +202,7 @@ const AlertForm = ({
|
|||
</Accordion>
|
||||
|
||||
{defaultValues?.createdBy && (
|
||||
<Paper px="md" py="sm" bg="dark.6" radius="xs" mt="sm">
|
||||
<Paper px="md" py="sm" radius="xs" mt="sm">
|
||||
<Text size="xxs" opacity={0.5} mb={4}>
|
||||
Created by
|
||||
</Text>
|
||||
|
|
@ -390,7 +390,7 @@ export const DBSearchPageAlertModal = ({
|
|||
/>
|
||||
<Stack gap={0} mb="md">
|
||||
<Group>
|
||||
<Text c="dark.1" size="sm">
|
||||
<Text size="sm">
|
||||
Alerts for <strong>{savedSearch?.name}</strong>
|
||||
</Text>
|
||||
{!id && (
|
||||
|
|
@ -403,9 +403,7 @@ export const DBSearchPageAlertModal = ({
|
|||
/>
|
||||
)}
|
||||
</Group>
|
||||
<Text c="dark.2" size="xxs">
|
||||
{savedSearch?.where}
|
||||
</Text>
|
||||
<Text size="xxs">{savedSearch?.where}</Text>
|
||||
</Stack>
|
||||
|
||||
<Tabs value={activeIndex} onChange={setTab} mb="xs">
|
||||
|
|
@ -419,10 +417,7 @@ export const DBSearchPageAlertModal = ({
|
|||
))}
|
||||
<Tabs.Tab value="stage">
|
||||
<Group gap={4}>
|
||||
<i
|
||||
className="bi bi-plus fs-5 text-slate-400"
|
||||
style={{ marginLeft: -8 }}
|
||||
/>
|
||||
<i className="bi bi-plus fs-5 " style={{ marginLeft: -8 }} />
|
||||
New Alert
|
||||
</Group>
|
||||
</Tabs.Tab>
|
||||
|
|
|
|||
|
|
@ -90,14 +90,12 @@ function DBServiceMapPage() {
|
|||
<Box
|
||||
data-testid="service-map-page"
|
||||
p="sm"
|
||||
className="bg-hdx-dark"
|
||||
className="bg-body"
|
||||
style={{ display: 'flex', flexDirection: 'column', height: '100vh' }}
|
||||
>
|
||||
<Group mb="md" justify="space-between">
|
||||
<Group>
|
||||
<Text c="gray.4" size="xl">
|
||||
Service Map
|
||||
</Text>
|
||||
<Text size="xl">Service Map</Text>
|
||||
<SourceSelectControlled
|
||||
control={control}
|
||||
name="source"
|
||||
|
|
@ -109,7 +107,7 @@ function DBServiceMapPage() {
|
|||
/>
|
||||
</Group>
|
||||
<Group justify="flex-end">
|
||||
<Text c="gray.4" bg="inherit" size="sm">
|
||||
<Text bg="inherit" size="sm">
|
||||
Sampling {samplingLabel}
|
||||
</Text>
|
||||
<div style={{ minWidth: '200px' }}>
|
||||
|
|
|
|||
|
|
@ -43,13 +43,13 @@ const URLHoverCard = memo(({ url }: { url: string }) => {
|
|||
<table className="table fs-8 mb-0">
|
||||
<tr>
|
||||
<td>
|
||||
<i className="bi bi-globe fs-8 text-slate-300"></i>
|
||||
<i className="bi bi-globe fs-8 "></i>
|
||||
</td>
|
||||
<td>{parsedUrl?.host}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<i className="bi bi-link-45deg text-slate-300 fs-7"></i>
|
||||
<i className="bi bi-link-45deg fs-7"></i>
|
||||
</td>
|
||||
<td>{parsedUrl?.pathname}</td>
|
||||
</tr>
|
||||
|
|
@ -470,7 +470,7 @@ export default function DOMPlayer({
|
|||
<ActionIcon
|
||||
onClick={() => setPlayerFullWidth(!playerFullWidth)}
|
||||
size="sm"
|
||||
color="gray.7"
|
||||
color="gray"
|
||||
>
|
||||
{playerFullWidth ? (
|
||||
<i className="bi bi-list"></i>
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ const DashboardFilterEditForm = ({
|
|||
</CustomInputWrapper>
|
||||
|
||||
<Group justify="flex-end" my="xs">
|
||||
<Button variant="outline" color="gray.2" onClick={onCancel}>
|
||||
<Button variant="default" onClick={onCancel}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit">Save filter</Button>
|
||||
|
|
@ -218,7 +218,7 @@ const EmptyState = ({ onCreateFilter, onClose }: EmptyStateProps) => {
|
|||
Add filters to let users quickly narrow data on key columns. Saved
|
||||
filters will stay with this dashboard.
|
||||
</Text>
|
||||
<Button variant="outline" color="gray.2" onClick={onCreateFilter}>
|
||||
<Button variant="filled" onClick={onCreateFilter}>
|
||||
Add new filter
|
||||
</Button>
|
||||
</Stack>
|
||||
|
|
@ -258,9 +258,9 @@ const DashboardFiltersList = ({
|
|||
<Paper
|
||||
key={filter.id}
|
||||
withBorder
|
||||
bg={'dark.5'}
|
||||
className={styles.filterPaper}
|
||||
p="xs"
|
||||
variant="muted"
|
||||
>
|
||||
<Group justify="space-between" className={styles.filterHeader}>
|
||||
<Text size="xs">{filter.name}</Text>
|
||||
|
|
@ -296,7 +296,7 @@ const DashboardFiltersList = ({
|
|||
</Stack>
|
||||
|
||||
<Group justify="center" my="sm">
|
||||
<Button variant="outline" color="gray.2" onClick={onAddNew}>
|
||||
<Button variant="filled" onClick={onAddNew}>
|
||||
Add new filter
|
||||
</Button>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -196,17 +196,16 @@ export const Table = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="overflow-auto h-100 fs-8 bg-inherit dark:bg-dark"
|
||||
ref={tableContainerRef}
|
||||
>
|
||||
<table className="w-100 bg-inherit" style={{ tableLayout: 'fixed' }}>
|
||||
<div className="overflow-auto h-100 fs-8" ref={tableContainerRef}>
|
||||
<table
|
||||
className="w-100"
|
||||
style={{ tableLayout: 'fixed', borderCollapse: 'collapse' }}
|
||||
>
|
||||
<thead
|
||||
className="bg-inherit"
|
||||
style={{
|
||||
background: 'inherit',
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
background: 'var(--color-bg-body)',
|
||||
}}
|
||||
>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
|
|
@ -229,7 +228,7 @@ export const Table = ({
|
|||
<CsvExportButton
|
||||
data={csvData}
|
||||
filename="HyperDX_table_results"
|
||||
className="fs-8 text-muted-hover ms-2"
|
||||
className="fs-8 ms-2"
|
||||
title="Download table as CSV"
|
||||
>
|
||||
<i className="bi bi-download" />
|
||||
|
|
@ -255,7 +254,7 @@ export const Table = ({
|
|||
return (
|
||||
<tr
|
||||
key={virtualRow.key}
|
||||
className="bg-default-dark-grey-hover"
|
||||
className="bg-muted-hover"
|
||||
data-index={virtualRow.index}
|
||||
ref={rowVirtualizer.measureElement}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -476,10 +476,7 @@ export const MemoChart = memo(function MemoChart({
|
|||
})}
|
||||
</defs>
|
||||
{isHovered && (
|
||||
<CartesianGrid
|
||||
strokeDasharray="3 3"
|
||||
stroke="var(--mantine-color-dark-6)"
|
||||
/>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="var(--color-border)" />
|
||||
)}
|
||||
<XAxis
|
||||
dataKey={timestampKey ?? 'ts_bucket'}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ export default function Icon({ size = 16 }: { size?: number }) {
|
|||
<g clipPath="url(#clip0_614_1164)">
|
||||
<path
|
||||
d="M256 0L477.703 128V384L256 512L34.2975 384V128L256 0Z"
|
||||
fill="#4FFA7A"
|
||||
fill="var(--color-bg-brand)"
|
||||
/>
|
||||
<path
|
||||
d="M311.365 84.4663C314.818 86.9946 316.431 92.1862 315.256 96.9926L284.313 223.563H341.409C344.836 223.563 347.936 226.127 349.295 230.086C350.655 234.046 350.014 238.644 347.665 241.786L210.211 425.598C207.472 429.26 203.089 430.062 199.635 427.534C196.182 425.005 194.569 419.814 195.744 415.007L226.686 288.437H169.591C166.164 288.437 163.064 285.873 161.705 281.914C160.345 277.954 160.986 273.356 163.335 270.214L300.789 86.4023C303.528 82.7403 307.911 81.938 311.365 84.4663Z"
|
||||
fill="#080A0B"
|
||||
fill="var(--color-text-inverted)"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import cx from 'classnames';
|
||||
import { Button, Modal } from 'react-bootstrap';
|
||||
import { Button, Modal } from '@mantine/core';
|
||||
|
||||
import api from './api';
|
||||
import Clipboard from './Clipboard';
|
||||
|
|
@ -12,12 +12,12 @@ function CopyableValue({
|
|||
value: string;
|
||||
}) {
|
||||
return (
|
||||
<Clipboard text={value} className="d-block mx-auto p-0 w-100">
|
||||
<Clipboard text={value} className="d-flex mx-auto p-0 w-100">
|
||||
{({ isCopied }) => {
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
'd-flex fs-6 py-2 px-3 bg-grey rounded align-items-center justify-content-between cursor-pointer text-white-hover',
|
||||
'd-flex w-100 py-2 px-2 gap-2 rounded align-items-center justify-content-between cursor-pointer',
|
||||
{
|
||||
'text-success': isCopied,
|
||||
},
|
||||
|
|
@ -25,7 +25,7 @@ function CopyableValue({
|
|||
>
|
||||
<div className="fs-7 d-flex text-truncate align-items-center">
|
||||
{label}
|
||||
<pre className="mb-0 user-select-all d-inline text-truncate fs-7 lh-1">
|
||||
<pre className="m-0 user-select-all d-inline text-truncate fs-7 lh-1">
|
||||
{value}
|
||||
</pre>
|
||||
</div>
|
||||
|
|
@ -36,7 +36,7 @@ function CopyableValue({
|
|||
'bi-clipboard-check': isCopied,
|
||||
})}
|
||||
></i>
|
||||
{isCopied ? 'Copied to Clipboard!' : 'Copy'}
|
||||
{isCopied ? 'Copied!' : 'Copy'}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -56,14 +56,13 @@ export default function InstallInstructionModal({
|
|||
|
||||
return (
|
||||
<Modal
|
||||
aria-labelledby="contained-modal-title-vcenter"
|
||||
centered
|
||||
onHide={onHide}
|
||||
show={show}
|
||||
opened={show}
|
||||
onClose={onHide}
|
||||
title="Start Sending Telemetry"
|
||||
size="lg"
|
||||
centered
|
||||
>
|
||||
<Modal.Body className="bg-hdx-dark rounded inter">
|
||||
<div className="fs-4 mb-4">Start Sending Telemetry</div>
|
||||
<div className="inter">
|
||||
{team != null && (
|
||||
<div className="mb-4">
|
||||
<CopyableValue
|
||||
|
|
@ -80,106 +79,115 @@ export default function InstallInstructionModal({
|
|||
Click on a link below to view installation instructions for your
|
||||
application.
|
||||
</div>
|
||||
<div className="fs-5 mb-2">Backend</div>
|
||||
<div className="fs-6 mb-2">
|
||||
<div className="fs-6 mb-2">Backend</div>
|
||||
<div className="mb-2">
|
||||
<a
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack/sdks/nodejs"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-link"
|
||||
>
|
||||
Node.js
|
||||
</a>
|
||||
<span className="ms-2 text-muted">(Logs + Traces)</span>
|
||||
</div>
|
||||
<div className="fs-6 mb-2">
|
||||
<div className="mb-2">
|
||||
<a
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack/sdks/golang"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-link"
|
||||
>
|
||||
Go
|
||||
</a>
|
||||
<span className="ms-2 text-muted">(Logs + Traces)</span>
|
||||
</div>
|
||||
<div className="fs-6 mb-2">
|
||||
<div className="mb-2">
|
||||
<a
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack/sdks/python"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-link"
|
||||
>
|
||||
Python
|
||||
</a>
|
||||
<span className="ms-2 text-muted">(Logs + Traces)</span>
|
||||
</div>
|
||||
<div className="fs-6 mb-2">
|
||||
<div className="mb-2">
|
||||
<a
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack/sdks/java"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-link"
|
||||
>
|
||||
Java
|
||||
</a>
|
||||
<span className="ms-2 text-muted">(Logs + Traces)</span>
|
||||
</div>
|
||||
<div className="fs-6 mb-2">
|
||||
<div className="mb-2">
|
||||
<a
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack/sdks/elixir"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-link"
|
||||
>
|
||||
Elixir
|
||||
</a>
|
||||
<span className="ms-2 text-muted">(Logs)</span>
|
||||
</div>
|
||||
<div className="fs-6 mb-2">
|
||||
<div className="mb-2">
|
||||
<a
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack/sdks/ruby-on-rails"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-link"
|
||||
>
|
||||
Ruby on Rails
|
||||
</a>
|
||||
<span className="ms-2 text-muted">(Traces)</span>
|
||||
</div>
|
||||
<div className="fs-5 mb-2 mt-4">Platform</div>
|
||||
<div className="fs-6 mb-2">
|
||||
<div className="fs-6 mb-2 mt-4">Platform</div>
|
||||
<div className="mb-2">
|
||||
<a
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack/ingesting-data/kubernetes"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-link"
|
||||
>
|
||||
Kubernetes
|
||||
</a>
|
||||
<span className="ms-2 text-muted">(Logs + Metrics)</span>
|
||||
</div>
|
||||
<div className="fs-5 mb-2 mt-4">Browser</div>
|
||||
<div className="fs-6 mb-2">
|
||||
<div className="fs-6 mb-2 mt-4">Browser</div>
|
||||
<div className="mb-2">
|
||||
<a
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack/sdks/browser"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-link"
|
||||
>
|
||||
JavaScript/TypeScript
|
||||
</a>
|
||||
<span className="ms-2 text-muted">(Logs + Traces)</span>
|
||||
</div>
|
||||
<div className="fs-5 mb-2 mt-4">Data Collector</div>
|
||||
<div className="fs-6 mb-2">
|
||||
<div className="fs-6 mb-2 mt-4">Data Collector</div>
|
||||
<div className="mb-2">
|
||||
<a
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack/ingesting-data/opentelemetry#sending-otel-data"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-link"
|
||||
>
|
||||
OpenTelemetry
|
||||
</a>
|
||||
<span className="ms-2 text-muted">(Logs + Traces)</span>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<Button variant="dark" onClick={() => onHide()}>
|
||||
<Button variant="default" onClick={() => onHide()}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</Modal.Body>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { useRouter } from 'next/router';
|
||||
import { NextSeo } from 'next-seo';
|
||||
import { Button, Form } from 'react-bootstrap';
|
||||
import { Paper } from '@mantine/core';
|
||||
import { Button, Paper, Text, TextInput } from '@mantine/core';
|
||||
|
||||
export default function JoinTeam() {
|
||||
const router = useRouter();
|
||||
|
|
@ -17,32 +16,30 @@ export default function JoinTeam() {
|
|||
</div>
|
||||
<Paper p="xl" withBorder>
|
||||
<div className="text-center">
|
||||
<Form
|
||||
<form
|
||||
className="text-start"
|
||||
action={`/api/team/setup/${token}`}
|
||||
method="POST"
|
||||
>
|
||||
<Form.Label
|
||||
htmlFor="password"
|
||||
className="text-start text-muted fs-7 mb-1"
|
||||
>
|
||||
Password
|
||||
</Form.Label>
|
||||
<Form.Control
|
||||
<TextInput
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
className="border-0"
|
||||
label="Password"
|
||||
styles={{
|
||||
label: {
|
||||
fontSize: '0.875rem',
|
||||
color: 'var(--color-text-muted)',
|
||||
marginBottom: 4,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{err != null && (
|
||||
<div
|
||||
className="text-danger mt-2"
|
||||
data-test-id="auth-error-msg"
|
||||
>
|
||||
<Text c="red" mt="sm" data-test-id="auth-error-msg">
|
||||
{err === 'invalid'
|
||||
? 'Password is invalid'
|
||||
: 'Unknown error occurred, please try again later.'}
|
||||
</div>
|
||||
</Text>
|
||||
)}
|
||||
<div className="text-center mt-4">
|
||||
<Button
|
||||
|
|
@ -54,7 +51,7 @@ export default function JoinTeam() {
|
|||
Setup a password
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</form>
|
||||
</div>
|
||||
</Paper>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ const Th = React.memo<{
|
|||
{children}
|
||||
{!!sort && (
|
||||
<i
|
||||
className={`ps-1 text-slate-400 fs-8.5 bi bi-caret-${
|
||||
className={`ps-1 fs-8.5 bi bi-caret-${
|
||||
sort === 'asc' ? 'up-fill' : 'down-fill'
|
||||
}`}
|
||||
/>
|
||||
|
|
@ -357,13 +357,11 @@ export const InfraPodsStatusTable = ({
|
|||
{isLoading ? (
|
||||
<TableLoading />
|
||||
) : isError ? (
|
||||
<div className="p-4 text-center text-slate-500 fs-8">
|
||||
<div className="p-4 text-center text-muted fs-8">
|
||||
Unable to load pod metrics
|
||||
</div>
|
||||
) : podsList.length === 0 ? (
|
||||
<div className="p-4 text-center text-slate-500 fs-8">
|
||||
No pods found
|
||||
</div>
|
||||
<div className="p-4 text-center text-muted fs-8">No pods found</div>
|
||||
) : (
|
||||
<Table horizontalSpacing="md" highlightOnHover>
|
||||
<Table.Thead className="muted-thead">
|
||||
|
|
@ -428,7 +426,7 @@ export const InfraPodsStatusTable = ({
|
|||
>
|
||||
<Text
|
||||
span
|
||||
c={pod.cpuLimitUtilization ? undefined : 'gray.7'}
|
||||
c={pod.cpuLimitUtilization ? undefined : 'gray'}
|
||||
>
|
||||
{pod.cpuLimitUtilization
|
||||
? formatNumber(
|
||||
|
|
@ -449,7 +447,7 @@ export const InfraPodsStatusTable = ({
|
|||
>
|
||||
<Text
|
||||
span
|
||||
c={pod.memLimitUtilization ? undefined : 'gray.7'}
|
||||
c={pod.memLimitUtilization ? undefined : 'gray'}
|
||||
>
|
||||
{pod.memLimitUtilization
|
||||
? formatNumber(
|
||||
|
|
@ -461,7 +459,7 @@ export const InfraPodsStatusTable = ({
|
|||
</Tooltip>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Text span c={pod.uptime ? undefined : 'gray.7'}>
|
||||
<Text span c={pod.uptime ? undefined : 'gray'}>
|
||||
{pod.uptime ? formatUptime(pod.uptime) : '–'}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
|
|
@ -469,10 +467,10 @@ export const InfraPodsStatusTable = ({
|
|||
<Text
|
||||
color={
|
||||
pod.restarts >= 10
|
||||
? 'red.6'
|
||||
? 'red'
|
||||
: pod.restarts >= 5
|
||||
? 'yellow.3'
|
||||
: 'grey.7'
|
||||
? 'yellow'
|
||||
: 'gray'
|
||||
}
|
||||
>
|
||||
{pod.restarts}
|
||||
|
|
@ -603,11 +601,11 @@ const NodesTable = ({
|
|||
{isLoading ? (
|
||||
<TableLoading />
|
||||
) : isError ? (
|
||||
<div className="p-4 text-center text-slate-500 fs-8">
|
||||
<div className="p-4 text-center text-muted fs-8">
|
||||
Unable to load nodes
|
||||
</div>
|
||||
) : nodesList.length === 0 ? (
|
||||
<div className="p-4 text-center text-slate-500 fs-8">
|
||||
<div className="p-4 text-center text-muted fs-8">
|
||||
No nodes found
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -800,11 +798,11 @@ const NamespacesTable = ({
|
|||
{isLoading ? (
|
||||
<TableLoading />
|
||||
) : isError ? (
|
||||
<div className="p-4 text-center text-slate-500 fs-8">
|
||||
<div className="p-4 text-center text-muted fs-8">
|
||||
Unable to load namespaces
|
||||
</div>
|
||||
) : namespacesList.length === 0 ? (
|
||||
<div className="p-4 text-center text-slate-500 fs-8">
|
||||
<div className="p-4 text-center text-muted fs-8">
|
||||
No namespaces found
|
||||
</div>
|
||||
) : (
|
||||
|
|
@ -1142,9 +1140,7 @@ function KubernetesDashboardPage() {
|
|||
)}
|
||||
<Group justify="space-between">
|
||||
<Group>
|
||||
<Text c="gray.4" size="xl">
|
||||
Kubernetes Dashboard
|
||||
</Text>
|
||||
<Text size="xl">Kubernetes Dashboard</Text>
|
||||
<SourceSelectControlled
|
||||
name="logSourceId"
|
||||
control={control}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
// @ts-nocheck TODO: remove this line
|
||||
|
||||
import Link from 'next/link';
|
||||
import { Button, Container, Nav, Navbar, NavDropdown } from 'react-bootstrap';
|
||||
import { Anchor, Burger, Button, Container, Group } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
|
||||
import api from './api';
|
||||
import Logo from './Logo';
|
||||
import NavHoverDropdown from './NavHoverDropdown';
|
||||
|
||||
export default function LandingHeader({
|
||||
activeKey,
|
||||
|
|
@ -18,69 +18,149 @@ export default function LandingHeader({
|
|||
const isLoggedIn = Boolean(me);
|
||||
|
||||
const { data: installation } = api.useInstallation();
|
||||
const [opened, { toggle }] = useDisclosure(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar
|
||||
collapseOnSelect
|
||||
expand="lg"
|
||||
variant="dark"
|
||||
fixed="top"
|
||||
style={{ background: '#0f1216b3', backdropFilter: 'blur(12px)' }}
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
background: 'var(--color-bg-body)',
|
||||
backdropFilter: 'blur(12px)',
|
||||
border: '1px solid var(--color-border)',
|
||||
zIndex: 100,
|
||||
}}
|
||||
>
|
||||
<Container fluid className="mx-md-4 mt-3">
|
||||
<Navbar.Brand href="/">
|
||||
<Logo />
|
||||
</Navbar.Brand>
|
||||
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
|
||||
<Navbar.Collapse className="justify-content-end">
|
||||
<Nav style={{ fontSize: 14 }} activeKey={activeKey}>
|
||||
<Nav.Link href="https://hyperdx.io" className="mx-2">
|
||||
<Container fluid px="xl" py="md">
|
||||
<Group justify="space-between" align="center">
|
||||
<Link href="/" style={{ textDecoration: 'none' }}>
|
||||
<Logo />
|
||||
</Link>
|
||||
|
||||
<Burger
|
||||
opened={opened}
|
||||
onClick={toggle}
|
||||
hiddenFrom="lg"
|
||||
color="white"
|
||||
/>
|
||||
|
||||
<Group gap="md" visibleFrom="lg" style={{ fontSize: 14 }}>
|
||||
<Anchor
|
||||
href="https://hyperdx.io"
|
||||
c={activeKey === 'cloud' ? 'green' : 'gray'}
|
||||
underline="never"
|
||||
style={{ fontWeight: activeKey === 'cloud' ? 600 : 400 }}
|
||||
>
|
||||
HyperDX Cloud
|
||||
</Nav.Link>
|
||||
<Nav.Link
|
||||
</Anchor>
|
||||
<Anchor
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack"
|
||||
className="mx-2"
|
||||
c={activeKey === 'docs' ? 'green' : 'gray'}
|
||||
underline="never"
|
||||
style={{ fontWeight: activeKey === 'docs' ? 600 : 400 }}
|
||||
>
|
||||
Docs
|
||||
</Nav.Link>
|
||||
</Anchor>
|
||||
{!isLoggedIn && installation?.isTeamExisting === true && (
|
||||
<Nav.Link
|
||||
<Anchor
|
||||
href="/login"
|
||||
active={activeKey === '/login'}
|
||||
className="mx-2"
|
||||
c={activeKey === '/login' ? 'green' : 'gray'}
|
||||
underline="never"
|
||||
style={{ fontWeight: activeKey === '/login' ? 600 : 400 }}
|
||||
>
|
||||
Login
|
||||
</Nav.Link>
|
||||
</Anchor>
|
||||
)}
|
||||
{!isLoggedIn &&
|
||||
activeKey !== '/register' &&
|
||||
installation?.isTeamExisting === false && (
|
||||
<div className="d-flex align-items-center mx-2">
|
||||
<Link href={'/register'} passHref legacyBehavior>
|
||||
<Button variant="outline-success" className="fs-7.5">
|
||||
Setup Account
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
{isLoggedIn && (
|
||||
<div className="d-flex align-items-center mx-2">
|
||||
<Link href="/search" passHref legacyBehavior>
|
||||
<Link href="/register" passHref legacyBehavior>
|
||||
<Button
|
||||
variant="outline-success"
|
||||
className="px-3"
|
||||
component="a"
|
||||
variant="outline"
|
||||
color="green"
|
||||
size="sm"
|
||||
>
|
||||
Go to Search
|
||||
Setup Account
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
{isLoggedIn && (
|
||||
<Link href="/search" passHref legacyBehavior>
|
||||
<Button
|
||||
component="a"
|
||||
variant="outline"
|
||||
color="green"
|
||||
size="sm"
|
||||
>
|
||||
Go to Search
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
</Group>
|
||||
</Group>
|
||||
|
||||
{/* Mobile menu */}
|
||||
{opened && (
|
||||
<Group gap="sm" mt="md" hiddenFrom="lg" style={{ fontSize: 14 }}>
|
||||
<Anchor
|
||||
href="https://hyperdx.io"
|
||||
underline="never"
|
||||
style={{ fontWeight: activeKey === 'cloud' ? 600 : 400 }}
|
||||
>
|
||||
HyperDX Cloud
|
||||
</Anchor>
|
||||
<Anchor
|
||||
href="https://clickhouse.com/docs/use-cases/observability/clickstack"
|
||||
underline="never"
|
||||
style={{ fontWeight: activeKey === 'docs' ? 600 : 400 }}
|
||||
>
|
||||
Docs
|
||||
</Anchor>
|
||||
{!isLoggedIn && installation?.isTeamExisting === true && (
|
||||
<Anchor
|
||||
href="/login"
|
||||
underline="never"
|
||||
style={{ fontWeight: activeKey === '/login' ? 600 : 400 }}
|
||||
>
|
||||
Login
|
||||
</Anchor>
|
||||
)}
|
||||
{!isLoggedIn &&
|
||||
activeKey !== '/register' &&
|
||||
installation?.isTeamExisting === false && (
|
||||
<Link href="/register" passHref legacyBehavior>
|
||||
<Button
|
||||
component="a"
|
||||
variant="outline"
|
||||
color="green"
|
||||
size="sm"
|
||||
fullWidth
|
||||
>
|
||||
Setup Account
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
{isLoggedIn && (
|
||||
<Link href="/search" passHref legacyBehavior>
|
||||
<Button
|
||||
component="a"
|
||||
variant="outline"
|
||||
color="green"
|
||||
size="sm"
|
||||
fullWidth
|
||||
>
|
||||
Go to Search
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
</Group>
|
||||
)}
|
||||
</Container>
|
||||
</Navbar>
|
||||
</div>
|
||||
{!fixed && <div style={{ height: 70 }} />}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ import { useEffect, useMemo, useState } from 'react';
|
|||
import Link from 'next/link';
|
||||
import cx from 'classnames';
|
||||
import { format } from 'date-fns';
|
||||
import { CloseButton } from 'react-bootstrap';
|
||||
import { JSONTree } from 'react-json-tree';
|
||||
import { Alert, Button, Text, Tooltip } from '@mantine/core';
|
||||
import { Alert, Button, CloseButton, Kbd, Text, Tooltip } from '@mantine/core';
|
||||
import { ColumnDef, Row, Table } from '@tanstack/react-table';
|
||||
|
||||
import HyperJson from './components/HyperJson';
|
||||
|
|
@ -39,7 +38,7 @@ export const CollapsibleSection = ({
|
|||
onClick={() => setCollapsed(!collapsed)}
|
||||
>
|
||||
<i className={`bi bi-chevron-${collapsed ? 'right' : 'down'} me-2`}></i>
|
||||
<div className="fs-7 text-slate-200">{title}</div>
|
||||
<div className="fs-7">{title}</div>
|
||||
</div>
|
||||
{collapsed ? null : <div className="mb-4">{children}</div>}
|
||||
</div>
|
||||
|
|
@ -73,7 +72,7 @@ export const StacktraceValue = ({
|
|||
borderRight: '1px solid #ffffff20',
|
||||
}}
|
||||
>
|
||||
<div className="text-slate-400">{label}</div>
|
||||
<div>{label}</div>
|
||||
<div className="fs-7">{value}</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -138,10 +137,7 @@ export const StacktraceRow = ({
|
|||
withArrow
|
||||
color="gray"
|
||||
>
|
||||
<i
|
||||
className="bi bi-box-seam text-slate-400 me-2"
|
||||
title="in_app: false"
|
||||
/>
|
||||
<i className="bi bi-box-seam me-2" title="in_app: false" />
|
||||
</Tooltip>
|
||||
)}
|
||||
{augmentedFrame && (
|
||||
|
|
@ -207,11 +203,7 @@ export const stacktraceColumns: ColumnDef<StacktraceFrame>[] = [
|
|||
* Breadcrumbs
|
||||
*/
|
||||
|
||||
const Url = ({ url }: { url?: string }) => (
|
||||
<span className="text-slate-300" title={url}>
|
||||
{url}
|
||||
</span>
|
||||
);
|
||||
const Url = ({ url }: { url?: string }) => <span title={url}>{url}</span>;
|
||||
|
||||
const StatusChip = React.memo(({ status }: { status?: number }) => {
|
||||
if (!status) {
|
||||
|
|
@ -240,7 +232,7 @@ const LevelChip = React.memo(({ level }: { level?: string }) => {
|
|||
? 'text-danger bg-danger'
|
||||
: level.includes('warn') || level.includes('warning')
|
||||
? 'text-warning bg-warning'
|
||||
: 'text-slate-300 bg-grey';
|
||||
: 'bg-muted';
|
||||
|
||||
return (
|
||||
<span
|
||||
|
|
@ -257,7 +249,7 @@ export const breadcrumbColumns: ColumnDef<StacktraceBreadcrumb>[] = [
|
|||
header: 'Category',
|
||||
size: 180,
|
||||
cell: ({ row }) => (
|
||||
<span className="text-slate-300 d-flex align-items-center gap-2">
|
||||
<span className="d-flex align-items-center gap-2">
|
||||
{row.original.category}
|
||||
{row.original.category === 'console' && (
|
||||
<LevelChip level={row.original.level} />
|
||||
|
|
@ -283,7 +275,7 @@ export const breadcrumbColumns: ColumnDef<StacktraceBreadcrumb>[] = [
|
|||
return (
|
||||
<div className="text-truncate">
|
||||
<span>{method} </span>
|
||||
<span className="text-slate-300" title={url}>
|
||||
<span title={url}>
|
||||
<Url url={url} />
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -295,11 +287,11 @@ export const breadcrumbColumns: ColumnDef<StacktraceBreadcrumb>[] = [
|
|||
const { from, to } = row.original.data;
|
||||
return (
|
||||
<div className="text-truncate">
|
||||
<span className="text-slate-300" title={from}>
|
||||
<span title={from}>
|
||||
<Url url={from} />
|
||||
</span>
|
||||
<span>{' → '}</span>
|
||||
<span className="text-slate-300" title={to}>
|
||||
<span title={to}>
|
||||
<Url url={to} />
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -310,10 +302,7 @@ export const breadcrumbColumns: ColumnDef<StacktraceBreadcrumb>[] = [
|
|||
if (row.original.category === 'console') {
|
||||
const { message } = row.original;
|
||||
return (
|
||||
<pre
|
||||
className="text-slate-300 mb-0 text-truncate fs-8"
|
||||
title={message}
|
||||
>
|
||||
<pre className="mb-0 text-truncate fs-8" title={message}>
|
||||
{message}
|
||||
</pre>
|
||||
);
|
||||
|
|
@ -323,14 +312,14 @@ export const breadcrumbColumns: ColumnDef<StacktraceBreadcrumb>[] = [
|
|||
return <div className="text-truncate">{row.original.message}</div>;
|
||||
}
|
||||
|
||||
return <span className="text-slate-500">Empty</span>;
|
||||
return <span className="text-muted">Empty</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Timestamp',
|
||||
size: 220,
|
||||
cell: ({ row }) => (
|
||||
<span className="text-slate-500">
|
||||
<span className="text-muted">
|
||||
<FormatTime value={row.original.timestamp * 1000} format="withMs" />
|
||||
</span>
|
||||
),
|
||||
|
|
@ -371,7 +360,7 @@ export const headerColumns: ColumnDef<[string, string]>[] = [
|
|||
header: 'Header',
|
||||
size: 260,
|
||||
cell: ({ row }) => (
|
||||
<div className="text-slate-300 text-truncate" title={row.original[0]}>
|
||||
<div className="text-truncate" title={row.original[0]}>
|
||||
{row.original[0]}
|
||||
</div>
|
||||
),
|
||||
|
|
@ -395,9 +384,7 @@ export const networkColumns: ColumnDef<{
|
|||
accessorKey: 'label',
|
||||
header: 'Label',
|
||||
size: 260,
|
||||
cell: ({ row }) => (
|
||||
<span className="text-slate-300">{row.original.label}</span>
|
||||
),
|
||||
cell: ({ row }) => <span>{row.original.label}</span>,
|
||||
},
|
||||
{
|
||||
size: UNDEFINED_WIDTH,
|
||||
|
|
@ -475,9 +462,9 @@ export const NetworkBody = ({
|
|||
)}
|
||||
</pre>
|
||||
) : body === '' ? (
|
||||
<div className="text-slate-400 px-4 py-3">{emptyMessage}</div>
|
||||
<div className="px-4 py-3">{emptyMessage}</div>
|
||||
) : (
|
||||
<div className="text-slate-400 px-4 py-3">{notCollectedMessage}</div>
|
||||
<div className="px-4 py-3">{notCollectedMessage}</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
@ -486,10 +473,6 @@ export const NetworkBody = ({
|
|||
/**
|
||||
* Keyboard shortcuts
|
||||
*/
|
||||
export const Kbd = ({ children }: { children: string }) => (
|
||||
<div className={styles.kbd}>{children}</div>
|
||||
);
|
||||
|
||||
export const LogSidePanelKbdShortcuts = () => {
|
||||
const [isDismissed, setDismissed] = useLocalStorage<boolean>(
|
||||
'kbd-shortcuts-dismissed',
|
||||
|
|
@ -509,8 +492,8 @@ export const LogSidePanelKbdShortcuts = () => {
|
|||
<div className="d-flex justify-content-between align-items-center ">
|
||||
<div className="d-flex align-items-center gap-3">
|
||||
<div>
|
||||
Use <Kbd>←</Kbd>
|
||||
<Kbd>→</Kbd> arrow keys or <Kbd>k</Kbd>
|
||||
Use <Kbd className="me-1">←</Kbd>
|
||||
<Kbd>→</Kbd> arrow keys or <Kbd className="me-1">k</Kbd>
|
||||
<Kbd>j</Kbd> to move through events
|
||||
</div>
|
||||
<div className={styles.kbdDivider} />
|
||||
|
|
@ -518,11 +501,7 @@ export const LogSidePanelKbdShortcuts = () => {
|
|||
<Kbd>ESC</Kbd> to close
|
||||
</div>
|
||||
</div>
|
||||
<CloseButton
|
||||
variant="white"
|
||||
aria-label="Hide"
|
||||
onClick={handleDismiss}
|
||||
/>
|
||||
<CloseButton aria-label="Hide" onClick={handleDismiss} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -546,7 +525,7 @@ export const SourceMapsFtux = () => {
|
|||
mt="xs"
|
||||
onClose={() => setIsDismissed(true)}
|
||||
>
|
||||
<Text size="xs" c="gray.4">
|
||||
<Text size="xs">
|
||||
<Icon name="code-square" /> Some of the stack frames are pointing to
|
||||
minified files. Use hyperdx-cli to upload your source maps and see the
|
||||
original code.
|
||||
|
|
|
|||
|
|
@ -31,16 +31,13 @@ export default function Logo({
|
|||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className="align-items-center d-flex"
|
||||
style={{
|
||||
color: 'white',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
>
|
||||
<div className="align-items-center d-flex">
|
||||
<div
|
||||
className="me-2"
|
||||
style={{ marginBottom: configs[size].iconMarginBottom }}
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Icon size={configs[size].iconSize} />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -43,12 +43,10 @@ const PodDetailsProperty = React.memo(
|
|||
if (!value) return null;
|
||||
return (
|
||||
<div className="pe-4">
|
||||
<Text size="xs" color="gray.6">
|
||||
<Text size="xs" color="gray">
|
||||
{label}
|
||||
</Text>
|
||||
<Text size="sm" color="gray.3">
|
||||
{value}
|
||||
</Text>
|
||||
<Text size="sm">{value}</Text>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
import { useState } from 'react';
|
||||
import { NavDropdown } from 'react-bootstrap';
|
||||
|
||||
export default function NavHoverDropdown(
|
||||
props: React.ComponentProps<typeof NavDropdown>,
|
||||
) {
|
||||
const [show, setShow] = useState(false);
|
||||
return (
|
||||
<NavDropdown
|
||||
{...props}
|
||||
show={show}
|
||||
onClick={() => setShow(v => !v)}
|
||||
onMouseEnter={() => setShow(true)}
|
||||
onMouseLeave={() => setShow(false)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -47,12 +47,10 @@ const PodDetailsProperty = React.memo(
|
|||
if (!value) return null;
|
||||
return (
|
||||
<div className="pe-4">
|
||||
<Text size="xs" color="gray.6">
|
||||
<Text size="xs" color="gray">
|
||||
{label}
|
||||
</Text>
|
||||
<Text size="sm" color="gray.3">
|
||||
{value}
|
||||
</Text>
|
||||
<Text size="sm">{value}</Text>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -146,19 +146,10 @@ const OnboardingChecklist = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
withBorder
|
||||
p="xs"
|
||||
mb="sm"
|
||||
radius="md"
|
||||
style={{
|
||||
background: 'var(--mantine-color-dark-8)',
|
||||
borderColor: 'var(--mantine-color-dark-4)',
|
||||
}}
|
||||
>
|
||||
<Card withBorder p="xs" mb="sm" radius="md">
|
||||
<Group justify="space-between" align="center" mb={isCollapsed ? 0 : 'xs'}>
|
||||
<Group gap="xs" align="center">
|
||||
<Text size="sm" fw="bold" c="gray.3">
|
||||
<Text size="sm" fw="bold">
|
||||
Get Started
|
||||
</Text>
|
||||
<Badge
|
||||
|
|
@ -175,7 +166,7 @@ const OnboardingChecklist = ({
|
|||
onClick={() => setIsCollapsed(!isCollapsed)}
|
||||
>
|
||||
<i
|
||||
className={`bi bi-chevron-${isCollapsed ? 'down' : 'up'} text-slate-400`}
|
||||
className={`bi bi-chevron-${isCollapsed ? 'down' : 'up'} `}
|
||||
style={{ fontSize: 12 }}
|
||||
/>
|
||||
</ActionIcon>
|
||||
|
|
@ -195,14 +186,14 @@ const OnboardingChecklist = ({
|
|||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
border: step.isComplete
|
||||
? '1px solid var(--mantine-color-green-6)'
|
||||
: 'none',
|
||||
? '1px solid var(--color-text-success)'
|
||||
: '1px solid var(--color-border)',
|
||||
backgroundColor: step.isComplete
|
||||
? 'transparent'
|
||||
: 'var(--mantine-color-dark-5)',
|
||||
: 'var(--color-bg-muted)',
|
||||
color: step.isComplete
|
||||
? 'var(--mantine-color-green-6)'
|
||||
: 'var(--mantine-color-gray-5)',
|
||||
? 'var(--color-text-success)'
|
||||
: 'var(--color-text)',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
|
|
@ -228,7 +219,6 @@ const OnboardingChecklist = ({
|
|||
<Text
|
||||
size="sm"
|
||||
fw="500"
|
||||
c={step.isComplete ? 'gray.5' : 'gray.3'}
|
||||
style={{
|
||||
textDecoration: step.isComplete ? 'line-through' : 'none',
|
||||
opacity: step.isComplete ? 0.8 : 1,
|
||||
|
|
@ -236,7 +226,7 @@ const OnboardingChecklist = ({
|
|||
>
|
||||
{step.title}
|
||||
</Text>
|
||||
<Text size="xs" c="gray.6">
|
||||
<Text size="xs" c="dimmed">
|
||||
{step.description}
|
||||
</Text>
|
||||
</div>
|
||||
|
|
@ -246,7 +236,7 @@ const OnboardingChecklist = ({
|
|||
className="bi bi-arrow-right"
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: 'var(--mantine-color-gray-5)',
|
||||
color: 'var(--color-text-muted)',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
|
@ -267,7 +257,7 @@ const OnboardingChecklist = ({
|
|||
borderRadius: 6,
|
||||
cursor: 'pointer',
|
||||
':hover': {
|
||||
backgroundColor: 'var(--mantine-color-dark-7)',
|
||||
backgroundColor: 'var(--color-bg-muted)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
|
@ -288,7 +278,7 @@ const OnboardingChecklist = ({
|
|||
borderRadius: 6,
|
||||
cursor: 'pointer',
|
||||
':hover': {
|
||||
backgroundColor: 'var(--mantine-color-dark-7)',
|
||||
backgroundColor: 'var(--color-bg-hover)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -61,10 +61,10 @@ export const PlaybarSlider = ({
|
|||
className={styles.markerDot}
|
||||
style={{
|
||||
backgroundColor: mark.isSuccess
|
||||
? 'var(--mantine-color-green-6)'
|
||||
? 'var(--color-text-success)'
|
||||
: mark.isError
|
||||
? 'var(--mantine-color-red-6)'
|
||||
: 'var(--mantine-color-gray-6)',
|
||||
? 'var(--color-text-danger)'
|
||||
: 'var(--color-text-muted)',
|
||||
left: `${((mark.ts - min) / (max - min)) * 100}%`,
|
||||
}}
|
||||
onClick={() => onChange(mark.ts)}
|
||||
|
|
@ -87,7 +87,7 @@ export const PlaybarSlider = ({
|
|||
<div className={styles.wrapper}>
|
||||
<div className={styles.markers}>{markersContent}</div>
|
||||
<Slider
|
||||
color={playerState === 'playing' ? 'green' : 'gray.5'}
|
||||
color={playerState === 'playing' ? 'green' : 'gray'}
|
||||
size="sm"
|
||||
min={min}
|
||||
max={max}
|
||||
|
|
|
|||
|
|
@ -42,12 +42,10 @@ const PodDetailsProperty = React.memo(
|
|||
if (!value) return null;
|
||||
return (
|
||||
<div className="pe-4">
|
||||
<Text size="xs" color="gray.6">
|
||||
<Text size="xs" color="gray">
|
||||
{label}
|
||||
</Text>
|
||||
<Text size="sm" color="gray.3">
|
||||
{value}
|
||||
</Text>
|
||||
<Text size="sm">{value}</Text>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ export default function SearchInputV2({
|
|||
<div className="mb-2 me-2">
|
||||
<span className="me-1">Full Text:</span>
|
||||
<code
|
||||
className="text-muted bg-body p-1 rounded border border-dark"
|
||||
className="text-muted bg-highlighted p-1 rounded border border-dark"
|
||||
role="button"
|
||||
onClick={() => {
|
||||
const newValue =
|
||||
|
|
@ -133,7 +133,7 @@ export default function SearchInputV2({
|
|||
<div className="mb-2 me-2">
|
||||
<span className="me-1">Substring:</span>
|
||||
<code
|
||||
className="text-muted bg-body p-1 rounded border border-dark"
|
||||
className="text-muted bg-highlighted p-1 rounded border border-dark"
|
||||
role="button"
|
||||
onClick={() => {
|
||||
const newValue =
|
||||
|
|
@ -148,7 +148,7 @@ export default function SearchInputV2({
|
|||
<div className="mb-2 me-2">
|
||||
<span className="me-1">Exact:</span>
|
||||
<code
|
||||
className="text-muted bg-body p-1 rounded border border-dark"
|
||||
className="text-muted bg-highlighted p-1 rounded border border-dark"
|
||||
role="button"
|
||||
onClick={() => {
|
||||
const newValue =
|
||||
|
|
@ -163,7 +163,7 @@ export default function SearchInputV2({
|
|||
<div className="mb-2 me-2">
|
||||
<span className="me-1">Not:</span>
|
||||
<code
|
||||
className="text-muted bg-body p-1 rounded border border-dark"
|
||||
className="text-muted bg-highlighted p-1 rounded border border-dark"
|
||||
role="button"
|
||||
onClick={() => {
|
||||
const newValue =
|
||||
|
|
@ -178,7 +178,7 @@ export default function SearchInputV2({
|
|||
<div className="mb-2 me-2">
|
||||
<span className="me-1">Existence:</span>
|
||||
<code
|
||||
className="text-muted bg-body p-1 rounded border border-dark"
|
||||
className="text-muted bg-highlighted p-1 rounded border border-dark"
|
||||
role="button"
|
||||
onClick={() => {
|
||||
const newValue =
|
||||
|
|
@ -193,7 +193,7 @@ export default function SearchInputV2({
|
|||
<div className="mb-2 me-2">
|
||||
<span className="me-1">Boolean:</span>
|
||||
<code
|
||||
className="text-muted bg-body p-1 rounded border border-dark"
|
||||
className="text-muted bg-highlighted p-1 rounded border border-dark"
|
||||
role="button"
|
||||
onClick={() => {
|
||||
const newValue =
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import {
|
|||
Text,
|
||||
Tooltip,
|
||||
} from '@mantine/core';
|
||||
import { IconPlayerPlay } from '@tabler/icons-react';
|
||||
|
||||
import {
|
||||
ERROR_RATE_PERCENTAGE_NUMBER_FORMAT,
|
||||
|
|
@ -174,14 +175,12 @@ export function EndpointLatencyChart({
|
|||
return (
|
||||
<ChartBox style={{ height: 350 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
Request Latency
|
||||
</Text>
|
||||
<Text size="sm">Request Latency</Text>
|
||||
<Box>
|
||||
<Button.Group>
|
||||
<Button
|
||||
variant="subtle"
|
||||
color={latencyChartType === 'line' ? 'green' : 'dark.2'}
|
||||
color={latencyChartType === 'line' ? 'green' : 'gray'}
|
||||
size="xs"
|
||||
title="Line Chart"
|
||||
onClick={() => setLatencyChartType('line')}
|
||||
|
|
@ -191,7 +190,7 @@ export function EndpointLatencyChart({
|
|||
|
||||
<Button
|
||||
variant="subtle"
|
||||
color={latencyChartType === 'histogram' ? 'green' : 'dark.2'}
|
||||
color={latencyChartType === 'histogram' ? 'green' : 'gray'}
|
||||
size="xs"
|
||||
title="Histogram"
|
||||
onClick={() => setLatencyChartType('histogram')}
|
||||
|
|
@ -302,9 +301,7 @@ function HttpTab({
|
|||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ height: 350 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
Request Error Rate
|
||||
</Text>
|
||||
<Text size="sm">Request Error Rate</Text>
|
||||
<SegmentedControl
|
||||
size="xs"
|
||||
value={reqChartType}
|
||||
|
|
@ -354,9 +351,7 @@ function HttpTab({
|
|||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ height: 350 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
Request Throughput
|
||||
</Text>
|
||||
<Text size="sm">Request Throughput</Text>
|
||||
</Group>
|
||||
{source && (
|
||||
<DBTimeChart
|
||||
|
|
@ -389,9 +384,7 @@ function HttpTab({
|
|||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ height: 350, overflow: 'auto' }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
20 Top Most Time Consuming Endpoints
|
||||
</Text>
|
||||
<Text size="sm">20 Top Most Time Consuming Endpoints</Text>
|
||||
</Group>
|
||||
|
||||
{source && (
|
||||
|
|
@ -466,7 +459,7 @@ function HttpTab({
|
|||
<Grid.Col span={12}>
|
||||
<ChartBox style={{ height: 350 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
<Text size="sm">
|
||||
Top 20{' '}
|
||||
{topEndpointsChartType === 'time'
|
||||
? 'Most Time Consuming'
|
||||
|
|
@ -569,9 +562,7 @@ function DatabaseTab({
|
|||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ height: 350 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
Total Time Consumed per Query
|
||||
</Text>
|
||||
<Text size="sm">Total Time Consumed per Query</Text>
|
||||
</Group>
|
||||
{source && (
|
||||
<DBTimeChart
|
||||
|
|
@ -604,9 +595,7 @@ function DatabaseTab({
|
|||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ height: 350 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
Throughput per Query
|
||||
</Text>
|
||||
<Text size="sm">Throughput per Query</Text>
|
||||
</Group>
|
||||
{source && (
|
||||
<DBTimeChart
|
||||
|
|
@ -642,14 +631,12 @@ function DatabaseTab({
|
|||
<Grid.Col span={12}>
|
||||
<ChartBox style={{ height: 350, overflow: 'auto' }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
Top 20 Most Time Consuming Queries
|
||||
</Text>
|
||||
<Text size="sm">Top 20 Most Time Consuming Queries</Text>
|
||||
<Box>
|
||||
<Button.Group>
|
||||
<Button
|
||||
variant="subtle"
|
||||
color={chartType === 'list' ? 'green' : 'dark.2'}
|
||||
color={chartType === 'list' ? 'green' : 'gray'}
|
||||
size="xs"
|
||||
title="List"
|
||||
onClick={() => setChartType('list')}
|
||||
|
|
@ -659,7 +646,7 @@ function DatabaseTab({
|
|||
|
||||
<Button
|
||||
variant="subtle"
|
||||
color={chartType === 'table' ? 'green' : 'dark.2'}
|
||||
color={chartType === 'table' ? 'green' : 'gray'}
|
||||
size="xs"
|
||||
title="Table"
|
||||
onClick={() => setChartType('table')}
|
||||
|
|
@ -815,9 +802,7 @@ function ErrorsTab({
|
|||
<Grid.Col span={12}>
|
||||
<ChartBox style={{ height: 350 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
Error Events per Service
|
||||
</Text>
|
||||
<Text size="sm">Error Events per Service</Text>
|
||||
</Group>
|
||||
{source && (
|
||||
<DBTimeChart
|
||||
|
|
@ -1019,7 +1004,7 @@ function ServicesDashboardPage() {
|
|||
</Button>
|
||||
</Tooltip>
|
||||
<Button variant="outline" type="submit" px="sm">
|
||||
<i className="bi bi-play"></i>
|
||||
<IconPlayerPlay size={16} />
|
||||
</Button>
|
||||
</Group>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import cx from 'classnames';
|
|||
import { ChartConfigWithOptDateRange } from '@hyperdx/common-utils/dist/types';
|
||||
import { ScrollArea, Skeleton, Stack } from '@mantine/core';
|
||||
import { useThrottledCallback, useThrottledValue } from '@mantine/hooks';
|
||||
import { IconPlayerPlay } from '@tabler/icons-react';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
|
||||
import useRowWhere from '@/hooks/useRowWhere';
|
||||
|
|
@ -78,7 +79,7 @@ const EventRow = React.forwardRef(
|
|||
</div>
|
||||
</div>
|
||||
<div className={styles.eventRowTimestamp} onClick={onTimeClick}>
|
||||
<i className="bi bi-play-fill me-1 fs-8" />
|
||||
<IconPlayerPlay size={12} className="me-1" />
|
||||
{event.formattedTimestamp}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { useState } from 'react';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import {
|
||||
|
|
@ -8,6 +7,7 @@ import {
|
|||
SearchConditionLanguage,
|
||||
TSource,
|
||||
} from '@hyperdx/common-utils/dist/types';
|
||||
import { Button } from '@mantine/core';
|
||||
import { Drawer } from '@mantine/core';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
|
||||
|
|
@ -85,11 +85,10 @@ export default function SessionSidePanel({
|
|||
styles={{
|
||||
body: {
|
||||
padding: 0,
|
||||
background: '#0F1216',
|
||||
height: '100vh',
|
||||
},
|
||||
}}
|
||||
className="border-start border-dark"
|
||||
className="border-start"
|
||||
>
|
||||
<ZIndexContext.Provider value={zIndex}>
|
||||
<div className="d-flex flex-column h-100">
|
||||
|
|
@ -111,7 +110,7 @@ export default function SessionSidePanel({
|
|||
<span>{session?.sessionCount} Events</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="d-flex">
|
||||
<div className="d-flex gap-2">
|
||||
<CopyToClipboard
|
||||
text={window.location.href}
|
||||
onCopy={() => {
|
||||
|
|
@ -122,19 +121,19 @@ export default function SessionSidePanel({
|
|||
}}
|
||||
>
|
||||
<Button
|
||||
variant="dark"
|
||||
className="text-muted-hover mx-2 d-flex align-items-center fs-8"
|
||||
variant="default"
|
||||
size="sm"
|
||||
leftSection={<i className="bi bi-link-45deg fs-7.5" />}
|
||||
style={{ fontSize: '12px' }}
|
||||
>
|
||||
<i className="bi bi-link-45deg me-2 fs-7.5" />
|
||||
Share Session
|
||||
</Button>
|
||||
</CopyToClipboard>
|
||||
<Button
|
||||
variant="dark"
|
||||
className="text-muted-hover d-flex align-items-center"
|
||||
variant="default"
|
||||
size="sm"
|
||||
onClick={onClose}
|
||||
style={{ padding: '4px 8px' }}
|
||||
>
|
||||
<i className="bi bi-x-lg" />
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -610,7 +610,7 @@ export default function SessionSubpanel({
|
|||
{showRelativeTime ? (
|
||||
<>
|
||||
{formatmmss((focus?.ts ?? 0) - minTs)}
|
||||
<span className="fw-normal text-slate-300 ms-2">
|
||||
<span className="fw-normal ms-2">
|
||||
{' / '}
|
||||
{formatmmss(maxTs - minTs)}
|
||||
</span>
|
||||
|
|
@ -625,7 +625,7 @@ export default function SessionSubpanel({
|
|||
<Tooltip label="Go 15 seconds back" color="gray">
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="gray.8"
|
||||
color="gray"
|
||||
size="md"
|
||||
radius="xl"
|
||||
onClick={skipBackward}
|
||||
|
|
@ -640,7 +640,7 @@ export default function SessionSubpanel({
|
|||
>
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="gray.8"
|
||||
color="gray"
|
||||
size="lg"
|
||||
radius="xl"
|
||||
onClick={togglePlayerState}
|
||||
|
|
@ -655,7 +655,7 @@ export default function SessionSubpanel({
|
|||
<Tooltip label="Skip 15 seconds" color="gray">
|
||||
<ActionIcon
|
||||
variant="filled"
|
||||
color="gray.8"
|
||||
color="gray"
|
||||
size="md"
|
||||
radius="xl"
|
||||
onClick={skipForward}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import {
|
|||
Text,
|
||||
} from '@mantine/core';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { IconPlayerPlay } from '@tabler/icons-react';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
|
||||
import { SourceSelectControlled } from '@/components/SourceSelect';
|
||||
|
|
@ -53,6 +54,8 @@ import { useSource, useSources } from './source';
|
|||
import { FormatTime } from './useFormatTime';
|
||||
import { formatDistanceToNowStrictShort } from './utils';
|
||||
|
||||
import styles from '../styles/SessionsPage.module.scss';
|
||||
|
||||
function SessionCard({
|
||||
email,
|
||||
maxTime,
|
||||
|
|
@ -84,14 +87,11 @@ function SessionCard({
|
|||
return (
|
||||
<div
|
||||
data-testid={`session-card-${sessionId}`}
|
||||
className="bg-hdx-dark rounded p-3 d-flex align-items-center justify-content-between text-white-hover-success-trigger"
|
||||
className={`bg-muted rounded p-3 d-flex align-items-center justify-content-between ${styles.sessionCard}`}
|
||||
onClick={onClick}
|
||||
role="button"
|
||||
>
|
||||
<div
|
||||
style={{ width: '50%', maxWidth: 500 }}
|
||||
className="child-hover-trigger"
|
||||
>
|
||||
<div style={{ width: '50%', maxWidth: 500 }} className={styles.emailText}>
|
||||
{email || `Anonymous Session ${sessionId}`}
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -491,7 +491,7 @@ export default function SessionsPage() {
|
|||
}}
|
||||
/>
|
||||
<Button variant="outline" type="submit" px="sm">
|
||||
<i className="bi bi-play"></i>
|
||||
<IconPlayerPlay size={16} />
|
||||
</Button>
|
||||
</Group>
|
||||
</Flex>
|
||||
|
|
@ -510,7 +510,7 @@ export default function SessionsPage() {
|
|||
<>
|
||||
{sessionSource && sessionSource.kind !== SourceKind.Session && (
|
||||
<Alert
|
||||
icon={<i className="bi bi-info-circle-fill text-slate-400" />}
|
||||
icon={<i className="bi bi-info-circle-fill " />}
|
||||
color="gray"
|
||||
py="xs"
|
||||
mt="md"
|
||||
|
|
@ -544,7 +544,7 @@ function SessionSetupInstructions() {
|
|||
return (
|
||||
<>
|
||||
<Stack w={500} mx="auto" mt="xl" gap="xxs">
|
||||
<i className="bi bi-laptop text-slate-600 fs-1"></i>
|
||||
<i className="bi bi-laptop text-muted fs-1"></i>
|
||||
<Text c="gray" fw={500} size="xs">
|
||||
Instructions
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -23,11 +23,13 @@ export default function TabItem({
|
|||
{...props}
|
||||
>
|
||||
<span>{children}</span>
|
||||
<div className="w-100 mt-2" style={{ height: 4 }}>
|
||||
<div className="w-100 mt-2" style={{ height: 2 }}>
|
||||
<div
|
||||
className="h-100 w-100"
|
||||
style={{
|
||||
background: active ? '#50FA7B' : '#242d33',
|
||||
background: active
|
||||
? 'var(--color-bg-success)'
|
||||
: 'var(--color-border)',
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { Fragment, useCallback, useMemo, useState } from 'react';
|
||||
import Head from 'next/head';
|
||||
import { HTTPError } from 'ky';
|
||||
import { Button as BSButton, Modal as BSModal } from 'react-bootstrap';
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { DEFAULT_METADATA_MAX_ROWS_TO_READ } from '@hyperdx/common-utils/dist/core/metadata';
|
||||
|
|
@ -18,7 +17,7 @@ import {
|
|||
Group,
|
||||
InputLabel,
|
||||
Loader,
|
||||
Modal as MModal,
|
||||
Modal,
|
||||
Stack,
|
||||
Table,
|
||||
Text,
|
||||
|
|
@ -72,7 +71,7 @@ function InviteTeamMemberForm({
|
|||
placeholder="you@company.com"
|
||||
withAsterisk={false}
|
||||
/>
|
||||
<div className="text-slate-300 fs-8">
|
||||
<div className="fs-8">
|
||||
The invite link will automatically expire after 30 days.
|
||||
</div>
|
||||
<Button variant="light" type="submit" disabled={!email || isSubmitting}>
|
||||
|
|
@ -93,11 +92,9 @@ function ConnectionsSection() {
|
|||
|
||||
return (
|
||||
<Box id="connections">
|
||||
<Text size="md" c="gray.4">
|
||||
Connections
|
||||
</Text>
|
||||
<Text size="md">Connections</Text>
|
||||
<Divider my="md" />
|
||||
<Card>
|
||||
<Card variant="muted">
|
||||
<Stack mb="md">
|
||||
{connections?.map(c => (
|
||||
<Box key={c.id}>
|
||||
|
|
@ -119,7 +116,6 @@ function ConnectionsSection() {
|
|||
{editedConnectionId !== c.id && (
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="gray.4"
|
||||
onClick={() => setEditedConnectionId(c.id)}
|
||||
size="sm"
|
||||
>
|
||||
|
|
@ -129,7 +125,6 @@ function ConnectionsSection() {
|
|||
{editedConnectionId === c.id && (
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="gray.4"
|
||||
onClick={() => setEditedConnectionId(null)}
|
||||
size="sm"
|
||||
>
|
||||
|
|
@ -156,7 +151,6 @@ function ConnectionsSection() {
|
|||
(IS_LOCAL_MODE ? (connections?.length ?? 0) < 1 : true) && (
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray.4"
|
||||
onClick={() => setIsCreatingConnection(true)}
|
||||
>
|
||||
Add Connection
|
||||
|
|
@ -193,11 +187,9 @@ function SourcesSection() {
|
|||
|
||||
return (
|
||||
<Box id="sources">
|
||||
<Text size="md" c="gray.4">
|
||||
Sources
|
||||
</Text>
|
||||
<Text size="md">Sources</Text>
|
||||
<Divider my="md" />
|
||||
<Card>
|
||||
<Card variant="muted">
|
||||
<Stack>
|
||||
{sources?.map(s => (
|
||||
<>
|
||||
|
|
@ -227,7 +219,6 @@ function SourcesSection() {
|
|||
{editedSourceId !== s.id && (
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="gray.4"
|
||||
onClick={() => setEditedSourceId(s.id)}
|
||||
size="sm"
|
||||
>
|
||||
|
|
@ -237,7 +228,6 @@ function SourcesSection() {
|
|||
{editedSourceId === s.id && (
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="gray.4"
|
||||
onClick={() => setEditedSourceId(null)}
|
||||
size="sm"
|
||||
>
|
||||
|
|
@ -264,11 +254,7 @@ function SourcesSection() {
|
|||
/>
|
||||
)}
|
||||
{!IS_LOCAL_MODE && !isCreatingSource && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setIsCreatingSource(true)}
|
||||
color="gray.4"
|
||||
>
|
||||
<Button variant="default" onClick={() => setIsCreatingSource(true)}>
|
||||
Add Source
|
||||
</Button>
|
||||
)}
|
||||
|
|
@ -467,15 +453,13 @@ function TeamMembersSection() {
|
|||
|
||||
return (
|
||||
<Box id="team_members">
|
||||
<Text size="md" c="gray.4">
|
||||
Team
|
||||
</Text>
|
||||
<Text size="md">Team</Text>
|
||||
<Divider my="md" />
|
||||
|
||||
<Card>
|
||||
<Card.Section withBorder py="sm" px="lg">
|
||||
<Group align="center" justify="space-between">
|
||||
<div className="text-slate-300 fs-7">Team Members</div>
|
||||
<div className="fs-7">Team Members</div>
|
||||
<Button
|
||||
variant="light"
|
||||
leftSection={<i className="bi bi-person-plus-fill" />}
|
||||
|
|
@ -506,7 +490,7 @@ function TeamMembersSection() {
|
|||
<Group mt={4} fz="xs">
|
||||
<div>{member.email}</div>
|
||||
{member.hasPasswordAuth && (
|
||||
<div className="text-slate-300">
|
||||
<div>
|
||||
<i className="bi bi-lock-fill" /> Password Auth
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -566,7 +550,7 @@ function TeamMembersSection() {
|
|||
</span>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Badge variant="dot" color="gray.6" fw="normal" tt="none">
|
||||
<Badge variant="dot" color="gray" fw="normal" tt="none">
|
||||
Pending Invite
|
||||
</Badge>
|
||||
<CopyToClipboard text={invitation.url}>
|
||||
|
|
@ -602,7 +586,7 @@ function TeamMembersSection() {
|
|||
</Card.Section>
|
||||
</Card>
|
||||
|
||||
<MModal
|
||||
<Modal
|
||||
centered
|
||||
onClose={() => setTeamInviteModalShow(false)}
|
||||
opened={teamInviteModalShow}
|
||||
|
|
@ -612,58 +596,56 @@ function TeamMembersSection() {
|
|||
onSubmit={onSubmitTeamInviteForm}
|
||||
isSubmitting={saveTeamInvitation.isPending}
|
||||
/>
|
||||
</MModal>
|
||||
</Modal>
|
||||
|
||||
<BSModal
|
||||
aria-labelledby="contained-modal-title-vcenter"
|
||||
<Modal
|
||||
centered
|
||||
onHide={() =>
|
||||
onClose={() =>
|
||||
setDeleteTeamMemberConfirmationModalData({
|
||||
mode: null,
|
||||
id: null,
|
||||
email: null,
|
||||
})
|
||||
}
|
||||
show={deleteTeamMemberConfirmationModalData.id != null}
|
||||
opened={deleteTeamMemberConfirmationModalData.id != null}
|
||||
size="lg"
|
||||
title="Delete Team Member"
|
||||
>
|
||||
<BSModal.Body className="bg-grey rounded">
|
||||
<h3 className="text-muted">Delete Team Member</h3>
|
||||
<p className="text-muted">
|
||||
<Stack>
|
||||
<Text>
|
||||
Deleting this team member (
|
||||
{deleteTeamMemberConfirmationModalData.email}) will revoke their
|
||||
access to the team's resources and services. This action is not
|
||||
reversible.
|
||||
</p>
|
||||
<BSButton
|
||||
variant="outline-secondary"
|
||||
className="mt-2 px-4 ms-2 float-end"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
setDeleteTeamMemberConfirmationModalData({
|
||||
mode: null,
|
||||
id: null,
|
||||
email: null,
|
||||
})
|
||||
}
|
||||
>
|
||||
Cancel
|
||||
</BSButton>
|
||||
<BSButton
|
||||
variant="outline-danger"
|
||||
className="mt-2 px-4 float-end"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
deleteTeamMemberConfirmationModalData.id &&
|
||||
onConfirmDeleteTeamMember(
|
||||
deleteTeamMemberConfirmationModalData.id,
|
||||
)
|
||||
}
|
||||
>
|
||||
Confirm
|
||||
</BSButton>
|
||||
</BSModal.Body>
|
||||
</BSModal>
|
||||
</Text>
|
||||
<Group justify="flex-end" gap="xs">
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={() =>
|
||||
setDeleteTeamMemberConfirmationModalData({
|
||||
mode: null,
|
||||
id: null,
|
||||
email: null,
|
||||
})
|
||||
}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
color="red"
|
||||
onClick={() =>
|
||||
deleteTeamMemberConfirmationModalData.id &&
|
||||
onConfirmDeleteTeamMember(
|
||||
deleteTeamMemberConfirmationModalData.id,
|
||||
)
|
||||
}
|
||||
>
|
||||
Confirm
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
@ -741,11 +723,9 @@ function IntegrationsSection() {
|
|||
|
||||
return (
|
||||
<Box id="integrations">
|
||||
<Text size="md" c="gray.4">
|
||||
Integrations
|
||||
</Text>
|
||||
<Text size="md">Integrations</Text>
|
||||
<Divider my="md" />
|
||||
<Card>
|
||||
<Card variant="muted">
|
||||
<Text mb="xs">Webhooks</Text>
|
||||
|
||||
<Stack>
|
||||
|
|
@ -770,7 +750,6 @@ function IntegrationsSection() {
|
|||
<>
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="gray.4"
|
||||
onClick={() => setEditedWebhookId(webhook._id)}
|
||||
size="compact-xs"
|
||||
leftSection={<IconPencil size={14} />}
|
||||
|
|
@ -787,7 +766,6 @@ function IntegrationsSection() {
|
|||
{editedWebhookId === webhook._id && (
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="gray.4"
|
||||
onClick={() => setEditedWebhookId(null)}
|
||||
size="compact-xs"
|
||||
>
|
||||
|
|
@ -812,7 +790,7 @@ function IntegrationsSection() {
|
|||
</Stack>
|
||||
|
||||
{!isAddWebhookModalOpen ? (
|
||||
<Button variant="outline" color="gray.4" onClick={openWebhookModal}>
|
||||
<Button variant="outline" onClick={openWebhookModal}>
|
||||
Add Webhook
|
||||
</Button>
|
||||
) : (
|
||||
|
|
@ -866,11 +844,9 @@ function TeamNameSection() {
|
|||
);
|
||||
return (
|
||||
<Box id="team_name">
|
||||
<Text size="md" c="gray.4">
|
||||
Team Name
|
||||
</Text>
|
||||
<Text size="md">Team Name</Text>
|
||||
<Divider my="md" />
|
||||
<Card>
|
||||
<Card variant="muted">
|
||||
{isEditingTeamName ? (
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<Group gap="xs">
|
||||
|
|
@ -912,12 +888,12 @@ function TeamNameSection() {
|
|||
</form>
|
||||
) : (
|
||||
<Group gap="lg">
|
||||
<div className="text-slate-300 fs-7">{team.name}</div>
|
||||
<div className="fs-7">{team.name}</div>
|
||||
{hasAdminAccess && (
|
||||
<Button
|
||||
size="xs"
|
||||
variant="default"
|
||||
leftSection={<i className="bi bi-pencil text-slate-300" />}
|
||||
leftSection={<i className="bi bi-pencil " />}
|
||||
onClick={() => {
|
||||
setIsEditingTeamName(true);
|
||||
}}
|
||||
|
|
@ -1022,12 +998,10 @@ function ClickhouseSettingForm({
|
|||
return (
|
||||
<Stack gap="xs" mb="md">
|
||||
<Group gap="xs">
|
||||
<InputLabel c="gray.3" size="md">
|
||||
{label}
|
||||
</InputLabel>
|
||||
<InputLabel size="md">{label}</InputLabel>
|
||||
{tooltip && (
|
||||
<Tooltip label={tooltip}>
|
||||
<Text c="gray.5" size="sm" style={{ cursor: 'help' }}>
|
||||
<Text size="sm" style={{ cursor: 'help' }}>
|
||||
<i className="bi bi-question-circle" />
|
||||
</Text>
|
||||
</Tooltip>
|
||||
|
|
@ -1113,7 +1087,7 @@ function ClickhouseSettingForm({
|
|||
<Button
|
||||
size="xs"
|
||||
variant="default"
|
||||
leftSection={<i className="bi bi-pencil text-slate-300" />}
|
||||
leftSection={<i className="bi bi-pencil " />}
|
||||
onClick={() => setIsEditing(true)}
|
||||
>
|
||||
Change
|
||||
|
|
@ -1136,11 +1110,9 @@ function TeamQueryConfigSection() {
|
|||
|
||||
return (
|
||||
<Box id="team_name">
|
||||
<Text size="md" c="gray.4">
|
||||
ClickHouse Client Settings
|
||||
</Text>
|
||||
<Text size="md">ClickHouse Client Settings</Text>
|
||||
<Divider my="md" />
|
||||
<Card>
|
||||
<Card variant="muted">
|
||||
<Stack>
|
||||
<ClickhouseSettingForm
|
||||
settingKey="searchRowLimit"
|
||||
|
|
@ -1202,7 +1174,7 @@ const APIKeyCopyButton = ({
|
|||
variant={copied ? 'light' : 'default'}
|
||||
color="gray"
|
||||
rightSection={
|
||||
<div className="text-slate-300 ms-2 text-nowrap">
|
||||
<div className="ms-2 text-nowrap">
|
||||
{copied ? (
|
||||
<i className="bi bi-check-lg me-2" />
|
||||
) : (
|
||||
|
|
@ -1254,14 +1226,10 @@ function ApiKeysSection() {
|
|||
|
||||
return (
|
||||
<Box id="api_keys">
|
||||
<Text size="md" c="gray.4">
|
||||
API Keys
|
||||
</Text>
|
||||
<Text size="md">API Keys</Text>
|
||||
<Divider my="md" />
|
||||
<Card>
|
||||
<Text c="gray.3" mb="md">
|
||||
Ingestion API Key
|
||||
</Text>
|
||||
<Card variant="muted" mb="md">
|
||||
<Text mb="md">Ingestion API Key</Text>
|
||||
<Group gap="xs">
|
||||
{team?.apiKey && (
|
||||
<APIKeyCopyButton value={team.apiKey} dataTestId="api-key" />
|
||||
|
|
@ -1276,7 +1244,7 @@ function ApiKeysSection() {
|
|||
</Button>
|
||||
)}
|
||||
</Group>
|
||||
<MModal
|
||||
<Modal
|
||||
aria-labelledby="contained-modal-title-vcenter"
|
||||
centered
|
||||
onClose={() => setRotateApiKeyConfirmationModalShow(false)}
|
||||
|
|
@ -1288,15 +1256,14 @@ function ApiKeysSection() {
|
|||
</Text>
|
||||
}
|
||||
>
|
||||
<MModal.Body>
|
||||
<Modal.Body>
|
||||
<Text size="md">
|
||||
Rotating the API key will invalidate your existing API key and
|
||||
generate a new one for you. This action is <b>not reversible</b>.
|
||||
</Text>
|
||||
<Group justify="end">
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray.5"
|
||||
variant="default"
|
||||
className="mt-2 px-4 ms-2 float-end"
|
||||
size="sm"
|
||||
onClick={() => setRotateApiKeyConfirmationModalShow(false)}
|
||||
|
|
@ -1305,7 +1272,7 @@ function ApiKeysSection() {
|
|||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
color="red.6"
|
||||
color="red"
|
||||
className="mt-2 px-4 float-end"
|
||||
size="sm"
|
||||
onClick={onConfirmUpdateTeamApiKey}
|
||||
|
|
@ -1313,15 +1280,13 @@ function ApiKeysSection() {
|
|||
Confirm
|
||||
</Button>
|
||||
</Group>
|
||||
</MModal.Body>
|
||||
</MModal>
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
</Card>
|
||||
{!isLoadingMe && me != null && (
|
||||
<Card>
|
||||
<Card variant="muted">
|
||||
<Card.Section p="md">
|
||||
<Text c="gray.3" mb="md">
|
||||
Personal API Access Key
|
||||
</Text>
|
||||
<Text mb="md">Personal API Access Key</Text>
|
||||
<APIKeyCopyButton value={me.accessKey} dataTestId="api-key" />
|
||||
</Card.Section>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
ActionIcon,
|
||||
Button,
|
||||
MantineProvider,
|
||||
MantineTheme,
|
||||
MantineThemeOverride,
|
||||
rem,
|
||||
Select,
|
||||
Text,
|
||||
} from '@mantine/core';
|
||||
import { Notifications } from '@mantine/notifications';
|
||||
|
||||
|
|
@ -13,11 +16,11 @@ const makeTheme = ({
|
|||
}: {
|
||||
fontFamily?: string;
|
||||
}): MantineThemeOverride => ({
|
||||
defaultRadius: 'xs',
|
||||
cursorType: 'pointer',
|
||||
fontFamily,
|
||||
primaryColor: 'green',
|
||||
primaryShade: 8,
|
||||
autoContrast: true,
|
||||
white: '#fff',
|
||||
fontSizes: {
|
||||
xxs: '11px',
|
||||
|
|
@ -37,18 +40,18 @@ const makeTheme = ({
|
|||
xl: 'calc(2rem * var(--mantine-scale))',
|
||||
},
|
||||
colors: {
|
||||
// https://mantine.dev/colors-generator/?color=09D99C
|
||||
// https://uicolors.app/generate/00c28a
|
||||
green: [
|
||||
'#e2fff8',
|
||||
'#cefef0',
|
||||
'#a0fbe0',
|
||||
'#6df9cf',
|
||||
'#09D99C', // Toned Down
|
||||
'#2ff5b8',
|
||||
'#1ef5b3',
|
||||
'#09da9d',
|
||||
'#eafff6',
|
||||
'#cdfee7',
|
||||
'#a0fad5',
|
||||
'#63f2bf',
|
||||
'#25e2a5',
|
||||
'#00c28a',
|
||||
'#00a875',
|
||||
'#00a475',
|
||||
'#008362',
|
||||
'#00674e',
|
||||
'#005542',
|
||||
],
|
||||
// https://mantine.dev/colors-generator/?color=A1A1AA
|
||||
// Customized with FAFAFA, D7D8DB, A1A1AA
|
||||
|
|
@ -75,7 +78,6 @@ const makeTheme = ({
|
|||
'#1A1B1E',
|
||||
'#141517',
|
||||
'#101113',
|
||||
'#14171b',
|
||||
],
|
||||
},
|
||||
headings: {
|
||||
|
|
@ -104,24 +106,99 @@ const makeTheme = ({
|
|||
Select: Select.extend({
|
||||
styles: {
|
||||
input: {
|
||||
border: '1px solid var(--mantine-color-gray-7)',
|
||||
border: '1px solid var(--color-border)',
|
||||
},
|
||||
},
|
||||
}),
|
||||
Input: {
|
||||
styles: {
|
||||
input: {
|
||||
border: '1px solid var(--mantine-color-gray-7)',
|
||||
backgroundColor: 'var(--color-bg-field)',
|
||||
border: '1px solid var(--color-border)',
|
||||
},
|
||||
},
|
||||
},
|
||||
Card: {
|
||||
styles: (_theme: MantineTheme, props: { variant?: string }) => {
|
||||
if (props.variant === 'muted') {
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: 'var(--color-bg-muted)',
|
||||
border: '1px solid var(--color-border)',
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: 'var(--color-bg-body)',
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
Divider: {
|
||||
styles: {
|
||||
root: {
|
||||
backgroundColor: '#191B1F',
|
||||
borderColor: 'var(--color-border)',
|
||||
borderTopColor: 'var(--color-border)',
|
||||
'--divider-color': 'var(--color-border)',
|
||||
'--item-border-color': 'var(--color-border)',
|
||||
},
|
||||
},
|
||||
},
|
||||
Accordion: {
|
||||
styles: {
|
||||
control: {
|
||||
'--item-border-color': 'var(--color-border)',
|
||||
},
|
||||
item: {
|
||||
borderColor: 'var(--color-border)',
|
||||
},
|
||||
},
|
||||
},
|
||||
UnstyledButton: {
|
||||
styles: {
|
||||
root: {
|
||||
'--item-border-color': 'var(--color-border)',
|
||||
},
|
||||
},
|
||||
},
|
||||
Paper: {
|
||||
classNames: (_theme: MantineTheme, props: { variant?: string }) => {
|
||||
if (props.variant === 'muted') {
|
||||
return {
|
||||
root: 'paper-muted',
|
||||
};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
styles: (_theme: MantineTheme, props: { variant?: string }) => {
|
||||
if (props.variant === 'muted') {
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: 'var(--color-bg-muted)',
|
||||
border: '1px solid var(--color-border)',
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
root: {
|
||||
border: '1px solid var(--color-border)',
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
Text: Text.extend({
|
||||
styles: (theme, props) => {
|
||||
if (props.variant === 'danger') {
|
||||
return {
|
||||
root: {
|
||||
color: 'var(--color-text-danger)',
|
||||
},
|
||||
};
|
||||
}
|
||||
return {};
|
||||
},
|
||||
}),
|
||||
Button: Button.extend({
|
||||
vars: (theme, props) => {
|
||||
if (props.size === 'xxs') {
|
||||
|
|
@ -137,20 +214,64 @@ const makeTheme = ({
|
|||
return { root: {} };
|
||||
},
|
||||
}),
|
||||
ActionIcon: ActionIcon.extend({
|
||||
defaultProps: {
|
||||
variant: 'subtle',
|
||||
color: 'gray',
|
||||
},
|
||||
styles: (theme, props) => {
|
||||
// Subtle variant stays transparent
|
||||
if (props.variant === 'subtle') {
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: 'transparent',
|
||||
color: 'var(--color-text)',
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--color-bg-hover)',
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: 'var(--color-bg-muted)',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Default variant
|
||||
if (props.variant === 'default') {
|
||||
return {
|
||||
root: {
|
||||
backgroundColor: 'var(--color-bg-hover)',
|
||||
color: 'var(--color-text)',
|
||||
border: 'none',
|
||||
'&:hover': {
|
||||
backgroundColor: 'var(--color-bg-muted)',
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: 'var(--color-bg-muted)',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {};
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
export const ThemeWrapper = ({
|
||||
fontFamily,
|
||||
colorScheme = 'dark',
|
||||
children,
|
||||
}: {
|
||||
fontFamily?: string;
|
||||
colorScheme?: 'dark' | 'light';
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const theme = React.useMemo(() => makeTheme({ fontFamily }), [fontFamily]);
|
||||
|
||||
return (
|
||||
<MantineProvider forceColorScheme="dark" theme={theme}>
|
||||
<MantineProvider forceColorScheme={colorScheme} theme={theme}>
|
||||
<Notifications zIndex={999999} />
|
||||
{children}
|
||||
</MantineProvider>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { memo, RefObject, useEffect, useMemo, useRef, useState } from 'react';
|
|||
import cx from 'classnames';
|
||||
import { Tooltip } from '@mantine/core';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import { color } from '@uiw/react-codemirror';
|
||||
|
||||
import useResizable from './hooks/useResizable';
|
||||
import { useDrag, usePrevious } from './utils';
|
||||
|
|
@ -165,12 +166,10 @@ function TimelineXAxis({
|
|||
width: 1,
|
||||
marginRight: -1,
|
||||
marginLeft: i === 0 ? 0 : `${percSpacing.toFixed(6)}%`,
|
||||
background: 'rgba(255, 255, 255, 0.08)',
|
||||
background: 'var(--color-bg-surface)',
|
||||
}}
|
||||
>
|
||||
<div className="ms-2 text-slate-400 fs-8.5">
|
||||
{renderMs(i * interval)}
|
||||
</div>
|
||||
<div className="ms-2 fs-8.5">{renderMs(i * interval)}</div>
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
|
|
@ -338,6 +337,7 @@ type Row = {
|
|||
style?: any;
|
||||
type?: string;
|
||||
className?: string;
|
||||
isActive?: boolean;
|
||||
};
|
||||
|
||||
export default function TimelineChart({
|
||||
|
|
@ -560,6 +560,7 @@ export default function TimelineChart({
|
|||
'd-flex align-items-center overflow-hidden',
|
||||
row.className,
|
||||
styles.timelineRow,
|
||||
row.isActive && styles.timelineRowActive,
|
||||
)}`}
|
||||
style={{
|
||||
// position: 'absolute',
|
||||
|
|
@ -582,7 +583,7 @@ export default function TimelineChart({
|
|||
<div
|
||||
className={resizeStyles.resizeHandle}
|
||||
onMouseDown={startResize}
|
||||
style={{ backgroundColor: '#3a3a44' }}
|
||||
style={{ backgroundColor: 'var(--color-bg-neutral)' }}
|
||||
/>
|
||||
</div>
|
||||
<NewTimelineRow
|
||||
|
|
@ -590,10 +591,11 @@ export default function TimelineChart({
|
|||
height={rowHeight}
|
||||
maxVal={maxVal}
|
||||
eventStyles={{
|
||||
boxShadow: '0px 0px 4px rgba(0, 0, 0, 0.5)',
|
||||
borderRadius: 2,
|
||||
fontSize: rowHeight * 0.5,
|
||||
border: '1px solid #FFFFFF10',
|
||||
border: '1px solid var(--color-border)',
|
||||
backgroundColor: 'var(--color-bg-neutral)',
|
||||
color: 'var(--color-text)',
|
||||
}}
|
||||
scale={scale}
|
||||
offset={offset}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ const SettingContainer = ({
|
|||
<div style={{ flex: 1 }}>
|
||||
{label}
|
||||
{description && (
|
||||
<Text c="gray.6" size="xs" mt={2}>
|
||||
<Text size="xs" mt={2}>
|
||||
{description}
|
||||
</Text>
|
||||
)}
|
||||
|
|
@ -87,7 +87,7 @@ export const UserPreferencesModal = ({
|
|||
title={
|
||||
<>
|
||||
<span>Preferences</span>
|
||||
<Text size="xs" c="gray.6" mt={6}>
|
||||
<Text size="xs" mt={6}>
|
||||
Customize your experience
|
||||
</Text>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ export function ChartBox({
|
|||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
background:
|
||||
'linear-gradient(180deg, rgba(250,250,250,0.018) 0%, rgba(250,250,250,0.008) 100%)',
|
||||
borderRadius: 2,
|
||||
background: 'var(--color-bg-body)',
|
||||
borderRadius: 'var(--mantine-radius-sm)',
|
||||
border: '1px solid var(--color-border)',
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { sql } from '@codemirror/lang-sql';
|
|||
import { format } from '@hyperdx/common-utils/dist/sqlFormatter';
|
||||
import { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types';
|
||||
import { Button, Paper } from '@mantine/core';
|
||||
import { IconCheck, IconCopy } from '@tabler/icons-react';
|
||||
import CodeMirror from '@uiw/react-codemirror';
|
||||
|
||||
import { useRenderedSqlChartConfig } from '@/hooks/useChartConfig';
|
||||
|
|
@ -19,20 +20,29 @@ function tryFormat(data?: string) {
|
|||
}
|
||||
}
|
||||
|
||||
function CopyButton({ text = '' }: { text?: string }) {
|
||||
function CopyButton({
|
||||
text = '',
|
||||
size = 'md',
|
||||
}: {
|
||||
text?: string;
|
||||
size?: 'xs' | 'md';
|
||||
}) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const iconSize = size === 'xs' ? 14 : 16;
|
||||
const buttonSize = size === 'xs' ? 'compact-xs' : 'sm';
|
||||
|
||||
return (
|
||||
<CopyToClipboard text={text ?? ''} onCopy={() => setCopied(true)}>
|
||||
<Button
|
||||
variant={copied ? 'light' : 'outline'}
|
||||
color="gray"
|
||||
variant={copied ? 'light' : 'default'}
|
||||
size={buttonSize}
|
||||
className="position-absolute top-0 end-0"
|
||||
>
|
||||
{copied ? (
|
||||
<i className="bi bi-check-lg me-2" />
|
||||
<IconCheck size={iconSize} className="me-2" />
|
||||
) : (
|
||||
<i className="bi bi-clipboard-fill me-2" />
|
||||
<IconCopy size={iconSize} className="me-2" />
|
||||
)}
|
||||
{copied ? 'Copied!' : 'Copy'}
|
||||
</Button>
|
||||
|
|
@ -44,10 +54,12 @@ export function SQLPreview({
|
|||
data,
|
||||
formatData = true,
|
||||
enableCopy = false,
|
||||
copyButtonSize = 'md',
|
||||
}: {
|
||||
data?: string;
|
||||
formatData?: boolean;
|
||||
enableCopy?: boolean;
|
||||
copyButtonSize?: 'xs' | 'md';
|
||||
}) {
|
||||
const displayed = formatData ? tryFormat(data) : data;
|
||||
|
||||
|
|
@ -66,7 +78,7 @@ export function SQLPreview({
|
|||
extensions={[sql()]}
|
||||
editable={false}
|
||||
/>
|
||||
{enableCopy && <CopyButton text={displayed} />}
|
||||
{enableCopy && <CopyButton text={displayed} size={copyButtonSize} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,20 +52,15 @@ export const ColorSwatchInput = ({
|
|||
size="compact-xs"
|
||||
variant="light"
|
||||
color="gray"
|
||||
bg="gray.8"
|
||||
onClick={() => setOpened(o => !o)}
|
||||
>
|
||||
{value ? (
|
||||
<Group gap="xs">
|
||||
<Text size="xs" c="gray.5">
|
||||
Color
|
||||
</Text>
|
||||
<Text size="xs">Color</Text>
|
||||
<ColorSwatch color={value} size={14} />
|
||||
</Group>
|
||||
) : (
|
||||
<Text size="xs" c="gray.5">
|
||||
Choose color
|
||||
</Text>
|
||||
<Text size="xs">Choose color</Text>
|
||||
)}
|
||||
</Button>
|
||||
</Popover.Target>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ export default function ConfirmDeleteMenu({
|
|||
return (
|
||||
<Menu withArrow>
|
||||
<Menu.Target>
|
||||
<Button variant="outline" color="gray.4" size="xs">
|
||||
<Button variant="outline" size="xs">
|
||||
Delete
|
||||
</Button>
|
||||
</Menu.Target>
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ export function ConnectionForm({
|
|||
>
|
||||
<Stack gap="md">
|
||||
<Box>
|
||||
<Text c="gray.4" size="xs" mb="xs">
|
||||
<Text size="xs" mb="xs">
|
||||
Connection Name
|
||||
</Text>
|
||||
<InputControlled
|
||||
|
|
@ -221,7 +221,7 @@ export function ConnectionForm({
|
|||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text c="gray.4" size="xs" mb="xs">
|
||||
<Text size="xs" mb="xs">
|
||||
Host
|
||||
</Text>
|
||||
<InputControlled
|
||||
|
|
@ -233,7 +233,7 @@ export function ConnectionForm({
|
|||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text c="gray.4" size="xs" mb="xs">
|
||||
<Text size="xs" mb="xs">
|
||||
Username
|
||||
</Text>
|
||||
<InputControlled
|
||||
|
|
@ -244,14 +244,13 @@ export function ConnectionForm({
|
|||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text c="gray.4" size="xs" mb="xs">
|
||||
<Text size="xs" mb="xs">
|
||||
Password
|
||||
</Text>
|
||||
{!showUpdatePassword && !isNew && (
|
||||
<Button
|
||||
data-testid="update-password-button"
|
||||
variant="outline"
|
||||
color="gray.4"
|
||||
onClick={() => {
|
||||
setShowUpdatePassword(true);
|
||||
}}
|
||||
|
|
@ -272,7 +271,6 @@ export function ConnectionForm({
|
|||
<Button
|
||||
data-testid="cancel-password-button"
|
||||
variant="outline"
|
||||
color="gray.4"
|
||||
onClick={() => {
|
||||
setShowUpdatePassword(false);
|
||||
resetField('password');
|
||||
|
|
@ -339,7 +337,7 @@ export function ConnectionForm({
|
|||
/>
|
||||
)}
|
||||
{onClose && showCancelButton && (
|
||||
<Button variant="outline" color="gray.4" onClick={onClose}>
|
||||
<Button variant="outline" onClick={onClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -247,8 +247,6 @@ export default function ContextSubpanel({
|
|||
<Flex direction="column" mih="0px" style={{ flexGrow: 1 }}>
|
||||
<Group justify="space-between" p="sm">
|
||||
<SegmentedControl
|
||||
bg="dark.7"
|
||||
color="dark.5"
|
||||
size="xs"
|
||||
data={generateSegmentedControlData()}
|
||||
value={contextBy}
|
||||
|
|
@ -287,8 +285,6 @@ export default function ContextSubpanel({
|
|||
/>
|
||||
)}
|
||||
<SegmentedControl
|
||||
bg="dark.7"
|
||||
color="dark.5"
|
||||
size="xs"
|
||||
data={[
|
||||
{ label: '100ms', value: ms('100ms').toString() },
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ const HDXBarChartTooltip = withErrorBoundary(
|
|||
<div className={styles.chartTooltip}>
|
||||
<div className={styles.chartTooltipContent}>
|
||||
{title && (
|
||||
<Text size="xs" mb="xs" c="gray.4">
|
||||
<Text size="xs" mb="xs">
|
||||
{title}
|
||||
</Text>
|
||||
)}
|
||||
|
|
@ -212,7 +212,7 @@ function PropertyComparisonChart({
|
|||
|
||||
return (
|
||||
<div style={{ width: 340, height: 120 }}>
|
||||
<Text size="xs" c="gray.4" ta="center" title={name}>
|
||||
<Text size="xs" ta="center" title={name}>
|
||||
{truncateMiddle(name, 32)}
|
||||
</Text>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ import {
|
|||
Text,
|
||||
Textarea,
|
||||
} from '@mantine/core';
|
||||
import { IconPlayerPlay } from '@tabler/icons-react';
|
||||
|
||||
import { AGG_FNS } from '@/ChartUtils';
|
||||
import { AlertChannelForm, getAlertReferenceLines } from '@/components/Alerts';
|
||||
|
|
@ -229,7 +230,6 @@ function ChartSeriesEditorComponent({
|
|||
)}
|
||||
</Group>
|
||||
}
|
||||
c="dark.2"
|
||||
labelPosition="right"
|
||||
mb={8}
|
||||
mt="sm"
|
||||
|
|
@ -677,7 +677,7 @@ export default function EditTimeChartForm({
|
|||
)}
|
||||
/>
|
||||
<Flex align="center" gap="sm" mb="sm">
|
||||
<Text c="gray.4" size="sm" className="text-nowrap">
|
||||
<Text size="sm" className="text-nowrap">
|
||||
Chart Name
|
||||
</Text>
|
||||
<InputControlled
|
||||
|
|
@ -704,7 +704,7 @@ export default function EditTimeChartForm({
|
|||
},
|
||||
}}
|
||||
/>
|
||||
<Box p="md" bg="dark.6" mb="md">
|
||||
<Box p="md" mb="md">
|
||||
<HDXMarkdownChart
|
||||
config={{
|
||||
markdown: watch('markdown') || 'Preview',
|
||||
|
|
@ -715,7 +715,7 @@ export default function EditTimeChartForm({
|
|||
) : (
|
||||
<>
|
||||
<Flex mb="md" align="center" gap="sm">
|
||||
<Text c="gray.4" pe="md" size="sm">
|
||||
<Text pe="md" size="sm">
|
||||
Data Source
|
||||
</Text>
|
||||
<SourceSelectControlled
|
||||
|
|
@ -758,7 +758,6 @@ export default function EditTimeChartForm({
|
|||
<Divider mt="md" mb="sm" />
|
||||
<Flex align="center" mt="sm">
|
||||
<Text
|
||||
c="gray.4"
|
||||
me="sm"
|
||||
size="sm"
|
||||
style={{
|
||||
|
|
@ -888,7 +887,7 @@ export default function EditTimeChartForm({
|
|||
{alert && (
|
||||
<Paper my="sm">
|
||||
<Stack gap="xs">
|
||||
<Paper px="md" py="sm" bg="dark.6" radius="xs">
|
||||
<Paper px="md" py="sm" radius="xs">
|
||||
<Group gap="xs" justify="space-between">
|
||||
<Group gap="xs">
|
||||
<Text size="sm" opacity={0.7}>
|
||||
|
|
@ -962,7 +961,7 @@ export default function EditTimeChartForm({
|
|||
{onClose != null && (
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="dark.2"
|
||||
color="dark"
|
||||
onClick={onClose}
|
||||
disabled={isSaving}
|
||||
>
|
||||
|
|
@ -994,10 +993,9 @@ export default function EditTimeChartForm({
|
|||
data-testid="chart-run-query-button"
|
||||
variant="outline"
|
||||
type="submit"
|
||||
color="green"
|
||||
onClick={onSubmit}
|
||||
>
|
||||
<i className="bi bi-play"></i>
|
||||
<IconPlayerPlay size={16} />
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
|
@ -1005,7 +1003,7 @@ export default function EditTimeChartForm({
|
|||
{!queryReady && activeTab !== 'markdown' ? (
|
||||
<Paper shadow="xs" p="xl">
|
||||
<Center mih={400}>
|
||||
<Text size="sm" c="gray.4">
|
||||
<Text size="sm">
|
||||
Please start by selecting a database, table, and timestamp column
|
||||
above and then click the play button to query data.
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -481,7 +481,7 @@ function HeatmapContainer({
|
|||
if (isLoading || isMinMaxLoading) {
|
||||
return (
|
||||
<Paper shadow="xs" p="xl">
|
||||
<Text size="sm" c="gray.4" ta="center">
|
||||
<Text size="sm" ta="center">
|
||||
Loading...
|
||||
</Text>
|
||||
</Paper>
|
||||
|
|
@ -539,7 +539,7 @@ function HeatmapContainer({
|
|||
if (time.length < 2 || generatedTsBuckets?.length < 2) {
|
||||
return (
|
||||
<Paper shadow="xs" p="xl">
|
||||
<Text size="sm" c="gray.4" ta="center">
|
||||
<Text size="sm" ta="center">
|
||||
Not enough data points to render heatmap. Try expanding your search
|
||||
criteria.
|
||||
</Text>
|
||||
|
|
@ -845,7 +845,7 @@ function Heatmap({
|
|||
}}
|
||||
/>
|
||||
<div
|
||||
className="px-2 py-1 fs-8 text-slate-200"
|
||||
className="px-2 py-1 fs-8"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: highlightedPoint.yCoord + 5,
|
||||
|
|
@ -885,7 +885,7 @@ function Heatmap({
|
|||
)}
|
||||
{selectingInfo != null && onFilter != null && (
|
||||
<div
|
||||
className="px-2 py-1 text-slate-200 fs-8"
|
||||
className="px-2 py-1 fs-8"
|
||||
style={{
|
||||
backdropFilter: 'blur(4px)',
|
||||
backgroundColor: 'rgba(#1A1D23 0.4)',
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ const HDXHistogramChartTooltip = (props: any) => {
|
|||
|
||||
return (
|
||||
<div
|
||||
className="bg-grey px-3 py-2 rounded fs-8"
|
||||
className="bg-muted px-3 py-2 rounded fs-8"
|
||||
style={{ pointerEvents: 'auto' }}
|
||||
>
|
||||
<div className="mb-2">
|
||||
|
|
|
|||
|
|
@ -72,10 +72,8 @@ const InfraSubpanelGroup = ({
|
|||
<div data-testid={`infra-subpanel-${fieldPrefix}`}>
|
||||
<Group justify="space-between" align="center">
|
||||
<Group align="center">
|
||||
<h4 className="text-slate-300 fs-6 m-0">{title}</h4>
|
||||
<h4 className="fs-6 m-0">{title}</h4>
|
||||
<SegmentedControl
|
||||
bg="dark.7"
|
||||
color="dark.5"
|
||||
size="xs"
|
||||
data={[
|
||||
{ label: '30m', value: '30m' },
|
||||
|
|
@ -88,8 +86,6 @@ const InfraSubpanelGroup = ({
|
|||
</Group>
|
||||
<Group align="center">
|
||||
<SegmentedControl
|
||||
bg="dark.7"
|
||||
color="dark.5"
|
||||
size="xs"
|
||||
data={[
|
||||
{ label: 'SM', value: 'sm' },
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ export function RowDataPanel({
|
|||
const jsonColumns = getJSONColumnNames(data?.meta);
|
||||
|
||||
return (
|
||||
<div className="flex-grow-1 bg-body overflow-auto" data-testid={dataTestId}>
|
||||
<div className="flex-grow-1 overflow-auto" data-testid={dataTestId}>
|
||||
<Box mx="md" my="sm">
|
||||
<DBRowJsonViewer data={firstRow} jsonColumns={jsonColumns} />
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ function HyperJsonMenu() {
|
|||
return (
|
||||
<Group>
|
||||
<UnstyledButton
|
||||
color="gray.0"
|
||||
color="gray"
|
||||
onClick={() =>
|
||||
setJsonOptions({ ...jsonOptions, lineWrap: !jsonOptions.lineWrap })
|
||||
}
|
||||
|
|
@ -403,7 +403,7 @@ export function DBRowJsonViewer({
|
|||
const jsonOptions = useAtomValue(viewerOptionsAtom);
|
||||
|
||||
return (
|
||||
<div className="flex-grow-1 bg-body overflow-auto">
|
||||
<div className="flex-grow-1 overflow-auto">
|
||||
<Box py="xs">
|
||||
<Group gap="xs">
|
||||
<Input
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ export function RowOverviewPanel({
|
|||
: undefined;
|
||||
|
||||
return (
|
||||
<div className="flex-grow-1 bg-body overflow-auto" data-testid={dataTestId}>
|
||||
<div className="flex-grow-1 overflow-auto" data-testid={dataTestId}>
|
||||
{!hideHeader && (
|
||||
<Box px="32px" pt="md">
|
||||
<DBRowSidePanelHeader
|
||||
|
|
@ -187,7 +187,7 @@ export function RowOverviewPanel({
|
|||
{isHttpRequest && (
|
||||
<Accordion.Item value="network">
|
||||
<Accordion.Control>
|
||||
<Text size="sm" c="gray.2" ps="md">
|
||||
<Text size="sm" ps="md">
|
||||
HTTP Request
|
||||
</Text>
|
||||
</Accordion.Control>
|
||||
|
|
@ -206,7 +206,7 @@ export function RowOverviewPanel({
|
|||
{hasException && (
|
||||
<Accordion.Item value="exception">
|
||||
<Accordion.Control>
|
||||
<Text size="sm" c="gray.2" ps="md">
|
||||
<Text size="sm" ps="md">
|
||||
Exception
|
||||
</Text>
|
||||
</Accordion.Control>
|
||||
|
|
@ -227,7 +227,7 @@ export function RowOverviewPanel({
|
|||
{hasSpanEvents && (
|
||||
<Accordion.Item value="spanEvents">
|
||||
<Accordion.Control>
|
||||
<Text size="sm" c="gray.2" ps="md">
|
||||
<Text size="sm" ps="md">
|
||||
Span Events
|
||||
</Text>
|
||||
</Accordion.Control>
|
||||
|
|
@ -242,7 +242,7 @@ export function RowOverviewPanel({
|
|||
{Object.keys(topLevelAttributes).length > 0 && (
|
||||
<Accordion.Item value="topLevelAttributes">
|
||||
<Accordion.Control>
|
||||
<Text size="sm" c="gray.2" ps="md">
|
||||
<Text size="sm" ps="md">
|
||||
Top Level Attributes
|
||||
</Text>
|
||||
</Accordion.Control>
|
||||
|
|
@ -259,7 +259,7 @@ export function RowOverviewPanel({
|
|||
|
||||
<Accordion.Item value="eventAttributes">
|
||||
<Accordion.Control>
|
||||
<Text size="sm" c="gray.2" ps="md">
|
||||
<Text size="sm" ps="md">
|
||||
{source.kind === 'log' ? 'Log' : 'Span'} Attributes
|
||||
</Text>
|
||||
</Accordion.Control>
|
||||
|
|
@ -275,7 +275,7 @@ export function RowOverviewPanel({
|
|||
|
||||
<Accordion.Item value="resourceAttributes">
|
||||
<Accordion.Control>
|
||||
<Text size="sm" c="gray.2" ps="md">
|
||||
<Text size="sm" ps="md">
|
||||
Resource Attributes
|
||||
</Text>
|
||||
</Accordion.Control>
|
||||
|
|
|
|||
|
|
@ -567,7 +567,7 @@ export default function DBRowSidePanelErrorBoundary({
|
|||
An error occurred while rendering this event.
|
||||
</div>
|
||||
|
||||
<div className="px-2 py-1 m-2 fs-7 font-monospace bg-dark-grey p-4">
|
||||
<div className="px-2 py-1 m-2 fs-7 font-monospace bg-body p-4">
|
||||
{error?.error?.message}
|
||||
</div>
|
||||
</Stack>
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ function BreadcrumbNavigation({
|
|||
onClick={() => handleBreadcrumbItemClick(index)}
|
||||
style={{ textDecoration: 'none' }}
|
||||
>
|
||||
<Text size="sm" c="blue.4" style={{ cursor: 'pointer' }}>
|
||||
<Text size="sm" c="blue" style={{ cursor: 'pointer' }}>
|
||||
{index === 0 ? 'Original Event' : crumb.label}
|
||||
</Text>
|
||||
</UnstyledButton>
|
||||
|
|
@ -99,7 +99,7 @@ function BreadcrumbNavigation({
|
|||
|
||||
// Add current level
|
||||
items.push(
|
||||
<Text key="current" size="sm" c="gray.2">
|
||||
<Text key="current" size="sm">
|
||||
Selected Event
|
||||
</Text>,
|
||||
);
|
||||
|
|
@ -198,12 +198,12 @@ export default function DBRowSidePanelHeader({
|
|||
<Flex>
|
||||
{severityText && <LogLevel level={severityText} />}
|
||||
{severityText && isValidDate(date) && (
|
||||
<Text size="xs" mx="xs" c="gray.4">
|
||||
<Text size="xs" mx="xs">
|
||||
·
|
||||
</Text>
|
||||
)}
|
||||
{isValidDate(date) && (
|
||||
<Text c="gray.4" size="xs">
|
||||
<Text size="xs">
|
||||
<FormatTime value={date} /> ·{' '}
|
||||
{formatDistanceToNowStrictShort(date)} ago
|
||||
</Text>
|
||||
|
|
@ -211,7 +211,6 @@ export default function DBRowSidePanelHeader({
|
|||
</Flex>
|
||||
{mainContent ? (
|
||||
<Paper
|
||||
bg="dark.7"
|
||||
p="xs"
|
||||
mt="sm"
|
||||
style={{
|
||||
|
|
@ -222,15 +221,12 @@ export default function DBRowSidePanelHeader({
|
|||
ref={headerRef}
|
||||
>
|
||||
<Flex justify="space-between" mb="xs">
|
||||
<Text size="xs" c="gray.4">
|
||||
{mainContentHeader}
|
||||
</Text>
|
||||
<Text size="xs">{mainContentHeader}</Text>
|
||||
{/* Toggles expanded sidebar header*/}
|
||||
{headerHeight >= maxBoxHeight && (
|
||||
<Button
|
||||
size="compact-xs"
|
||||
variant="subtle"
|
||||
color="gray.3"
|
||||
onClick={() =>
|
||||
setUserPreference({
|
||||
...userPreferences,
|
||||
|
|
@ -262,8 +258,8 @@ export default function DBRowSidePanelHeader({
|
|||
)}
|
||||
</Paper>
|
||||
) : (
|
||||
<Paper bg="dark.7" p="xs" mt="sm">
|
||||
<Text size="xs" c="gray.4" mb="xs">
|
||||
<Paper p="xs" mt="sm">
|
||||
<Text size="xs" mb="xs">
|
||||
[Empty]
|
||||
</Text>
|
||||
</Paper>
|
||||
|
|
|
|||
|
|
@ -720,7 +720,7 @@ export const RawLogTable = memo(
|
|||
return (
|
||||
<div
|
||||
data-testid="search-results-table"
|
||||
className="overflow-auto h-100 fs-8 bg-inherit"
|
||||
className="overflow-auto h-100 fs-8"
|
||||
onScroll={e => {
|
||||
fetchMoreOnBottomReached(e.target as HTMLDivElement);
|
||||
|
||||
|
|
@ -740,7 +740,7 @@ export const RawLogTable = memo(
|
|||
config={config}
|
||||
/>
|
||||
)}
|
||||
<table className={cx('w-100 bg-inherit', styles.table)} id={tableId}>
|
||||
<table className={cx('w-100', styles.table)} id={tableId}>
|
||||
<thead className={styles.tableHead}>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<tr key={headerGroup.id}>
|
||||
|
|
@ -946,7 +946,7 @@ export const RawLogTable = memo(
|
|||
})}
|
||||
<tr>
|
||||
<td colSpan={800}>
|
||||
<div className="rounded fs-7 bg-grey text-center d-flex align-items-center justify-content-center mt-3">
|
||||
<div className="rounded fs-7 bg-muted text-center d-flex align-items-center justify-content-center mt-3">
|
||||
{isLoading ? (
|
||||
<div className="my-3">
|
||||
<div className="spin-animate d-inline-block">
|
||||
|
|
@ -1020,12 +1020,12 @@ export const RawLogTable = memo(
|
|||
dedupedRows.length === 0 ? (
|
||||
<div className="my-3" data-testid="db-row-table-no-results">
|
||||
No results found.
|
||||
<Text mt="sm" c="gray.3">
|
||||
<Text mt="sm">
|
||||
Try checking the query explainer in the search bar if
|
||||
there are any search syntax issues.
|
||||
</Text>
|
||||
{dateRange?.[0] != null && dateRange?.[1] != null ? (
|
||||
<Text mt="sm" c="gray.3">
|
||||
<Text mt="sm">
|
||||
Searched Time Range:{' '}
|
||||
{formatDistance(dateRange?.[1], dateRange?.[0])} {'('}
|
||||
<FormatTime
|
||||
|
|
@ -1389,14 +1389,12 @@ function DBSqlRowTableComponent({
|
|||
</Text>
|
||||
<Box mah={100} style={{ overflow: 'auto' }}>
|
||||
{noisyPatterns.data?.map(p => (
|
||||
<Text c="gray.3" fz="xs" key={p.id}>
|
||||
<Text fz="xs" key={p.id}>
|
||||
{p.pattern}
|
||||
</Text>
|
||||
))}
|
||||
{noisyPatternIds.length === 0 && (
|
||||
<Text c="gray.3" fz="xs">
|
||||
No noisy patterns found
|
||||
</Text>
|
||||
<Text fz="xs">No noisy patterns found</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ export const TextButton = ({
|
|||
className={classes.textButton}
|
||||
data-testid={dataTestId}
|
||||
>
|
||||
<Text size="xxs" c="gray.6" lh={1} ms={ms}>
|
||||
<Text size="xxs" lh={1} ms={ms}>
|
||||
{label}
|
||||
</Text>
|
||||
</UnstyledButton>
|
||||
|
|
@ -125,7 +125,7 @@ const FilterPercentage = ({ percentage, isLoading }: FilterPercentageProps) => {
|
|||
: `~${Math.round(percentage)}%`;
|
||||
|
||||
return (
|
||||
<Text size="xs" c="gray.3" className={isLoading ? 'effect-pulse' : ''}>
|
||||
<Text size="xs" className={isLoading ? 'effect-pulse' : ''}>
|
||||
{formattedPercentage}
|
||||
</Text>
|
||||
);
|
||||
|
|
@ -154,7 +154,6 @@ export const FilterCheckbox = ({
|
|||
onClick={() => onChange?.(!value)}
|
||||
style={{ minWidth: 0 }}
|
||||
wrap="nowrap"
|
||||
align="flex-start"
|
||||
>
|
||||
<Checkbox
|
||||
checked={!!value}
|
||||
|
|
@ -184,7 +183,11 @@ export const FilterCheckbox = ({
|
|||
>
|
||||
<Text
|
||||
size="xs"
|
||||
c={value === 'excluded' ? 'red.4' : 'gray.3'}
|
||||
c={
|
||||
value === 'excluded'
|
||||
? 'var(--color-text-danger)'
|
||||
: 'var(--color-text)'
|
||||
}
|
||||
truncate="end"
|
||||
flex={1}
|
||||
title={label}
|
||||
|
|
@ -222,7 +225,7 @@ export const FilterCheckbox = ({
|
|||
/>
|
||||
</div>
|
||||
{pinned && (
|
||||
<Text size="xxs" c="gray.6">
|
||||
<Text size="xxs">
|
||||
<i className="bi bi-pin-angle-fill"></i>
|
||||
</Text>
|
||||
)}
|
||||
|
|
@ -457,6 +460,7 @@ export const FilterGroup = ({
|
|||
component={UnstyledButton}
|
||||
flex="1"
|
||||
p="0"
|
||||
pr="xxxs"
|
||||
data-testid="filter-group-control"
|
||||
classNames={{
|
||||
chevron: 'm-0',
|
||||
|
|
@ -488,15 +492,8 @@ export const FilterGroup = ({
|
|||
}
|
||||
}}
|
||||
styles={{ input: { transition: 'padding 0.2s' } }}
|
||||
rightSectionWidth={isExpanded ? 20 : 2}
|
||||
rightSection={
|
||||
<IconSearch
|
||||
size={15}
|
||||
stroke={2}
|
||||
className={`${isExpanded ? 'opacity-100' : 'opacity-0'}`}
|
||||
style={{ transition: 'opacity 0.4s 0.2s' }}
|
||||
/>
|
||||
}
|
||||
rightSectionWidth={20}
|
||||
rightSection={<IconSearch size={12} stroke={2} />}
|
||||
classNames={{
|
||||
input: 'ps-0.5',
|
||||
}}
|
||||
|
|
@ -581,7 +578,7 @@ export const FilterGroup = ({
|
|||
))}
|
||||
{optionsLoading ? (
|
||||
<Group m={6} gap="xs">
|
||||
<Loader size={12} color="gray.6" />
|
||||
<Loader size={12} color="gray" />
|
||||
<Text c="dimmed" size="xs">
|
||||
Loading...
|
||||
</Text>
|
||||
|
|
@ -624,7 +621,7 @@ export const FilterGroup = ({
|
|||
<div className="d-flex m-1">
|
||||
{loadMoreLoading ? (
|
||||
<Group m={6} gap="xs">
|
||||
<Loader size={12} color="gray.6" />
|
||||
<Loader size={12} color="gray" />
|
||||
<Text c="dimmed" size="xs">
|
||||
Loading more...
|
||||
</Text>
|
||||
|
|
@ -961,15 +958,15 @@ const DBSearchPageFiltersComponent = ({
|
|||
placement="right"
|
||||
>
|
||||
<Tabs.List w="100%">
|
||||
<Tabs.Tab value="results" size="xs" c="gray.4" h="24px">
|
||||
<Tabs.Tab value="results" size="xs" h="24px">
|
||||
<Text size="xs">Results Table</Text>
|
||||
</Tabs.Tab>
|
||||
{showDelta && (
|
||||
<Tabs.Tab value="delta" size="xs" c="gray.4" h="24px">
|
||||
<Tabs.Tab value="delta" size="xs" h="24px">
|
||||
<Text size="xs">Event Deltas</Text>
|
||||
</Tabs.Tab>
|
||||
)}
|
||||
<Tabs.Tab value="pattern" size="xs" c="gray.4" h="24px">
|
||||
<Tabs.Tab value="pattern" size="xs" h="24px">
|
||||
<Text size="xs">Event Patterns</Text>
|
||||
</Tabs.Tab>
|
||||
</Tabs.List>
|
||||
|
|
@ -1014,7 +1011,7 @@ const DBSearchPageFiltersComponent = ({
|
|||
withArrow
|
||||
label="Denoise results will visually remove events matching common event patterns from the results table."
|
||||
>
|
||||
<Text size="xs" c="gray.3" mt="-1px">
|
||||
<Text size="xs" mt="-1px">
|
||||
<i className="bi bi-noise-reduction"></i> Denoise Results
|
||||
</Text>
|
||||
</Tooltip>
|
||||
|
|
@ -1052,9 +1049,7 @@ const DBSearchPageFiltersComponent = ({
|
|||
</Flex>
|
||||
) : (
|
||||
shownFacets.length === 0 && (
|
||||
<Text size="xxs" c="gray.6">
|
||||
No filters available
|
||||
</Text>
|
||||
<Text size="xxs">No filters available</Text>
|
||||
)
|
||||
)}
|
||||
{/* Show facets even when loading to ensure pinned filters are visible while loading */}
|
||||
|
|
@ -1118,10 +1113,10 @@ const DBSearchPageFiltersComponent = ({
|
|||
|
||||
{showMoreFields && (
|
||||
<div>
|
||||
<Text size="xs" c="gray.6" fw="bold">
|
||||
<Text size="xs" fw="bold">
|
||||
Not seeing a filter?
|
||||
</Text>
|
||||
<Text size="xxs" c="gray.6">
|
||||
<Text size="xxs">
|
||||
{`Try searching instead (e.g. column:foo)`}
|
||||
</Text>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ function RowOverviewPanelWrapper({
|
|||
|
||||
return (
|
||||
<div className="position-relative">
|
||||
<div className="bg-body px-3 pt-2 position-relative">
|
||||
<div className="px-3 pt-2 position-relative">
|
||||
<TabBar
|
||||
className="fs-8"
|
||||
items={[
|
||||
|
|
@ -155,7 +155,7 @@ function RowOverviewPanelWrapper({
|
|||
onClick={setActiveTab}
|
||||
/>
|
||||
</div>
|
||||
<div className="bg-body">
|
||||
<div>
|
||||
{activeTab === InlineTab.Overview && (
|
||||
<div className="inline-overview-panel">
|
||||
<RowOverviewPanel source={source} rowId={rowId} />
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import { flexRender, Header } from '@tanstack/react-table';
|
|||
|
||||
import { UNDEFINED_WIDTH } from '@/tableUtils';
|
||||
|
||||
import styles from '../Table.module.scss';
|
||||
|
||||
export default function TableHeader({
|
||||
isLast,
|
||||
header,
|
||||
|
|
@ -20,7 +22,7 @@ export default function TableHeader({
|
|||
}) {
|
||||
return (
|
||||
<th
|
||||
className="overflow-hidden bg-hdx-dark"
|
||||
className="overflow-hidden"
|
||||
key={header.id}
|
||||
colSpan={header.colSpan}
|
||||
style={{
|
||||
|
|
@ -47,7 +49,7 @@ export default function TableHeader({
|
|||
>
|
||||
<>
|
||||
{header.isPlaceholder ? null : (
|
||||
<Text truncate="end" size="xs" flex="1" c="white">
|
||||
<Text truncate="end" size="xs" flex="1">
|
||||
{flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
|
|
@ -83,7 +85,7 @@ export default function TableHeader({
|
|||
onMouseDown={header.getResizeHandler()}
|
||||
onTouchStart={header.getResizeHandler()}
|
||||
className={cx(
|
||||
`resizer text-gray-600 cursor-col-resize`,
|
||||
`resizer ${styles.cursorColResize}`,
|
||||
header.column.getIsResizing() && 'isResizing',
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ function DBTimeChartComponent({
|
|||
// only View Events for single series
|
||||
(!Array.isArray(config.select) || config.select.length === 1) ? (
|
||||
<div
|
||||
className="bg-grey px-3 py-2 rounded fs-8"
|
||||
className="bg-muted px-3 py-2 rounded fs-8"
|
||||
style={{
|
||||
zIndex: 5,
|
||||
position: 'absolute',
|
||||
|
|
@ -290,7 +290,7 @@ function DBTimeChartComponent({
|
|||
) : null}
|
||||
{/* {totalGroups > groupKeys.length ? (
|
||||
<div
|
||||
className="bg-grey px-3 py-2 rounded fs-8"
|
||||
className="bg-muted px-3 py-2 rounded fs-8"
|
||||
style={{
|
||||
zIndex: 5,
|
||||
position: 'absolute',
|
||||
|
|
@ -310,7 +310,7 @@ function DBTimeChartComponent({
|
|||
) : null*/}
|
||||
{showDisplaySwitcher && (
|
||||
<div
|
||||
className="bg-grey px-3 py-2 rounded fs-8"
|
||||
className="bg-muted px-3 py-2 rounded fs-8"
|
||||
style={{
|
||||
zIndex: 5,
|
||||
position: 'absolute',
|
||||
|
|
|
|||
|
|
@ -127,14 +127,13 @@ export default function DBTracePanel({
|
|||
<div data-testid={dataTestId}>
|
||||
<Flex align="center" justify="space-between" mb="sm">
|
||||
<Flex align="center">
|
||||
<Text c="dark.2" size="xs" me="xs">
|
||||
<Text size="xs" me="xs">
|
||||
{parentSourceData?.traceIdExpression}:{' '}
|
||||
{traceId || 'No trace id found for event'}
|
||||
</Text>
|
||||
{traceId != null && (
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="gray.4"
|
||||
size="xs"
|
||||
onClick={() => setShowTraceIdInput(v => !v)}
|
||||
>
|
||||
|
|
@ -143,7 +142,7 @@ export default function DBTracePanel({
|
|||
)}
|
||||
</Flex>
|
||||
<Group gap="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
<Text size="sm">
|
||||
{parentSourceData?.kind === SourceKind.Log
|
||||
? 'Trace Source'
|
||||
: 'Correlated Log Source'}
|
||||
|
|
@ -153,9 +152,7 @@ export default function DBTracePanel({
|
|||
</Flex>
|
||||
{(showTraceIdInput || !traceId) && parentSourceId != null && (
|
||||
<Stack gap="xs">
|
||||
<Text c="gray.4" size="xs">
|
||||
Trace ID Expression
|
||||
</Text>
|
||||
<Text size="xs">Trace ID Expression</Text>
|
||||
<Flex>
|
||||
<SQLInlineEditorControlled
|
||||
tableConnection={tcFromSource(parentSourceData)}
|
||||
|
|
@ -185,7 +182,6 @@ export default function DBTracePanel({
|
|||
<Button
|
||||
ms="sm"
|
||||
variant="outline"
|
||||
color="gray.4"
|
||||
onClick={() => setShowTraceIdInput(false)}
|
||||
size="xs"
|
||||
>
|
||||
|
|
@ -209,7 +205,7 @@ export default function DBTracePanel({
|
|||
)}
|
||||
{traceSourceData != null && eventRowWhere != null && (
|
||||
<>
|
||||
<Text size="sm" c="dark.2" my="sm">
|
||||
<Text size="sm" my="sm">
|
||||
Event Details
|
||||
</Text>
|
||||
<TabBar
|
||||
|
|
@ -252,9 +248,7 @@ export default function DBTracePanel({
|
|||
{traceSourceData != null && !eventRowWhere && (
|
||||
<Paper shadow="xs" p="xl" mt="md">
|
||||
<Center mih={100}>
|
||||
<Text size="sm" c="gray.4">
|
||||
Please select a span above to view details.
|
||||
</Text>
|
||||
<Text size="sm">Please select a span above to view details.</Text>
|
||||
</Center>
|
||||
</Paper>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -480,7 +480,7 @@ export function DBTraceWaterfallChartContainer({
|
|||
<div
|
||||
key={index}
|
||||
style={{
|
||||
borderLeft: '1px solid var(--mantine-color-dark-4)',
|
||||
borderLeft: '1px solid var(--color-border)',
|
||||
marginLeft: 5,
|
||||
width: 8,
|
||||
minWidth: 8,
|
||||
|
|
@ -490,7 +490,6 @@ export function DBTraceWaterfallChartContainer({
|
|||
></div>
|
||||
))}
|
||||
<Text
|
||||
c="dark.2"
|
||||
span
|
||||
me="xxs"
|
||||
style={{
|
||||
|
|
@ -506,7 +505,7 @@ export function DBTraceWaterfallChartContainer({
|
|||
}`}
|
||||
/>{' '}
|
||||
</Text>
|
||||
<Text span size="xxs" c="dark.2" me="xs" pt="2px">
|
||||
<Text span size="xxs" me="xs" pt="2px">
|
||||
{result.children.length > 0 ? `(${result.children.length})` : ''}
|
||||
</Text>
|
||||
<Text
|
||||
|
|
@ -535,8 +534,8 @@ export function DBTraceWaterfallChartContainer({
|
|||
style: {
|
||||
// paddingTop: 1,
|
||||
marginTop: i === 0 ? 32 : 0,
|
||||
backgroundColor: isHighlighted ? '#202127' : undefined,
|
||||
},
|
||||
isActive: isHighlighted,
|
||||
events: [
|
||||
{
|
||||
id,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export default function EventTag({
|
|||
|
||||
if (!hasActions) {
|
||||
return (
|
||||
<div key={name} className="bg-hdx-dark px-2 py-0.5 me-1 my-1">
|
||||
<div key={name} className="bg-highlighted px-2 py-0.5 me-1 my-1">
|
||||
{displayedKey || name}: {value}
|
||||
</div>
|
||||
);
|
||||
|
|
@ -46,7 +46,7 @@ export default function EventTag({
|
|||
<Popover.Target>
|
||||
<div
|
||||
key={name}
|
||||
className="text-muted-hover bg-hdx-dark px-2 py-0.5 me-1 my-1 cursor-pointer"
|
||||
className="bg-highlighted px-2 py-0.5 me-1 my-1 cursor-pointer"
|
||||
onClick={() => setOpened(!opened)}
|
||||
>
|
||||
{displayedKey || name}: {value}
|
||||
|
|
|
|||
|
|
@ -62,17 +62,17 @@ export const StacktraceFrame = ({
|
|||
return (
|
||||
<Group gap="xs" display="inline-flex">
|
||||
<div
|
||||
className="text-slate-200 fs-8"
|
||||
className=" fs-8"
|
||||
style={{
|
||||
opacity: isLoading ? 0.8 : 1,
|
||||
filter: isLoading ? 'blur(1px)' : 'none',
|
||||
}}
|
||||
>
|
||||
{filename}
|
||||
<span className="text-slate-400">
|
||||
<span>
|
||||
:{lineno}:{colno}
|
||||
</span>
|
||||
<span className="text-slate-400">{' in '}</span>
|
||||
<span>{' in '}</span>
|
||||
{functionName && (
|
||||
<span
|
||||
style={{
|
||||
|
|
@ -127,7 +127,7 @@ export const CollapsibleSection = ({
|
|||
onClick={() => setCollapsed(!collapsed)}
|
||||
>
|
||||
<i className={`bi bi-chevron-${collapsed ? 'right' : 'down'} me-2`}></i>
|
||||
<div className="fs-7 text-slate-200">{title}</div>
|
||||
<div className="fs-7">{title}</div>
|
||||
</div>
|
||||
{collapsed ? null : <div className="mb-4">{children}</div>}
|
||||
</div>
|
||||
|
|
@ -161,7 +161,7 @@ export const StacktraceValue = ({
|
|||
borderRight: '1px solid #ffffff20',
|
||||
}}
|
||||
>
|
||||
<div className="text-slate-400">{label}</div>
|
||||
<div>{label}</div>
|
||||
<div className="fs-7">{value}</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -226,10 +226,7 @@ export const StacktraceRow = ({
|
|||
withArrow
|
||||
color="gray"
|
||||
>
|
||||
<i
|
||||
className="bi bi-box-seam text-slate-400 me-2"
|
||||
title="in_app: false"
|
||||
/>
|
||||
<i className="bi bi-box-seam me-2" title="in_app: false" />
|
||||
</Tooltip>
|
||||
)}
|
||||
{augmentedFrame && (
|
||||
|
|
@ -295,11 +292,7 @@ export const stacktraceColumns: ColumnDef<TStacktraceFrame>[] = [
|
|||
* Breadcrumbs
|
||||
*/
|
||||
|
||||
const Url = ({ url }: { url?: string }) => (
|
||||
<span className="text-slate-300" title={url}>
|
||||
{url}
|
||||
</span>
|
||||
);
|
||||
const Url = ({ url }: { url?: string }) => <span title={url}>{url}</span>;
|
||||
|
||||
const StatusChip = React.memo(({ status }: { status?: number }) => {
|
||||
if (!status) {
|
||||
|
|
@ -328,7 +321,7 @@ const LevelChip = React.memo(({ level }: { level?: string }) => {
|
|||
? 'text-danger bg-danger'
|
||||
: level.includes('warn') || level.includes('warning')
|
||||
? 'text-warning bg-warning'
|
||||
: 'text-slate-300 bg-grey';
|
||||
: 'bg-muted';
|
||||
|
||||
return (
|
||||
<span
|
||||
|
|
@ -345,7 +338,7 @@ export const breadcrumbColumns: ColumnDef<StacktraceBreadcrumb>[] = [
|
|||
header: 'Category',
|
||||
size: 180,
|
||||
cell: ({ row }) => (
|
||||
<span className="text-slate-300 d-flex align-items-center gap-2">
|
||||
<span className="d-flex align-items-center gap-2">
|
||||
{row.original.category}
|
||||
{row.original.category === 'console' && (
|
||||
<LevelChip level={row.original.level} />
|
||||
|
|
@ -371,7 +364,7 @@ export const breadcrumbColumns: ColumnDef<StacktraceBreadcrumb>[] = [
|
|||
return (
|
||||
<div className="text-truncate">
|
||||
<span>{method} </span>
|
||||
<span className="text-slate-300" title={url}>
|
||||
<span title={url}>
|
||||
<Url url={url} />
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -383,11 +376,11 @@ export const breadcrumbColumns: ColumnDef<StacktraceBreadcrumb>[] = [
|
|||
const { from, to } = row.original.data;
|
||||
return (
|
||||
<div className="text-truncate">
|
||||
<span className="text-slate-300" title={from}>
|
||||
<span title={from}>
|
||||
<Url url={from} />
|
||||
</span>
|
||||
<span>{' → '}</span>
|
||||
<span className="text-slate-300" title={to}>
|
||||
<span title={to}>
|
||||
<Url url={to} />
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -398,10 +391,7 @@ export const breadcrumbColumns: ColumnDef<StacktraceBreadcrumb>[] = [
|
|||
if (row.original.category === 'console') {
|
||||
const { message } = row.original;
|
||||
return (
|
||||
<pre
|
||||
className="text-slate-300 mb-0 text-truncate fs-8"
|
||||
title={message}
|
||||
>
|
||||
<pre className="mb-0 text-truncate fs-8" title={message}>
|
||||
{message}
|
||||
</pre>
|
||||
);
|
||||
|
|
@ -411,14 +401,14 @@ export const breadcrumbColumns: ColumnDef<StacktraceBreadcrumb>[] = [
|
|||
return <div className="text-truncate">{row.original.message}</div>;
|
||||
}
|
||||
|
||||
return <span className="text-slate-500">Empty</span>;
|
||||
return <span className="text-muted">Empty</span>;
|
||||
},
|
||||
},
|
||||
{
|
||||
header: 'Timestamp',
|
||||
size: 220,
|
||||
cell: ({ row }) => (
|
||||
<span className="text-slate-500">
|
||||
<span className="text-muted">
|
||||
<FormatTime value={row.original.timestamp * 1000} format="withMs" />
|
||||
</span>
|
||||
),
|
||||
|
|
@ -540,7 +530,7 @@ export const ExceptionSubpanel = ({
|
|||
firstException && (
|
||||
<>
|
||||
<div>
|
||||
<Text fw="bold" component="span" size="sm" c="red.6">
|
||||
<Text fw="bold" component="span" size="sm" variant="danger">
|
||||
{firstException.type}:{' '}
|
||||
</Text>
|
||||
<span className="text-muted">{firstException.value}</span>
|
||||
|
|
@ -621,8 +611,7 @@ export const ExceptionSubpanel = ({
|
|||
|
||||
{stacktraceHiddenRowsCount ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray.6"
|
||||
variant="default"
|
||||
size="xs"
|
||||
m="xs"
|
||||
onClick={handleStacktraceToggleMoreRows}
|
||||
|
|
@ -651,8 +640,7 @@ export const ExceptionSubpanel = ({
|
|||
/>
|
||||
{breadcrumbHiddenRowsCount ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray.6"
|
||||
variant="default"
|
||||
size="xs"
|
||||
m="xs"
|
||||
onClick={handleBreadcrumbToggleMoreRows}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ export const ExpandedLogRow = memo(
|
|||
<td colSpan={columnsLength} className="p-0 border-0">
|
||||
<div className={cx('mx-2 mb-2 rounded', styles.expandedRowContent)}>
|
||||
<div className="position-relative">
|
||||
<div className="bg-body px-3 pt-2 position-relative">
|
||||
<div className="px-3 pt-2 position-relative">
|
||||
{openSidebar && (
|
||||
<button
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/* stylelint-disable */
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
|
|
@ -29,15 +30,13 @@
|
|||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 2px 8px;
|
||||
|
||||
// margin: 0 -10px;margin
|
||||
align-items: flex-start;
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
background-color: #26282b;
|
||||
background-color: var(--color-bg-muted);
|
||||
|
||||
.lineMenu {
|
||||
display: flex;
|
||||
|
|
@ -47,7 +46,7 @@
|
|||
&.expanded {
|
||||
.object,
|
||||
.array {
|
||||
color: #999;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +63,7 @@
|
|||
}
|
||||
|
||||
&:active {
|
||||
background-color: #1e1f21;
|
||||
background-color: var(--color-bg-highlighted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -77,34 +76,30 @@
|
|||
top: 0;
|
||||
height: calc(100% + 1px);
|
||||
max-height: 24px;
|
||||
border-bottom: 1px solid #34373e;
|
||||
border-bottom: 1px solid var(color-border);
|
||||
}
|
||||
|
||||
.lineMenuBtn {
|
||||
border: 0;
|
||||
background-color: rgb(0 0 0 / 20%);
|
||||
backdrop-filter: blur(4px);
|
||||
color: #b8b7c9;
|
||||
border-left: 1px solid #444;
|
||||
color: var(--color-text);
|
||||
border-left: 1px solid var(--color-border);
|
||||
padding: 0 8px;
|
||||
height: 100%;
|
||||
|
||||
&:hover {
|
||||
background-color: #34373e;
|
||||
background-color: var(--color-bg-highlighted);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #1e1f21;
|
||||
background-color: var(--color-bg-muted);
|
||||
}
|
||||
}
|
||||
|
||||
.nestedLine {
|
||||
// margin-bottom: 8px;margin-bottom
|
||||
}
|
||||
|
||||
.clickable {
|
||||
&:active {
|
||||
background-color: #1e1f21;
|
||||
background-color: var(--color-bg-highlighted);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,7 +114,7 @@
|
|||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: #26282b;
|
||||
background-color: var(--color-bg-muted);
|
||||
|
||||
.hoverContent {
|
||||
display: flex;
|
||||
|
|
@ -129,7 +124,7 @@
|
|||
.hoverContent {
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
background-color: #26282b;
|
||||
background-color: var(--color-bg-muted);
|
||||
padding-right: 8px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
|
@ -141,26 +136,25 @@
|
|||
|
||||
.jsonBtn {
|
||||
display: block;
|
||||
color: #b8b7c9;
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
background-color: #26282b;
|
||||
background-color: var(--color-bg-muted);
|
||||
font-size: 11px;
|
||||
border: 1px solid #444;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 3px;
|
||||
padding: 0 6px;
|
||||
margin-bottom: 2px;
|
||||
|
||||
&:hover {
|
||||
background-color: #444;
|
||||
background-color: var(--color-border);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: #1e1f21;
|
||||
background-color: var(--color-bg-highlighted);
|
||||
}
|
||||
}
|
||||
|
||||
.keyContainer {
|
||||
// min-width: 160px;min-width
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
|
@ -169,7 +163,7 @@
|
|||
.key {
|
||||
align-items: center;
|
||||
width: auto;
|
||||
color: #8378ff;
|
||||
color: var(--color-json-key);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
|
|
@ -178,7 +172,7 @@
|
|||
// @extend .clickable;
|
||||
|
||||
i {
|
||||
color: #555;
|
||||
color: var(--color-json-punctuation);
|
||||
margin-right: -4px;
|
||||
}
|
||||
}
|
||||
|
|
@ -203,37 +197,36 @@
|
|||
}
|
||||
|
||||
.string {
|
||||
color: #a6e22e;
|
||||
color: var(--color-json-string);
|
||||
word-break: break-all;
|
||||
|
||||
// margin-left: -6px;margin-left
|
||||
&::before,
|
||||
&::after {
|
||||
content: '"';
|
||||
color: #888;
|
||||
color: var(--color-json-punctuation);
|
||||
}
|
||||
}
|
||||
|
||||
.number {
|
||||
color: #f90;
|
||||
color: var(--color-json-number);
|
||||
}
|
||||
|
||||
.boolean {
|
||||
color: #f90;
|
||||
color: var(--color-json-boolean);
|
||||
}
|
||||
|
||||
.object {
|
||||
color: #a6e22e;
|
||||
color: var(--color-json-object);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.array {
|
||||
color: #a6e22e;
|
||||
color: var(--color-json-array);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.expandMoreProps {
|
||||
color: #f90;
|
||||
color: var(--color-json-number);
|
||||
font-weight: 500;
|
||||
|
||||
i {
|
||||
|
|
|
|||
|
|
@ -408,11 +408,7 @@ const HyperJson = ({
|
|||
[styles.withLineWrap]: lineWrap,
|
||||
})}
|
||||
>
|
||||
{isEmpty ? (
|
||||
<div className="text-slate-400">Empty</div>
|
||||
) : (
|
||||
<TreeNode data={data} />
|
||||
)}
|
||||
{isEmpty ? <div>Empty</div> : <TreeNode data={data} />}
|
||||
</div>
|
||||
</HydrateAtoms>
|
||||
</Provider>
|
||||
|
|
|
|||
|
|
@ -12,25 +12,32 @@ export default function InputLanguageSwitch({
|
|||
return (
|
||||
<Flex wrap="nowrap" gap="xxxs" px="sm">
|
||||
{showHotkey && (
|
||||
<Text c="gray.3" size="xxs" bg="gray.8" px={4} py={0} mr={4} lh={1.4}>
|
||||
<Text
|
||||
size="xxs"
|
||||
bg="var(--color-bg-neutral)"
|
||||
c="white"
|
||||
px={4}
|
||||
py={0}
|
||||
mr={4}
|
||||
lh={1.4}
|
||||
>
|
||||
/
|
||||
</Text>
|
||||
)}
|
||||
<Text
|
||||
c={language === 'sql' ? 'green' : 'dark.0'}
|
||||
c={language === 'sql' ? 'var(--color-text-success)' : 'gray'}
|
||||
onClick={() => onLanguageChange('sql')}
|
||||
size="xs"
|
||||
role="button"
|
||||
>
|
||||
SQL
|
||||
</Text>
|
||||
<Text c="dark.2" size="xs">
|
||||
|
|
||||
</Text>
|
||||
<Text size="xs">|</Text>
|
||||
<Text
|
||||
size="xs"
|
||||
role="button"
|
||||
c={language === 'lucene' ? 'green' : 'dark.0'}
|
||||
fw={500}
|
||||
c={language === 'lucene' ? 'var(--color-text-success)' : 'gray'}
|
||||
onClick={() => onLanguageChange('lucene')}
|
||||
>
|
||||
Lucene
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ const renderKubeEvent = (source: TSource) => (event: KubeEvent) => {
|
|||
return (
|
||||
<Timeline.Item key={event.id}>
|
||||
<Link href={href} passHref legacyBehavior>
|
||||
<Anchor size="xs" fz={11} c="gray.6" title={event.timestamp}>
|
||||
<Anchor size="xs" fz={11} title={event.timestamp}>
|
||||
<FormatTime value={event.timestamp} />
|
||||
</Anchor>
|
||||
</Link>
|
||||
|
|
@ -285,7 +285,7 @@ export const KubeTimeline = ({
|
|||
<Timeline bulletSize={12} lineWidth={1}>
|
||||
{podEventsAfterAnchor.map(renderKubeEvent(logSource))}
|
||||
<Timeline.Item key={anchorEvent.timestamp} ref={anchorRef}>
|
||||
<Text size="xs" fz={11} c="gray.6" title={anchorEvent.timestamp}>
|
||||
<Text size="xs" fz={11} title={anchorEvent.timestamp}>
|
||||
<FormatTime value={anchorEvent.timestamp} />
|
||||
</Text>
|
||||
<Group gap="xs" my={4}>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export default function LogLevel({
|
|||
? 'red'
|
||||
: levelClass === 'warn'
|
||||
? 'yellow'
|
||||
: 'gray.4'
|
||||
: 'gray'
|
||||
}
|
||||
{...props}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -179,9 +179,9 @@ export const NetworkBody = ({
|
|||
)}
|
||||
</pre>
|
||||
) : body === '' ? (
|
||||
<div className="text-slate-400 px-4 py-3">{emptyMessage}</div>
|
||||
<div className="px-4 py-3">{emptyMessage}</div>
|
||||
) : (
|
||||
<div className="text-slate-400 px-4 py-3">{notCollectedMessage}</div>
|
||||
<div className="px-4 py-3">{notCollectedMessage}</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
@ -232,7 +232,7 @@ export function NetworkPropertySubpanel({
|
|||
});
|
||||
}}
|
||||
>
|
||||
<Button size="xs" color="gray.4" variant="light">
|
||||
<Button size="xs" variant="light">
|
||||
<i className="bi bi-terminal-plus me-2" />
|
||||
Copy Request as Curl
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -115,9 +115,8 @@ export const NumberFormatForm: React.VFC<{
|
|||
</div>
|
||||
|
||||
<div style={{ marginTop: -6 }}>
|
||||
<Paper p="xs" py={4} bg="dark.8">
|
||||
<Paper p="xs" py={4}>
|
||||
<div
|
||||
className="text-slate-400"
|
||||
style={{
|
||||
fontSize: 11,
|
||||
}}
|
||||
|
|
@ -130,9 +129,7 @@ export const NumberFormatForm: React.VFC<{
|
|||
|
||||
{values.output !== 'time' && (
|
||||
<div>
|
||||
<div className="text-slate-300 fs-8 mt-2 fw-bold mb-1">
|
||||
Decimals
|
||||
</div>
|
||||
<div className="fs-8 mt-2 fw-bold mb-1">Decimals</div>
|
||||
<Slider
|
||||
mb="xl"
|
||||
min={0}
|
||||
|
|
|
|||
|
|
@ -366,7 +366,7 @@ export default function OnboardingModal({
|
|||
/>
|
||||
)}
|
||||
{!IS_LOCAL_MODE && (
|
||||
<Text size="xs" mt="md" c="gray.4">
|
||||
<Text size="xs" mt="md">
|
||||
You can always add and edit connections later.
|
||||
</Text>
|
||||
)}
|
||||
|
|
@ -375,7 +375,6 @@ export default function OnboardingModal({
|
|||
data-testid="demo-server-button"
|
||||
variant="outline"
|
||||
w="100%"
|
||||
color="gray.4"
|
||||
onClick={handleDemoServerClick}
|
||||
>
|
||||
Connect to Demo Server
|
||||
|
|
@ -386,7 +385,6 @@ export default function OnboardingModal({
|
|||
<>
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="gray.4"
|
||||
onClick={() => setStep('connection')}
|
||||
p="xs"
|
||||
mb="md"
|
||||
|
|
@ -403,7 +401,7 @@ export default function OnboardingModal({
|
|||
setStep(undefined);
|
||||
}}
|
||||
/>
|
||||
<Text size="xs" mt="lg" c="gray.4">
|
||||
<Text size="xs" mt="lg">
|
||||
You can always add and edit sources later.
|
||||
</Text>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
@import '../../styles/variables';
|
||||
|
||||
.header {
|
||||
align-items: center;
|
||||
background-color: $body-bg;
|
||||
border-bottom: 1px solid $slate-950;
|
||||
color: $slate-200;
|
||||
background-color: var(--color-bg-body);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
color: var(--color-text-default);
|
||||
display: flex;
|
||||
font-weight: 500;
|
||||
height: 60px;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|||
import { useController, UseControllerProps } from 'react-hook-form';
|
||||
import { acceptCompletion, startCompletion } from '@codemirror/autocomplete';
|
||||
import { sql, SQLDialect } from '@codemirror/lang-sql';
|
||||
import { Flex, Group, Paper, Text } from '@mantine/core';
|
||||
import { Flex, Group, Paper, Text, useMantineColorScheme } from '@mantine/core';
|
||||
import CodeMirror, {
|
||||
Compartment,
|
||||
EditorView,
|
||||
|
|
@ -34,6 +34,7 @@ export default function SQLEditor({
|
|||
placeholder,
|
||||
value,
|
||||
}: SQLInlineEditorProps) {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const ref = useRef<ReactCodeMirrorRef>(null);
|
||||
|
||||
const compartmentRef = useRef<Compartment>(new Compartment());
|
||||
|
|
@ -42,9 +43,8 @@ export default function SQLEditor({
|
|||
<Paper
|
||||
flex="auto"
|
||||
shadow="none"
|
||||
bg="dark.6"
|
||||
style={{
|
||||
border: '1px solid var(--mantine-color-gray-7)',
|
||||
bg: 'var(--color-bg-field)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
|
|
@ -56,7 +56,7 @@ export default function SQLEditor({
|
|||
ref={ref}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
theme={'dark'}
|
||||
theme={colorScheme === 'dark' ? 'dark' : 'light'}
|
||||
minHeight={'100px'}
|
||||
extensions={[
|
||||
styleTheme,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,13 @@ import {
|
|||
Field,
|
||||
TableConnectionChoice,
|
||||
} from '@hyperdx/common-utils/dist/core/metadata';
|
||||
import { Flex, Paper, Text, Tooltip } from '@mantine/core';
|
||||
import {
|
||||
Flex,
|
||||
Paper,
|
||||
Text,
|
||||
Tooltip,
|
||||
useMantineColorScheme,
|
||||
} from '@mantine/core';
|
||||
import { IconInfoCircle } from '@tabler/icons-react';
|
||||
import CodeMirror, {
|
||||
Compartment,
|
||||
|
|
@ -136,6 +142,46 @@ const createStyleTheme = (allowMultiline: boolean = false) =>
|
|||
whiteSpace: 'nowrap',
|
||||
wordWrap: 'break-word',
|
||||
maxWidth: '100%',
|
||||
backgroundColor: 'var(--color-bg-field) !important',
|
||||
border: '1px solid var(--color-border) !important',
|
||||
borderRadius: '8px',
|
||||
boxShadow:
|
||||
'0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
|
||||
padding: '4px',
|
||||
},
|
||||
'& .cm-tooltip-autocomplete > ul': {
|
||||
fontFamily: 'inherit',
|
||||
maxHeight: '300px',
|
||||
},
|
||||
'& .cm-tooltip-autocomplete > ul > li': {
|
||||
padding: '4px 8px',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
color: 'var(--color-text)',
|
||||
},
|
||||
'& .cm-tooltip-autocomplete > ul > li[aria-selected]': {
|
||||
backgroundColor: 'var(--color-bg-field-highlighted) !important',
|
||||
color: 'var(--color-text-muted) !important',
|
||||
},
|
||||
'& .cm-tooltip-autocomplete .cm-completionLabel': {
|
||||
color: 'var(--color-text)',
|
||||
},
|
||||
'& .cm-tooltip-autocomplete .cm-completionDetail': {
|
||||
color: 'var(--color-text-muted)',
|
||||
fontStyle: 'normal',
|
||||
marginLeft: '8px',
|
||||
},
|
||||
'& .cm-tooltip-autocomplete .cm-completionInfo': {
|
||||
backgroundColor: 'var(--color-bg-field)',
|
||||
border: '1px solid var(--color-border)',
|
||||
borderRadius: '4px',
|
||||
padding: '8px',
|
||||
color: 'var(--color-text)',
|
||||
},
|
||||
'& .cm-completionIcon': {
|
||||
width: '16px',
|
||||
marginRight: '6px',
|
||||
opacity: 0.7,
|
||||
},
|
||||
'& .cm-scroller': {
|
||||
overflowX: 'hidden',
|
||||
|
|
@ -167,6 +213,7 @@ export default function SQLInlineEditor({
|
|||
parentRef,
|
||||
allowMultiline = false,
|
||||
}: SQLInlineEditorProps & TableConnectionChoice) {
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const _tableConnections = tableConnection
|
||||
? [tableConnection]
|
||||
: tableConnections;
|
||||
|
|
@ -309,9 +356,9 @@ export default function SQLInlineEditor({
|
|||
<Paper
|
||||
flex="auto"
|
||||
shadow="none"
|
||||
bg="dark.6"
|
||||
style={{
|
||||
border: `1px solid ${error ? 'var(--mantine-color-red-7)' : 'var(--mantine-color-gray-7)'}`,
|
||||
backgroundColor: 'var(--color-bg-field)',
|
||||
border: `1px solid ${error ? 'var(--color-bg-danger)' : 'var(--color-border)'}`,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
minHeight: size === 'xs' ? 30 : 36,
|
||||
|
|
@ -320,7 +367,6 @@ export default function SQLInlineEditor({
|
|||
>
|
||||
{label != null && (
|
||||
<Text
|
||||
c="gray.4"
|
||||
mx="4px"
|
||||
size="xs"
|
||||
fw="bold"
|
||||
|
|
@ -343,7 +389,7 @@ export default function SQLInlineEditor({
|
|||
ref={ref}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
theme={'dark'}
|
||||
theme={colorScheme === 'dark' ? 'dark' : 'light'}
|
||||
onFocus={() => {
|
||||
setIsFocused(true);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ export function DBSearchHeatmapChart({
|
|||
) : (
|
||||
<Paper shadow="xs" p="xl" h="100%">
|
||||
<Center mih={100} h="100%">
|
||||
<Text size="sm" c="gray.4">
|
||||
<Text size="sm">
|
||||
Please highlight an outlier range in the heatmap to view the delta
|
||||
chart.
|
||||
</Text>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export default function SearchPageActionBar({
|
|||
<Menu.Target>
|
||||
<Button
|
||||
variant="outline"
|
||||
color="dark.2"
|
||||
color="gray"
|
||||
px="xs"
|
||||
size="xs"
|
||||
style={{ flexShrink: 0 }}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ export default function SearchTotalCountChart({
|
|||
);
|
||||
|
||||
return (
|
||||
<Text size="xs" c="gray.4" mb={4}>
|
||||
<Text size="xs" mb={4}>
|
||||
{isLoading ? (
|
||||
<span className="effect-pulse">··· Results</span>
|
||||
) : totalCount !== null && !isError ? (
|
||||
|
|
|
|||
|
|
@ -95,9 +95,7 @@ export default function ServiceDashboardDbQuerySidePanel({
|
|||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ height: 350 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
Total Query Time
|
||||
</Text>
|
||||
<Text size="sm">Total Query Time</Text>
|
||||
</Group>
|
||||
{source && (
|
||||
<DBTimeChart
|
||||
|
|
@ -125,9 +123,7 @@ export default function ServiceDashboardDbQuerySidePanel({
|
|||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ height: 350 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
Query Throughput
|
||||
</Text>
|
||||
<Text size="sm">Query Throughput</Text>
|
||||
</Group>
|
||||
{source && (
|
||||
<DBTimeChart
|
||||
|
|
|
|||
|
|
@ -88,9 +88,7 @@ export default function ServiceDashboardEndpointPerformanceChart({
|
|||
return (
|
||||
<ChartBox style={{ height: 350, overflow: 'auto' }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
20 Top Most Time Consuming Operations
|
||||
</Text>
|
||||
<Text size="sm">20 Top Most Time Consuming Operations</Text>
|
||||
</Group>
|
||||
{source && (
|
||||
<DBListBarChart
|
||||
|
|
|
|||
|
|
@ -100,9 +100,7 @@ export default function ServiceDashboardEndpointSidePanel({
|
|||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ height: 350 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
Request Error Rate
|
||||
</Text>
|
||||
<Text size="sm">Request Error Rate</Text>
|
||||
</Group>
|
||||
{source && (
|
||||
<DBTimeChart
|
||||
|
|
@ -134,9 +132,7 @@ export default function ServiceDashboardEndpointSidePanel({
|
|||
<Grid.Col span={6}>
|
||||
<ChartBox style={{ height: 350 }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
Request Throughput
|
||||
</Text>
|
||||
<Text size="sm">Request Throughput</Text>
|
||||
</Group>
|
||||
{source && (
|
||||
<DBTimeChart
|
||||
|
|
|
|||
|
|
@ -64,12 +64,8 @@ export default function SlowestEventsTile({
|
|||
return (
|
||||
<ChartBox style={{ height }}>
|
||||
<Group justify="space-between" align="center" mb="sm">
|
||||
<Text size="sm" c="gray.4">
|
||||
{title}
|
||||
</Text>
|
||||
<Text size="xs" c="dark.2">
|
||||
(Slower than {roundedP95}ms)
|
||||
</Text>
|
||||
<Text size="sm">{title}</Text>
|
||||
<Text size="xs">(Slower than {roundedP95}ms)</Text>
|
||||
</Group>
|
||||
{isLoading && !data ? (
|
||||
<div className="d-flex h-100 w-100 align-items-center justify-content-center text-muted">
|
||||
|
|
|
|||
|
|
@ -20,17 +20,10 @@ export default function ServiceMapSidePanel({
|
|||
return (
|
||||
<Stack w="100%">
|
||||
<Group gap={0}>
|
||||
<Text size="sm" c="gray.2" ps="sm">
|
||||
<Text size="sm" ps="sm">
|
||||
Service Map
|
||||
</Text>
|
||||
<Badge
|
||||
size="xs"
|
||||
ms="xs"
|
||||
color="gray.4"
|
||||
autoContrast
|
||||
radius="sm"
|
||||
className="align-text-bottom"
|
||||
>
|
||||
<Badge size="xs" ms="xs" color="gray" autoContrast radius="sm">
|
||||
Beta
|
||||
</Badge>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ function FormRow({
|
|||
}}
|
||||
>
|
||||
{typeof label === 'string' ? (
|
||||
<Text tt="capitalize" c="gray.6" size="sm">
|
||||
<Text tt="capitalize" size="sm">
|
||||
{label}
|
||||
</Text>
|
||||
) : (
|
||||
|
|
@ -120,7 +120,6 @@ function FormRow({
|
|||
)}
|
||||
</Stack>
|
||||
<Text
|
||||
c="gray.4"
|
||||
me="sm"
|
||||
style={{
|
||||
...(!helpText ? { opacity: 0, pointerEvents: 'none' } : {}),
|
||||
|
|
@ -196,7 +195,6 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
underline="always"
|
||||
onClick={() => setShowOptionalFields(true)}
|
||||
size="xs"
|
||||
c="gray.4"
|
||||
>
|
||||
<Text me="sm" span>
|
||||
<i className="bi bi-gear" />
|
||||
|
|
@ -209,7 +207,6 @@ export function LogTableModelForm({ control, watch }: TableModelProps) {
|
|||
onClick={() => setShowOptionalFields(false)}
|
||||
size="xs"
|
||||
variant="subtle"
|
||||
color="gray.4"
|
||||
>
|
||||
Hide Optional Fields
|
||||
</Button>
|
||||
|
|
@ -1126,15 +1123,10 @@ export function TableSourceForm({
|
|||
>
|
||||
<Stack gap="md" mb="md">
|
||||
<Flex justify="space-between" align="center" mb="lg">
|
||||
<Text c="gray.4">Source Settings</Text>
|
||||
<Text>Source Settings</Text>
|
||||
<Group>
|
||||
{onCancel && (
|
||||
<Button
|
||||
variant="outline"
|
||||
color="gray.4"
|
||||
onClick={onCancel}
|
||||
size="xs"
|
||||
>
|
||||
<Button variant="outline" onClick={onCancel} size="xs">
|
||||
Cancel
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useState } from 'react';
|
||||
import { MetricsDataType, TSource } from '@hyperdx/common-utils/dist/types';
|
||||
import { Modal, Paper, Tabs, TextProps, Tooltip } from '@mantine/core';
|
||||
import { Modal, Paper, Tabs, Text, TextProps, Tooltip } from '@mantine/core';
|
||||
import { IconCode } from '@tabler/icons-react';
|
||||
|
||||
import { useTableMetadata } from '@/hooks/useMetadata';
|
||||
|
|
@ -32,16 +32,18 @@ const SourceSchemaInfoIcon = ({
|
|||
<Tooltip
|
||||
label={tooltipText}
|
||||
color="dark"
|
||||
c="white"
|
||||
position="right"
|
||||
onClick={() => isEnabled && onClick()}
|
||||
>
|
||||
{variant === 'text' ? (
|
||||
<span
|
||||
<Text
|
||||
fw={500}
|
||||
size="xs"
|
||||
className="text-sucess-hover"
|
||||
style={{ cursor: isEnabled ? 'pointer' : 'default', ...iconStyles }}
|
||||
>
|
||||
Schema
|
||||
</span>
|
||||
</Text>
|
||||
) : (
|
||||
<IconCode size={16} />
|
||||
)}
|
||||
|
|
@ -67,7 +69,13 @@ const TableSchemaPreview = ({
|
|||
});
|
||||
|
||||
return (
|
||||
<Paper flex="auto" shadow="none" radius="sm" style={{ overflow: 'hidden' }}>
|
||||
<Paper
|
||||
flex="auto"
|
||||
shadow="none"
|
||||
radius="sm"
|
||||
p="xs"
|
||||
style={{ overflow: 'hidden' }}
|
||||
>
|
||||
{isLoading ? (
|
||||
<div className="spin-animate d-inline-block">
|
||||
<i className="bi bi-arrow-repeat" />
|
||||
|
|
@ -76,6 +84,7 @@ const TableSchemaPreview = ({
|
|||
<SQLPreview
|
||||
data={data?.create_table_query ?? 'Schema is not available'}
|
||||
enableCopy={!!data?.create_table_query}
|
||||
copyButtonSize="xs"
|
||||
/>
|
||||
)}
|
||||
</Paper>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const spanEventColumns: ColumnDef<SpanEventData>[] = [
|
|||
header: 'Timestamp',
|
||||
size: 120,
|
||||
cell: ({ row }) => (
|
||||
<span className="text-slate-500">
|
||||
<span className="text-muted">
|
||||
<FormatTime
|
||||
value={new Date(row.original.Timestamp).getTime()}
|
||||
format="withMs"
|
||||
|
|
@ -35,7 +35,7 @@ const spanEventColumns: ColumnDef<SpanEventData>[] = [
|
|||
header: 'Name',
|
||||
size: 180,
|
||||
cell: ({ row }) => (
|
||||
<span className="text-slate-300 d-flex align-items-center gap-2">
|
||||
<span className="d-flex align-items-center gap-2">
|
||||
{row.original.Name}
|
||||
</span>
|
||||
),
|
||||
|
|
@ -53,7 +53,7 @@ const spanEventColumns: ColumnDef<SpanEventData>[] = [
|
|||
</Box>
|
||||
);
|
||||
}
|
||||
return <span className="text-slate-500">Empty</span>;
|
||||
return <span className="text-muted">Empty</span>;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
@ -93,7 +93,7 @@ export const SpanEventsSubpanel = ({
|
|||
|
||||
if (!sortedEvents || sortedEvents.length === 0) {
|
||||
return (
|
||||
<div className="p-3 text-slate-500 fs-7">
|
||||
<div className="p-3 text-muted fs-7">
|
||||
No span events available for this trace
|
||||
</div>
|
||||
);
|
||||
|
|
@ -124,7 +124,6 @@ export const SpanEventsSubpanel = ({
|
|||
variant="default"
|
||||
size="xs"
|
||||
my="sm"
|
||||
c="gray.4"
|
||||
onClick={handleToggleMoreRows}
|
||||
>
|
||||
{isExpanded ? (
|
||||
|
|
|
|||
|
|
@ -16,17 +16,17 @@ export const StacktraceFrame = ({
|
|||
return (
|
||||
<Group gap="xs" display="inline-flex">
|
||||
<div
|
||||
className="text-slate-200 fs-8"
|
||||
className=" fs-8"
|
||||
style={{
|
||||
opacity: isLoading ? 0.8 : 1,
|
||||
filter: isLoading ? 'blur(1px)' : 'none',
|
||||
}}
|
||||
>
|
||||
{filename}
|
||||
<span className="text-slate-400">
|
||||
<span>
|
||||
:{lineno}:{colno}
|
||||
</span>
|
||||
<span className="text-slate-400">{' in '}</span>
|
||||
<span>{' in '}</span>
|
||||
{functionName && (
|
||||
<span
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
@import '../../styles/variables';
|
||||
|
||||
$compactVerticalPadding: 6px;
|
||||
$normalVerticalPadding: 10px;
|
||||
$comfortableVerticalPadding: 14px;
|
||||
|
|
@ -22,7 +20,7 @@ $horizontalPadding: 12px;
|
|||
}
|
||||
|
||||
tr {
|
||||
border-bottom: 1px solid $slate-950;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
tbody {
|
||||
|
|
@ -32,7 +30,7 @@ $horizontalPadding: 12px;
|
|||
}
|
||||
|
||||
th {
|
||||
color: $slate-300;
|
||||
color: red;
|
||||
text-transform: uppercase;
|
||||
font-size: 9px;
|
||||
font-weight: 500;
|
||||
|
|
@ -103,7 +101,7 @@ $horizontalPadding: 12px;
|
|||
}
|
||||
|
||||
.emptyMessage {
|
||||
color: $slate-500;
|
||||
color: var(--color-text);
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
|
@ -112,19 +110,25 @@ $horizontalPadding: 12px;
|
|||
display: flex;
|
||||
gap: 4px;
|
||||
border-radius: 4px;
|
||||
background-color: $slate-950;
|
||||
border: 1px solid $slate-900;
|
||||
color: $slate-300;
|
||||
background-color: var(--color-bg-field);
|
||||
border: 1px solid var(--color-border);
|
||||
color: var(--color-text);
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
|
||||
&:hover {
|
||||
background-color: $slate-900;
|
||||
border-color: $slate-800;
|
||||
background-color: var(--color-bg-field-highlighted);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $slate-950;
|
||||
border-color: $slate-950;
|
||||
background-color: var(--color-bg-field-highlighted);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
}
|
||||
|
||||
.cursorColResize {
|
||||
cursor: col-resize;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ export const Tags = React.memo(
|
|||
color="gray"
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<i className="bi bi-tags text-slate-300 fs-7" />
|
||||
<i className="bi bi-tags fs-7" />
|
||||
</ActionIcon>
|
||||
)}
|
||||
</Popover.Target>
|
||||
|
|
@ -127,11 +127,10 @@ export const Tags = React.memo(
|
|||
/>
|
||||
<ScrollArea viewportProps={{ style: { maxHeight: 200 } }}>
|
||||
{filtered.length === 0 && (
|
||||
<div className="pt-3 px-4 fs-8 text-slate-400 text-center">
|
||||
<div className="pt-3 px-4 fs-8 text-center">
|
||||
{allowCreate ? (
|
||||
<>
|
||||
Type and press <span className="text-slate-300">Enter</span>{' '}
|
||||
to create new tag
|
||||
Type and press <span>Enter</span> to create new tag
|
||||
</>
|
||||
) : (
|
||||
'No tags found'
|
||||
|
|
@ -171,9 +170,7 @@ export const Tags = React.memo(
|
|||
</Checkbox.Group>
|
||||
</ScrollArea>
|
||||
<div className="p-2 border-top border-dark d-flex justify-content-between align-items-center">
|
||||
<div className="ms-2 fs-8 text-slate-400">
|
||||
{values.length || 'None'} selected
|
||||
</div>
|
||||
<div className="ms-2 fs-8 ">{values.length || 'None'} selected</div>
|
||||
{values.length >= 1 && (
|
||||
<Button
|
||||
variant="default"
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ export function WebhookForm({
|
|||
/>
|
||||
</div>,
|
||||
<Alert
|
||||
icon={<i className="bi bi-info-circle-fill text-slate-400" />}
|
||||
icon={<i className="bi bi-info-circle-fill " />}
|
||||
key="5"
|
||||
className="mb-4"
|
||||
color="gray"
|
||||
|
|
|
|||
|
|
@ -22,11 +22,10 @@ import {
|
|||
} from '@mantine/core';
|
||||
import { DateInput, DateInputProps } from '@mantine/dates';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { IconBolt, IconCalendarFilled } from '@tabler/icons-react';
|
||||
|
||||
import { useUserPreferences } from '@/useUserPreferences';
|
||||
|
||||
import { Icon } from '../Icon';
|
||||
|
||||
import { TimePickerMode } from './types';
|
||||
import { useTimePickerForm } from './useTimePickerForm';
|
||||
import {
|
||||
|
|
@ -224,24 +223,21 @@ export const TimePicker = ({
|
|||
data-testid="time-picker-input"
|
||||
leftSection={
|
||||
isLiveMode ? (
|
||||
<Icon
|
||||
name="lightning-charge-fill"
|
||||
className="fs-8 text-success"
|
||||
/>
|
||||
<IconBolt size={16} className="text-success" />
|
||||
) : (
|
||||
<Icon name="calendar-fill" className="fs-8" />
|
||||
<IconCalendarFilled size={16} />
|
||||
)
|
||||
}
|
||||
styles={{
|
||||
input: {
|
||||
color: isLiveMode
|
||||
? 'var(--mantine-color-green-5)'
|
||||
: 'var(--mantine-color-gray-1)',
|
||||
? 'var(--color-text-success)'
|
||||
: 'var(--color-text)',
|
||||
},
|
||||
}}
|
||||
rightSection={
|
||||
opened && (
|
||||
<Text size="xxs" bg="gray.8" px={4}>
|
||||
<Text size="xxs" bg="var(--color-bg-neutral)" px={4} c="white">
|
||||
d
|
||||
</Text>
|
||||
)
|
||||
|
|
@ -322,7 +318,7 @@ export const TimePicker = ({
|
|||
<Stack gap={0} p="xs">
|
||||
{relativeTimeOptions.map((item, index) =>
|
||||
item === 'divider' ? (
|
||||
<Divider key={index} my={4} color="gray.9" />
|
||||
<Divider key={index} my={4} />
|
||||
) : (
|
||||
<Button
|
||||
key={item[0]}
|
||||
|
|
@ -445,7 +441,7 @@ export const TimePicker = ({
|
|||
</>
|
||||
)}
|
||||
</Stack>
|
||||
<Text size="xxs" lh={1.2} c="gray.7">
|
||||
<Text size="xxs" lh={1.2}>
|
||||
You can use natural language to select dates (e.g. yesterday, last
|
||||
monday at 5pm)
|
||||
</Text>
|
||||
|
|
@ -454,7 +450,7 @@ export const TimePicker = ({
|
|||
justify="flex-end"
|
||||
mt={8}
|
||||
pt={8}
|
||||
style={{ borderTop: '1px solid #282828' }}
|
||||
style={{ borderTop: '1px solid var(--color-border)' }}
|
||||
>
|
||||
<Button
|
||||
data-testid="time-picker-apply"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
@import './variables';
|
||||
|
||||
.sectionHeader {
|
||||
border-bottom: 1px solid $slate-950;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
margin-top: 30px;
|
||||
padding-bottom: 10px;
|
||||
font-size: 14px;
|
||||
color: $slate-400;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.historyCardWrapper {
|
||||
|
|
@ -22,7 +20,7 @@
|
|||
.historyCard {
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
background-color: $slate-950;
|
||||
background-color: var(--color-bg-muted);
|
||||
border-radius: 2px;
|
||||
margin: 0 1px;
|
||||
cursor: pointer;
|
||||
|
|
@ -34,11 +32,11 @@
|
|||
}
|
||||
|
||||
&.ok {
|
||||
background-color: #00d474;
|
||||
background-color: var(--color-success);
|
||||
}
|
||||
|
||||
&.alarm {
|
||||
background-color: #e74c3c;
|
||||
background-color: var(--color-danger);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -48,15 +46,15 @@
|
|||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid $slate-950;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.alertLink {
|
||||
font-weight: 500;
|
||||
color: $slate-200;
|
||||
color: var(--color-text-secondary);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
color: #fff;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@import './variables';
|
||||
/* stylelint-disable */
|
||||
|
||||
.wrapper {
|
||||
height: 100vh;
|
||||
|
|
@ -6,13 +6,12 @@
|
|||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: space-between;
|
||||
border-right: 1px solid $slate-950;
|
||||
border-right: 1px solid var(--color-border);
|
||||
background: var(--color-bg-sidebar);
|
||||
}
|
||||
|
||||
.list {
|
||||
// background-color: #00000030;
|
||||
// border-bottom: 1px solid $slate-950;
|
||||
border-top: 1px solid $slate-950;
|
||||
border-top: 1px solid var(--color-border);
|
||||
overflow-x: hidden;
|
||||
max-width: 100%;
|
||||
padding: 4px 16px;
|
||||
|
|
@ -21,7 +20,7 @@
|
|||
|
||||
.scrollbar {
|
||||
.thumb {
|
||||
background-color: var(--mantine-color-gray-9);
|
||||
background-color: var(--color-border);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -32,21 +31,20 @@
|
|||
}
|
||||
|
||||
.listGroupName {
|
||||
color: $slate-400;
|
||||
color: var(--color-text-muted);
|
||||
text-transform: uppercase;
|
||||
font-size: 11px;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 6px;
|
||||
margin-bottom: 2x;
|
||||
margin-top: 6px;
|
||||
padding-left: 16px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
// justify-content: space-between;
|
||||
gap: 6px;
|
||||
|
||||
&:hover {
|
||||
color: $slate-300;
|
||||
color: var(--color-text);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
|
@ -55,55 +53,82 @@
|
|||
display: flex;
|
||||
justify-content: space-between;
|
||||
text-decoration: none;
|
||||
color: $slate-200;
|
||||
font-size: 13px;
|
||||
margin-bottom: 4px;
|
||||
color: var(--color-text);
|
||||
font-size: 14px;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding-left: 16px;
|
||||
user-select: none;
|
||||
gap: 10px;
|
||||
|
||||
&:hover {
|
||||
color: $slate-100;
|
||||
color: var(--color-text-success);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
border-left: 2px solid $green;
|
||||
background: var(--color-bg-muted);
|
||||
}
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 12px;
|
||||
.linkIcon {
|
||||
margin-right: 8px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
vertical-align: middle;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-text-success);
|
||||
}
|
||||
}
|
||||
|
||||
.listEmptyMsg {
|
||||
color: $slate-400;
|
||||
color: var(--color-text-muted);
|
||||
font-size: 12px;
|
||||
margin: 8px 16px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.listLinkActive {
|
||||
// color: $green;
|
||||
color: var(--mantine-color-green-3);
|
||||
font-weight: 500;
|
||||
.nestedLink {
|
||||
display: block;
|
||||
color: var(--color-text);
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
padding-left: 16px;
|
||||
padding-block: 2px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
// color: $green;
|
||||
color: var(--mantine-color-green-3);
|
||||
&:hover {
|
||||
color: var(--color-text-success);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: none;
|
||||
background: var(--color-bg-muted);
|
||||
}
|
||||
}
|
||||
|
||||
.nestedLinkActive {
|
||||
color: var(--color-text-success);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.listLinkActive {
|
||||
color: var(--color-text-success);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.kbd {
|
||||
white-space: nowrap;
|
||||
letter-spacing: -1px;
|
||||
color: $slate-400;
|
||||
background: $slate-950;
|
||||
color: var(--color-text-muted);
|
||||
background: var(--color-bg-surface);
|
||||
border-radius: 4px;
|
||||
padding: 0 4px;
|
||||
font-size: 10px;
|
||||
|
|
@ -118,18 +143,90 @@
|
|||
}
|
||||
}
|
||||
|
||||
.appNavMenu {
|
||||
|
||||
// User menu trigger styling
|
||||
.userMenuTrigger {
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 5px 10px 10px #0f1215e0;
|
||||
background: var(--mantine-color-gray-9);
|
||||
margin: 8px 0.875rem 0.875rem;
|
||||
padding: 4px 8px;
|
||||
border-radius: 8px;
|
||||
transition: background-color 0.2s ease, transform 0.1s ease;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
background: var(--mantine-color-gray-8);
|
||||
background: var(--color-bg-muted);
|
||||
}
|
||||
|
||||
&:active {
|
||||
// background: var(--mantine-color-gray-9);
|
||||
transform: translate(0, 1px);
|
||||
}
|
||||
|
||||
&Collapsed {
|
||||
padding: 2px;
|
||||
margin: 8px auto 0.875rem;
|
||||
background: transparent;
|
||||
border: none !important;
|
||||
width: fit-content;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-bg-muted);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translate(0, 1px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Help menu trigger styling
|
||||
.helpMenuTrigger {
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
margin: 0 0.875rem 8px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid var(--color-border);
|
||||
transition: background-color 0.2s ease, transform 0.1s ease, border-color 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-bg-muted);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translate(0, 1px);
|
||||
}
|
||||
|
||||
&Collapsed {
|
||||
margin: 0 auto 8px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-bg-muted);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translate(0, 1px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bottom section with gradient overlay
|
||||
.bottomSection {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(
|
||||
to top,
|
||||
var(--color-bg-sidebar) 50%,
|
||||
transparent 100%
|
||||
);
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue