Remove Bootstrap Icons (#1480)

This PR removes bootstrap-icons entirely from the app. It also adds an eslint plugin to detect uses and throw an error, this will help in the immediate short term with PRs in flight and merging downstream.

Fixes HDX-3050
This commit is contained in:
Brandon Pereira 2025-12-15 10:06:40 -07:00 committed by GitHub
parent 58e78ab16f
commit 4ba37e557c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
69 changed files with 685 additions and 2470 deletions

View file

@ -0,0 +1,5 @@
---
"@hyperdx/app": patch
---
Swap out bootstrap icons for tabler icons across app

View file

@ -8,6 +8,7 @@
- **Charts/Visualization**: Recharts, uPlot
- **Code Editor**: CodeMirror (for SQL/JSON editing)
- **Styling**: Mantine's built-in system, SCSS modules when needed
- **Icons**: Use `@tabler/icons-react`
**UI Component Priority**: Mantine components first → Custom components on Mantine primitives → Custom SCSS modules as last resort

View file

@ -1,7 +1,3 @@
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css"
/>
<link
rel="preconnect"
href="https://fonts.gstatic.com"

View file

@ -68,6 +68,15 @@ export default [
],
},
],
// Temporary rule to enforce use of @tabler/icons-react instead of bi bi-icons
// Will remove after we've updated all icons and let some PRs merge.
'no-restricted-syntax': [
'error',
{
selector: 'Literal[value=/\\bbi-\\b/i]',
message: 'Please update to use @tabler/icons-react instead',
},
],
'react-hooks/exhaustive-deps': 'error',
'no-console': ['error', { allow: ['warn', 'error'] }],
},

View file

@ -17,10 +17,6 @@ export default function Document() {
<script src="/__ENV.js" />
{/* eslint-disable-next-line @next/next/no-sync-scripts */}
<script src="https://cdn.jsdelivr.net/pyodide/v0.27.2/full/pyodide.js"></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css"
/>
</Head>
<body>
<Main />

View file

@ -20,7 +20,17 @@ import {
Tooltip,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconBell } from '@tabler/icons-react';
import {
IconAlertTriangle,
IconBell,
IconBrandSlack,
IconChartLine,
IconCheck,
IconChevronRight,
IconHelpCircle,
IconInfoCircleFilled,
IconTableRow,
} from '@tabler/icons-react';
import { useQueryClient } from '@tanstack/react-query';
import { ErrorBoundary } from '@/components/ErrorBoundary';
@ -312,7 +322,7 @@ function AlertDetails({ alert }: { alert: AlertsPageItem }) {
{alert.dashboard?.name}
{tileName ? (
<>
<i className="bi bi-chevron-right fs-8 mx-1 " />
<IconChevronRight size={14} className="mx-1" />
{tileName}
</>
) : null}
@ -338,11 +348,11 @@ function AlertDetails({ alert }: { alert: AlertsPageItem }) {
const alertIcon = (() => {
switch (alert.source) {
case AlertSource.TILE:
return 'bi-graph-up';
return <IconChartLine size={14} />;
case AlertSource.SAVED_SEARCH:
return 'bi-layout-text-sidebar-reverse';
return <IconTableRow size={14} />;
default:
return 'bi-question';
return <IconHelpCircle size={14} />;
}
})();
@ -359,9 +369,9 @@ function AlertDetails({ alert }: { alert: AlertsPageItem }) {
const notificationMethod = React.useMemo(() => {
if (alert.channel.type === 'webhook') {
return (
<span>
Notify via <i className="bi bi-slack"></i> Webhook
</span>
<Group gap={2}>
Notify via <IconBrandSlack size={16} /> Webhook
</Group>
);
}
}, [alert]);
@ -400,8 +410,10 @@ function AlertDetails({ alert }: { alert: AlertsPageItem }) {
className={styles.alertLink}
title={linkTitle}
>
<i className={`bi ${alertIcon} me-2 fs-8`} />
{alertName}
<Group gap={2}>
{alertIcon}
{alertName}
</Group>
</Link>
</div>
<div className="fs-8 d-flex gap-2">
@ -435,18 +447,18 @@ function AlertCardList({ alerts }: { alerts: AlertsPageItem[] }) {
<div className="d-flex flex-column gap-4">
{alarmAlerts.length > 0 && (
<div>
<div className={styles.sectionHeader}>
<i className="bi bi-exclamation-triangle"></i> Triggered
</div>
<Group className={styles.sectionHeader}>
<IconAlertTriangle size={14} /> Triggered
</Group>
{alarmAlerts.map((alert, index) => (
<AlertDetails key={index} alert={alert} />
))}
</div>
)}
<div>
<div className={styles.sectionHeader}>
<i className="bi bi-check-lg"></i> OK
</div>
<Group className={styles.sectionHeader}>
<IconCheck size={14} /> OK
</Group>
{okData.length === 0 && (
<div className="text-center my-4 fs-8">No alerts</div>
)}
@ -472,7 +484,7 @@ export default function AlertsPage() {
<div className="my-4">
<Container maw={1500}>
<Alert
icon={<i className="bi bi-info-circle-fill " />}
icon={<IconInfoCircleFilled size={16} />}
color="gray"
py="xs"
mt="md"

View file

@ -14,8 +14,19 @@ import {
UnstyledButton,
} from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import {
IconBook,
IconBrandDiscord,
IconBulb,
IconChevronDown,
IconChevronRight,
IconChevronUp,
IconHelp,
IconLogout,
IconSettings,
IconUserCog,
} from '@tabler/icons-react';
import { Icon } from '@/components/Icon';
import InstallInstructionModal from '@/InstallInstructionsModal';
import { useSources } from '@/source';
@ -132,7 +143,7 @@ export const AppNavUserMenu = ({
</Text>
</div>
</Tooltip>
<Icon name="chevron-right" className="fs-8 " />
<IconChevronRight size={14} />
</>
)}
</Group>
@ -146,14 +157,14 @@ export const AppNavUserMenu = ({
data-testid="team-settings-menu-item"
href="/team"
component={Link}
leftSection={<Icon name="gear" />}
leftSection={<IconSettings size={16} />}
>
Team Settings
</Menu.Item>
)}
<Menu.Item
data-testid="user-preferences-menu-item"
leftSection={<Icon name="person-gear" />}
leftSection={<IconUserCog size={16} />}
onClick={onClickUserPreferences}
>
User Preferences
@ -164,7 +175,7 @@ export const AppNavUserMenu = ({
<Menu.Item
data-testid="logout-menu-item"
color="red"
leftSection={<Icon name="box-arrow-left" />}
leftSection={<IconLogout size={16} />}
component={Link}
href={logoutUrl}
>
@ -221,7 +232,7 @@ export const AppNavHelpMenu = ({
<Menu.Target>
<UnstyledButton data-testid="help-menu-trigger" w="100%">
<Group align="center" justify="center" h={28}>
<Icon name="question-lg" />
<IconHelp size={16} />
</Group>
</UnstyledButton>
</Menu.Target>
@ -239,13 +250,13 @@ export const AppNavHelpMenu = ({
data-testid="documentation-menu-item"
href="https://clickhouse.com/docs/use-cases/observability/clickstack"
component="a"
leftSection={<Icon name="book" />}
leftSection={<IconBook size={16} />}
>
Documentation
</Menu.Item>
<Menu.Item
data-testid="discord-menu-item"
leftSection={<Icon name="discord" />}
leftSection={<IconBrandDiscord size={16} />}
component="a"
href="https://hyperdx.io/discord"
target="_blank"
@ -254,7 +265,7 @@ export const AppNavHelpMenu = ({
</Menu.Item>
<Menu.Item
data-testid="setup-instructions-menu-item"
leftSection={<Icon name="lightbulb" />}
leftSection={<IconBulb size={16} />}
onClick={onAddDataClick}
>
Setup Instructions
@ -321,11 +332,11 @@ export const AppNavLink = ({
size="sm"
onClick={onToggle}
>
<i
className={`fs-8 bi bi-chevron-${
isExpanded ? 'up' : 'down'
} text-muted-hover`}
/>
{isExpanded ? (
<IconChevronUp size={14} className="text-muted-hover" />
) : (
<IconChevronDown size={14} className="text-muted-hover" />
)}
</ActionIcon>
)}
</Group>

View file

@ -27,10 +27,15 @@ import {
import { useDisclosure, useLocalStorage } from '@mantine/hooks';
import {
IconBell,
IconBellFilled,
IconChartDots,
IconChevronDown,
IconChevronRight,
IconCommand,
IconDeviceLaptop,
IconLayoutGrid,
IconLayoutSidebarLeftCollapse,
IconSearch,
IconSettings,
IconSitemap,
IconTable,
@ -135,7 +140,7 @@ function SearchInput({
return (
<div className={styles.kbd}>
{window.navigator.platform?.toUpperCase().includes('MAC') ? (
<i className="bi bi-command" />
<IconCommand size={8} />
) : (
<span style={{ letterSpacing: -2 }}>Ctrl</span>
)}
@ -161,7 +166,7 @@ function SearchInput({
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
onChange(e.currentTarget.value)
}
leftSection={<i className="bi bi-search fs-8 ps-1 " />}
leftSection={<IconSearch size={16} className="ps-1" />}
onKeyDown={handleKeyDown}
rightSection={
value ? (
@ -208,7 +213,11 @@ const AppNavGroupLabel = ({
}) => {
return (
<div className={styles.listGroupName} onClick={onClick}>
<i className={`bi bi-chevron-${collapsed ? 'right' : 'down'}`} />
{collapsed ? (
<IconChevronRight size={14} />
) : (
<IconChevronDown size={14} />
)}
<div>{name}</div>
</div>
);
@ -486,20 +495,25 @@ export default function AppNav({ fixed = false }: { fixed?: boolean }) {
draggable
data-savedsearchid={savedSearch.id}
>
<div className="d-inline-block text-truncate">{savedSearch.name}</div>
{Array.isArray(savedSearch.alerts) && savedSearch.alerts.length > 0 ? (
savedSearch.alerts.some(a => a.state === AlertState.ALERT) ? (
<i
className="bi bi-bell float-end text-danger ms-1"
title="Has Alerts and is in ALERT state"
></i>
) : (
<i
className="bi bi-bell float-end ms-1"
title="Has Alerts and is in OK state"
></i>
)
) : null}
<Group gap={2}>
<div className="d-inline-block text-truncate">{savedSearch.name}</div>
{Array.isArray(savedSearch.alerts) &&
savedSearch.alerts.length > 0 ? (
savedSearch.alerts.some(a => a.state === AlertState.ALERT) ? (
<IconBellFilled
size={14}
className="float-end text-danger ms-1"
aria-label="Has Alerts and is in ALERT state"
/>
) : (
<IconBell
size={14}
className="float-end ms-1"
aria-label="Has Alerts and is in OK state"
/>
)
) : null}
</Group>
</Link>
),
[

View file

@ -14,6 +14,7 @@ import {
Stack,
TextInput,
} from '@mantine/core';
import { IconAt, IconLock } from '@tabler/icons-react';
import api from './api';
import * as config from './config';
@ -150,7 +151,7 @@ export default function AuthPage({ action }: { action: 'register' | 'login' }) {
withAsterisk={false}
placeholder="you@company.com"
type="email"
leftSection={<i className="bi bi-at fs-5" />}
leftSection={<IconAt size={18} />}
error={errors.email?.message}
required
{...form.email}
@ -159,7 +160,7 @@ export default function AuthPage({ action }: { action: 'register' | 'login' }) {
size="md"
label="Password"
withAsterisk={false}
leftSection={<i className="bi bi-lock-fill" />}
leftSection={<IconLock size={16} />}
error={errors.password?.message}
required
placeholder="Password"
@ -179,7 +180,7 @@ export default function AuthPage({ action }: { action: 'register' | 'login' }) {
size="md"
required
withAsterisk={false}
leftSection={<i className="bi bi-lock-fill" />}
leftSection={<IconLock size={16} />}
error={errors.confirmPassword?.message}
placeholder="Confirm Password"
{...form.confirmPassword}

View file

@ -22,6 +22,7 @@ import {
Text,
Tooltip,
} from '@mantine/core';
import { IconRefresh } from '@tabler/icons-react';
import ReactCodeMirror from '@uiw/react-codemirror';
import { ConnectionSelectControlled } from '@/components/ConnectionSelect';
@ -529,7 +530,7 @@ function ClickhousePage() {
aria-label="Refresh dashboard"
px="xs"
>
<i className="bi bi-arrow-clockwise fs-5"></i>
<IconRefresh size={18} />
</Button>
</Tooltip>
</Group>

View file

@ -17,6 +17,11 @@ import {
Text,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import {
IconChevronDown,
IconChevronUp,
IconInfoCircle,
} from '@tabler/icons-react';
import api from '@/api';
import { DEFAULT_CHART_CONFIG, Granularity } from '@/ChartUtils';
@ -120,7 +125,7 @@ function AIAssistant({
<Box mb="sm">
<Alert
color="dark"
icon={<i className="bi bi-info-circle" />}
icon={<IconInfoCircle size={16} />}
variant="outline"
withCloseButton
onClose={() => setAlertDismissed(true)}
@ -150,9 +155,9 @@ function AIAssistant({
>
<Group gap="xs">
{opened ? (
<i className="bi bi-chevron-up" />
<IconChevronUp size={14} />
) : (
<i className="bi bi-chevron-down" />
<IconChevronDown size={14} />
)}
<Text size="xxs">AI Assistant [A]</Text>
</Group>

View file

@ -46,7 +46,19 @@ import {
} from '@mantine/core';
import { useHover } from '@mantine/hooks';
import { notifications } from '@mantine/notifications';
import { IconBell, IconFilterEdit, IconPlayerPlay } from '@tabler/icons-react';
import {
IconBell,
IconCopy,
IconDotsVertical,
IconDownload,
IconFilterEdit,
IconPencil,
IconPlayerPlay,
IconRefresh,
IconTags,
IconTrash,
IconUpload,
} from '@tabler/icons-react';
import { ContactSupportText } from '@/components/ContactSupportText';
import EditTimeChartForm from '@/components/DBEditTimeChartForm';
@ -277,7 +289,7 @@ const Tile = forwardRef(
onClick={onDuplicateClick}
title="Duplicate"
>
<i className="bi bi-copy fs-8"></i>
<IconCopy size={14} />
</Button>
<Button
data-testid={`tile-edit-button-${chart.id}`}
@ -287,7 +299,7 @@ const Tile = forwardRef(
onClick={onEditClick}
title="Edit"
>
<i className="bi bi-pencil"></i>
<IconPencil size={14} />
</Button>
<Button
data-testid={`tile-delete-button-${chart.id}`}
@ -297,7 +309,7 @@ const Tile = forwardRef(
onClick={onDeleteClick}
title="Delete"
>
<i className="bi bi-trash"></i>
<IconTrash size={14} />
</Button>
</Flex>
) : (
@ -505,7 +517,7 @@ function DashboardName({
size="xs"
onClick={() => setEditing(true)}
>
<i className="bi bi-pencil"></i>
<IconPencil size={14} />
</Button>
)}
</div>
@ -944,7 +956,7 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
size="xs"
style={{ flexShrink: 0 }}
>
<i className="bi bi-tags-fill me-2"></i>
<IconTags size={14} className="me-2" />
{dashboard?.tags?.length || 0}{' '}
{dashboard?.tags?.length === 1 ? 'Tag' : 'Tags'}
</Button>
@ -954,14 +966,14 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
<Menu width={250}>
<Menu.Target>
<Button variant="default" px="xs" size="xs">
<i className="bi bi-three-dots-vertical" />
<IconDotsVertical size={14} />
</Button>
</Menu.Target>
<Menu.Dropdown>
{hasTiles && (
<Menu.Item
leftSection={<i className="bi bi-download" />}
leftSection={<IconDownload size={16} />}
onClick={() => {
if (!sources || !dashboard) {
notifications.show({
@ -984,7 +996,7 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
</Menu.Item>
)}
<Menu.Item
leftSection={<i className="bi bi-upload" />}
leftSection={<IconUpload size={16} />}
onClick={() => {
if (dashboard && !dashboard.tiles.length) {
router.push(
@ -998,7 +1010,7 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
{hasTiles ? 'Import New Dashboard' : 'Import Dashboard'}
</Menu.Item>
<Menu.Item
leftSection={<i className="bi bi-trash-fill" />}
leftSection={<IconTrash size={16} />}
color="red"
onClick={() =>
deleteDashboard.mutate(dashboard?.id ?? '', {
@ -1097,7 +1109,7 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) {
title="Refresh dashboard"
px="xs"
>
<i className="bi bi-arrow-clockwise fs-5"></i>
<IconRefresh size={18} />
</Button>
</Tooltip>
<Tooltip withArrow label="Edit Filters" fz="xs" color="gray">

View file

@ -60,7 +60,15 @@ import {
useDocumentVisibility,
} from '@mantine/hooks';
import { notifications } from '@mantine/notifications';
import { IconPlayerPlay } from '@tabler/icons-react';
import {
IconBolt,
IconCirclePlus,
IconPlayerPlay,
IconPlus,
IconSettings,
IconTags,
IconX,
} from '@tabler/icons-react';
import { useIsFetching } from '@tanstack/react-query';
import { SortingState } from '@tanstack/react-table';
import CodeMirror from '@uiw/react-codemirror';
@ -381,7 +389,7 @@ function SaveSearchModal({
}}
size="xs"
>
<i className="bi bi-x" />
<IconX size={14} />
</ActionIcon>
}
>
@ -395,7 +403,7 @@ function SaveSearchModal({
color="gray"
size="xs"
>
<i className="bi bi-plus me-1"></i>
<IconPlus size={14} className="me-1" />
Add Tag
</Button>
</Tags>
@ -1376,7 +1384,7 @@ function DBSearchPage() {
title="Edit Source"
>
<Text size="xs">
<i className="bi bi-gear" />
<IconSettings size={14} />
</Text>
</ActionIcon>
</Menu.Target>
@ -1384,7 +1392,7 @@ function DBSearchPage() {
<Menu.Label>Sources</Menu.Label>
<Menu.Item
data-testid="create-new-source-menu-item"
leftSection={<i className="bi bi-plus-circle" />}
leftSection={<IconCirclePlus size={14} />}
onClick={() => setNewSourceModalOpened(true)}
>
Create New Source
@ -1392,7 +1400,7 @@ function DBSearchPage() {
{IS_LOCAL_MODE ? (
<Menu.Item
data-testid="edit-source-menu-item"
leftSection={<i className="bi bi-gear" />}
leftSection={<IconSettings size={14} />}
onClick={() => setModelFormExpanded(v => !v)}
>
Edit Source
@ -1400,7 +1408,7 @@ function DBSearchPage() {
) : (
<Menu.Item
data-testid="edit-sources-menu-item"
leftSection={<i className="bi bi-gear" />}
leftSection={<IconSettings size={14} />}
component={Link}
href="/team"
>
@ -1485,7 +1493,7 @@ function DBSearchPage() {
size="xs"
style={{ flexShrink: 0 }}
>
<i className="bi bi-tags-fill me-1"></i>
<IconTags size={14} className="me-1" />
{savedSearch.tags?.length || 0}
</Button>
</Tags>
@ -1755,7 +1763,10 @@ function DBSearchPage() {
variant="outline"
onClick={handleResumeLiveTail}
>
<i className="bi text-success bi-lightning-charge-fill me-2" />
<IconBolt
size={14}
className="text-success me-2"
/>
Resume Live Tail
</Button>
)}

View file

@ -28,6 +28,12 @@ import {
Text,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import {
IconBrandSlack,
IconChartLine,
IconInfoCircleFilled,
IconPlus,
} from '@tabler/icons-react';
import { useQueryClient } from '@tanstack/react-query';
import { useCreateSavedSearch } from '@/savedSearch';
@ -56,7 +62,7 @@ const SavedSearchAlertFormSchema = z
.passthrough();
const CHANNEL_ICONS = {
webhook: <i className="bi bi-slack fs-7 " />,
webhook: <IconBrandSlack size={14} />,
};
const AlertForm = ({
@ -166,7 +172,7 @@ const AlertForm = ({
</Paper>
{groupBy && thresholdType === AlertThresholdType.BELOW && (
<MantineAlert
icon={<i className="bi bi-info-circle-fill " />}
icon={<IconInfoCircleFilled size={16} />}
bg="dark"
py="xs"
>
@ -181,7 +187,7 @@ const AlertForm = ({
<Accordion defaultValue={'chart'} mt="sm" mx={-16}>
<Accordion.Item value="chart">
<Accordion.Control icon={<i className="bi bi-chart"></i>}>
<Accordion.Control icon={<IconChartLine size={16} />}>
<Text size="sm">Threshold chart</Text>
</Accordion.Control>
<Accordion.Panel>
@ -417,7 +423,7 @@ export const DBSearchPageAlertModal = ({
))}
<Tabs.Tab value="stage">
<Group gap={4}>
<i className="bi bi-plus fs-5 " style={{ marginLeft: -8 }} />
<IconPlus size={18} style={{ marginLeft: -8 }} />
New Alert
</Group>
</Tabs.Tab>

View file

@ -4,6 +4,14 @@ import throttle from 'lodash/throttle';
import { useHotkeys } from 'react-hotkeys-hook';
import { Replayer } from 'rrweb';
import { ActionIcon, CopyButton, HoverCard } from '@mantine/core';
import {
IconArrowsMaximize,
IconCheck,
IconCopy,
IconGlobe,
IconLink,
IconList,
} from '@tabler/icons-react';
import { useRRWebEventStream } from '@/sessions';
import { useDebugMode } from '@/utils';
@ -43,13 +51,13 @@ const URLHoverCard = memo(({ url }: { url: string }) => {
<table className="table fs-8 mb-0">
<tr>
<td>
<i className="bi bi-globe fs-8 "></i>
<IconGlobe size={14} />
</td>
<td>{parsedUrl?.host}</td>
</tr>
<tr>
<td>
<i className="bi bi-link-45deg fs-7"></i>
<IconLink size={14} />
</td>
<td>{parsedUrl?.pathname}</td>
</tr>
@ -474,9 +482,9 @@ export default function DOMPlayer({
color="gray"
>
{playerFullWidth ? (
<i className="bi bi-list"></i>
<IconList size={14} />
) : (
<i className="bi bi-arrows-fullscreen fs-8"></i>
<IconArrowsMaximize size={14} />
)}
</ActionIcon>
<CopyButton value={lastHref}>
@ -490,11 +498,7 @@ export default function DOMPlayer({
size="sm"
color="gray"
>
{copied ? (
<i className="bi bi-check2 fs-8" />
) : (
<i className="bi bi-copy fs-8" />
)}
{copied ? <IconCheck size={14} /> : <IconCopy size={14} />}
</ActionIcon>
</>
)}

View file

@ -20,7 +20,13 @@ import {
Tooltip,
UnstyledButton,
} from '@mantine/core';
import { IconFilter, IconPencil, IconTrash } from '@tabler/icons-react';
import {
IconFilter,
IconInfoCircle,
IconPencil,
IconStack,
IconTrash,
} from '@tabler/icons-react';
import SourceSchemaPreview from './components/SourceSchemaPreview';
import { SourceSelectControlled } from './components/SourceSelect';
@ -55,7 +61,7 @@ const CustomInputWrapper = ({
<Input.Label>{label}</Input.Label>
{tooltipText && (
<Tooltip label={tooltipText}>
<i className="bi bi-info-circle ms-2" />
<IconInfoCircle size={14} className="ms-2" />
</Tooltip>
)}
{errorMessage && (
@ -280,7 +286,7 @@ const DashboardFiltersList = ({
</Group>
</Group>
<Group gap="xs">
<i className="bi bi-collection"></i>
<IconStack size={14} />
<Text size="xs">
{sources?.find(s => s.id === filter.source)?.name}
</Text>
@ -329,7 +335,6 @@ const DashboardFiltersModal = ({
if (opened) {
setSelectedFilter(undefined);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [opened]);
const handleRemoveFilter = (id: string) => {

View file

@ -1,8 +1,8 @@
import { memo, useCallback, useMemo, useRef, useState } from 'react';
import { useCallback, useMemo, useRef, useState } from 'react';
import Link from 'next/link';
import cx from 'classnames';
import { Flex, Text, UnstyledButton } from '@mantine/core';
import { IconGripVertical } from '@tabler/icons-react';
import { UnstyledButton } from '@mantine/core';
import { IconDownload, IconTextWrap } from '@tabler/icons-react';
import {
flexRender,
getCoreRowModel,
@ -228,7 +228,7 @@ export const Table = ({
<UnstyledButton
onClick={() => setWrapLinesEnabled(prev => !prev)}
>
<i className="bi bi-text-wrap" />
<IconTextWrap size={14} />
</UnstyledButton>
<CsvExportButton
data={csvData}
@ -236,7 +236,7 @@ export const Table = ({
className="fs-8 ms-2"
title="Download table as CSV"
>
<i className="bi bi-download" />
<IconDownload size={14} />
</CsvExportButton>
</div>
)}

View file

@ -1,5 +1,6 @@
import cx from 'classnames';
import { Button, Modal } from '@mantine/core';
import { Button, Group, Modal } from '@mantine/core';
import { IconClipboard, IconClipboardCheck } from '@tabler/icons-react';
import api from './api';
import Clipboard from './Clipboard';
@ -29,15 +30,14 @@ function CopyableValue({
{value}
</pre>
</div>
<div className={cx('fs-7 text-end text-nowrap')}>
<i
className={cx('bi me-2', {
'bi-clipboard': !isCopied,
'bi-clipboard-check': isCopied,
})}
></i>
<Group gap={2} wrap="nowrap" className={cx('fs-7 text-end')}>
{isCopied ? (
<IconClipboardCheck size={14} />
) : (
<IconClipboard size={14} />
)}
{isCopied ? 'Copied!' : 'Copy'}
</div>
</Group>
</div>
);
}}

View file

@ -24,6 +24,11 @@ import {
Tooltip,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import {
IconCaretDownFilled,
IconCaretUpFilled,
IconRefresh,
} from '@tabler/icons-react';
import { useVirtualizer } from '@tanstack/react-virtual';
import { TimePicker } from '@/components/TimePicker';
@ -81,13 +86,12 @@ const Th = React.memo<{
onClick={() => onSort?.(sort === 'asc' ? 'desc' : 'asc')}
>
{children}
{!!sort && (
<i
className={`ps-1 fs-8.5 bi bi-caret-${
sort === 'asc' ? 'up-fill' : 'down-fill'
}`}
/>
)}
{!!sort &&
(sort === 'asc' ? (
<IconCaretUpFilled size={12} className="ps-1" />
) : (
<IconCaretDownFilled size={12} className="ps-1" />
))}
</Table.Th>
);
});
@ -1213,7 +1217,7 @@ function KubernetesDashboardPage() {
aria-label="Refresh dashboard"
px="xs"
>
<i className="bi bi-arrow-clockwise fs-5"></i>
<IconRefresh size={18} />
</Button>
</Tooltip>
</Group>
@ -1350,7 +1354,7 @@ function KubernetesDashboardPage() {
legacyBehavior
>
<Anchor size="xs" color="dimmed">
Search <i className="bi bi-box-arrow-up-right"></i>
Search <IconExternalLink size={12} style={{ display: 'inline' }} />
</Anchor>
</Link>
*/}

View file

@ -5,10 +5,15 @@ import cx from 'classnames';
import { format } from 'date-fns';
import { JSONTree } from 'react-json-tree';
import { Alert, Button, CloseButton, Kbd, Text, Tooltip } from '@mantine/core';
import {
IconChevronDown,
IconChevronRight,
IconCode,
IconPackage,
} from '@tabler/icons-react';
import { ColumnDef, Row, Table } from '@tanstack/react-table';
import HyperJson from './components/HyperJson';
import { Icon } from './components/Icon';
import { StacktraceFrame as StacktraceFrameCmp } from './components/StacktraceFrame';
import { TableCellButton } from './components/Table';
import { UNDEFINED_WIDTH } from './tableUtils';
@ -37,7 +42,11 @@ export const CollapsibleSection = ({
role="button"
onClick={() => setCollapsed(!collapsed)}
>
<i className={`bi bi-chevron-${collapsed ? 'right' : 'down'} me-2`}></i>
{collapsed ? (
<IconChevronRight size={14} className="me-2" />
) : (
<IconChevronDown size={14} className="me-2" />
)}
<div className="fs-7">{title}</div>
</div>
{collapsed ? null : <div className="mb-4">{children}</div>}
@ -137,7 +146,11 @@ export const StacktraceRow = ({
withArrow
color="gray"
>
<i className="bi bi-box-seam me-2" title="in_app: false" />
<IconPackage
size={14}
className="me-2"
aria-label="in_app: false"
/>
</Tooltip>
)}
{augmentedFrame && (
@ -526,9 +539,9 @@ export const SourceMapsFtux = () => {
onClose={() => setIsDismissed(true)}
>
<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.
<IconCode size={16} /> Some of the stack frames are pointing to minified
files. Use hyperdx-cli to upload your source maps and see the original
code.
</Text>
<Link href="https://www.npmjs.com/package/@hyperdx/cli" target="_blank">
<Button size="compact-xs" variant="light" mt="xs">

View file

@ -175,7 +175,7 @@ function NamespaceLogs({
legacyBehavior
>
<Anchor size="xs" color="dimmed">
Search <i className="bi bi-box-arrow-up-right"></i>
Search <IconExternalLink size={12} style={{ display: 'inline' }} />
</Anchor>
</Link>
*/}

View file

@ -191,7 +191,7 @@ function NodeLogs({
legacyBehavior
>
<Anchor size="xs" color="dimmed">
Search <i className="bi bi-box-arrow-up-right"></i>
Search <IconExternalLink size={12} style={{ display: 'inline' }} />
</Anchor>
</Link>
*/}

View file

@ -12,6 +12,12 @@ import {
Text,
UnstyledButton,
} from '@mantine/core';
import {
IconArrowRight,
IconCheck,
IconChevronDown,
IconChevronUp,
} from '@tabler/icons-react';
import { useQueriedChartConfig } from './hooks/useChartConfig';
import api from './api';
@ -165,10 +171,11 @@ const OnboardingChecklist = ({
size="sm"
onClick={() => setIsCollapsed(!isCollapsed)}
>
<i
className={`bi bi-chevron-${isCollapsed ? 'down' : 'up'} `}
style={{ fontSize: 12 }}
/>
{isCollapsed ? (
<IconChevronDown size={12} />
) : (
<IconChevronUp size={12} />
)}
</ActionIcon>
</Group>
@ -200,8 +207,8 @@ const OnboardingChecklist = ({
{step.isLoading ? (
<Loader size="xs" color="gray" />
) : step.isComplete ? (
<i
className="bi bi-check"
<IconCheck
size={16}
style={{
fontSize: 12,
fontWeight: 'bold',
@ -232,10 +239,9 @@ const OnboardingChecklist = ({
</div>
{!step.isComplete && (step.href || step.onClick) && (
<i
className="bi bi-arrow-right"
<IconArrowRight
size={12}
style={{
fontSize: 12,
color: 'var(--color-text-muted)',
}}
/>

View file

@ -1,4 +1,5 @@
import { useMemo } from 'react';
import { IconCheck, IconX } from '@tabler/icons-react';
const checkLength = (password: string) => password.length >= 12;
const checkOneUpper = (password: string) => /[A-Z]+/.test(password);
@ -71,6 +72,6 @@ export const CheckOrX = ({
);
};
const Check = () => <i className={'bi bi-check2'}></i>;
const Check = () => <IconCheck size={14} />;
const XShape = () => <i className={'bi bi-x'}></i>;
const XShape = () => <IconX size={14} />;

View file

@ -1,4 +1,4 @@
// Only use this file if you can't find any icon in bootstrap icons
// Only use this file if you can't find any icon in tabler icons
type IconProps = {
style?: React.CSSProperties;

View file

@ -6,6 +6,8 @@ import {
TableConnectionChoice,
} from '@hyperdx/common-utils/dist/core/metadata';
import { genEnglishExplanation } from '@hyperdx/common-utils/dist/queryParser';
import { Group } from '@mantine/core';
import { IconBook } from '@tabler/icons-react';
import AutocompleteInput from '@/AutocompleteInput';
@ -205,17 +207,17 @@ export default function SearchInputV2({
</code>
</div>
<div className="mb-2 me-2">
<a
className="text-muted"
target="_blank"
href="https://clickhouse.com/docs/use-cases/observability/clickstack/search"
rel="noreferrer"
>
<i className="bi bi-book me-1" />
<span className="me-1">Docs</span>
</a>
</div>
<a
className="text-muted mb-2"
target="_blank"
href="https://clickhouse.com/docs/use-cases/observability/clickstack/search"
rel="noreferrer"
>
<Group gap={5}>
<IconBook size={14} />
<span>Docs</span>
</Group>
</a>
</>
}
/>

View file

@ -29,7 +29,13 @@ import {
Text,
Tooltip,
} from '@mantine/core';
import { IconPlayerPlay } from '@tabler/icons-react';
import {
IconChartLine,
IconFilter,
IconPlayerPlay,
IconRefresh,
IconTable,
} from '@tabler/icons-react';
import {
convertDateRangeToGranularityString,
@ -207,7 +213,7 @@ export function EndpointLatencyChart({
title="Line Chart"
onClick={() => setLatencyChartType('line')}
>
<i className="bi bi-graph-up" />
<IconChartLine size={14} />
</Button>
<Button
@ -1086,7 +1092,7 @@ function DatabaseTab({
title="List"
onClick={() => setChartType('list')}
>
<i className="bi bi-filter-left" />
<IconFilter size={14} />
</Button>
<Button
@ -1096,7 +1102,7 @@ function DatabaseTab({
title="Table"
onClick={() => setChartType('table')}
>
<i className="bi bi-table" />
<IconTable size={14} />
</Button>
</Button.Group>
</Box>
@ -1491,7 +1497,7 @@ function ServicesDashboardPage() {
aria-label="Refresh dashboard"
px="xs"
>
<i className="bi bi-arrow-clockwise fs-5"></i>
<IconRefresh size={18} />
</Button>
</Tooltip>
<Button variant="outline" type="submit" px="sm">

View file

@ -3,7 +3,14 @@ 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 {
IconArrowsLeftRight,
IconMapPin,
IconMessage,
IconPlayerPlay,
IconPointer,
IconTerminal,
} from '@tabler/icons-react';
import { useVirtualizer } from '@tanstack/react-virtual';
import useRowWhere from '@/hooks/useRowWhere';
@ -28,11 +35,11 @@ type SessionEvent = {
duration: number;
};
const EVENT_ROW_SOURCE_ICONS = {
navigation: 'bi bi-geo-alt',
chat: 'bi bi-chat-dots',
network: 'bi bi-arrow-left-right',
custom: 'bi bi-cursor',
const EVENT_ROW_SOURCE_ICONS: Record<string, React.ReactNode> = {
navigation: <IconMapPin size={14} />,
chat: <IconMessage size={14} />,
network: <IconArrowsLeftRight size={14} />,
custom: <IconPointer size={14} />,
};
const EventRow = React.forwardRef(
@ -63,11 +70,9 @@ const EventRow = React.forwardRef(
})}
>
<div className={styles.eventRowIcon}>
<i
className={
EVENT_ROW_SOURCE_ICONS[event.eventSource] || 'bi bi-terminal'
}
/>
{EVENT_ROW_SOURCE_ICONS[event.eventSource] || (
<IconTerminal size={14} />
)}
</div>
<div className={styles.eventRowContent} onClick={onClick}>
<div className={styles.eventRowTitle}>

View file

@ -10,6 +10,7 @@ import {
import { Button } from '@mantine/core';
import { Drawer } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconLink, IconX } from '@tabler/icons-react';
import { Session } from './sessions';
import SessionSubpanel from './SessionSubpanel';
@ -123,7 +124,7 @@ export default function SessionSidePanel({
<Button
variant="default"
size="sm"
leftSection={<i className="bi bi-link-45deg fs-7.5" />}
leftSection={<IconLink size={14} />}
style={{ fontSize: '12px' }}
>
Share Session
@ -135,7 +136,7 @@ export default function SessionSidePanel({
onClick={onClose}
style={{ padding: '4px 8px' }}
>
<i className="bi bi-x-lg" />
<IconX size={14} />
</Button>
</div>
</div>

View file

@ -20,6 +20,15 @@ import {
SegmentedControl,
Tooltip,
} from '@mantine/core';
import {
IconArrowBackUp,
IconArrowForwardUp,
IconArrowsMinimize,
IconPlayerPause,
IconPlayerPlay,
IconToggleLeft,
IconToggleRight,
} from '@tabler/icons-react';
import DBRowSidePanel from '@/components/DBRowSidePanel';
@ -531,7 +540,7 @@ export default function SessionSubpanel({
setEventsFollowPlayerPosition(!eventsFollowPlayerPosition)
}
>
<i className="bi bi-chevron-bar-contract fs-6" />
<IconArrowsMinimize size={18} />
</ActionIcon>
</Tooltip>
</Group>
@ -631,7 +640,7 @@ export default function SessionSubpanel({
onClick={skipBackward}
disabled={(focus?.ts || 0) <= minTs}
>
<i className="bi bi-arrow-counterclockwise fs-6" />
<IconArrowBackUp size={18} />
</ActionIcon>
</Tooltip>
<Tooltip
@ -645,11 +654,11 @@ export default function SessionSubpanel({
radius="xl"
onClick={togglePlayerState}
>
<i
className={`bi fs-4 ${
playerState === 'paused' ? 'bi-play-fill' : 'bi-pause-fill'
}`}
/>
{playerState === 'paused' ? (
<IconPlayerPlay size={20} />
) : (
<IconPlayerPause size={20} />
)}
</ActionIcon>
</Tooltip>
<Tooltip label="Skip 15 seconds" color="gray">
@ -661,7 +670,7 @@ export default function SessionSubpanel({
onClick={skipForward}
disabled={(focus?.ts || 0) >= maxTs}
>
<i className="bi bi-arrow-clockwise fs-6" />
<IconArrowForwardUp size={18} />
</ActionIcon>
</Tooltip>
</Group>
@ -672,11 +681,11 @@ export default function SessionSubpanel({
variant="light"
fw="normal"
rightSection={
<i
className={`bi ${
skipInactive ? 'bi-toggle-off' : 'bi-toggle-on'
} fs-6 pe-1`}
/>
skipInactive ? (
<IconToggleLeft size={18} className="pe-1" />
) : (
<IconToggleRight size={18} className="pe-1" />
)
}
onClick={() => setSkipInactive(!skipInactive)}
>

View file

@ -36,7 +36,11 @@ import {
Text,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconPlayerPlay } from '@tabler/icons-react';
import {
IconDeviceLaptop,
IconInfoCircleFilled,
IconPlayerPlay,
} from '@tabler/icons-react';
import { useVirtualizer } from '@tanstack/react-virtual';
import { SourceSelectControlled } from '@/components/SourceSelect';
@ -511,7 +515,7 @@ export default function SessionsPage() {
<>
{sessionSource && sessionSource.kind !== SourceKind.Session && (
<Alert
icon={<i className="bi bi-info-circle-fill " />}
icon={<IconInfoCircleFilled size={16} />}
color="gray"
py="xs"
mt="md"
@ -545,7 +549,7 @@ function SessionSetupInstructions() {
return (
<>
<Stack w={500} mx="auto" mt="xl" gap="xxs">
<i className="bi bi-laptop text-muted fs-1"></i>
<IconDeviceLaptop size={32} className="text-muted" />
<Text c="gray" fw={500} size="xs">
Instructions
</Text>

View file

@ -3,6 +3,19 @@
import * as React from 'react';
import { useRouter } from 'next/router';
import { Spotlight, SpotlightActionData } from '@mantine/spotlight';
import {
IconActivityHeartbeat,
IconBell,
IconChartLine,
IconDeviceLaptop,
IconGridDots,
IconHelpCircle,
IconLayout,
IconLayoutSidebar,
IconLogs,
IconSearch,
IconSettings,
} from '@tabler/icons-react';
import api from './api';
import Logo from './Icon';
@ -27,7 +40,7 @@ export const useSpotlightActions = () => {
logViewActions.push({
id: logView._id,
group: 'Saved searches',
leftSection: <i className="bi bi-layout-text-sidebar-reverse" />,
leftSection: <IconLogs size={16} />,
description: logView.query,
label: logView.name,
keywords: ['search', 'log', 'saved'],
@ -42,7 +55,7 @@ export const useSpotlightActions = () => {
logViewActions.push({
id: dashboard._id,
group: 'Dashboards',
leftSection: <i className="bi bi-grid-1x2" />,
leftSection: <IconLayout size={16} />,
label: dashboard.name,
keywords: ['dashboard'],
onClick: () => {
@ -55,7 +68,7 @@ export const useSpotlightActions = () => {
{
id: 'search',
group: 'Menu',
leftSection: <i className="bi bi-layout-text-sidebar-reverse" />,
leftSection: <IconLogs size={16} />,
label: 'Search',
description: 'Start a new search',
keywords: ['log', 'events', 'logs'],
@ -66,7 +79,7 @@ export const useSpotlightActions = () => {
{
id: 'chart-explorer',
group: 'Menu',
leftSection: <i className="bi bi-graph-up" />,
leftSection: <IconChartLine size={16} />,
label: 'Chart Explorer',
description: 'Explore your data',
keywords: ['graph', 'metrics'],
@ -77,7 +90,7 @@ export const useSpotlightActions = () => {
{
id: 'new-dashboard',
group: 'Menu',
leftSection: <i className="bi bi-grid-1x2" />,
leftSection: <IconGridDots size={16} />,
label: 'New Dashboard',
description: 'Create a new dashboard',
keywords: ['graph'],
@ -88,7 +101,7 @@ export const useSpotlightActions = () => {
{
id: 'sessions',
group: 'Menu',
leftSection: <i className="bi bi-laptop" />,
leftSection: <IconDeviceLaptop size={16} />,
label: 'Client Sessions',
description: 'View client sessions',
keywords: ['browser', 'web'],
@ -99,7 +112,7 @@ export const useSpotlightActions = () => {
{
id: 'alerts',
group: 'Menu',
leftSection: <i className="bi bi-bell" />,
leftSection: <IconBell size={16} />,
label: 'Alerts',
description: 'View and manage alerts',
onClick: () => {
@ -110,7 +123,7 @@ export const useSpotlightActions = () => {
id: 'service-health',
group: 'Menu',
label: 'Service Health',
leftSection: <i className="bi bi-heart-pulse" />,
leftSection: <IconActivityHeartbeat size={16} />,
description: 'HTTP, Database and Infrastructure metrics',
onClick: () => {
router.push('/services');
@ -119,7 +132,7 @@ export const useSpotlightActions = () => {
{
id: 'team-settings',
group: 'Menu',
leftSection: <i className="bi bi-gear" />,
leftSection: <IconSettings size={16} />,
label: 'Team Settings',
onClick: () => {
@ -129,7 +142,7 @@ export const useSpotlightActions = () => {
{
id: 'documentation',
group: 'Menu',
leftSection: <i className="bi bi-question-circle" />,
leftSection: <IconHelpCircle size={16} />,
label: 'Documentation',
keywords: ['help', 'docs'],
onClick: () => {
@ -170,7 +183,7 @@ export const HDXSpotlightProvider = ({
<Spotlight
shortcut="mod + K"
searchProps={{
leftSection: <i className="bi bi-search" />,
leftSection: <IconSearch size={16} />,
placeholder: 'Search',
}}
nothingFound="Nothing found"

View file

@ -28,7 +28,17 @@ import {
} from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { notifications } from '@mantine/notifications';
import { IconPencil } from '@tabler/icons-react';
import {
IconCheck,
IconChevronDown,
IconChevronUp,
IconClipboard,
IconDatabase,
IconHelpCircle,
IconPencil,
IconServer,
IconX,
} from '@tabler/icons-react';
import { ConnectionForm } from '@/components/ConnectionForm';
import SelectControlled from '@/components/SelectControlled';
@ -84,7 +94,7 @@ function ConnectionsSection() {
onClick={() => setEditedConnectionId(c.id)}
size="sm"
>
<i className="bi bi-pencil-fill me-2" /> Edit
<IconPencil size={14} className="me-2" /> Edit
</Button>
)}
{editedConnectionId === c.id && (
@ -93,7 +103,7 @@ function ConnectionsSection() {
onClick={() => setEditedConnectionId(null)}
size="sm"
>
<i className="bi bi-x-lg me-2" /> Cancel
<IconX size={14} className="me-2" /> Cancel
</Button>
)}
</Flex>
@ -162,23 +172,27 @@ function SourcesSection() {
<div>
<Text>{s.name}</Text>
<Text size="xxs" c="dimmed" mt="xs">
{capitalizeFirstLetter(s.kind)}
<Text px="md" span>
<span className="bi-hdd-stack me-1" />
{connections?.find(c => c.id === s.connection)?.name}
</Text>
{s.from && (
<>
<span className="bi-database me-1" />
{s.from.databaseName}
{
s.kind === SourceKind.Metric
? ''
: '.' /** Metrics dont have table names */
}
{s.from.tableName}
</>
)}
<Group gap="xs">
{capitalizeFirstLetter(s.kind)}
<Group gap={2}>
<IconServer size={14} />
{connections?.find(c => c.id === s.connection)?.name}
</Group>
<Group gap={2}>
{s.from && (
<>
<IconDatabase size={14} />
{s.from.databaseName}
{
s.kind === SourceKind.Metric
? ''
: '.' /** Metrics dont have table names */
}
{s.from.tableName}
</>
)}
</Group>
</Group>
</Text>
</div>
{editedSourceId !== s.id && (
@ -187,7 +201,7 @@ function SourcesSection() {
onClick={() => setEditedSourceId(s.id)}
size="sm"
>
<i className="bi bi-chevron-down" />
<IconChevronDown size={14} />
</Button>
)}
{editedSourceId === s.id && (
@ -196,7 +210,7 @@ function SourcesSection() {
onClick={() => setEditedSourceId(null)}
size="sm"
>
<i className="bi bi-chevron-up" />
<IconChevronUp size={14} />
</Button>
)}
</Flex>
@ -348,7 +362,7 @@ function IntegrationsSection() {
onClick={() => setEditedWebhookId(null)}
size="compact-xs"
>
<i className="bi bi-x-lg me-2" /> Cancel
<IconX size={14} className="me-2" /> Cancel
</Button>
)}
</Group>
@ -472,7 +486,7 @@ function TeamNameSection() {
<Button
size="xs"
variant="default"
leftSection={<i className="bi bi-pencil " />}
leftSection={<IconPencil size={16} />}
onClick={() => {
setIsEditingTeamName(true);
}}
@ -578,7 +592,7 @@ function ClickhouseSettingForm({
{tooltip && (
<Tooltip label={tooltip}>
<Text size="sm" style={{ cursor: 'help' }}>
<i className="bi bi-question-circle" />
<IconHelpCircle size={14} />
</Text>
</Tooltip>
)}
@ -663,7 +677,7 @@ function ClickhouseSettingForm({
<Button
size="xs"
variant="default"
leftSection={<i className="bi bi-pencil " />}
leftSection={<IconPencil size={16} />}
onClick={() => setIsEditing(true)}
>
Change
@ -756,14 +770,10 @@ const APIKeyCopyButton = ({
variant={copied ? 'light' : 'default'}
color="gray"
rightSection={
<div className="ms-2 text-nowrap">
{copied ? (
<i className="bi bi-check-lg me-2" />
) : (
<i className="bi bi-clipboard-fill me-2" />
)}
<Group wrap="nowrap" gap={4} ms="xs">
{copied ? <IconCheck size={14} /> : <IconClipboard size={14} />}
{copied ? 'Copied!' : 'Copy'}
</div>
</Group>
}
>
<div data-test-id={dataTestId} className="text-wrap text-break">

View file

@ -13,6 +13,7 @@ import {
Switch,
Text,
} from '@mantine/core';
import { IconWorld } from '@tabler/icons-react';
import { OPTIONS_FONTS } from './config/fonts';
import { UserPreferences, useUserPreferences } from './useUserPreferences';
@ -215,7 +216,7 @@ export const UserPreferencesModal = ({
<Input
placeholder="https:// or data:"
value={userPreferences.backgroundUrl}
leftSection={<i className="bi bi-globe" />}
leftSection={<IconWorld size={16} />}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setUserPreference({
backgroundUrl: e.currentTarget.value,

View file

@ -1,4 +1,5 @@
import { Button, Menu } from '@mantine/core';
import { IconTrash } from '@tabler/icons-react';
export default function ConfirmDeleteMenu({
onDelete,
@ -13,10 +14,7 @@ export default function ConfirmDeleteMenu({
</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
leftSection={<i className="bi bi-trash-fill" />}
onClick={onDelete}
>
<Menu.Item leftSection={<IconTrash size={16} />} onClick={onDelete}>
Confirm Delete
</Menu.Item>
</Menu.Dropdown>

View file

@ -1,5 +1,6 @@
import { useMemo } from 'react';
import { UseControllerProps } from 'react-hook-form';
import { IconServer } from '@tabler/icons-react';
import SelectControlled from '@/components/SelectControlled';
import { useConnections } from '@/connection';
@ -28,7 +29,7 @@ export function ConnectionSelectControlled({
comboboxProps={{ withinPortal: false }}
searchable
placeholder="Connection"
leftSection={<i className="bi bi-hdd-stack"></i>}
leftSection={<IconServer size={16} />}
maxDropdownHeight={280}
size={size}
/>

View file

@ -40,7 +40,22 @@ import {
Text,
Textarea,
} from '@mantine/core';
import { IconPlayerPlay } from '@tabler/icons-react';
import {
IconArrowDown,
IconArrowUp,
IconBell,
IconChartLine,
IconCirclePlus,
IconCode,
IconDotsVertical,
IconLayoutGrid,
IconList,
IconMarkdown,
IconNumbers,
IconPlayerPlay,
IconTable,
IconTrash,
} from '@tabler/icons-react';
import { SortingState } from '@tanstack/react-table';
import {
@ -216,7 +231,7 @@ function ChartSeriesEditorComponent({
onClick={() => onSwapSeries(index, index - 1)}
title="Move up"
>
<i className="bi bi-arrow-up" />
<IconArrowUp size={14} />
</Button>
)}
{(index ?? -1) < length - 1 && (
@ -227,7 +242,7 @@ function ChartSeriesEditorComponent({
onClick={() => onSwapSeries(index, index + 1)}
title="Move down"
>
<i className="bi bi-arrow-down" />
<IconArrowDown size={14} />
</Button>
)}
{((index ?? -1) > 0 || length > 1) && (
@ -237,7 +252,7 @@ function ChartSeriesEditorComponent({
size="xs"
onClick={() => onRemoveSeries(index)}
>
<i className="bi bi-trash me-2" />
<IconTrash size={14} className="me-2" />
Remove Series
</Button>
)}
@ -700,31 +715,31 @@ export default function EditTimeChartForm({
<Tabs.List>
<Tabs.Tab
value={DisplayType.Line}
leftSection={<i className="bi bi-graph-up" />}
leftSection={<IconChartLine size={16} />}
>
Line/Bar
</Tabs.Tab>
<Tabs.Tab
value={DisplayType.Table}
leftSection={<i className="bi bi-table" />}
leftSection={<IconTable size={16} />}
>
Table
</Tabs.Tab>
<Tabs.Tab
value={DisplayType.Number}
leftSection={<i className="bi bi-123" />}
leftSection={<IconNumbers size={16} />}
>
Number
</Tabs.Tab>
<Tabs.Tab
value={DisplayType.Search}
leftSection={<i className="bi bi-card-list" />}
leftSection={<IconList size={16} />}
>
Search
</Tabs.Tab>
<Tabs.Tab
value={DisplayType.Markdown}
leftSection={<i className="bi bi-markdown" />}
leftSection={<IconMarkdown size={16} />}
>
Markdown
</Tabs.Tab>
@ -851,7 +866,7 @@ export default function EditTimeChartForm({
});
}}
>
<i className="bi bi-plus-circle me-2" />
<IconCirclePlus size={14} className="me-2" />
Add Series
</Button>
)}
@ -885,7 +900,7 @@ export default function EditTimeChartForm({
)
}
>
<i className="bi bi-bell-fill me-2" />
<IconBell size={14} className="me-2" />
{!alert ? 'Add Alert' : 'Remove Alert'}
</Button>
)}
@ -1070,12 +1085,12 @@ export default function EditTimeChartForm({
<Menu width={250}>
<Menu.Target>
<Button variant="outline" color="gray" px="xs" size="xs">
<i className="bi bi-three-dots-vertical" />
<IconDotsVertical size={14} />
</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
leftSection={<i className="bi bi-layout-three-columns" />}
leftSection={<IconLayoutGrid size={16} />}
onClick={() => setSaveToDashboardModalOpen(true)}
>
Save to Dashboard
@ -1221,7 +1236,7 @@ export default function EditTimeChartForm({
{showSampleEvents && (
<Accordion defaultValue="sample">
<Accordion.Item value="sample">
<Accordion.Control icon={<i className="bi bi-card-list"></i>}>
<Accordion.Control icon={<IconList size={16} />}>
<Text size="sm" style={{ alignSelf: 'center' }}>
Sample Matched Events
</Text>
@ -1247,7 +1262,7 @@ export default function EditTimeChartForm({
)}
<Accordion defaultValue="">
<Accordion.Item value={'SQL'}>
<Accordion.Control icon={<i className="bi bi-code-square"></i>}>
<Accordion.Control icon={<IconCode size={16} />}>
<Text size="sm" style={{ alignSelf: 'center' }}>
Generated SQL
</Text>

View file

@ -15,6 +15,17 @@ import {
} from '@mantine/core';
import { useDebouncedValue } from '@mantine/hooks';
import { notifications } from '@mantine/notifications';
import {
IconChartLine,
IconCheck,
IconCopy,
IconFilter,
IconMinus,
IconPlus,
IconSearch,
IconSettings,
IconTextWrap,
} from '@tabler/icons-react';
import HyperJson, { GetLineActions, LineAction } from '@/components/HyperJson';
import { mergePath } from '@/utils';
@ -81,12 +92,12 @@ function HyperJsonMenu() {
setJsonOptions({ ...jsonOptions, lineWrap: !jsonOptions.lineWrap })
}
>
<i className="bi bi-text-wrap" />
<IconTextWrap size={14} />
</UnstyledButton>
<Menu width={240} withinPortal={false}>
<Menu.Target>
<UnstyledButton>
<i className="bi bi-gear" />
<IconSettings size={14} />
</UnstyledButton>
</Menu.Target>
<Menu.Dropdown>
@ -104,7 +115,7 @@ function HyperJsonMenu() {
py={8}
rightSection={
jsonOptions.normallyExpanded ? (
<i className="ps-2 bi bi-check2" />
<IconCheck size={14} className="ps-2" />
) : null
}
>
@ -114,7 +125,9 @@ function HyperJsonMenu() {
lh="1"
py={8}
rightSection={
jsonOptions.tabulate ? <i className="ps-2 bi bi-check2" /> : null
jsonOptions.tabulate ? (
<IconCheck size={14} className="ps-2" />
) : null
}
onClick={() =>
setJsonOptions({
@ -183,10 +196,10 @@ export function DBRowJsonViewer({
actions.push({
key: 'add-to-search',
label: (
<>
<i className="bi bi-funnel-fill me-1" />
<Group gap={2}>
<IconFilter size={14} />
Add to Filters
</>
</Group>
),
title: 'Add to Filters',
onClick: () => {
@ -226,10 +239,10 @@ export function DBRowJsonViewer({
actions.push({
key: 'search',
label: (
<>
<i className="bi bi-search me-1" />
<Group gap={2}>
<IconSearch size={14} />
Search
</>
</Group>
),
title: 'Search for this value only',
onClick: () => {
@ -271,7 +284,7 @@ export function DBRowJsonViewer({
if (generateChartUrl && typeof value === 'number') {
actions.push({
key: 'chart',
label: <i className="bi bi-graph-up" />,
label: <IconChartLine size={14} />,
title: 'Chart',
onClick: () => {
let chartFieldPath = fieldPath;
@ -317,15 +330,15 @@ export function DBRowJsonViewer({
actions.push({
key: 'toggle-column',
label: isIncluded ? (
<>
<i className="bi bi-dash fs-7 me-1" />
<Group gap={2}>
<IconMinus size={14} />
Column
</>
</Group>
) : (
<>
<i className="bi bi-plus fs-7 me-1" />
<Group gap={2}>
<IconPlus size={14} />
Column
</>
</Group>
),
title: isIncluded
? `Remove ${fieldPath} column from results table`
@ -372,7 +385,12 @@ export function DBRowJsonViewer({
} else {
actions.push({
key: 'copy-value',
label: 'Copy Value',
label: (
<Group gap={2}>
<IconCopy size={14} />
Copy Value
</Group>
),
onClick: () => {
window.navigator.clipboard.writeText(
typeof value === 'string'
@ -415,7 +433,7 @@ export function DBRowJsonViewer({
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setFilter(e.currentTarget.value)
}
leftSection={<i className="bi bi-search" />}
leftSection={<IconSearch size={16} />}
/>
{filter && (
<Button

View file

@ -16,6 +16,10 @@ import {
Tooltip,
UnstyledButton,
} from '@mantine/core';
import {
IconArrowsDiagonal,
IconArrowsDiagonalMinimize2,
} from '@tabler/icons-react';
import { FormatTime } from '@/useFormatTime';
import { useUserPreferences } from '@/useUserPreferences';
@ -240,9 +244,9 @@ export default function DBRowSidePanelHeader({
>
{/* TODO: Only show expand button when maxHeight = 120? */}
{expandSidebarHeader ? (
<i className="bi bi-arrows-angle-contract" />
<IconArrowsDiagonalMinimize2 size={14} />
) : (
<i className="bi bi-arrows-angle-expand" />
<IconArrowsDiagonal size={14} />
)}
</Button>
)}

View file

@ -49,6 +49,7 @@ import {
import {
IconCode,
IconDownload,
IconRefresh,
IconRotateClockwise,
IconSettings,
IconTextWrap,
@ -272,8 +273,8 @@ const SqlModal = ({
<SQLPreview data={sql} enableCopy={true} />
) : isLoadingSql ? (
<div className="text-center my-2">
<div className="spin-animate d-inline-block me-2">
<i className="bi bi-arrow-repeat" />
<div className="d-inline-block me-2">
<IconRefresh size={14} className="spin-animate" />
</div>
Loading SQL...
</div>
@ -956,8 +957,8 @@ export const RawLogTable = memo(
<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">
<i className="bi bi-arrow-repeat" />
<div className="d-inline-block">
<IconRefresh size={14} className="spin-animate" />
</div>{' '}
{loadingDate != null && (
<>

View file

@ -29,7 +29,19 @@ import {
UnstyledButton,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconSearch } from '@tabler/icons-react';
import {
IconChartBar,
IconChartBarOff,
IconChevronDown,
IconChevronRight,
IconChevronUp,
IconPin,
IconPinFilled,
IconRefresh,
IconSearch,
IconShadow,
IconSitemap,
} from '@tabler/icons-react';
import {
useAllFields,
@ -229,13 +241,13 @@ export const FilterCheckbox = ({
)}
<TextButton
onClick={onClickPin}
label={<i className={`bi bi-pin-angle${pinned ? '-fill' : ''}`}></i>}
label={pinned ? <IconPinFilled size={14} /> : <IconPin size={14} />}
data-testid={`filter-pin-${label}`}
/>
</div>
{pinned && (
<Text size="xxs">
<i className="bi bi-pin-angle-fill"></i>
<IconPinFilled size={12} />
</Text>
)}
</div>
@ -602,9 +614,13 @@ export const FilterGroup = ({
aria-checked={showDistributions}
role="checkbox"
>
<i
className={`bi ${isFetchingDistribution ? 'spinner-border spinner-border-sm' : showDistributions ? 'bi-bar-chart-line-fill' : 'bi-bar-chart-line'}`}
/>
{isFetchingDistribution ? (
<span className="spinner-border spinner-border-sm" />
) : showDistributions ? (
<IconChartBar size={14} />
) : (
<IconChartBarOff size={14} />
)}
</ActionIcon>
{onFieldPinClick && (
<ActionIcon
@ -615,9 +631,11 @@ export const FilterGroup = ({
title={isFieldPinned ? 'Unpin field' : 'Pin field'}
me={'4px'}
>
<i
className={`bi bi-pin-angle${isFieldPinned ? '-fill' : ''}`}
/>
{isFieldPinned ? (
<IconPinFilled size={14} />
) : (
<IconPin size={14} />
)}
</ActionIcon>
)}
</>
@ -729,11 +747,11 @@ export const FilterGroup = ({
label={
shouldShowMore ? (
<>
<span className="bi-chevron-up" /> Less
<IconChevronUp size={12} /> Less
</>
) : (
<>
<span className="bi-chevron-right" /> Show more
<IconChevronRight size={12} /> Show more
</>
)
}
@ -757,7 +775,7 @@ export const FilterGroup = ({
display={hasLoadedMore ? 'none' : undefined}
label={
<>
<span className="bi-chevron-right" /> Load more
<IconChevronRight size={12} /> Load more
</>
}
onClick={() => onLoadMore(name)}
@ -1143,8 +1161,9 @@ const DBSearchPageFiltersComponent = ({
{showRefreshButton && (
<TextButton
label={
<i
className="bi-arrow-clockwise ms-1 fs-7"
<IconRefresh
size={14}
className="ms-1"
onClick={() => setDateRange(chartConfig.dateRange)}
/>
}
@ -1174,8 +1193,14 @@ const DBSearchPageFiltersComponent = ({
withArrow
label="Denoise results will visually remove events matching common event patterns from the results table."
>
<Text size="xs" mt="-1px">
<i className="bi bi-noise-reduction"></i> Denoise Results
<Text size="xs" mt="-2px">
<Group gap={2}>
<IconShadow
size={14}
style={{ display: 'inline', verticalAlign: 'middle' }}
/>
Denoise Results
</Group>
</Text>
</Tooltip>
}
@ -1197,8 +1222,14 @@ const DBSearchPageFiltersComponent = ({
withArrow
label="Only show root spans (spans with no parent span)."
>
<Text size="xs" mt="-1px">
<i className="bi bi-diagram-3"></i> Root Spans Only
<Text size="xs" mt="-2px">
<Group gap={2}>
<IconSitemap
size={14}
style={{ display: 'inline', verticalAlign: 'middle' }}
/>
Root Spans Only
</Group>
</Text>
</Tooltip>
}
@ -1344,7 +1375,11 @@ const DBSearchPageFiltersComponent = ({
size="compact-xs"
loading={isFacetsFetching}
rightSection={
<i className={`bi-chevron-${showMoreFields ? 'up' : 'down'}`} />
showMoreFields ? (
<IconChevronUp size={14} />
) : (
<IconChevronDown size={14} />
)
}
onClick={() => setShowMoreFields(!showMoreFields)}
>

View file

@ -1,5 +1,6 @@
import { useController, UseControllerProps } from 'react-hook-form';
import { Flex, Select } from '@mantine/core';
import { IconTable } from '@tabler/icons-react';
import { useTablesDirect } from '@/clickhouse';
@ -55,7 +56,7 @@ export default function DBTableSelect({
<Select
searchable
placeholder="Table"
leftSection={<i className="bi bi-table"></i>}
leftSection={<IconTable size={16} />}
maxDropdownHeight={280}
data={data}
disabled={isTablesLoading}

View file

@ -610,7 +610,7 @@ function DBTimeChartComponent({
} groups are hidden. Try grouping by a different field.`}
>
<span className="text-muted-hover text-decoration-none fs-8">
<i className="bi bi-exclamation-triangle"></i> Only top{' '}
<IconAlertTriangle size={14} style={{ display: 'inline' }} /> Only top{' '}
{groupKeys.length} groups shown
</span>
</div>

View file

@ -13,6 +13,7 @@ import {
Stack,
Text,
} from '@mantine/core';
import { IconPencil } from '@tabler/icons-react';
import { DBTraceWaterfallChartContainer } from '@/components/DBTraceWaterfallChart';
import { useSource, useUpdateSource } from '@/source';
@ -135,7 +136,7 @@ export default function DBTracePanel({
size="xs"
onClick={() => setShowTraceIdInput(v => !v)}
>
<i className="bi bi-pencil"></i>
<IconPencil size={14} />
</Button>
)}
</Flex>

View file

@ -10,7 +10,12 @@ import {
SourceKind,
TSource,
} from '@hyperdx/common-utils/dist/types';
import { Anchor, Box, Code, Divider, Group, Text } from '@mantine/core';
import { Anchor, Box, Center, Code, Divider, Group, Text } from '@mantine/core';
import {
IconChevronDown,
IconChevronRight,
IconLogs,
} from '@tabler/icons-react';
import { ContactSupportText } from '@/components/ContactSupportText';
import useOffsetPaginatedQuery from '@/hooks/useOffsetPaginatedQuery';
@ -667,9 +672,7 @@ export function DBTraceWaterfallChartContainer({
}}
></div>
))}
<Text
span
me="xxs"
<Center
style={{
opacity: result.children.length > 0 ? 1 : 0,
}}
@ -677,12 +680,12 @@ export function DBTraceWaterfallChartContainer({
toggleCollapse(id);
}}
>
<i
className={`bi bi-chevron-${
collapsedIds.has(id) ? 'right' : 'down'
}`}
/>{' '}
</Text>
{collapsedIds.has(id) ? (
<IconChevronRight size={16} className="me-1 text-muted-hover" />
) : (
<IconChevronDown size={16} className="me-1 text-muted-hover" />
)}{' '}
</Center>
{!isFilterActive && (
<Text span size="xxs" me="xs" pt="2px">
{result.children.length > 0
@ -690,26 +693,30 @@ export function DBTraceWaterfallChartContainer({
: ''}
</Text>
)}
<Text
size="xxs"
truncate="end"
// style={{ width: 200 }}
span
// onClick={() => {
// toggleCollapse(id);
// }}
title={`${serviceName}${hasHttpAttributes && httpUrl ? ` | ${displayText}` : ''}`}
role="button"
>
<Group gap={0} wrap="nowrap">
{type === SourceKind.Log ? (
<i
className="bi bi-card-text fs-8 me-2 align-middle"
title="Correlated Log Line"
<IconLogs
size={14}
className="align-middle me-2"
aria-label="Correlated Log Line"
/>
) : null}
{serviceName ? `${serviceName} | ` : ''}
{displayText}
</Text>
<Text
size="xxs"
truncate="end"
// style={{ width: 200 }}
span
// onClick={() => {
// toggleCollapse(id);
// }}
title={`${serviceName}${hasHttpAttributes && httpUrl ? ` | ${displayText}` : ''}`}
role="button"
>
{serviceName ? `${serviceName} | ` : ''}
{displayText}
</Text>
</Group>
</div>
</div>
),

View file

@ -1,5 +1,6 @@
import { useController, UseControllerProps } from 'react-hook-form';
import { Select } from '@mantine/core';
import { IconDatabase } from '@tabler/icons-react';
import { useDatabasesDirect } from '@/clickhouse';
@ -36,7 +37,7 @@ export default function DatabaseSelect({
<Select
searchable
placeholder="Database"
leftSection={<i className="bi bi-database"></i>}
leftSection={<IconDatabase size={16} />}
maxDropdownHeight={280}
data={data}
disabled={isDatabasesLoading}

View file

@ -1,8 +1,7 @@
import React from 'react';
import { ErrorBoundary as ReactErrorBoundary } from 'react-error-boundary';
import { Alert, Button, Stack, Text } from '@mantine/core';
import { Icon } from './Icon';
import { IconInfoCircleFilled } from '@tabler/icons-react';
type ErrorBoundaryProps = {
children: React.ReactNode;
@ -33,7 +32,7 @@ export const ErrorBoundary = ({
<Alert
p="xs"
color="orange"
icon={<Icon name="info-circle-fill" />}
icon={<IconInfoCircleFilled size={16} />}
title={message || 'Something went wrong'}
>
{(showErrorMessage || showRetry) && (

View file

@ -3,7 +3,7 @@ import Link from 'next/link';
import SqlString from 'sqlstring';
import { SearchConditionLanguage } from '@hyperdx/common-utils/dist/types';
import { Button, Group, Popover, Stack, Text, Tooltip } from '@mantine/core';
import { IconLink } from '@tabler/icons-react';
import { IconCirclePlus, IconLink, IconSearch } from '@tabler/icons-react';
import { isLinkableUrl } from '@/utils/highlightedAttributes';
@ -103,7 +103,7 @@ export default function EventTag({
color="gray"
variant="subtle"
size="xs"
rightSection={<i className="bi bi-plus-circle" />}
rightSection={<IconCirclePlus size={14} />}
onClick={() => {
onPropertyAddClick(sqlExpression, value);
setOpened(false);
@ -123,7 +123,7 @@ export default function EventTag({
color="gray"
variant="subtle"
size="xs"
rightSection={<i className="bi bi-search" />}
rightSection={<IconSearch size={14} />}
>
Search This Value
</Button>

View file

@ -3,6 +3,12 @@ import cx from 'classnames';
import { ErrorBoundary } from 'react-error-boundary';
import { Button, Text, Tooltip } from '@mantine/core';
import { Group, Loader } from '@mantine/core';
import {
IconChevronDown,
IconChevronRight,
IconChevronUp,
IconLogs,
} from '@tabler/icons-react';
import { ColumnDef, Row, Table as TanstackTable } from '@tanstack/react-table';
import { StacktraceFrame as TStacktraceFrame } from '@/types';
@ -126,7 +132,11 @@ export const CollapsibleSection = ({
role="button"
onClick={() => setCollapsed(!collapsed)}
>
<i className={`bi bi-chevron-${collapsed ? 'right' : 'down'} me-2`}></i>
{collapsed ? (
<IconChevronRight size={14} className="me-2" />
) : (
<IconChevronDown size={14} className="me-2" />
)}
<div className="fs-7">{title}</div>
</div>
{collapsed ? null : <div className="mb-4">{children}</div>}
@ -226,7 +236,7 @@ export const StacktraceRow = ({
withArrow
color="gray"
>
<i className="bi bi-box-seam me-2" title="in_app: false" />
<IconLogs size={14} className="me-2" aria-label="in_app: false" />
</Tooltip>
)}
{augmentedFrame && (
@ -618,11 +628,11 @@ export const ExceptionSubpanel = ({
>
{stacktraceExpanded ? (
<>
<i className="bi bi-chevron-up me-2" /> Hide stack trace
<IconChevronUp size={14} className="me-2" /> Hide stack trace
</>
) : (
<>
<i className="bi bi-chevron-down me-2" />
<IconChevronDown size={14} className="me-2" />
Show {stacktraceHiddenRowsCount} more frames
</>
)}
@ -647,11 +657,12 @@ export const ExceptionSubpanel = ({
>
{breadcrumbExpanded ? (
<>
<i className="bi bi-chevron-up me-2" /> Hide breadcrumbs
<IconChevronUp size={14} className="me-2" /> Hide
breadcrumbs
</>
) : (
<>
<i className="bi bi-chevron-down me-2" />
<IconChevronDown size={14} className="me-2" />
Show {breadcrumbHiddenRowsCount} more breadcrumbs
</>
)}

View file

@ -2,7 +2,7 @@ import React, { memo, useCallback, useState } from 'react';
import cx from 'classnames';
import { useQueryState } from 'nuqs';
import { TSource } from '@hyperdx/common-utils/dist/types';
import { IconChevronRight } from '@tabler/icons-react';
import { IconArrowsMaximize, IconChevronRight } from '@tabler/icons-react';
import styles from '../../styles/LogTable.module.scss';
@ -68,7 +68,7 @@ export const ExpandedLogRow = memo(
lineHeight: 1,
}}
>
<i className="bi bi-arrows-angle-expand" />
<IconArrowsMaximize size={14} />
</button>
)}
{children}

View file

@ -11,6 +11,11 @@ import {
isString,
} from 'lodash';
import { useHover } from '@mantine/hooks';
import {
IconCaretDownFilled,
IconCaretRightFilled,
IconClipboard,
} from '@tabler/icons-react';
import styles from './HyperJson.module.scss';
@ -264,13 +269,13 @@ const Line = React.memo(
<div className={styles.key}>
{isExpandable &&
(isExpanded ? (
<i className="bi bi-caret-down-fill fs-9"></i>
<IconCaretDownFilled size={10} />
) : (
<i className="bi bi-caret-right-fill fs-9"></i>
<IconCaretRightFilled size={10} />
))}
{keyName}
<div className={styles.hoverContent}>
<i className="bi bi-clipboard" />
<IconClipboard size={14} />
</div>
</div>
</div>

File diff suppressed because it is too large Load diff

View file

@ -3,15 +3,9 @@ import Link from 'next/link';
import { pickBy } from 'lodash';
import CopyToClipboard from 'react-copy-to-clipboard';
import { JSONTree } from 'react-json-tree';
import {
Accordion,
Box,
Button,
CopyButton,
TableData,
Text,
} from '@mantine/core';
import { Accordion, Box, Button, CopyButton, TableData } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconTerminal } from '@tabler/icons-react';
import HyperJson from '@/components/HyperJson';
import { Table } from '@/components/Table';
@ -229,7 +223,7 @@ export function NetworkPropertySubpanel({
}}
>
<Button size="xs" variant="light">
<i className="bi bi-terminal-plus me-2" />
<IconTerminal size={14} className="me-2" />
Copy Request as Curl
</Button>
</CopyToClipboard>
@ -240,7 +234,7 @@ export function NetworkPropertySubpanel({
size="sm"
as="a"
>
<i className="bi bi-graph-up me-2" />
<IconChartLine size={14} className="me-2" />
Endpoint Trends
</Button>
</Link> */}

View file

@ -11,6 +11,14 @@ import {
TextInput,
} from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import {
IconClock,
IconCurrencyDollar,
IconDatabase,
IconNumbers,
IconPercentage,
IconX,
} from '@tabler/icons-react';
import { NumberFormat } from '../types';
import { formatNumber } from '../utils';
@ -23,12 +31,12 @@ const FORMAT_NAMES: Record<string, string> = {
time: 'Time',
};
const FORMAT_ICONS: Record<string, string> = {
number: '123',
currency: 'currency-dollar',
percent: 'percent',
byte: 'database',
time: 'clock',
const FORMAT_ICONS: Record<string, React.ReactNode> = {
number: <IconNumbers size={14} />,
currency: <IconCurrencyDollar size={14} />,
percent: <IconPercentage size={14} />,
byte: <IconDatabase size={14} />,
time: <IconClock size={14} />,
};
export const NumberFormatForm: React.FC<{
@ -83,11 +91,7 @@ export const NumberFormatForm: React.FC<{
>
<NativeSelect
label="Output format"
leftSection={
values.output && (
<i className={`bi bi-${FORMAT_ICONS[values.output]}`} />
)
}
leftSection={values.output && FORMAT_ICONS[values.output]}
style={{ flex: 1 }}
data={[
{ value: 'number', label: 'Number' },
@ -234,11 +238,7 @@ export const NumberFormatInput: React.FC<{
size="compact-sm"
color="dark"
variant="default"
leftSection={
value?.output && (
<i className={`bi bi-${FORMAT_ICONS[value.output]}`} />
)
}
leftSection={value?.output && FORMAT_ICONS[value.output]}
>
{value?.output ? FORMAT_NAMES[value.output] : 'Set number format'}
</Button>
@ -250,7 +250,7 @@ export const NumberFormatInput: React.FC<{
px="xs"
onClick={() => handleApply(undefined)}
>
<i className="bi bi-x-lg" />
<IconX size={14} />
</Button>
)}
</Button.Group>

View file

@ -6,6 +6,7 @@ import {
} from '@hyperdx/common-utils/dist/types';
import { Button, Divider, Modal, Text } from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconArrowLeft } from '@tabler/icons-react';
import { ConnectionForm } from '@/components/ConnectionForm';
import { IS_LOCAL_MODE } from '@/config';
@ -400,7 +401,7 @@ export default function OnboardingModal({
p="xs"
mb="md"
>
<i className="bi bi-arrow-left me-2" /> Back
<IconArrowLeft size={14} className="me-2" /> Back
</Button>
<Text size="sm" mb="md">
Lets set up a source table to query telemetry from.

View file

@ -1,4 +1,5 @@
import { Button, Menu, Text } from '@mantine/core';
import { IconDotsVertical, IconForms, IconTrash } from '@tabler/icons-react';
export default function SearchPageActionBar({
onClickDeleteSavedSearch,
@ -17,19 +18,19 @@ export default function SearchPageActionBar({
size="xs"
style={{ flexShrink: 0 }}
>
<i className="bi bi-three-dots-vertical" />
<IconDotsVertical size={14} />
</Button>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item
leftSection={<i className="bi bi-trash-fill" />}
leftSection={<IconTrash size={16} />}
onClick={onClickDeleteSavedSearch}
>
Delete Saved Search
</Menu.Item>
<Menu.Item
leftSection={<i className="bi bi-input-cursor-text" />}
leftSection={<IconForms size={16} />}
onClick={onClickRenameSavedSearch}
>
Rename Saved Search

View file

@ -2,6 +2,7 @@ import { useCallback, useMemo } from 'react';
import { parseAsString, useQueryState } from 'nuqs';
import type { Filter } from '@hyperdx/common-utils/dist/types';
import { Drawer, Grid, Group, Text } from '@mantine/core';
import { IconServer } from '@tabler/icons-react';
import { INTEGER_NUMBER_FORMAT, MS_NUMBER_FORMAT } from '@/ChartUtils';
import { ChartBox } from '@/components/ChartBox';
@ -76,7 +77,7 @@ export default function ServiceDashboardDbQuerySidePanel({
Details for {dbQuery}
{service && (
<Text component="span" c="gray" fz="xs">
<i className="bi bi-hdd ms-3 me-1" />
<IconServer size={14} className="ms-3 me-1" />
{service}
</Text>
)}

View file

@ -2,6 +2,7 @@ import { useCallback, useMemo } from 'react';
import { parseAsString, useQueryState } from 'nuqs';
import type { Filter } from '@hyperdx/common-utils/dist/types';
import { Drawer, Grid, Group, Text } from '@mantine/core';
import { IconServer } from '@tabler/icons-react';
import {
ERROR_RATE_PERCENTAGE_NUMBER_FORMAT,
@ -83,7 +84,7 @@ export default function ServiceDashboardEndpointSidePanel({
Details for {endpoint}
{service && (
<Text component="span" c="gray" fz="xs">
<i className="bi bi-hdd ms-3 me-1" />
<IconServer size={14} className="ms-3 me-1" />
{service}
</Text>
)}

View file

@ -20,6 +20,7 @@ import {
Anchor,
Box,
Button,
Center,
Divider,
Flex,
Grid,
@ -31,7 +32,12 @@ import {
Tooltip,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconTrash } from '@tabler/icons-react';
import {
IconCirclePlus,
IconHelpCircle,
IconSettings,
IconTrash,
} from '@tabler/icons-react';
import { SourceSelectControlled } from '@/components/SourceSelect';
import { IS_METRICS_ENABLED, IS_SESSIONS_ENABLED } from '@/config';
@ -125,16 +131,17 @@ function FormRow({
label
)}
</Stack>
<Text
<Center
me="sm"
ms="sm"
style={{
...(!helpText ? { opacity: 0, pointerEvents: 'none' } : {}),
}}
>
<Tooltip label={helpText} color="dark" c="white" multiline maw={600}>
<i className="bi bi-question-circle cursor-pointer" />
<IconHelpCircle size={20} className="cursor-pointer" />
</Tooltip>
</Text>
</Center>
</Flex>
<Box
w="100%"
@ -229,7 +236,7 @@ function HighlightedAttributeExpressionsFormRow({
multiline
maw={600}
>
<i className="bi bi-question-circle cursor-pointer" />
<IconHelpCircle size={14} className="cursor-pointer" />
</Tooltip>
</Text>
</Grid.Col>
@ -250,7 +257,7 @@ function HighlightedAttributeExpressionsFormRow({
});
}}
>
<i className="bi bi-plus-circle me-2" />
<IconCirclePlus size={14} className="me-2" />
Add expression
</Button>
</FormRow>
@ -313,10 +320,10 @@ export function LogTableModelForm(props: TableModelProps) {
onClick={() => setShowOptionalFields(true)}
size="xs"
>
<Text me="sm" span>
<i className="bi bi-gear" />
</Text>
Configure Optional Fields
<Group gap="xs">
<IconSettings size={14} />
Configure Optional Fields
</Group>
</Anchor>
)}
{showOptionalFields && (

View file

@ -1,7 +1,7 @@
import { useState } from 'react';
import { MetricsDataType, TSource } from '@hyperdx/common-utils/dist/types';
import { Modal, Paper, Tabs, Text, TextProps, Tooltip } from '@mantine/core';
import { IconCode } from '@tabler/icons-react';
import { IconCode, IconRefresh } from '@tabler/icons-react';
import { useTableMetadata } from '@/hooks/useMetadata';
@ -77,8 +77,8 @@ const TableSchemaPreview = ({
style={{ overflow: 'hidden' }}
>
{isLoading ? (
<div className="spin-animate d-inline-block">
<i className="bi bi-arrow-repeat" />
<div className="d-inline-block">
<IconRefresh className="spin-animate" />
</div>
) : (
<SQLPreview

View file

@ -3,6 +3,7 @@ import { UseControllerProps } from 'react-hook-form';
import { SourceKind } from '@hyperdx/common-utils/dist/types';
import { SelectProps, UnstyledButton } from '@mantine/core';
import { ComboboxChevron } from '@mantine/core';
import { IconStack } from '@tabler/icons-react';
import SelectControlled from '@/components/SelectControlled';
import { HDX_LOCAL_DEFAULT_SOURCES } from '@/config';
@ -94,7 +95,7 @@ function SourceSelectControlledComponent({
comboboxProps={{ withinPortal: false, ...comboboxProps }}
searchable
placeholder="Data Source"
leftSection={<i className="bi bi-collection"></i>}
leftSection={<IconStack size={16} />}
maxDropdownHeight={280}
size={size}
onCreate={onCreate}

View file

@ -1,6 +1,7 @@
import React, { useMemo } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { Box, Button, Text } from '@mantine/core';
import { IconChevronDown, IconChevronUp } from '@tabler/icons-react';
import { ColumnDef } from '@tanstack/react-table';
import { FormatTime } from '@/useFormatTime';
@ -128,11 +129,11 @@ export const SpanEventsSubpanel = ({
>
{isExpanded ? (
<>
<i className="bi bi-chevron-up me-2" /> Hide events
<IconChevronUp size={14} className="me-2" /> Hide events
</>
) : (
<>
<i className="bi bi-chevron-down me-2" />
<IconChevronDown size={14} className="me-2" />
Show {hiddenRowsCount} more events
</>
)}

View file

@ -1,5 +1,6 @@
import * as React from 'react';
import cx from 'classnames';
import { IconChevronDown, IconChevronUp } from '@tabler/icons-react';
import {
ColumnDef,
flexRender,
@ -108,13 +109,14 @@ export const Table = <T extends Record<string, unknown> | string[]>({
export const TableCellButton: React.FC<{
title?: string;
label: React.ReactNode;
biIcon?: string;
biIcon?: 'chevron-up' | 'chevron-down';
onClick: VoidFunction;
}> = ({ onClick, title, label, biIcon }) => {
return (
<button className={styles.tableCellButton} title={title} onClick={onClick}>
{label && <span>{label}</span>}
{biIcon ? <i className={`bi bi-${biIcon}`} /> : null}
{biIcon === 'chevron-up' && <IconChevronUp size={14} />}
{biIcon === 'chevron-down' && <IconChevronDown size={14} />}
</button>
);
};

View file

@ -10,6 +10,7 @@ import {
ScrollArea,
Stack,
} from '@mantine/core';
import { IconSearch, IconTags } from '@tabler/icons-react';
import api from '@/api';
@ -101,7 +102,7 @@ export const Tags = React.memo(
color="gray"
style={{ cursor: 'pointer' }}
>
<i className="bi bi-tags fs-7" />
<IconTags size={14} />
</ActionIcon>
)}
</Popover.Target>
@ -112,7 +113,7 @@ export const Tags = React.memo(
size="xs"
placeholder={allowCreate ? 'Search or create tag' : 'Search tag'}
variant="filled"
leftSection={<i className="bi bi-search" />}
leftSection={<IconSearch size={16} />}
autoFocus
m={8}
mb={0}

View file

@ -15,6 +15,7 @@ import {
TextInput,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconLock, IconUserPlus } from '@tabler/icons-react';
import api from '@/api';
@ -215,7 +216,7 @@ export default function TeamMembersSection() {
<div className="fs-7">Team Members</div>
<Button
variant="light"
leftSection={<i className="bi bi-person-plus-fill" />}
leftSection={<IconUserPlus size={16} />}
onClick={() => setTeamInviteModalShow(true)}
>
Invite Team Member
@ -244,7 +245,7 @@ export default function TeamMembersSection() {
<div>{member.email}</div>
{member.hasPasswordAuth && (
<div>
<i className="bi bi-lock-fill" /> Password Auth
<IconLock size={14} /> Password Auth
</div>
)}
</Group>

View file

@ -16,6 +16,7 @@ import {
TextInput,
} from '@mantine/core';
import { notifications } from '@mantine/notifications';
import { IconInfoCircleFilled } from '@tabler/icons-react';
import ReactCodeMirror, {
EditorView,
placeholder,
@ -373,7 +374,7 @@ export function WebhookForm({
/>
</div>,
<Alert
icon={<i className="bi bi-info-circle-fill " />}
icon={<IconInfoCircleFilled size={16} />}
key="5"
className="mb-4"
color="gray"

View file

@ -1,5 +1,6 @@
import { Button } from '@mantine/core';
import type { Meta } from '@storybook/nextjs';
import { IconStarFilled } from '@tabler/icons-react';
// Just a test story, can be deleted
@ -14,7 +15,7 @@ const meta: Meta = {
export const Default = () => (
<Button
variant="light"
leftSection={<i className="bi bi-star-fill" />}
leftSection={<IconStarFilled size={14} />}
size="compact-sm"
>
Assign exception to Warren

View file

@ -8,7 +8,6 @@
// Custom utilities (bg-surface, border-dark, etc.)
@use './_utilities';
@import url('https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css');
.inter {
font-family: var(--font-inter), 'Helvetica Neue', sans-serif;

View file

@ -176,7 +176,9 @@ test.describe('Kubernetes Dashboard', { tag: ['@kubernetes'] }, () => {
});
test.describe('Pods Table Sorting', () => {
const SORT_ICON_SELECTOR = 'i.bi-caret-down-fill, i.bi-caret-up-fill';
// Tabler icons render as SVG elements with class 'tabler-icon'
const SORT_ICON_SELECTOR =
'svg.tabler-icon-caret-down-filled, svg.tabler-icon-caret-up-filled';
async function waitForTableLoad(page: Page): Promise<Locator> {
const podsTable = page.getByTestId('k8s-pods-table');
@ -196,7 +198,9 @@ test.describe('Kubernetes Dashboard', { tag: ['@kubernetes'] }, () => {
const podsTable = await waitForTableLoad(page);
const restartsHeader = getColumnHeader(podsTable, 'Restarts');
await expect(restartsHeader.locator('i.bi-caret-down-fill')).toBeVisible({
await expect(
restartsHeader.locator('svg.tabler-icon-caret-down-filled'),
).toBeVisible({
timeout: 10000,
});
@ -210,7 +214,9 @@ test.describe('Kubernetes Dashboard', { tag: ['@kubernetes'] }, () => {
await restartsHeader.click();
await page.waitForTimeout(500);
await expect(restartsHeader.locator('i.bi-caret-up-fill')).toBeVisible();
await expect(
restartsHeader.locator('svg.tabler-icon-caret-up-filled'),
).toBeVisible();
const firstRestartsAfter = await podsTable
.locator('tbody tr')