Compare commits

...

2 Commits

Author SHA1 Message Date
sawhil
b07c500b3b feat: table consumption in domain list in external apis 2025-07-07 12:40:39 +05:30
sawhil
979d238e7b feat: consumed data table in services page 2025-07-06 23:11:22 +05:30
10 changed files with 439 additions and 101 deletions

View File

@@ -0,0 +1,45 @@
{
"name": "@signozhq/table",
"version": "0.0.0",
"license": "MIT",
"type": "module",
"exports": {
".": {
"types": "./dist/data-table.d.ts",
"import": "./dist/data-table.js"
}
},
"main": "./dist/data-table.js",
"module": "./dist/data-table.js",
"types": "./dist/data-table.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"lint": "eslint . --max-warnings 0",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist"
},
"dependencies": {
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-slot": "^1.1.0",
"@tanstack/react-table": "^8.21.3",
"@tanstack/react-virtual": "^3.13.9",
"@types/lodash-es": "^4.17.12",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"lodash-es": "^4.17.21",
"lucide-react": "^0.445.0",
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7"
},
"peerDependencies": {
"react": "^18.2.0"
},
"publishConfig": {
"access": "public"
},
"description": "Table component with granular control using tanstack",
"yalcSig": "a82ce696abf1d0eaafed9c9884edbe12"
}

View File

@@ -0,0 +1 @@
a82ce696abf1d0eaafed9c9884edbe12

View File

@@ -41,6 +41,7 @@
"@sentry/react": "8.41.0",
"@sentry/webpack-plugin": "2.22.6",
"@signozhq/design-tokens": "1.1.4",
"@signozhq/table": "file:.yalc/@signozhq/table",
"@tanstack/react-table": "8.20.6",
"@tanstack/react-virtual": "3.11.2",
"@uiw/react-md-editor": "3.23.5",

View File

@@ -1,7 +1,7 @@
import '../Explorer.styles.scss';
import { LoadingOutlined } from '@ant-design/icons';
import { Spin, Table, Typography } from 'antd';
import { DataTable } from '@signozhq/table';
import { Typography } from 'antd';
import axios from 'api';
import logEvent from 'api/common/logEvent';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
@@ -27,7 +27,7 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { DEFAULT_PARAMS, useApiMonitoringParams } from '../../queryParams';
import {
columnsConfig,
dataTableColumnsConfig,
formatDataForTable,
hardcodedAttributeKeys,
} from '../../utils';
@@ -157,50 +157,42 @@ function DomainList(): JSX.Element {
hardcodedAttributeKeys={hardcodedAttributeKeys}
/>
</div>
<Table
className={cx('api-monitoring-domain-list-table')}
dataSource={isFetching || isLoading ? [] : formattedDataForTable}
columns={columnsConfig}
loading={{
spinning: isFetching || isLoading,
indicator: <Spin indicator={<LoadingOutlined size={14} spin />} />,
{!isFetching && !isLoading && formattedDataForTable.length === 0 && (
<div className="no-filtered-domains-message-container">
<div className="no-filtered-domains-message-content">
<img
src="/Icons/emptyState.svg"
alt="thinking-emoji"
className="empty-state-svg"
/>
<Typography.Text className="no-filtered-domains-message">
This query had no results. Edit your query and try again!
</Typography.Text>
</div>
</div>
)}
<DataTable
columns={dataTableColumnsConfig}
data={isFetching || isLoading ? [] : formattedDataForTable}
tableId="api-monitoring-domain-list-table"
isLoading={isFetching || isLoading}
enablePagination={false}
showHeaders
enableSorting
enableColumnResizing={false}
enableColumnReordering={false}
enableColumnPinning={false}
enableGlobalFilter={false}
onRowClick={(record, index): void => {
if (index !== undefined) {
const dataIndex = formattedDataForTable.findIndex(
(item) => item.key === record.original.key,
);
setSelectedDomainIndex(dataIndex);
setParams({ selectedDomain: record.original.domainName });
logEvent('API Monitoring: Domain name row clicked', {});
}
}}
locale={{
emptyText:
isFetching || isLoading ? null : (
<div className="no-filtered-domains-message-container">
<div className="no-filtered-domains-message-content">
<img
src="/Icons/emptyState.svg"
alt="thinking-emoji"
className="empty-state-svg"
/>
<Typography.Text className="no-filtered-domains-message">
This query had no results. Edit your query and try again!
</Typography.Text>
</div>
</div>
),
}}
scroll={{ x: true }}
tableLayout="fixed"
onRow={(record, index): { onClick: () => void; className: string } => ({
onClick: (): void => {
if (index !== undefined) {
const dataIndex = formattedDataForTable.findIndex(
(item) => item.key === record.key,
);
setSelectedDomainIndex(dataIndex);
setParams({ selectedDomain: record.domainName });
logEvent('API Monitoring: Domain name row clicked', {});
}
},
className: 'expanded-clickable-row',
})}
rowClassName={(_, index): string =>
index % 2 === 0 ? 'table-row-dark' : 'table-row-light'
}
/>
{selectedDomainIndex !== -1 && (
<DomainDetails

View File

@@ -1,5 +1,7 @@
/* eslint-disable sonarjs/no-identical-functions */
/* eslint-disable sonarjs/no-duplicate-string */
import { Color } from '@signozhq/design-tokens';
import { ColumnDef } from '@tanstack/react-table';
import { Progress, Tag, Tooltip } from 'antd';
import { ColumnType } from 'antd/es/table';
import {
@@ -249,6 +251,152 @@ export const columnsConfig: ColumnType<APIDomainsRowData>[] = [
},
];
// DataTable columns configuration for SignozHq DataTable
export const dataTableColumnsConfig: ColumnDef<APIDomainsRowData>[] = [
{
accessorKey: 'domainName',
header: (): React.ReactNode => (
<div className="domain-list-name-col-header">Domain</div>
),
size: 237,
enableSorting: false,
cell: ({ getValue }): React.ReactNode => (
<div className="domain-list-name-col-value">{getValue() as string}</div>
),
},
{
accessorKey: 'endpointCount',
header: 'Endpoints in use',
size: 142,
enableSorting: true,
sortingFn: (rowA, rowB): number => {
const endpointA =
rowA.original.endpointCount === '-' || rowA.original.endpointCount === 'n/a'
? ''
: rowA.original.endpointCount;
const endpointB =
rowB.original.endpointCount === '-' || rowB.original.endpointCount === 'n/a'
? ''
: rowB.original.endpointCount;
if (!endpointA && !endpointB) return 0;
if (!endpointA) return 1;
if (!endpointB) return -1;
return Number(endpointA) - Number(endpointB);
},
},
{
accessorKey: 'lastUsed',
header: 'Last used',
size: 142,
enableSorting: true,
sortingFn: (rowA, rowB): number => {
const dateA =
rowA.original.lastUsed === '-' || rowA.original.lastUsed === 'n/a'
? new Date(0).toISOString()
: rowA.original.lastUsed;
const dateB =
rowB.original.lastUsed === '-' || rowB.original.lastUsed === 'n/a'
? new Date(0).toISOString()
: rowB.original.lastUsed;
return new Date(dateB).getTime() - new Date(dateA).getTime();
},
cell: ({ getValue }): React.ReactNode => {
const lastUsed = getValue() as string;
return lastUsed === 'n/a' || lastUsed === '-'
? '-'
: getLastUsedRelativeTime(new Date(lastUsed).getTime());
},
},
{
accessorKey: 'rate',
header: (): React.ReactNode => (
<div>
Rate <span className="round-metric-tag">ops/s</span>
</div>
),
size: 142,
enableSorting: true,
sortingFn: (rowA, rowB): number => {
const rateA =
rowA.original.rate === '-' || rowA.original.rate === 'n/a'
? 0
: rowA.original.rate;
const rateB =
rowB.original.rate === '-' || rowB.original.rate === 'n/a'
? 0
: rowB.original.rate;
return Number(rateA) - Number(rateB);
},
},
{
accessorKey: 'errorRate',
header: (): React.ReactNode => (
<div>
Error <span className="round-metric-tag">%</span>
</div>
),
size: 142,
enableSorting: true,
sortingFn: (rowA, rowB): number => {
const errorRateA =
rowA.original.errorRate === '-' || rowA.original.errorRate === 'n/a'
? 0
: rowA.original.errorRate;
const errorRateB =
rowB.original.errorRate === '-' || rowB.original.errorRate === 'n/a'
? 0
: rowB.original.errorRate;
return Number(errorRateA) - Number(errorRateB);
},
cell: ({ getValue }): React.ReactNode => {
const errorRate = getValue() as number | string;
const errorRateValue =
errorRate === 'n/a' || errorRate === '-' ? 0 : errorRate;
return (
<Progress
status="active"
percent={Number((errorRateValue as number).toFixed(2))}
strokeLinecap="butt"
size="small"
strokeColor={((): string => {
const errorRatePercent = Number((errorRateValue as number).toFixed(2));
if (errorRatePercent >= 90) return Color.BG_SAKURA_500;
if (errorRatePercent >= 60) return Color.BG_AMBER_500;
return Color.BG_FOREST_500;
})()}
className="progress-bar error-rate"
/>
);
},
},
{
accessorKey: 'latency',
header: (): React.ReactNode => (
<div>
Avg. Latency <span className="round-metric-tag">ms</span>
</div>
),
size: 142,
enableSorting: true,
sortingFn: (rowA, rowB): number => {
const latencyA =
rowA.original.latency === '-' || rowA.original.latency === 'n/a'
? 0
: rowA.original.latency;
const latencyB =
rowB.original.latency === '-' || rowB.original.latency === 'n/a'
? 0
: rowB.original.latency;
return Number(latencyA) - Number(latencyB);
},
},
];
// Rename this to a proper name
export const hardcodedAttributeKeys: BaseAutocompleteData[] = [
{

View File

@@ -1,8 +1,9 @@
import { WarningFilled } from '@ant-design/icons';
import { DataTable } from '@signozhq/table';
import { Flex, Typography } from 'antd';
import { ResizeTable } from 'components/ResizeTable';
import { MAX_RPS_LIMIT } from 'constants/global';
import ResourceAttributesFilter from 'container/ResourceAttributesFilter';
import { getTableColumns } from 'container/ServiceTable/Columns/ServiceColumn';
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
import { useAppContext } from 'providers/App/App';
import { useEffect, useMemo, useState } from 'react';
@@ -10,7 +11,6 @@ import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom';
import { getTotalRPS } from 'utils/services';
import { getColumns } from '../Columns/ServiceColumn';
import ServiceTableProps from '../types';
function ServiceTraceTable({
@@ -23,7 +23,7 @@ function ServiceTraceTable({
const { isFetchingActiveLicense, trialInfo } = useAppContext();
const { isCloudUser: isCloudUserVal } = useGetTenantLicense();
const tableColumns = useMemo(() => getColumns(search, false), [search]);
const tableColumns = useMemo(() => getTableColumns(search), [search]);
useEffect(() => {
if (
@@ -65,13 +65,19 @@ function ServiceTraceTable({
<ResourceAttributesFilter />
<ResizeTable
pagination={paginationConfig}
<DataTable
columns={tableColumns}
loading={loading}
dataSource={services}
rowKey="serviceName"
className="service-traces-table"
data={services}
tableId="service-traces-table"
isLoading={loading}
enablePagination
pageSize={paginationConfig.defaultPageSize}
showHeaders
enableSorting
enableColumnResizing={false}
enableColumnReordering={false}
enableColumnPinning={false}
enableGlobalFilter={false}
/>
</div>
);

View File

@@ -1,46 +0,0 @@
import type { ColumnsType } from 'antd/es/table';
import { ServicesList } from 'types/api/metrics/getService';
import {
ColumnKey,
ColumnTitle,
ColumnWidth,
SORTING_ORDER,
} from './ColumnContants';
import { getColumnSearchProps } from './GetColumnSearchProps';
export const getColumns = (search: string): ColumnsType<ServicesList> => [
{
title: ColumnTitle[ColumnKey.Application],
dataIndex: ColumnKey.Application,
width: ColumnWidth.Application,
key: ColumnKey.Application,
...getColumnSearchProps('serviceName', search),
},
{
title: ColumnTitle[ColumnKey.P99],
dataIndex: ColumnKey.P99,
key: ColumnKey.P99,
width: ColumnWidth.P99,
defaultSortOrder: SORTING_ORDER,
sorter: (a: ServicesList, b: ServicesList): number => a.p99 - b.p99,
render: (value: number): string => (value / 1000000).toFixed(2),
},
{
title: ColumnTitle[ColumnKey.ErrorRate],
dataIndex: ColumnKey.ErrorRate,
key: ColumnKey.ErrorRate,
width: 150,
sorter: (a: ServicesList, b: ServicesList): number =>
a.errorRate - b.errorRate,
render: (value: number): string => value.toFixed(2),
},
{
title: ColumnTitle[ColumnKey.Operations],
dataIndex: ColumnKey.Operations,
key: ColumnKey.Operations,
width: ColumnWidth.Operations,
sorter: (a: ServicesList, b: ServicesList): number => a.callRate - b.callRate,
render: (value: number): string => value.toFixed(2),
},
];

View File

@@ -0,0 +1,92 @@
import { ColumnDef, createColumnHelper } from '@tanstack/react-table';
import type { ColumnsType } from 'antd/es/table';
import ROUTES from 'constants/routes';
import { routeConfig } from 'container/SideNav/config';
import { getQueryString } from 'container/SideNav/helper';
import { Link } from 'react-router-dom';
import { ServicesList } from 'types/api/metrics/getService';
import { Name } from '../styles';
import {
ColumnKey,
ColumnTitle,
ColumnWidth,
SORTING_ORDER,
} from './ColumnContants';
import { getColumnSearchProps } from './GetColumnSearchProps';
export const getColumns = (search: string): ColumnsType<ServicesList> => [
{
title: ColumnTitle[ColumnKey.Application],
dataIndex: ColumnKey.Application,
width: ColumnWidth.Application,
key: ColumnKey.Application,
...getColumnSearchProps('serviceName', search),
},
{
title: ColumnTitle[ColumnKey.P99],
dataIndex: ColumnKey.P99,
key: ColumnKey.P99,
width: ColumnWidth.P99,
defaultSortOrder: SORTING_ORDER,
sorter: (a: ServicesList, b: ServicesList): number => a.p99 - b.p99,
render: (value: number): string => (value / 1000000).toFixed(2),
},
{
title: ColumnTitle[ColumnKey.ErrorRate],
dataIndex: ColumnKey.ErrorRate,
key: ColumnKey.ErrorRate,
width: 150,
sorter: (a: ServicesList, b: ServicesList): number =>
a.errorRate - b.errorRate,
render: (value: number): string => value.toFixed(2),
},
{
title: ColumnTitle[ColumnKey.Operations],
dataIndex: ColumnKey.Operations,
key: ColumnKey.Operations,
width: ColumnWidth.Operations,
sorter: (a: ServicesList, b: ServicesList): number => a.callRate - b.callRate,
render: (value: number): string => value.toFixed(2),
},
];
// Utility to convert AntD columns to ColumnDef (minimal for your columns)
const columnHelper = createColumnHelper<ServicesList>();
export const getTableColumns = (
search: string,
): ColumnDef<ServicesList, any>[] => [
columnHelper.accessor(ColumnKey.Application, {
header: ColumnTitle[ColumnKey.Application],
cell: (info) => {
const metrics = info.getValue();
const urlParams = new URLSearchParams(search);
const availableParams = routeConfig[ROUTES.SERVICE_METRICS];
const queryString = getQueryString(availableParams, urlParams);
return (
<Link to={`${ROUTES.APPLICATION}/${metrics}?${queryString.join('')}`}>
<Name>{metrics}</Name>
</Link>
);
},
enableColumnFilter: true, // Enable filtering only for this column
}),
columnHelper.accessor(ColumnKey.P99, {
header: ColumnTitle[ColumnKey.P99],
cell: (info) => (info.getValue() / 1000000).toFixed(2),
sortingFn: 'basic',
enableColumnFilter: false,
}),
columnHelper.accessor(ColumnKey.ErrorRate, {
header: ColumnTitle[ColumnKey.ErrorRate],
cell: (info) => info.getValue().toFixed(2),
sortingFn: 'basic',
enableColumnFilter: false,
}),
columnHelper.accessor(ColumnKey.Operations, {
header: ColumnTitle[ColumnKey.Operations],
cell: (info) => info.getValue().toFixed(2),
sortingFn: 'basic',
enableColumnFilter: false,
}),
];

9
frontend/yalc.lock Normal file
View File

@@ -0,0 +1,9 @@
{
"version": "v1",
"packages": {
"@signozhq/table": {
"signature": "a82ce696abf1d0eaafed9c9884edbe12",
"file": true
}
}
}

View File

@@ -3228,6 +3228,11 @@
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs@1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz#a2c4c47af6337048ee78ff6dc0d090b390d2bb30"
integrity sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==
"@radix-ui/react-context@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c"
@@ -3254,6 +3259,11 @@
"@radix-ui/react-use-callback-ref" "1.0.1"
"@radix-ui/react-use-escape-keydown" "1.0.3"
"@radix-ui/react-icons@^1.3.0":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.3.2.tgz#09be63d178262181aeca5fb7f7bc944b10a7f441"
integrity sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==
"@radix-ui/react-id@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.1.tgz#73cdc181f650e4df24f0b6a5b7aa426b912c88c0"
@@ -3328,6 +3338,13 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "1.0.1"
"@radix-ui/react-slot@^1.1.0":
version "1.2.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.3.tgz#502d6e354fc847d4169c3bc5f189de777f68cfe1"
integrity sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==
dependencies:
"@radix-ui/react-compose-refs" "1.1.2"
"@radix-ui/react-tabs@1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.0.4.tgz#993608eec55a5d1deddd446fa9978d2bc1053da2"
@@ -3673,6 +3690,21 @@
resolved "https://registry.yarnpkg.com/@signozhq/design-tokens/-/design-tokens-1.1.4.tgz#5d5de5bd9d19b6a3631383db015cc4b70c3f7661"
integrity sha512-ICZz5szxTq8NcKAsk6LP+nSybPyEcyy8eu2zfxlPQCnJ1YjJP1PglaKLlF0N6+D60gAd3yC5he06BqR8/HxjNg==
"@signozhq/table@file:.yalc/@signozhq/table":
version "0.0.0"
dependencies:
"@radix-ui/react-icons" "^1.3.0"
"@radix-ui/react-slot" "^1.1.0"
"@tanstack/react-table" "^8.21.3"
"@tanstack/react-virtual" "^3.13.9"
"@types/lodash-es" "^4.17.12"
class-variance-authority "^0.7.0"
clsx "^2.1.1"
lodash-es "^4.17.21"
lucide-react "^0.445.0"
tailwind-merge "^2.5.2"
tailwindcss-animate "^1.0.7"
"@sinclair/typebox@^0.25.16":
version "0.25.24"
resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz"
@@ -3709,6 +3741,13 @@
dependencies:
"@tanstack/table-core" "8.20.5"
"@tanstack/react-table@^8.21.3":
version "8.21.3"
resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.21.3.tgz#2c38c747a5731c1a07174fda764b9c2b1fb5e91b"
integrity sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==
dependencies:
"@tanstack/table-core" "8.21.3"
"@tanstack/react-virtual@3.11.2":
version "3.11.2"
resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.11.2.tgz#d6b9bd999c181f0a2edce270c87a2febead04322"
@@ -3716,16 +3755,33 @@
dependencies:
"@tanstack/virtual-core" "3.11.2"
"@tanstack/react-virtual@^3.13.9":
version "3.13.12"
resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz#d372dc2783739cc04ec1a728ca8203937687a819"
integrity sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==
dependencies:
"@tanstack/virtual-core" "3.13.12"
"@tanstack/table-core@8.20.5":
version "8.20.5"
resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.20.5.tgz#3974f0b090bed11243d4107283824167a395cf1d"
integrity sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==
"@tanstack/table-core@8.21.3":
version "8.21.3"
resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.21.3.tgz#2977727d8fc8dfa079112d9f4d4c019110f1732c"
integrity sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==
"@tanstack/virtual-core@3.11.2":
version "3.11.2"
resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz#00409e743ac4eea9afe5b7708594d5fcebb00212"
integrity sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==
"@tanstack/virtual-core@3.13.12":
version "3.13.12"
resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz#1dff176df9cc8f93c78c5e46bcea11079b397578"
integrity sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==
"@testing-library/dom@^8.5.0":
version "8.20.0"
resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz"
@@ -4188,6 +4244,13 @@
resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/lodash-es@^4.17.12":
version "4.17.12"
resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b"
integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==
dependencies:
"@types/lodash" "*"
"@types/lodash-es@^4.17.4":
version "4.17.7"
resolved "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.7.tgz"
@@ -6508,6 +6571,13 @@ cjs-module-lexer@^1.0.0:
resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz"
integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
class-variance-authority@^0.7.0:
version "0.7.1"
resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz#4008a798a0e4553a781a57ac5177c9fb5d043787"
integrity sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==
dependencies:
clsx "^2.1.1"
classnames@2.3.2, classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2:
version "2.3.2"
resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz"
@@ -6602,6 +6672,11 @@ clsx@^1.1.1:
resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz"
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
clsx@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
co@^4.6.0:
version "4.6.0"
resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz"
@@ -11897,6 +11972,11 @@ lucide-react@0.498.0:
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.498.0.tgz#3109eac93dfd0c1561db7a5cddfe4b9b20c14315"
integrity sha512-k8IKbvMNV5Dj7CHRrKyIc1wAtmGdEF0r6SCaiGAt5cZ8KnjcEao8mfdydKkWspy65l40MdlcfdK0kT3QrxpnIg==
lucide-react@^0.445.0:
version "0.445.0"
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.445.0.tgz#35c42341e98fbf0475b2a6cf74fd25ef7cbfcd62"
integrity sha512-YrLf3aAHvmd4dZ8ot+mMdNFrFpJD7YRwQ2pUcBhgqbmxtrMP4xDzIorcj+8y+6kpuXBF4JB0NOCTUWIYetJjgA==
lz-string@^1.4.4:
version "1.5.0"
resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz"
@@ -16653,6 +16733,16 @@ table@^6.0.9:
string-width "^4.2.3"
strip-ansi "^6.0.1"
tailwind-merge@^2.5.2:
version "2.6.0"
resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.6.0.tgz#ac5fb7e227910c038d458f396b7400d93a3142d5"
integrity sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==
tailwindcss-animate@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4"
integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==
tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0:
version "2.2.1"
resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz"