mirror of
https://github.com/SigNoz/signoz.git
synced 2026-09-24 12:20:41 +01:00
Compare commits
11 Commits
bottom-str
...
feat/updat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
911ee2912d | ||
|
|
d4a4217865 | ||
|
|
925cbafde6 | ||
|
|
1f57634472 | ||
|
|
662c91884a | ||
|
|
95bbd1b501 | ||
|
|
f0037ae4bd | ||
|
|
8c3688131e | ||
|
|
db286c4f92 | ||
|
|
e14a6add4e | ||
|
|
3e2caa8110 |
11
.github/CODEOWNERS
vendored
11
.github/CODEOWNERS
vendored
@@ -200,15 +200,6 @@ go.mod @therealpandey
|
||||
/frontend/src/container/ListAlertRules/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/TriggeredAlerts/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/AnomalyAlertEvaluationView/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/RoutingPolicies/ @SigNoz/pulse-frontend
|
||||
/frontend/src/components/AlertBreadcrumb/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/EditRules/ @SigNoz/pulse-frontend
|
||||
/frontend/src/components/AlertDetailsFilters/ @SigNoz/pulse-frontend
|
||||
/frontend/src/components/Alerts/ @SigNoz/pulse-frontend
|
||||
/frontend/src/hooks/routingPolicies/ @SigNoz/pulse-frontend
|
||||
/frontend/src/types/api/alerts/ @SigNoz/pulse-frontend
|
||||
/frontend/src/providers/Alert.tsx @SigNoz/pulse-frontend
|
||||
/frontend/src/constants/alerts.ts @SigNoz/pulse-frontend
|
||||
|
||||
## Notification Channels
|
||||
/frontend/src/pages/ChannelsEdit/ @SigNoz/pulse-frontend
|
||||
@@ -216,8 +207,6 @@ go.mod @therealpandey
|
||||
/frontend/src/container/AllAlertChannels/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/CreateAlertChannels/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/EditAlertChannels/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/FormAlertChannels/ @SigNoz/pulse-frontend
|
||||
/frontend/src/hooks/notificationChannels/ @SigNoz/pulse-frontend
|
||||
|
||||
## OpenAPI Schema - Generated
|
||||
/frontend/src/api/generated/services/ @therealpandey @vikrantgupta25 @srikanthccv
|
||||
|
||||
@@ -12,14 +12,13 @@ import { CmdKPalette } from 'components/cmdKPalette/cmdKPalette';
|
||||
import NotFound from 'components/NotFound';
|
||||
import { ShiftHoldOverlayController } from 'components/ShiftOverlay/ShiftHoldOverlayController';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import ROUTES from 'constants/routes';
|
||||
import AppLayout from 'container/AppLayout';
|
||||
import Hex from 'crypto-js/enc-hex';
|
||||
import HmacSHA256 from 'crypto-js/hmac-sha256';
|
||||
import { useIsAIAssistantEnabled } from 'hooks/useIsAIAssistantEnabled';
|
||||
import { useSavedViewEnabled } from 'hooks/useSavedViewEnabled';
|
||||
import { ChatSupportState, useChatSupport } from 'hooks/useChatSupport';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||
import { StatusCodes } from 'http-status-codes';
|
||||
@@ -59,6 +58,7 @@ function App(): JSX.Element {
|
||||
isFetchingActiveLicense,
|
||||
activeLicenseFetchError,
|
||||
userFetchError,
|
||||
featureFlagsFetchError,
|
||||
isLoggedIn: isLoggedInState,
|
||||
featureFlags,
|
||||
org,
|
||||
@@ -66,8 +66,6 @@ function App(): JSX.Element {
|
||||
} = useAppContext();
|
||||
const [routes, setRoutes] = useState<AppRoutes[]>(defaultRoutes);
|
||||
const isAIAssistantEnabled = useIsAIAssistantEnabled();
|
||||
const isSavedViewEnabled = useSavedViewEnabled();
|
||||
const chatSupport = useChatSupport();
|
||||
|
||||
const { hostname } = window.location;
|
||||
const [pathname, setPathname] = useState(history.location.pathname);
|
||||
@@ -255,9 +253,7 @@ function App(): JSX.Element {
|
||||
}, [isDarkMode]);
|
||||
|
||||
useEffect(() => {
|
||||
// The bottom strip carries Support, so the floating bubble goes entirely.
|
||||
if (
|
||||
isSavedViewEnabled ||
|
||||
pathname === ROUTES.ONBOARDING ||
|
||||
pathname.startsWith('/public/dashboard/') ||
|
||||
pathname === '/ai-assistant' ||
|
||||
@@ -267,32 +263,71 @@ function App(): JSX.Element {
|
||||
} else {
|
||||
window.Pylon?.('showChatBubble');
|
||||
}
|
||||
}, [pathname, isSavedViewEnabled]);
|
||||
}, [pathname]);
|
||||
|
||||
// Identity for the Pylon widget. Whether this user gets Pylon at all is
|
||||
// `useChatSupport`'s call — this only fills in who they are.
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
useEffect(() => {
|
||||
if (chatSupport !== ChatSupportState.Pylon) {
|
||||
return;
|
||||
// feature flag shouldn't be loading and featureFlags or fetchError any one of this should be true indicating that req is complete
|
||||
// licenses should also be present. there is no check for licenses for loading and error as that is mandatory if not present then routing
|
||||
// to something went wrong which would ideally need a reload.
|
||||
if (
|
||||
!isFetchingFeatureFlags &&
|
||||
(featureFlags || featureFlagsFetchError) &&
|
||||
activeLicense &&
|
||||
trialInfo
|
||||
) {
|
||||
let isChatSupportEnabled = false;
|
||||
let isPremiumSupportEnabled = false;
|
||||
if (featureFlags && featureFlags.length > 0) {
|
||||
isChatSupportEnabled =
|
||||
featureFlags.find((flag) => flag.name === FeatureKeys.CHAT_SUPPORT)
|
||||
?.active || false;
|
||||
|
||||
isPremiumSupportEnabled =
|
||||
featureFlags.find((flag) => flag.name === FeatureKeys.PREMIUM_SUPPORT)
|
||||
?.active || false;
|
||||
}
|
||||
const showAddCreditCardModal =
|
||||
!isPremiumSupportEnabled && !trialInfo?.trialConvertedToSubscription;
|
||||
|
||||
if (
|
||||
isLoggedInState &&
|
||||
isChatSupportEnabled &&
|
||||
!showAddCreditCardModal &&
|
||||
(isCloudUser || isEnterpriseSelfHostedUser) &&
|
||||
window.signozBootData?.settings?.pylon?.enabled
|
||||
) {
|
||||
const email = user.email || '';
|
||||
const secret = window.signozBootData?.settings?.pylon?.identitySecret || '';
|
||||
let emailHash = '';
|
||||
|
||||
if (email && secret) {
|
||||
emailHash = HmacSHA256(email, Hex.parse(secret)).toString(Hex);
|
||||
}
|
||||
|
||||
window.pylon = {
|
||||
chat_settings: {
|
||||
app_id: window.signozBootData?.settings?.pylon?.appId,
|
||||
email: user.email,
|
||||
name: user.displayName || user.email,
|
||||
email_hash: emailHash,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const email = user.email || '';
|
||||
const secret = window.signozBootData?.settings?.pylon?.identitySecret || '';
|
||||
let emailHash = '';
|
||||
|
||||
if (email && secret) {
|
||||
emailHash = HmacSHA256(email, Hex.parse(secret)).toString(Hex);
|
||||
}
|
||||
|
||||
window.pylon = {
|
||||
chat_settings: {
|
||||
app_id: window.signozBootData?.settings?.pylon?.appId,
|
||||
email: user.email,
|
||||
name: user.displayName || user.email,
|
||||
email_hash: emailHash,
|
||||
},
|
||||
};
|
||||
}, [chatSupport, user]);
|
||||
}, [
|
||||
isLoggedInState,
|
||||
user,
|
||||
pathname,
|
||||
trialInfo?.trialConvertedToSubscription,
|
||||
featureFlags,
|
||||
isFetchingFeatureFlags,
|
||||
featureFlagsFetchError,
|
||||
activeLicense,
|
||||
trialInfo,
|
||||
isCloudUser,
|
||||
isEnterpriseSelfHostedUser,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isFetchingUser && isCloudUser && user && user.email) {
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
import { useMutation } from 'react-query';
|
||||
import { Button, Modal } from 'antd';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { createSubscription } from 'api/generated/services/subscriptions';
|
||||
import type { CreateSubscription201 } from 'api/generated/services/sigNoz.schemas';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import AuthZTooltip from 'lib/authz/components/AuthZTooltip/AuthZTooltip';
|
||||
import { SubscriptionCreatePermission } from 'lib/authz/hooks/useAuthZ/permissions/subscription.permissions';
|
||||
import { CreditCard, X } from '@signozhq/icons';
|
||||
import APIError from 'types/api/error';
|
||||
import { getBaseUrl } from 'utils/basePath';
|
||||
|
||||
interface AddCreditCardModalProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onAddCreditCard?: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shown to trial users who have not added a card, in place of chat support.
|
||||
* Submitting creates the subscription and opens the returned billing URL.
|
||||
*/
|
||||
function AddCreditCardModal({
|
||||
open,
|
||||
onClose,
|
||||
onAddCreditCard,
|
||||
}: AddCreditCardModalProps): JSX.Element {
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
const handleBillingOnSuccess = (data: CreateSubscription201): void => {
|
||||
if (data?.data?.redirectURL) {
|
||||
const newTab = document.createElement('a');
|
||||
newTab.href = data.data.redirectURL;
|
||||
newTab.target = '_blank';
|
||||
newTab.rel = 'noopener noreferrer';
|
||||
newTab.click();
|
||||
}
|
||||
};
|
||||
|
||||
const handleBillingOnError = (error: APIError): void => {
|
||||
notifications.error({
|
||||
message: error.getErrorCode(),
|
||||
description: error.getErrorMessage(),
|
||||
});
|
||||
};
|
||||
|
||||
const { mutate: updateCreditCard, isLoading: isLoadingBilling } = useMutation(
|
||||
createSubscription,
|
||||
{ onSuccess: handleBillingOnSuccess, onError: handleBillingOnError },
|
||||
);
|
||||
|
||||
const handleAddCreditCard = (): void => {
|
||||
onAddCreditCard?.();
|
||||
updateCreditCard({ url: getBaseUrl() });
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className="add-credit-card-modal"
|
||||
title={<span className="title">Add Credit Card for Chat Support</span>}
|
||||
open={open}
|
||||
closable
|
||||
onCancel={onClose}
|
||||
destroyOnClose
|
||||
footer={[
|
||||
<Button
|
||||
key="cancel"
|
||||
onClick={onClose}
|
||||
className="cancel-btn"
|
||||
icon={<X size={16} />}
|
||||
>
|
||||
Cancel
|
||||
</Button>,
|
||||
<AuthZTooltip
|
||||
key="submit"
|
||||
checks={[SubscriptionCreatePermission]}
|
||||
withPortal={false}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<CreditCard size={16} />}
|
||||
size="middle"
|
||||
loading={isLoadingBilling}
|
||||
disabled={isLoadingBilling}
|
||||
onClick={handleAddCreditCard}
|
||||
className="add-credit-card-btn"
|
||||
>
|
||||
Add Credit Card
|
||||
</Button>
|
||||
</AuthZTooltip>,
|
||||
]}
|
||||
>
|
||||
<Typography.Text className="add-credit-card-text">
|
||||
You're currently on <span className="highlight-text">Trial plan</span>.
|
||||
Add a credit card to access SigNoz chat support to your workspace.
|
||||
</Typography.Text>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
AddCreditCardModal.defaultProps = { onAddCreditCard: undefined };
|
||||
|
||||
export default AddCreditCardModal;
|
||||
@@ -26,6 +26,7 @@ function BreadcrumbItem({
|
||||
|
||||
return (
|
||||
<Button
|
||||
size="md"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
className={styles.item}
|
||||
|
||||
@@ -34,21 +34,23 @@ function ErrorEmptyState({
|
||||
</div>
|
||||
<div className={styles.actions}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
prefix={<LifeBuoy size={14} />}
|
||||
onClick={onContactSupport}
|
||||
data-testid="error-contact-support-button"
|
||||
testId="error-contact-support-button"
|
||||
>
|
||||
Contact Support
|
||||
</Button>
|
||||
{onRefresh && (
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<RefreshCw size={14} />}
|
||||
onClick={onRefresh}
|
||||
data-testid="error-refresh-button"
|
||||
testId="error-refresh-button"
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import { Copy } from '@signozhq/icons';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import {
|
||||
TooltipContent,
|
||||
TooltipRoot,
|
||||
TooltipTrigger,
|
||||
} from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
|
||||
@@ -16,20 +12,7 @@ import { BADGE_GAP, estimateBadgeWidth, OVERFLOW_BADGE_WIDTH } from './utils';
|
||||
|
||||
export interface LabelColumnProps {
|
||||
labels: string[];
|
||||
color?:
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| 'success'
|
||||
| 'error'
|
||||
| 'warning'
|
||||
| 'robin'
|
||||
| 'forest'
|
||||
| 'amber'
|
||||
| 'sienna'
|
||||
| 'cherry'
|
||||
| 'sakura'
|
||||
| 'aqua'
|
||||
| 'vanilla';
|
||||
color?: BadgeColorType;
|
||||
value?: { [key: string]: string };
|
||||
}
|
||||
|
||||
@@ -104,20 +87,10 @@ function LabelColumn({
|
||||
<LabelTag key={label} label={label} color={color} value={value?.[label]} />
|
||||
))}
|
||||
{remainingLabels.length > 0 && (
|
||||
<TooltipRoot>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Badge
|
||||
color={color}
|
||||
className={styles.overflowBadge}
|
||||
variant="outline"
|
||||
data-testid="label-overflow-badge"
|
||||
>
|
||||
+{remainingLabels.length}
|
||||
</Badge>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom" align="end">
|
||||
<Tooltip
|
||||
side="bottom"
|
||||
align="end"
|
||||
title={
|
||||
<div className={styles.tooltipContent}>
|
||||
<span>
|
||||
{remainingLabels
|
||||
@@ -140,8 +113,19 @@ function LabelColumn({
|
||||
<Copy size={12} />
|
||||
</button>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</TooltipRoot>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<Badge
|
||||
color={color}
|
||||
className={styles.overflowBadge}
|
||||
variant="outlined"
|
||||
testId="label-overflow-badge"
|
||||
>
|
||||
+{remainingLabels.length}
|
||||
</Badge>
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,31 +1,14 @@
|
||||
import { Copy } from '@signozhq/icons';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import {
|
||||
TooltipContent,
|
||||
TooltipRoot,
|
||||
TooltipTrigger,
|
||||
} from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
|
||||
import styles from './LabelTag.module.scss';
|
||||
|
||||
export interface LabelTagProps {
|
||||
label: string;
|
||||
color?:
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| 'success'
|
||||
| 'error'
|
||||
| 'warning'
|
||||
| 'robin'
|
||||
| 'forest'
|
||||
| 'amber'
|
||||
| 'sienna'
|
||||
| 'cherry'
|
||||
| 'sakura'
|
||||
| 'aqua'
|
||||
| 'vanilla';
|
||||
color?: BadgeColorType;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
@@ -41,20 +24,8 @@ function LabelTag({ label, value, color }: LabelTagProps): JSX.Element {
|
||||
};
|
||||
|
||||
return (
|
||||
<TooltipRoot>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Badge
|
||||
color={color}
|
||||
className={styles.labelBadge}
|
||||
variant="outline"
|
||||
data-testid={`label-tag-${label}`}
|
||||
>
|
||||
<span className={styles.labelValue}>{displayText}</span>
|
||||
</Badge>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<Tooltip
|
||||
title={
|
||||
<div className={styles.tooltipContent}>
|
||||
<span>{displayText}</span>
|
||||
<button
|
||||
@@ -66,8 +37,19 @@ function LabelTag({ label, value, color }: LabelTagProps): JSX.Element {
|
||||
<Copy size={12} />
|
||||
</button>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</TooltipRoot>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<Badge
|
||||
color={color ?? 'secondary'}
|
||||
className={styles.labelBadge}
|
||||
variant="outlined"
|
||||
testId={`label-tag-${label}`}
|
||||
>
|
||||
<span className={styles.labelValue}>{displayText}</span>
|
||||
</Badge>
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,21 +30,23 @@ function NoResultsEmptyState({
|
||||
<div className={styles.actions}>
|
||||
{onClear && (
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={onClear}
|
||||
data-testid="no-results-clear-button"
|
||||
testId="no-results-clear-button"
|
||||
>
|
||||
{clearButtonText}
|
||||
</Button>
|
||||
)}
|
||||
{onRefresh && (
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<RefreshCw size={14} />}
|
||||
onClick={onRefresh}
|
||||
data-testid="no-results-refresh-button"
|
||||
testId="no-results-refresh-button"
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BadgeColor } from '@signozhq/ui/badge';
|
||||
import type { BadgeColorType } from '@signozhq/ui/badge';
|
||||
|
||||
export const STATE_ORDER = ['firing', 'pending', 'inactive', 'disabled'];
|
||||
export const SEVERITY_ORDER = ['critical', 'error', 'warning', 'info'];
|
||||
@@ -24,9 +24,9 @@ export const SEVERITY_COLORS: Record<string, string> = {
|
||||
info: 'var(--bg-robin-500)',
|
||||
};
|
||||
|
||||
export const SEVERITY_BADGE_COLORS: Record<string, BadgeColor> = {
|
||||
critical: 'error',
|
||||
error: 'error',
|
||||
export const SEVERITY_BADGE_COLORS: Record<string, BadgeColorType> = {
|
||||
critical: 'danger',
|
||||
error: 'danger',
|
||||
warning: 'warning',
|
||||
info: 'primary',
|
||||
};
|
||||
|
||||
@@ -22,11 +22,12 @@ function AuthHeader(): JSX.Element {
|
||||
<span className="auth-header-logo-text">SigNoz</span>
|
||||
</div>
|
||||
<Button
|
||||
size="md"
|
||||
className="auth-header-help-button"
|
||||
prefix={<LifeBuoy size={12} />}
|
||||
onClick={handleGetHelp}
|
||||
variant="solid"
|
||||
color="none"
|
||||
color="secondary"
|
||||
>
|
||||
Get Help
|
||||
</Button>
|
||||
|
||||
@@ -48,14 +48,22 @@ function Badges({ tags, setTags }: AddTagsProps): JSX.Element {
|
||||
<div className="tags-container">
|
||||
{tags.map<React.ReactNode>((tag) => (
|
||||
<Badge
|
||||
variant="solid"
|
||||
key={tag}
|
||||
color="vanilla"
|
||||
color="secondary"
|
||||
style={{ userSelect: 'none' }}
|
||||
closable
|
||||
onClose={(e): void => {
|
||||
e.preventDefault();
|
||||
handleClose(tag);
|
||||
}}
|
||||
suffix={
|
||||
<button
|
||||
type="button"
|
||||
aria-label={`Remove ${tag}`}
|
||||
onClick={(e): void => {
|
||||
e.preventDefault();
|
||||
handleClose(tag);
|
||||
}}
|
||||
>
|
||||
<X size={12} />
|
||||
</button>
|
||||
}
|
||||
>
|
||||
{tag}
|
||||
</Badge>
|
||||
|
||||
@@ -1,15 +1,63 @@
|
||||
import { useState } from 'react';
|
||||
import { useMutation } from 'react-query';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { Button } from 'antd';
|
||||
import AddCreditCardModal from 'components/AddCreditCardModal/AddCreditCardModal';
|
||||
import { Button, Modal } from 'antd';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { MessageSquareText } from '@signozhq/icons';
|
||||
import { createSubscription } from 'api/generated/services/subscriptions';
|
||||
import type { CreateSubscription201 } from 'api/generated/services/sigNoz.schemas';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import AuthZTooltip from 'lib/authz/components/AuthZTooltip/AuthZTooltip';
|
||||
import { SubscriptionCreatePermission } from 'lib/authz/hooks/useAuthZ/permissions/subscription.permissions';
|
||||
import { CreditCard, MessageSquareText, X } from '@signozhq/icons';
|
||||
import APIError from 'types/api/error';
|
||||
import { getBaseUrl } from 'utils/basePath';
|
||||
|
||||
export default function ChatSupportGateway(): JSX.Element {
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const handleBillingOnSuccess = (data: CreateSubscription201): void => {
|
||||
if (data?.data?.redirectURL) {
|
||||
const newTab = document.createElement('a');
|
||||
newTab.href = data.data.redirectURL;
|
||||
newTab.target = '_blank';
|
||||
newTab.rel = 'noopener noreferrer';
|
||||
newTab.click();
|
||||
}
|
||||
};
|
||||
|
||||
const handleBillingOnError = (error: APIError): void => {
|
||||
notifications.error({
|
||||
message: error.getErrorCode(),
|
||||
description: error.getErrorMessage(),
|
||||
});
|
||||
};
|
||||
|
||||
const { mutate: updateCreditCard, isLoading: isLoadingBilling } = useMutation(
|
||||
createSubscription,
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
handleBillingOnSuccess(data);
|
||||
},
|
||||
onError: handleBillingOnError,
|
||||
},
|
||||
);
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const handleAddCreditCard = (): void => {
|
||||
logEvent('Add Credit card modal: Clicked', {
|
||||
source: `chat support icon`,
|
||||
page: pathname,
|
||||
});
|
||||
|
||||
updateCreditCard({
|
||||
url: getBaseUrl(),
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="chat-support-gateway">
|
||||
@@ -28,16 +76,47 @@ export default function ChatSupportGateway(): JSX.Element {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<AddCreditCardModal
|
||||
{/* Add Credit Card Modal */}
|
||||
<Modal
|
||||
className="add-credit-card-modal"
|
||||
title={<span className="title">Add Credit Card for Chat Support</span>}
|
||||
open={isAddCreditCardModalOpen}
|
||||
onClose={(): void => setIsAddCreditCardModalOpen(false)}
|
||||
onAddCreditCard={(): void => {
|
||||
logEvent('Add Credit card modal: Clicked', {
|
||||
source: `chat support icon`,
|
||||
page: pathname,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
closable
|
||||
onCancel={(): void => setIsAddCreditCardModalOpen(false)}
|
||||
destroyOnClose
|
||||
footer={[
|
||||
<Button
|
||||
key="cancel"
|
||||
onClick={(): void => setIsAddCreditCardModalOpen(false)}
|
||||
className="cancel-btn"
|
||||
icon={<X size={16} />}
|
||||
>
|
||||
Cancel
|
||||
</Button>,
|
||||
<AuthZTooltip
|
||||
key="submit"
|
||||
checks={[SubscriptionCreatePermission]}
|
||||
withPortal={false}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<CreditCard size={16} />}
|
||||
size="middle"
|
||||
loading={isLoadingBilling}
|
||||
disabled={isLoadingBilling}
|
||||
onClick={handleAddCreditCard}
|
||||
className="add-credit-card-btn"
|
||||
>
|
||||
Add Credit Card
|
||||
</Button>
|
||||
</AuthZTooltip>,
|
||||
]}
|
||||
>
|
||||
<Typography.Text className="add-credit-card-text">
|
||||
You're currently on <span className="highlight-text">Trial plan</span>
|
||||
. Add a credit card to access SigNoz chat support to your workspace.
|
||||
</Typography.Text>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
CloudintegrationtypesCollectedMetricDTO,
|
||||
} from 'api/generated/services/sigNoz.schemas';
|
||||
import { BarChart, Info, ScrollText } from '@signozhq/icons';
|
||||
import { TooltipProvider, TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { TooltipProvider, Tooltip } from '@signozhq/ui/tooltip';
|
||||
|
||||
import './CloudServiceDataCollected.styles.scss';
|
||||
|
||||
@@ -89,12 +89,10 @@ function CloudServiceDataCollected({
|
||||
Metrics
|
||||
{metricsInfoTooltip && (
|
||||
<TooltipProvider>
|
||||
<TooltipSimple
|
||||
<Tooltip
|
||||
className={'cloud-service-data-collected-table-tooltip'}
|
||||
title={metricsInfoTooltip}
|
||||
side="top"
|
||||
tooltipContentProps={{
|
||||
className: 'cloud-service-data-collected-table-tooltip',
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="cloud-service-data-collected-table-heading-info"
|
||||
@@ -103,7 +101,7 @@ function CloudServiceDataCollected({
|
||||
>
|
||||
<Info size={12} />
|
||||
</span>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useMemo, useState } from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { Check, Copy } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import SyntaxHighlighter, {
|
||||
a11yDark,
|
||||
} from 'components/MarkdownRenderer/syntaxHighlighter';
|
||||
@@ -52,16 +53,19 @@ function CodeBlock({
|
||||
data-testid="code-block-container"
|
||||
>
|
||||
{showCopyButton ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
onClick={handleCopy}
|
||||
prefix={isCopied ? <Check size={14} /> : <Copy size={14} />}
|
||||
aria-label="Copy code"
|
||||
title={isCopied ? 'Copied' : 'Copy'}
|
||||
style={{ position: 'absolute', right: 8, top: 8, zIndex: 1 }}
|
||||
/>
|
||||
<Tooltip title={isCopied ? 'Copied' : 'Copy'}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
icon
|
||||
onClick={handleCopy}
|
||||
aria-label="Copy code"
|
||||
style={{ position: 'absolute', right: 8, top: 8, zIndex: 1 }}
|
||||
>
|
||||
{isCopied ? <Check size={14} /> : <Copy size={14} />}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
<SyntaxHighlighter
|
||||
style={a11yDark}
|
||||
|
||||
@@ -134,26 +134,33 @@ function CreateServiceAccountModal(): JSX.Element {
|
||||
|
||||
<DialogFooter className="create-sa-modal__footer">
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
onClick={handleClose}
|
||||
data-testid="create-sa-cancel-btn"
|
||||
testId="create-sa-cancel-btn"
|
||||
>
|
||||
<X size={12} />
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<AuthZButton
|
||||
size="md"
|
||||
checks={[SACreatePermission]}
|
||||
withPortal={false}
|
||||
type="submit"
|
||||
form="create-sa-form"
|
||||
type="button"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
loading={isSubmitting}
|
||||
disabled={!isValid}
|
||||
data-testid="create-sa-submit-btn"
|
||||
testId="create-sa-submit-btn"
|
||||
onClick={(): void => {
|
||||
const form = document.getElementById('create-sa-form');
|
||||
if (form instanceof HTMLFormElement) {
|
||||
form.requestSubmit();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Create Service Account
|
||||
</AuthZButton>
|
||||
|
||||
@@ -656,14 +656,19 @@ function CustomTimePicker({
|
||||
}
|
||||
>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
size="md"
|
||||
className="zoom-out-btn"
|
||||
onClick={handleZoomOut}
|
||||
disabled={zoomOutDisabled}
|
||||
data-testid="zoom-out-btn"
|
||||
prefix={<ZoomOut size={14} />}
|
||||
testId="zoom-out-btn"
|
||||
icon
|
||||
aria-label="Zoom out"
|
||||
variant="solid"
|
||||
color="none"
|
||||
/>
|
||||
color="secondary"
|
||||
>
|
||||
<ZoomOut size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -27,12 +27,14 @@ function DetailsHeader({
|
||||
const closeButton = (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={onClose}
|
||||
aria-label="Close"
|
||||
prefix={<X size={14} />}
|
||||
></Button>
|
||||
>
|
||||
<X size={14} />
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { Button, Popover, Tooltip } from 'antd';
|
||||
import { RadioGroup, RadioGroupItem } from '@signozhq/ui/radio-group';
|
||||
import { RadioGroup } from '@signozhq/ui/radio-group';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { TelemetryFieldKey } from 'api/v5/v5';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
@@ -68,10 +68,15 @@ export default function DownloadOptionsMenu({
|
||||
>
|
||||
<div className="export-format">
|
||||
<Typography.Text className="title">FORMAT</Typography.Text>
|
||||
<RadioGroup value={exportFormat} onChange={setExportFormat}>
|
||||
<RadioGroupItem value={DownloadFormats.CSV}>csv</RadioGroupItem>
|
||||
<RadioGroupItem value={DownloadFormats.JSONL}>jsonl</RadioGroupItem>
|
||||
</RadioGroup>
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
value={exportFormat}
|
||||
onChange={setExportFormat}
|
||||
items={[
|
||||
{ value: DownloadFormats.CSV, label: 'csv' },
|
||||
{ value: DownloadFormats.JSONL, label: 'jsonl' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="horizontal-line" />
|
||||
@@ -79,19 +84,15 @@ export default function DownloadOptionsMenu({
|
||||
<div className="row-limit">
|
||||
<Typography.Text className="title">Number of Rows</Typography.Text>
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
value={String(rowLimit)}
|
||||
onChange={(value): void => setRowLimit(Number(value))}
|
||||
>
|
||||
<RadioGroupItem value={String(DownloadRowCounts.TEN_K)}>
|
||||
10k
|
||||
</RadioGroupItem>
|
||||
<RadioGroupItem value={String(DownloadRowCounts.THIRTY_K)}>
|
||||
30k
|
||||
</RadioGroupItem>
|
||||
<RadioGroupItem value={String(DownloadRowCounts.FIFTY_K)}>
|
||||
50k
|
||||
</RadioGroupItem>
|
||||
</RadioGroup>
|
||||
items={[
|
||||
{ value: String(DownloadRowCounts.TEN_K), label: '10k' },
|
||||
{ value: String(DownloadRowCounts.THIRTY_K), label: '30k' },
|
||||
{ value: String(DownloadRowCounts.FIFTY_K), label: '50k' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{dataSource !== DataSource.TRACES && (
|
||||
@@ -100,12 +101,15 @@ export default function DownloadOptionsMenu({
|
||||
|
||||
<div className="columns-scope">
|
||||
<Typography.Text className="title">Columns</Typography.Text>
|
||||
<RadioGroup value={columnsScope} onChange={setColumnsScope}>
|
||||
<RadioGroupItem value={DownloadColumnsScopes.ALL}>All</RadioGroupItem>
|
||||
<RadioGroupItem value={DownloadColumnsScopes.SELECTED}>
|
||||
Selected
|
||||
</RadioGroupItem>
|
||||
</RadioGroup>
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
value={columnsScope}
|
||||
onChange={setColumnsScope}
|
||||
items={[
|
||||
{ value: DownloadColumnsScopes.ALL, label: 'All' },
|
||||
{ value: DownloadColumnsScopes.SELECTED, label: 'Selected' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
221
frontend/src/components/DropdownMenu/DropdownMenuSimple.tsx
Normal file
221
frontend/src/components/DropdownMenu/DropdownMenuSimple.tsx
Normal file
@@ -0,0 +1,221 @@
|
||||
import { isValidElement, type ReactElement, type ReactNode } from 'react';
|
||||
import {
|
||||
Dropdown,
|
||||
type DropdownItemType,
|
||||
type DropdownProps,
|
||||
} from '@signozhq/ui/dropdown';
|
||||
|
||||
/**
|
||||
* The menu-item shape SigNoz built against `@signozhq/ui/dropdown-menu`.
|
||||
* `Dropdown` only accepts its own `items` array, so this module maps the old
|
||||
* rows onto that array and renders them.
|
||||
*/
|
||||
export type BaseMenuItem = {
|
||||
key?: string;
|
||||
label?: ReactNode;
|
||||
disabled?: boolean;
|
||||
disabledTooltip?: ReactNode;
|
||||
icon?: ReactNode;
|
||||
rightIcon?: ReactNode;
|
||||
shortcut?: ReactNode;
|
||||
onClick?: (info: { key: string; keyPath: string[] }) => void;
|
||||
danger?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export type MenuGroup = BaseMenuItem & {
|
||||
type: 'group';
|
||||
label: string;
|
||||
children: MenuItem[];
|
||||
};
|
||||
|
||||
export type MenuDivider = {
|
||||
type: 'divider';
|
||||
key?: string;
|
||||
};
|
||||
|
||||
export type SubMenuItem = BaseMenuItem & {
|
||||
children: MenuItem[];
|
||||
};
|
||||
|
||||
export type CheckboxMenuItem = BaseMenuItem & {
|
||||
type: 'checkbox';
|
||||
key: string;
|
||||
label: ReactNode;
|
||||
checked?: boolean;
|
||||
onCheckedChange?: (checked: boolean) => void;
|
||||
};
|
||||
|
||||
export type RadioMenuItem = {
|
||||
type: 'radio';
|
||||
key: string;
|
||||
label: ReactNode;
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export type RadioGroupMenuItem = {
|
||||
type: 'radio-group';
|
||||
key?: string;
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
children: RadioMenuItem[];
|
||||
};
|
||||
|
||||
export type MenuItem =
|
||||
| MenuGroup
|
||||
| MenuDivider
|
||||
| CheckboxMenuItem
|
||||
| RadioGroupMenuItem
|
||||
| (SubMenuItem & { type?: never })
|
||||
| (BaseMenuItem & { type?: never; children?: never });
|
||||
|
||||
export type MenuProps = {
|
||||
items: MenuItem[];
|
||||
search?: {
|
||||
placeholder?: string;
|
||||
onSearchChange?: (value: string) => void;
|
||||
};
|
||||
loading?: boolean | { text?: string };
|
||||
};
|
||||
|
||||
type Align = DropdownProps['align'];
|
||||
type Side = DropdownProps['side'];
|
||||
|
||||
function elementOf(node: ReactNode): ReactElement | undefined {
|
||||
return isValidElement(node) ? node : undefined;
|
||||
}
|
||||
|
||||
function disabledFields(item: {
|
||||
disabled?: boolean;
|
||||
disabledTooltip?: ReactNode;
|
||||
}): { disabled: boolean; disabledTooltip: ReactNode } | Record<string, never> {
|
||||
if (item.disabled === undefined && item.disabledTooltip === undefined) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
disabled: Boolean(item.disabled),
|
||||
disabledTooltip: item.disabledTooltip,
|
||||
};
|
||||
}
|
||||
|
||||
function mapItem(item: MenuItem, index: number): DropdownItemType {
|
||||
if ('type' in item && item.type === 'divider') {
|
||||
return { type: 'separator', value: item.key ?? `separator-${index}` };
|
||||
}
|
||||
|
||||
if ('type' in item && item.type === 'group') {
|
||||
return {
|
||||
type: 'group',
|
||||
value: item.key ?? `group-${index}`,
|
||||
label: item.label,
|
||||
items: item.children.map((child, childIndex) => mapItem(child, childIndex)),
|
||||
} as DropdownItemType;
|
||||
}
|
||||
|
||||
if ('type' in item && item.type === 'checkbox') {
|
||||
return {
|
||||
type: 'checkbox',
|
||||
name: item.key,
|
||||
label: item.label,
|
||||
value: item.checked,
|
||||
onChange: item.onCheckedChange,
|
||||
prefix: elementOf(item.icon),
|
||||
...disabledFields(item),
|
||||
};
|
||||
}
|
||||
|
||||
if ('type' in item && item.type === 'radio-group') {
|
||||
return {
|
||||
type: 'radio-group',
|
||||
name: item.key ?? `radio-${index}`,
|
||||
value: item.value,
|
||||
onChange: item.onChange,
|
||||
items: item.children.map((child) => ({
|
||||
value: child.value,
|
||||
label: child.label,
|
||||
...disabledFields(child),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
if ('children' in item && item.children) {
|
||||
const key = item.key ?? `submenu-${index}`;
|
||||
return {
|
||||
type: 'submenu',
|
||||
value: key,
|
||||
label: item.label ?? '',
|
||||
prefix: elementOf(item.icon),
|
||||
items: item.children.map((child, childIndex) => mapItem(child, childIndex)),
|
||||
...disabledFields(item),
|
||||
} as DropdownItemType;
|
||||
}
|
||||
|
||||
const key = item.key ?? `item-${index}`;
|
||||
const shortcut = 'shortcut' in item ? item.shortcut : undefined;
|
||||
const suffix = elementOf('rightIcon' in item ? item.rightIcon : undefined);
|
||||
return {
|
||||
type: 'item',
|
||||
value: key,
|
||||
label: item.label ?? '',
|
||||
danger: 'danger' in item ? item.danger : undefined,
|
||||
prefix: elementOf('icon' in item ? item.icon : undefined),
|
||||
...(shortcut != null ? { shortcut } : { suffix }),
|
||||
onClick:
|
||||
'onClick' in item && item.onClick
|
||||
? (): void => {
|
||||
item.onClick?.({ key, keyPath: [key] });
|
||||
}
|
||||
: undefined,
|
||||
...disabledFields(item),
|
||||
};
|
||||
}
|
||||
|
||||
interface DropdownMenuSimpleProps {
|
||||
menu: MenuProps;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
align?: Align;
|
||||
side?: Side;
|
||||
testId?: string;
|
||||
nativeButton?: boolean;
|
||||
}
|
||||
|
||||
export function DropdownMenuSimple({
|
||||
menu,
|
||||
children,
|
||||
className,
|
||||
align = 'end',
|
||||
side = 'bottom',
|
||||
testId,
|
||||
nativeButton = true,
|
||||
}: DropdownMenuSimpleProps): JSX.Element {
|
||||
const loading = menu.loading;
|
||||
const loadingText = typeof loading === 'object' ? loading.text : undefined;
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
items={menu.items.map(mapItem) as DropdownItemType[]}
|
||||
nativeButton={nativeButton}
|
||||
align={align}
|
||||
side={side}
|
||||
className={className}
|
||||
testId={testId}
|
||||
loading={Boolean(loading)}
|
||||
loadingContent={loadingText}
|
||||
searchInputProps={
|
||||
menu.search
|
||||
? {
|
||||
placeholder: menu.search.placeholder,
|
||||
onChange: menu.search.onSearchChange,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
export default DropdownMenuSimple;
|
||||
@@ -38,13 +38,15 @@ function DeleteMemberDialog({
|
||||
|
||||
const footer = (
|
||||
<>
|
||||
<Button variant="solid" color="secondary" onClick={onClose}>
|
||||
<Button size="md" variant="solid" color="secondary" onClick={onClose}>
|
||||
<X size={12} />
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="destructive"
|
||||
color="danger"
|
||||
disabled={isDeleting}
|
||||
onClick={onConfirm}
|
||||
loading={isDeleting}
|
||||
|
||||
@@ -519,7 +519,7 @@ function EditMemberDrawer({
|
||||
localRoles.map((roleId) => {
|
||||
const role = availableRoles.find((r) => r.id === roleId);
|
||||
return (
|
||||
<Badge key={roleId} color="vanilla">
|
||||
<Badge variant="solid" key={roleId} color="secondary">
|
||||
{role?.name ?? roleId}
|
||||
</Badge>
|
||||
);
|
||||
@@ -559,15 +559,15 @@ function EditMemberDrawer({
|
||||
<div className="edit-member-drawer__meta-item">
|
||||
<span className="edit-member-drawer__meta-label">Status</span>
|
||||
{member?.status === MemberStatus.Active ? (
|
||||
<Badge color="forest" variant="outline">
|
||||
<Badge color="success" variant="outlined">
|
||||
ACTIVE
|
||||
</Badge>
|
||||
) : member?.status === MemberStatus.Deleted ? (
|
||||
<Badge color="cherry" variant="outline">
|
||||
<Badge color="danger" variant="outlined">
|
||||
DELETED
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge color="amber" variant="outline">
|
||||
<Badge color="warning" variant="outlined">
|
||||
INVITED
|
||||
</Badge>
|
||||
)}
|
||||
@@ -575,12 +575,16 @@ function EditMemberDrawer({
|
||||
|
||||
<div className="edit-member-drawer__meta-item">
|
||||
<span className="edit-member-drawer__meta-label">{joinedOnLabel}</span>
|
||||
<Badge color="vanilla">{formatTimestamp(member?.joinedOn)}</Badge>
|
||||
<Badge variant="solid" color="secondary">
|
||||
{formatTimestamp(member?.joinedOn)}
|
||||
</Badge>
|
||||
</div>
|
||||
{!isInvited && (
|
||||
<div className="edit-member-drawer__meta-item">
|
||||
<span className="edit-member-drawer__meta-label">Last Modified</span>
|
||||
<Badge color="vanilla">{formatTimestamp(member?.updatedAt)}</Badge>
|
||||
<Badge variant="solid" color="secondary">
|
||||
{formatTimestamp(member?.updatedAt)}
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -614,10 +618,12 @@ function EditMemberDrawer({
|
||||
<Tooltip title={getDeleteTooltip(isRootUser, isSelf)}>
|
||||
<span className="edit-member-drawer__tooltip-wrapper">
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
size="md"
|
||||
onClick={(): void => setShowDeleteConfirm(true)}
|
||||
disabled={isRootUser || isSelf}
|
||||
variant="link"
|
||||
color="destructive"
|
||||
color="danger"
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
{isInvited ? 'Revoke Invite' : 'Delete Member'}
|
||||
@@ -629,6 +635,8 @@ function EditMemberDrawer({
|
||||
<Tooltip title={isRootUser ? ROOT_USER_TOOLTIP : undefined}>
|
||||
<span className="edit-member-drawer__tooltip-wrapper">
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
size="md"
|
||||
onClick={handleGenerateResetLink}
|
||||
disabled={isGeneratingLink || isRootUser || isLoadingTokenStatus}
|
||||
variant="link"
|
||||
@@ -651,12 +659,19 @@ function EditMemberDrawer({
|
||||
</div>
|
||||
|
||||
<div className="edit-member-drawer__footer-right">
|
||||
<Button variant="outlined" color="secondary" onClick={handleClose}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<X size={14} />
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
disabled={!isDirty || isSaving || isRootUser}
|
||||
|
||||
@@ -45,6 +45,7 @@ function ResetLinkDialog({
|
||||
<span className="reset-link-dialog__link-text">{resetLink}</span>
|
||||
</div>
|
||||
<Button
|
||||
size="md"
|
||||
variant="link"
|
||||
color="secondary"
|
||||
onClick={onCopy}
|
||||
|
||||
@@ -53,7 +53,7 @@ function ErrorModal({
|
||||
onClick={(): void => setVisible(true)}
|
||||
onKeyDown={undefined}
|
||||
>
|
||||
<Badge color="error">
|
||||
<Badge variant="solid" color="danger">
|
||||
<CircleAlert size={14} color={Color.BG_CHERRY_500} /> error
|
||||
</Badge>
|
||||
</span>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { useState } from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { Button, Col, Popover, Row, Select, Space } from 'antd';
|
||||
import { DropdownMenuSimple, type MenuProps } from '@signozhq/ui/dropdown-menu';
|
||||
import {
|
||||
DropdownMenuSimple,
|
||||
type MenuProps,
|
||||
} from 'components/DropdownMenu/DropdownMenuSimple';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import axios from 'axios';
|
||||
import TextToolTip from 'components/TextToolTip';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Download } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@signozhq/ui/popover';
|
||||
import { RadioGroup, RadioGroupItem } from '@signozhq/ui/radio-group';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { RadioGroup } from '@signozhq/ui/radio-group';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import {
|
||||
ClientExportData,
|
||||
@@ -51,31 +51,40 @@ export default function ExportMenu({
|
||||
|
||||
return (
|
||||
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
|
||||
<TooltipSimple title="Download">
|
||||
<Tooltip title="Download">
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
aria-label="Download"
|
||||
data-testid={`export-menu-${dataSource}`}
|
||||
testId={`export-menu-${dataSource}`}
|
||||
disabled={isExporting}
|
||||
loading={isExporting}
|
||||
>
|
||||
<Download size={14} />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
<PopoverContent align="end" className="export-menu-popover">
|
||||
<div className="export-format">
|
||||
<Typography.Text className="title">FORMAT</Typography.Text>
|
||||
<RadioGroup value={exportFormat} onChange={setExportFormat}>
|
||||
<RadioGroupItem value={ExportFormat.Csv}>csv</RadioGroupItem>
|
||||
<RadioGroupItem value={ExportFormat.Jsonl}>jsonl</RadioGroupItem>
|
||||
</RadioGroup>
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
value={exportFormat}
|
||||
onChange={setExportFormat}
|
||||
items={[
|
||||
{ value: ExportFormat.Csv, label: 'csv' },
|
||||
{ value: ExportFormat.Jsonl, label: 'jsonl' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
className="export-button"
|
||||
|
||||
@@ -58,8 +58,8 @@ function SortableField({
|
||||
{!isRequired && (
|
||||
<Button
|
||||
className={cx(styles.removeBtn, 'periscope-btn')}
|
||||
variant="outlined"
|
||||
color="destructive"
|
||||
variant="solid"
|
||||
color="danger"
|
||||
size="sm"
|
||||
onClick={(): void => onRemove(field)}
|
||||
>
|
||||
|
||||
@@ -173,6 +173,7 @@ function FieldsSelectorContent({
|
||||
{hasUnsavedChanges && (
|
||||
<div className={styles.footer}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={handleDiscard}
|
||||
@@ -181,6 +182,7 @@ function FieldsSelectorContent({
|
||||
Discard
|
||||
</Button>
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
onClick={handleSave}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { Button, Input } from 'antd';
|
||||
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
|
||||
import { ToggleGroup } from '@signozhq/ui/toggle-group';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { handleContactSupport } from 'container/Integrations/utils';
|
||||
@@ -102,7 +102,10 @@ function FeedbackModal({ onClose }: { onClose: () => void }): JSX.Element {
|
||||
return (
|
||||
<div className="feedback-modal-container">
|
||||
<div className="feedback-modal-header">
|
||||
<ToggleGroupSimple
|
||||
<ToggleGroup
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
type="single"
|
||||
value={activeTab}
|
||||
className="feedback-modal-tabs"
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { Dot } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import Noz from 'components/Noz/Noz';
|
||||
import { NOZ_TOOLTIP_TITLE } from 'components/Noz/Noz.constants';
|
||||
import { Popover } from 'antd';
|
||||
@@ -113,8 +113,9 @@ function HeaderRightSection({
|
||||
</span>
|
||||
) : null}
|
||||
|
||||
<TooltipSimple title={NOZ_TOOLTIP_TITLE}>
|
||||
<Tooltip title={NOZ_TOOLTIP_TITLE}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
className="noz-wave"
|
||||
@@ -130,7 +131,7 @@ function HeaderRightSection({
|
||||
>
|
||||
<Typography.Text>Noz</Typography.Text>
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -147,13 +148,16 @@ function HeaderRightSection({
|
||||
onOpenChange={handleOpenFeedbackModalChange}
|
||||
>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
className="share-feedback-btn"
|
||||
aria-label="Feedback"
|
||||
prefix={<SquarePen size={14} />}
|
||||
onClick={handleOpenFeedbackModal}
|
||||
/>
|
||||
>
|
||||
<SquarePen size={14} />
|
||||
</Button>
|
||||
</Popover>
|
||||
)}
|
||||
|
||||
@@ -170,16 +174,19 @@ function HeaderRightSection({
|
||||
onOpenChange={handleOpenAnnouncementsModalChange}
|
||||
>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
aria-label="Announcements"
|
||||
prefix={<Inbox size={14} />}
|
||||
onClick={(): void => {
|
||||
logEvent('Announcements: Clicked', {
|
||||
page: location.pathname,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<Inbox size={14} />
|
||||
</Button>
|
||||
</Popover>
|
||||
)}
|
||||
|
||||
@@ -196,12 +203,15 @@ function HeaderRightSection({
|
||||
onOpenChange={handleOpenShareURLModalChange}
|
||||
>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
aria-label="Share"
|
||||
prefix={<Globe size={14} />}
|
||||
onClick={handleOpenShareURLModal}
|
||||
/>
|
||||
>
|
||||
<Globe size={14} />
|
||||
</Button>
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -149,6 +149,9 @@ function ShareURLModal({ extraOption }: ShareURLModalProps): JSX.Element {
|
||||
<Info size={14} color={Color.BG_AMBER_600} />
|
||||
)}
|
||||
<Switch
|
||||
color="primary"
|
||||
textPlacement="right"
|
||||
disabledTooltip={undefined}
|
||||
value={enableAbsoluteTime}
|
||||
disabled={!isValidateRelativeTime}
|
||||
onChange={(): void => {
|
||||
@@ -173,6 +176,8 @@ function ShareURLModal({ extraOption }: ShareURLModalProps): JSX.Element {
|
||||
</Typography.Text>
|
||||
<div className="absolute-relative-time-toggler">
|
||||
<Switch
|
||||
color="primary"
|
||||
textPlacement="right"
|
||||
value={enableExtraOption}
|
||||
onChange={(): void => setEnableExtraOption((prev) => !prev)}
|
||||
/>
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
|
||||
|
||||
type BadgeColor =
|
||||
| 'vanilla'
|
||||
| 'robin'
|
||||
| 'forest'
|
||||
| 'amber'
|
||||
| 'sienna'
|
||||
| 'cherry'
|
||||
| 'sakura'
|
||||
| 'aqua';
|
||||
function getStatusCodeColor(statusCode: number): BadgeColorType {
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
return 'success';
|
||||
}
|
||||
if (statusCode >= 300 && statusCode < 400) {
|
||||
return 'primary';
|
||||
}
|
||||
if (statusCode >= 400 && statusCode < 500) {
|
||||
return 'warning';
|
||||
}
|
||||
if (statusCode >= 500) {
|
||||
return 'danger';
|
||||
}
|
||||
if (statusCode >= 100 && statusCode < 200) {
|
||||
return 'secondary';
|
||||
}
|
||||
return 'primary';
|
||||
}
|
||||
|
||||
interface HttpStatusBadgeProps {
|
||||
statusCode: string | number;
|
||||
@@ -16,25 +25,6 @@ interface HttpStatusBadgeProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function getStatusCodeColor(statusCode: number): BadgeColor {
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
return 'forest'; // Success - green
|
||||
}
|
||||
if (statusCode >= 300 && statusCode < 400) {
|
||||
return 'robin'; // Redirect - blue
|
||||
}
|
||||
if (statusCode >= 400 && statusCode < 500) {
|
||||
return 'amber'; // Client error - amber
|
||||
}
|
||||
if (statusCode >= 500) {
|
||||
return 'cherry'; // Server error - red
|
||||
}
|
||||
if (statusCode >= 100 && statusCode < 200) {
|
||||
return 'vanilla'; // Informational - neutral
|
||||
}
|
||||
return 'robin'; // Default fallback
|
||||
}
|
||||
|
||||
function HttpStatusBadge({
|
||||
statusCode,
|
||||
testId,
|
||||
@@ -49,12 +39,7 @@ function HttpStatusBadge({
|
||||
const color = getStatusCodeColor(numericStatusCode);
|
||||
|
||||
return (
|
||||
<Badge
|
||||
color={color}
|
||||
variant="outline"
|
||||
data-testid={testId}
|
||||
className={className}
|
||||
>
|
||||
<Badge color={color} variant="outlined" testId={testId} className={className}>
|
||||
{statusCode}
|
||||
</Badge>
|
||||
);
|
||||
|
||||
@@ -119,11 +119,12 @@ function InviteMembers({
|
||||
<div className={styles.cellAction}>
|
||||
{canRemoveRow && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="destructive"
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="danger"
|
||||
onClick={(): void => removeRow(row.id)}
|
||||
aria-label="Remove row"
|
||||
data-testid={`invite-remove-${row.id}`}
|
||||
testId={`invite-remove-${row.id}`}
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
</Button>
|
||||
@@ -136,11 +137,12 @@ function InviteMembers({
|
||||
{showAddButton && (
|
||||
<div className={styles.addRow}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="dashed"
|
||||
color="secondary"
|
||||
prefix={<Plus size={12} />}
|
||||
onClick={addRow}
|
||||
data-testid="invite-add-row"
|
||||
testId="invite-add-row"
|
||||
>
|
||||
Add another
|
||||
</Button>
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useMutation } from 'react-query';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import AddCreditCardModal from 'components/AddCreditCardModal/AddCreditCardModal';
|
||||
import { Button, Modal, Tooltip } from 'antd';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { createSubscription } from 'api/generated/services/subscriptions';
|
||||
import type { CreateSubscription201 } from 'api/generated/services/sigNoz.schemas';
|
||||
import cx from 'classnames';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import AuthZTooltip from 'lib/authz/components/AuthZTooltip/AuthZTooltip';
|
||||
import { SubscriptionCreatePermission } from 'lib/authz/hooks/useAuthZ/permissions/subscription.permissions';
|
||||
import { defaultTo } from 'lodash-es';
|
||||
import { CircleHelp } from '@signozhq/icons';
|
||||
import { CircleHelp, CreditCard, X } from '@signozhq/icons';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import APIError from 'types/api/error';
|
||||
import { getBaseUrl } from 'utils/basePath';
|
||||
|
||||
import './LaunchChatSupport.styles.scss';
|
||||
|
||||
@@ -33,6 +41,7 @@ function LaunchChatSupport({
|
||||
chatMessageDisabled = false,
|
||||
}: LaunchChatSupportProps): JSX.Element | null {
|
||||
const { isCloudUser: isCloudUserVal } = useGetTenantLicense();
|
||||
const { notifications } = useNotifications();
|
||||
const {
|
||||
trialInfo,
|
||||
featureFlags,
|
||||
@@ -110,12 +119,43 @@ function LaunchChatSupport({
|
||||
}
|
||||
};
|
||||
|
||||
const handleBillingOnSuccess = (data: CreateSubscription201): void => {
|
||||
if (data?.data?.redirectURL) {
|
||||
const newTab = document.createElement('a');
|
||||
newTab.href = data.data.redirectURL;
|
||||
newTab.target = '_blank';
|
||||
newTab.rel = 'noopener noreferrer';
|
||||
newTab.click();
|
||||
}
|
||||
};
|
||||
|
||||
const handleBillingOnError = (error: APIError): void => {
|
||||
notifications.error({
|
||||
message: error.getErrorCode(),
|
||||
description: error.getErrorMessage(),
|
||||
});
|
||||
};
|
||||
|
||||
const { mutate: updateCreditCard, isLoading: isLoadingBilling } = useMutation(
|
||||
createSubscription,
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
handleBillingOnSuccess(data);
|
||||
},
|
||||
onError: handleBillingOnError,
|
||||
},
|
||||
);
|
||||
|
||||
const handleAddCreditCard = (): void => {
|
||||
logEvent('Add Credit card modal: Clicked', {
|
||||
source: `facing issues button`,
|
||||
page: pathname,
|
||||
...attributes,
|
||||
});
|
||||
|
||||
updateCreditCard({
|
||||
url: getBaseUrl(),
|
||||
});
|
||||
};
|
||||
|
||||
return isCloudUserVal && isChatSupportEnabled ? ( // Note: we would need to move this condition to license based in future
|
||||
@@ -135,11 +175,47 @@ function LaunchChatSupport({
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<AddCreditCardModal
|
||||
{/* Add Credit Card Modal */}
|
||||
<Modal
|
||||
className="add-credit-card-modal"
|
||||
title={<span className="title">Add Credit Card for Chat Support</span>}
|
||||
open={isAddCreditCardModalOpen}
|
||||
onClose={(): void => setIsAddCreditCardModalOpen(false)}
|
||||
onAddCreditCard={handleAddCreditCard}
|
||||
/>
|
||||
closable
|
||||
onCancel={(): void => setIsAddCreditCardModalOpen(false)}
|
||||
destroyOnClose
|
||||
footer={[
|
||||
<Button
|
||||
key="cancel"
|
||||
onClick={(): void => setIsAddCreditCardModalOpen(false)}
|
||||
className="cancel-btn"
|
||||
icon={<X size={16} />}
|
||||
>
|
||||
Cancel
|
||||
</Button>,
|
||||
<AuthZTooltip
|
||||
key="submit"
|
||||
checks={[SubscriptionCreatePermission]}
|
||||
withPortal={false}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<CreditCard size={16} />}
|
||||
size="middle"
|
||||
loading={isLoadingBilling}
|
||||
disabled={isLoadingBilling}
|
||||
onClick={handleAddCreditCard}
|
||||
className="add-credit-card-btn"
|
||||
>
|
||||
Add Credit Card
|
||||
</Button>
|
||||
</AuthZTooltip>,
|
||||
]}
|
||||
>
|
||||
<Typography.Text className="add-credit-card-text">
|
||||
You're currently on <span className="highlight-text">Trial plan</span>
|
||||
. Add a credit card to access SigNoz chat support to your workspace.
|
||||
</Typography.Text>
|
||||
</Modal>
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Divider } from '@signozhq/ui/divider';
|
||||
import { DropdownMenuSimple as Dropdown } from '@signozhq/ui/dropdown-menu';
|
||||
import { DropdownMenuSimple as Dropdown } from 'components/DropdownMenu/DropdownMenuSimple';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { aggregateAttributesResourcesToString } from 'container/LogDetailedView/utils';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
@@ -23,8 +23,6 @@ import { useCopyToClipboard } from 'react-use';
|
||||
|
||||
import styles from './LogDetailsHeader.module.scss';
|
||||
|
||||
const TOOLTIP_CONTENT_PROPS = { className: styles.tooltipContent };
|
||||
|
||||
interface LogDetailsHeaderProps {
|
||||
log: ILog;
|
||||
onNavigatePrev: () => void;
|
||||
@@ -91,6 +89,7 @@ function LogDetailsHeader({
|
||||
<div className={styles.actions}>
|
||||
{showOpenInExplorer && (
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<Compass size={16} />}
|
||||
@@ -104,47 +103,59 @@ function LogDetailsHeader({
|
||||
menu={{ items: menuItems }}
|
||||
align="end"
|
||||
className={styles.dropdownContent}
|
||||
onClick={(e: MouseEvent): void => e.stopPropagation()}
|
||||
>
|
||||
<Button
|
||||
size="md"
|
||||
variant="link"
|
||||
color="secondary"
|
||||
prefix={<Ellipsis size={16} />}
|
||||
data-testid="log-details-header-menu"
|
||||
/>
|
||||
icon
|
||||
aria-label="Log actions"
|
||||
testId="log-details-header-menu"
|
||||
onClick={(e: MouseEvent): void => e.stopPropagation()}
|
||||
>
|
||||
<Ellipsis size={16} />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
|
||||
<div className={styles.arrows}>
|
||||
<TooltipSimple
|
||||
<Tooltip
|
||||
title="Move to previous log"
|
||||
side="top"
|
||||
open={isPrevDisabled ? false : undefined}
|
||||
tooltipContentProps={TOOLTIP_CONTENT_PROPS}
|
||||
>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<ChevronUp size={14} />}
|
||||
icon
|
||||
aria-label="Move to previous log"
|
||||
disabled={isPrevDisabled}
|
||||
onClick={onNavigatePrev}
|
||||
data-testid="log-details-header-prev"
|
||||
/>
|
||||
</TooltipSimple>
|
||||
<TooltipSimple
|
||||
testId="log-details-header-prev"
|
||||
>
|
||||
<ChevronUp size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title="Move to next log"
|
||||
side="top"
|
||||
open={isNextDisabled ? false : undefined}
|
||||
tooltipContentProps={TOOLTIP_CONTENT_PROPS}
|
||||
>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<ChevronDown size={14} />}
|
||||
icon
|
||||
aria-label="Move to next log"
|
||||
disabled={isNextDisabled}
|
||||
onClick={onNavigateNext}
|
||||
data-testid="log-details-header-next"
|
||||
/>
|
||||
</TooltipSimple>
|
||||
testId="log-details-header-next"
|
||||
>
|
||||
<ChevronDown size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { Badge, BadgeColor } from '@signozhq/ui/badge';
|
||||
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
|
||||
import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator';
|
||||
import { getLogIndicatorType } from 'components/Logs/LogStateIndicator/utils';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
@@ -8,13 +8,13 @@ import styles from './LogHighlights.module.scss';
|
||||
import TraceIdField from './TraceIdField';
|
||||
|
||||
// Severity badge color mirrors the LogStateIndicator bar
|
||||
const SEVERITY_COLOR: Record<string, BadgeColor> = {
|
||||
[LogType.TRACE]: 'forest',
|
||||
[LogType.DEBUG]: 'aqua',
|
||||
[LogType.INFO]: 'robin',
|
||||
[LogType.WARN]: 'amber',
|
||||
[LogType.ERROR]: 'cherry',
|
||||
[LogType.FATAL]: 'sakura',
|
||||
const SEVERITY_COLOR: Record<string, BadgeColorType> = {
|
||||
[LogType.TRACE]: 'success',
|
||||
[LogType.DEBUG]: 'info',
|
||||
[LogType.INFO]: 'primary',
|
||||
[LogType.WARN]: 'warning',
|
||||
[LogType.ERROR]: 'danger',
|
||||
[LogType.FATAL]: 'highlight-danger',
|
||||
};
|
||||
|
||||
export interface LogHighlightConfig {
|
||||
@@ -32,9 +32,13 @@ const getAttr = (log: ILog, key: string): string =>
|
||||
|
||||
const valueBadge = (
|
||||
value: string,
|
||||
options?: { prefix?: ReactNode; color?: BadgeColor },
|
||||
options?: { prefix?: ReactNode; color?: BadgeColorType },
|
||||
): ReactNode => (
|
||||
<Badge color={options?.color ?? 'vanilla'} className={styles.valueBadge}>
|
||||
<Badge
|
||||
variant="solid"
|
||||
color={options?.color ?? 'secondary'}
|
||||
className={styles.valueBadge}
|
||||
>
|
||||
{options?.prefix}
|
||||
<span className={styles.badgeText} title={value}>
|
||||
{value}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useCopyToClipboard } from 'react-use';
|
||||
import { Color, Spacing } from '@signozhq/design-tokens';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Drawer, Tooltip } from 'antd';
|
||||
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
|
||||
import { ToggleGroup } from '@signozhq/ui/toggle-group';
|
||||
import { Divider } from '@signozhq/ui/divider';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import cx from 'classnames';
|
||||
@@ -328,13 +328,18 @@ function LogDetailInner({
|
||||
mouseLeaveDelay={0}
|
||||
>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<ChevronUp size={14} />}
|
||||
icon
|
||||
aria-label="Move to previous log"
|
||||
className="log-arrow-btn log-arrow-btn-up"
|
||||
disabled={isPrevDisabled}
|
||||
onClick={goToPrev}
|
||||
/>
|
||||
>
|
||||
<ChevronUp size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={isNextDisabled ? '' : 'Move to next log'}
|
||||
@@ -342,18 +347,24 @@ function LogDetailInner({
|
||||
mouseLeaveDelay={0}
|
||||
>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<ChevronDown size={14} />}
|
||||
icon
|
||||
aria-label="Move to next log"
|
||||
className="log-arrow-btn log-arrow-btn-down"
|
||||
disabled={isNextDisabled}
|
||||
onClick={goToNext}
|
||||
/>
|
||||
>
|
||||
<ChevronDown size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
{handleOpenInExplorer && (
|
||||
<div>
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<Compass size={16} />}
|
||||
@@ -410,7 +421,10 @@ function LogDetailInner({
|
||||
{isLogDetailsV2 && <div className="log-detail-drawer__section-divider" />}
|
||||
|
||||
<div className="tabs-and-search">
|
||||
<ToggleGroupSimple
|
||||
<ToggleGroup
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
type="single"
|
||||
className="views-tabs"
|
||||
onChange={handleModeChange}
|
||||
@@ -473,9 +487,12 @@ function LogDetailInner({
|
||||
variant="link"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
prefix={<Filter size="lg" />}
|
||||
icon
|
||||
aria-label="Show Filters"
|
||||
onClick={handleFilterVisible}
|
||||
/>
|
||||
>
|
||||
<Filter size="lg" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
@@ -493,9 +510,14 @@ function LogDetailInner({
|
||||
variant="link"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
prefix={<Copy size={12} />}
|
||||
icon
|
||||
aria-label={
|
||||
selectedView === VIEW_TYPES.JSON ? 'Copy JSON' : 'Copy Log Link'
|
||||
}
|
||||
onClick={selectedView === VIEW_TYPES.JSON ? handleJSONCopy : onLogCopy}
|
||||
/>
|
||||
>
|
||||
<Copy size={12} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import {
|
||||
Bold,
|
||||
CodeXml,
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
Type,
|
||||
} from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
|
||||
import InsertVariableMenu from './InsertVariableMenu';
|
||||
@@ -20,7 +20,7 @@ import type { EditorCommand, EditorVariable } from './types';
|
||||
|
||||
import styles from './MarkdownEditor.module.scss';
|
||||
|
||||
const COMMAND_ICONS: Record<string, ReactNode> = {
|
||||
const COMMAND_ICONS: Record<string, ReactElement> = {
|
||||
heading: <Heading size={14} />,
|
||||
bold: <Bold size={14} />,
|
||||
italic: <Italic size={14} />,
|
||||
@@ -61,20 +61,22 @@ function EditorToolbar({
|
||||
<span className={styles.toolbarDivider} />
|
||||
<div className={styles.commands}>
|
||||
{commands.map((command) => (
|
||||
<TooltipSimple key={command.id} title={command.label}>
|
||||
<Tooltip key={command.id} title={command.label}>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
type="button"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
disabled={disabled}
|
||||
aria-label={command.label}
|
||||
data-testid={`markdown-command-${command.id}`}
|
||||
testId={`markdown-command-${command.id}`}
|
||||
onClick={(): void => onRunCommand(command)}
|
||||
>
|
||||
{COMMAND_ICONS[command.id]}
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.toolbarEnd}>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { ChevronDown, DollarSign, Search } from '@signozhq/icons';
|
||||
import { ChevronDown, DollarSign } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { DropdownMenuSimple, type MenuItem } from '@signozhq/ui/dropdown-menu';
|
||||
import {
|
||||
DropdownMenuSimple,
|
||||
type MenuItem,
|
||||
} from 'components/DropdownMenu/DropdownMenuSimple';
|
||||
|
||||
import type { EditorVariable } from './types';
|
||||
|
||||
@@ -66,12 +69,12 @@ function InsertVariableMenu({
|
||||
items,
|
||||
search: {
|
||||
placeholder: 'Search variables',
|
||||
searchIcon: <Search size={14} />,
|
||||
onSearchChange: setSearch,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
type="button"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
@@ -80,7 +83,7 @@ function InsertVariableMenu({
|
||||
prefix={<DollarSign size={14} className={styles.insertVariableIcon} />}
|
||||
suffix={<ChevronDown size={14} />}
|
||||
className={styles.insertVariable}
|
||||
data-testid="markdown-insert-variable"
|
||||
testId="markdown-insert-variable"
|
||||
>
|
||||
Insert variable
|
||||
</Button>
|
||||
|
||||
@@ -15,9 +15,10 @@ function MarkdownHelp(): JSX.Element {
|
||||
type="button"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
aria-label="Markdown syntax help"
|
||||
data-testid="markdown-help-trigger"
|
||||
testId="markdown-help-trigger"
|
||||
>
|
||||
<CircleHelp size={14} />
|
||||
</Button>
|
||||
|
||||
@@ -55,14 +55,14 @@ function NameEmailCell({
|
||||
function StatusBadge({ status }: { status: MemberRow['status'] }): JSX.Element {
|
||||
if (status === MemberStatus.Active) {
|
||||
return (
|
||||
<Badge color="forest" variant="outline">
|
||||
<Badge color="success" variant="outlined">
|
||||
ACTIVE
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
if (status === MemberStatus.Deleted) {
|
||||
return (
|
||||
<Badge color="cherry" variant="outline">
|
||||
<Badge color="danger" variant="outlined">
|
||||
DELETED
|
||||
</Badge>
|
||||
);
|
||||
@@ -70,13 +70,17 @@ function StatusBadge({ status }: { status: MemberRow['status'] }): JSX.Element {
|
||||
|
||||
if (status === MemberStatus.Invited) {
|
||||
return (
|
||||
<Badge color="amber" variant="outline">
|
||||
<Badge color="warning" variant="outlined">
|
||||
INVITED
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
return <Badge color="vanilla">⎯</Badge>;
|
||||
return (
|
||||
<Badge variant="solid" color="secondary">
|
||||
⎯
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
function MembersEmptyState({
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Select } from 'antd';
|
||||
import { Checkbox } from '@signozhq/ui/checkbox';
|
||||
import { TooltipProvider, TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { TooltipProvider, Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import cx from 'classnames';
|
||||
import TextToolTip from 'components/TextToolTip/TextToolTip';
|
||||
@@ -758,9 +758,14 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
>
|
||||
<Checkbox
|
||||
color="primary"
|
||||
value={isSelected}
|
||||
className="option-checkbox"
|
||||
onClick={(e): void => selectFromButton(e, 'checkbox')}
|
||||
onChange={(): void => {
|
||||
handleItemSelection('checkbox');
|
||||
setActiveChipIndex(-1);
|
||||
setActiveIndex(-1);
|
||||
}}
|
||||
>
|
||||
<div className="option-content">
|
||||
<Typography.Text truncate={1} className="option-label-text">
|
||||
@@ -1595,7 +1600,11 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
|
||||
<Checkbox value={allOptionsSelected} className="option-checkbox">
|
||||
<Checkbox
|
||||
color="primary"
|
||||
value={allOptionsSelected}
|
||||
className="option-checkbox"
|
||||
>
|
||||
<div className="option-content">
|
||||
<div className="all-option-text">ALL</div>
|
||||
</div>
|
||||
@@ -1973,13 +1982,9 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
|
||||
// `label` arrives already cut to maxTagTextLength, so the reveal reads the
|
||||
// option's own text (falling back to the raw value for freeform tags).
|
||||
return (
|
||||
<TooltipSimple
|
||||
side="top"
|
||||
delayDuration={300}
|
||||
title={findOptionLabelText(options, value)}
|
||||
>
|
||||
<Tooltip side="top" title={findOptionLabelText(options, value)}>
|
||||
{tag}
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
|
||||
import { ToggleGroup } from '@signozhq/ui/toggle-group';
|
||||
import InputWithLabel from 'components/InputWithLabel/InputWithLabel';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { GroupByFilter } from 'container/QueryBuilder/filters/GroupByFilter/GroupByFilter';
|
||||
@@ -562,7 +562,10 @@ function QueryAddOns({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ToggleGroupSimple
|
||||
<ToggleGroup
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
type="multiple"
|
||||
className="add-ons-tabs"
|
||||
value={selectedViews.map((view) => view.key)}
|
||||
|
||||
@@ -724,26 +724,62 @@ function QuerySearch({
|
||||
// Helper function to render a badge for the current context mode
|
||||
const renderContextBadge = (): JSX.Element => {
|
||||
if (!editingMode) {
|
||||
return <Badge color="vanilla">Unknown</Badge>;
|
||||
return (
|
||||
<Badge variant="solid" color="secondary">
|
||||
Unknown
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
switch (editingMode) {
|
||||
case 'key':
|
||||
return <Badge color="robin">Key</Badge>;
|
||||
return (
|
||||
<Badge variant="solid" color="primary">
|
||||
Key
|
||||
</Badge>
|
||||
);
|
||||
case 'operator':
|
||||
return <Badge color="sakura">Operator</Badge>;
|
||||
return (
|
||||
<Badge variant="solid" color="highlight-danger">
|
||||
Operator
|
||||
</Badge>
|
||||
);
|
||||
case 'value':
|
||||
return <Badge color="forest">Value</Badge>;
|
||||
return (
|
||||
<Badge variant="solid" color="success">
|
||||
Value
|
||||
</Badge>
|
||||
);
|
||||
case 'conjunction':
|
||||
return <Badge color="amber">Conjunction</Badge>;
|
||||
return (
|
||||
<Badge variant="solid" color="warning">
|
||||
Conjunction
|
||||
</Badge>
|
||||
);
|
||||
case 'function':
|
||||
return <Badge color="aqua">Function</Badge>;
|
||||
return (
|
||||
<Badge variant="solid" color="info">
|
||||
Function
|
||||
</Badge>
|
||||
);
|
||||
case 'parenthesis':
|
||||
return <Badge color="sakura">Parenthesis</Badge>;
|
||||
return (
|
||||
<Badge variant="solid" color="highlight-danger">
|
||||
Parenthesis
|
||||
</Badge>
|
||||
);
|
||||
case 'bracketList':
|
||||
return <Badge color="cherry">Bracket List</Badge>;
|
||||
return (
|
||||
<Badge variant="solid" color="danger">
|
||||
Bracket List
|
||||
</Badge>
|
||||
);
|
||||
default:
|
||||
return <Badge color="vanilla">Unknown</Badge>;
|
||||
return (
|
||||
<Badge variant="solid" color="secondary">
|
||||
Unknown
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1465,27 +1501,44 @@ function QuerySearch({
|
||||
Currently editing: {renderContextBadge()}
|
||||
{queryContext?.keyToken && (
|
||||
<span className="triplet-info">
|
||||
Key: <Badge color="vanilla">{queryContext.keyToken}</Badge>
|
||||
Key:{' '}
|
||||
<Badge variant="solid" color="secondary">
|
||||
{queryContext.keyToken}
|
||||
</Badge>
|
||||
</span>
|
||||
)}
|
||||
{queryContext?.operatorToken && (
|
||||
<span className="triplet-info">
|
||||
Operator: <Badge color="vanilla">{queryContext.operatorToken}</Badge>
|
||||
Operator:{' '}
|
||||
<Badge variant="solid" color="secondary">
|
||||
{queryContext.operatorToken}
|
||||
</Badge>
|
||||
</span>
|
||||
)}
|
||||
{queryContext?.valueToken && (
|
||||
<span className="triplet-info">
|
||||
Value: <Badge color="vanilla">{queryContext.valueToken}</Badge>
|
||||
Value:{' '}
|
||||
<Badge variant="solid" color="secondary">
|
||||
{queryContext.valueToken}
|
||||
</Badge>
|
||||
</span>
|
||||
)}
|
||||
{queryContext?.currentPair && (
|
||||
<span className="triplet-info query-pair-info">
|
||||
Current pair: <Badge color="robin">{queryContext.currentPair.key}</Badge>
|
||||
<Badge color="sakura">{queryContext.currentPair.operator}</Badge>
|
||||
Current pair:{' '}
|
||||
<Badge variant="solid" color="primary">
|
||||
{queryContext.currentPair.key}
|
||||
</Badge>
|
||||
<Badge variant="solid" color="highlight-danger">
|
||||
{queryContext.currentPair.operator}
|
||||
</Badge>
|
||||
{queryContext.currentPair.value && (
|
||||
<Badge color="forest">{queryContext.currentPair.value}</Badge>
|
||||
<Badge variant="solid" color="success">
|
||||
{queryContext.currentPair.value}
|
||||
</Badge>
|
||||
)}
|
||||
<Badge
|
||||
variant="solid"
|
||||
color={queryContext.currentPair.isComplete ? 'success' : 'warning'}
|
||||
>
|
||||
{queryContext.currentPair.isComplete ? 'Complete' : 'Incomplete'}
|
||||
@@ -1495,7 +1548,9 @@ function QuerySearch({
|
||||
{queryContext?.queryPairs && queryContext.queryPairs.length > 0 && (
|
||||
<span className="triplet-info">
|
||||
Total pairs:{' '}
|
||||
<Badge color="robin">{queryContext.queryPairs.length}</Badge>
|
||||
<Badge variant="solid" color="primary">
|
||||
{queryContext.queryPairs.length}
|
||||
</Badge>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
|
||||
import { DropdownMenuSimple } from 'components/DropdownMenu/DropdownMenuSimple';
|
||||
import cx from 'classnames';
|
||||
import { ENTITY_VERSION_V4, ENTITY_VERSION_V5 } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Button } from 'antd';
|
||||
import { Checkbox } from '@signozhq/ui/checkbox';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import cx from 'classnames';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
|
||||
interface CheckboxValueRowProps {
|
||||
value: string;
|
||||
@@ -28,6 +28,8 @@ function CheckboxValueRow({
|
||||
return (
|
||||
<div className="value">
|
||||
<Checkbox
|
||||
color="primary"
|
||||
disabledTooltip={undefined}
|
||||
onChange={(isChecked): void => onCheckboxChange(isChecked === true)}
|
||||
value={checked}
|
||||
disabled={disabled}
|
||||
@@ -47,11 +49,11 @@ function CheckboxValueRow({
|
||||
{customRendererForValue ? (
|
||||
customRendererForValue(value)
|
||||
) : (
|
||||
<TooltipSimple title={String(value)} side="top" align="start">
|
||||
<Tooltip title={String(value)} side="top" align="start">
|
||||
<Typography.Text className="value-string" truncate={1}>
|
||||
{String(value)}
|
||||
</Typography.Text>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
)}
|
||||
<div className="value-actions">
|
||||
<Button type="text" className="only-btn">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { ChevronDown, ChevronRight, Search, Undo2 } from '@signozhq/icons';
|
||||
|
||||
@@ -63,9 +63,7 @@ export function CheckboxFilterV2Header({
|
||||
<ChevronRight size={13} cursor="pointer" />
|
||||
)}
|
||||
{isTitleTruncated ? (
|
||||
<TooltipSimple title={title} delayDuration={400}>
|
||||
{titleText}
|
||||
</TooltipSimple>
|
||||
<Tooltip title={title}>{titleText}</Tooltip>
|
||||
) : (
|
||||
titleText
|
||||
)}
|
||||
|
||||
@@ -54,6 +54,7 @@ export function CheckboxFilterV2ValueRow({
|
||||
>
|
||||
<div className={styles.checkbox}>
|
||||
<Checkbox
|
||||
disabledTooltip={undefined}
|
||||
onChange={(isChecked): void =>
|
||||
onCheckboxChange(isChecked === true, checkedState)
|
||||
}
|
||||
@@ -97,7 +98,7 @@ export function CheckboxFilterV2ValueRow({
|
||||
<div className={styles.actions}>
|
||||
{badge && (
|
||||
<Badge
|
||||
variant="outline"
|
||||
variant="outlined"
|
||||
color={badge.color}
|
||||
className={styles.badge}
|
||||
testId={`badge-${badge.key}`}
|
||||
@@ -105,10 +106,20 @@ export function CheckboxFilterV2ValueRow({
|
||||
{badge.label}
|
||||
</Badge>
|
||||
)}
|
||||
<Button variant="ghost" color="secondary" className={styles.onlyButton}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
className={styles.onlyButton}
|
||||
>
|
||||
{onlyButtonLabel}
|
||||
</Button>
|
||||
<Button variant="ghost" color="secondary" className={styles.toggleButton}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
className={styles.toggleButton}
|
||||
>
|
||||
Toggle
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -64,7 +64,7 @@ describe('CheckboxFilterV2ValueRow', () => {
|
||||
render(
|
||||
<CheckboxFilterV2ValueRow
|
||||
{...defaultProps}
|
||||
badge={{ key: 'related', label: 'Related', color: 'robin' }}
|
||||
badge={{ key: 'related', label: 'Related', color: 'primary' }}
|
||||
/>,
|
||||
);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export enum SectionType {
|
||||
export interface BadgeConfig {
|
||||
key: string;
|
||||
label: string;
|
||||
color: 'robin' | 'warning' | 'secondary';
|
||||
color: 'primary' | 'warning' | 'secondary';
|
||||
}
|
||||
|
||||
export interface ItemConfig {
|
||||
|
||||
@@ -23,21 +23,22 @@ export function SectionActionButton({
|
||||
}: SectionActionButtonProps): JSX.Element {
|
||||
return (
|
||||
<Tooltip title={tooltip}>
|
||||
<Button
|
||||
variant="link"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
className={classNames(styles.iconBtn, className)}
|
||||
onMouseDown={(e): void => e.preventDefault()}
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
onClick();
|
||||
}}
|
||||
data-testid={testId}
|
||||
>
|
||||
{icon}
|
||||
</Button>
|
||||
<span onMouseDown={(e): void => e.preventDefault()}>
|
||||
<Button
|
||||
variant="link"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
className={classNames(styles.iconBtn, className)}
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
onClick();
|
||||
}}
|
||||
testId={testId}
|
||||
>
|
||||
{icon}
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
|
||||
.quick-filters-settings-container {
|
||||
flex: 0 0 0;
|
||||
|
||||
@@ -232,48 +232,55 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
<section className="right-actions">
|
||||
<Tooltip title="Reset All">
|
||||
<Button
|
||||
size="md"
|
||||
variant="link"
|
||||
color="secondary"
|
||||
icon
|
||||
aria-label="Reset All"
|
||||
className="right-action-icon-container"
|
||||
onClick={handleReset}
|
||||
prefix={<RefreshCw className="sync-icon" size="md" />}
|
||||
/>
|
||||
>
|
||||
<RefreshCw className="sync-icon" size="md" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
{showFilterCollapse && (
|
||||
<Tooltip title="Collapse Filters">
|
||||
<Button
|
||||
size="md"
|
||||
variant="link"
|
||||
color="secondary"
|
||||
icon
|
||||
aria-label="Collapse Filters"
|
||||
className="right-action-icon-container"
|
||||
onClick={handleFilterVisibilityChange}
|
||||
prefix={<ArrowUpToLine style={{ rotate: '270deg' }} size="md" />}
|
||||
/>
|
||||
>
|
||||
<ArrowUpToLine style={{ rotate: '270deg' }} size="md" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{isDynamicFilters && (
|
||||
<AuthZButton
|
||||
size="md"
|
||||
checks={QuickFilterManagePermissions}
|
||||
variant="link"
|
||||
color="secondary"
|
||||
icon
|
||||
aria-label="Settings"
|
||||
className={classNames('right-action-icon-container', {
|
||||
active: isSettingsOpen,
|
||||
})}
|
||||
onClick={(): void => setIsSettingsOpen(true)}
|
||||
testId="settings-icon-container"
|
||||
prefix={
|
||||
<Tooltip title="Settings" open={isSettingsDisabled ? false : undefined}>
|
||||
<SettingsIcon
|
||||
className="settings-icon"
|
||||
data-testid="settings-icon"
|
||||
width={14}
|
||||
height={14}
|
||||
/>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
>
|
||||
<Tooltip title="Settings" open={isSettingsDisabled ? false : undefined}>
|
||||
<SettingsIcon
|
||||
className="settings-icon"
|
||||
data-testid="settings-icon"
|
||||
width={14}
|
||||
height={14}
|
||||
/>
|
||||
</Tooltip>
|
||||
</AuthZButton>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
@@ -284,6 +291,8 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
|
||||
<div className="api-quick-filters-header">
|
||||
<Typography.Text>Show IP addresses</Typography.Text>
|
||||
<Switch
|
||||
color="primary"
|
||||
textPlacement="right"
|
||||
style={{ marginLeft: 'auto' }}
|
||||
value={showIP ?? true}
|
||||
onChange={(checked): void => {
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
// The one `overflow: hidden` in the chain. Ancestors (RouteTab, AppLayout)
|
||||
// only hand height down; each pane below owns its own scroll.
|
||||
.layout {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// Positioned so overlays (settings drawer) paint above the content pane
|
||||
// without changing this pane's layout width.
|
||||
.filters {
|
||||
width: 280px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
// Bounded box for the OverlayScrollbar inside it (`.overlay-scrollbar` is
|
||||
// `height: 100%`), which owns the scrolling.
|
||||
.content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
import { ComponentProps, ReactNode } from 'react';
|
||||
import cx from 'classnames';
|
||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||
|
||||
import QuickFilters from '../QuickFilters';
|
||||
|
||||
import styles from './QuickFiltersLayout.module.scss';
|
||||
|
||||
// Same optionality as `<QuickFilters />` in JSX (honours its defaultProps).
|
||||
type QuickFiltersElementProps = JSX.LibraryManagedAttributes<
|
||||
typeof QuickFilters,
|
||||
ComponentProps<typeof QuickFilters>
|
||||
>;
|
||||
|
||||
export interface QuickFiltersLayoutProps {
|
||||
quickFilterProps: QuickFiltersElementProps;
|
||||
showFilters: boolean;
|
||||
className?: string;
|
||||
contentClassName?: string;
|
||||
testId?: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
function QuickFiltersLayout({
|
||||
quickFilterProps,
|
||||
showFilters,
|
||||
className,
|
||||
contentClassName,
|
||||
testId,
|
||||
children,
|
||||
}: QuickFiltersLayoutProps): JSX.Element {
|
||||
return (
|
||||
<div className={cx(styles.layout, className)} data-testid={testId}>
|
||||
{showFilters && (
|
||||
<aside
|
||||
className={styles.filters}
|
||||
data-testid="quick-filters-layout-filters"
|
||||
>
|
||||
<QuickFilters {...quickFilterProps} />
|
||||
</aside>
|
||||
)}
|
||||
<section
|
||||
className={cx(styles.content, contentClassName)}
|
||||
data-testid="quick-filters-layout-content"
|
||||
>
|
||||
<OverlayScrollbar>
|
||||
<div>{children}</div>
|
||||
</OverlayScrollbar>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default QuickFiltersLayout;
|
||||
@@ -1,79 +0,0 @@
|
||||
import { render, screen } from 'tests/test-utils';
|
||||
|
||||
import { QuickFiltersSource } from '../../types';
|
||||
import QuickFiltersLayout from '../QuickFiltersLayout';
|
||||
|
||||
jest.mock('../QuickFiltersLayout.module.scss', () => ({
|
||||
__esModule: true,
|
||||
default: {
|
||||
layout: 'layout',
|
||||
filters: 'filters',
|
||||
content: 'content',
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('../../QuickFilters', () => ({
|
||||
__esModule: true,
|
||||
default: ({ source }: { source: string }): JSX.Element => (
|
||||
<div data-testid="quick-filters">{source}</div>
|
||||
),
|
||||
}));
|
||||
|
||||
const quickFilterProps = {
|
||||
source: QuickFiltersSource.TRACES_EXPLORER,
|
||||
handleFilterVisibilityChange: jest.fn(),
|
||||
};
|
||||
|
||||
describe('QuickFiltersLayout', () => {
|
||||
it('renders QuickFilters with the given props inside the filters pane', () => {
|
||||
render(
|
||||
<QuickFiltersLayout showFilters quickFilterProps={quickFilterProps}>
|
||||
<div>content</div>
|
||||
</QuickFiltersLayout>,
|
||||
);
|
||||
|
||||
const filtersPane = screen.getByTestId('quick-filters-layout-filters');
|
||||
expect(filtersPane).toContainElement(screen.getByTestId('quick-filters'));
|
||||
expect(screen.getByTestId('quick-filters')).toHaveTextContent(
|
||||
QuickFiltersSource.TRACES_EXPLORER,
|
||||
);
|
||||
expect(screen.getByTestId('quick-filters-layout-content')).toHaveTextContent(
|
||||
'content',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not render the filters pane when showFilters is false', () => {
|
||||
render(
|
||||
<QuickFiltersLayout showFilters={false} quickFilterProps={quickFilterProps}>
|
||||
<div>content</div>
|
||||
</QuickFiltersLayout>,
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.queryByTestId('quick-filters-layout-filters'),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('quick-filters')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('content')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('merges classNames onto the root and content panes', () => {
|
||||
render(
|
||||
<QuickFiltersLayout
|
||||
showFilters
|
||||
quickFilterProps={quickFilterProps}
|
||||
className="page-root"
|
||||
contentClassName="page-content"
|
||||
testId="page"
|
||||
>
|
||||
<div>content</div>
|
||||
</QuickFiltersLayout>,
|
||||
);
|
||||
|
||||
const root = screen.getByTestId('page');
|
||||
expect(root).toHaveClass('layout', 'page-root');
|
||||
expect(screen.getByTestId('quick-filters-layout-content')).toHaveClass(
|
||||
'content',
|
||||
'page-content',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -61,6 +61,7 @@ function AnnouncementTooltip({
|
||||
<p className="announcement-tooltip__message">{message}</p>
|
||||
<div className="announcement-tooltip__footer">
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
onClick={closeTooltip}
|
||||
|
||||
@@ -6,12 +6,27 @@
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
width: 342px;
|
||||
height: 100%;
|
||||
background: var(--l1-background);
|
||||
transition: width 0.05s ease-in-out;
|
||||
overflow: hidden;
|
||||
color: var(--l1-foreground);
|
||||
|
||||
&.qf-logs-explorer {
|
||||
height: calc(100vh - 45px);
|
||||
}
|
||||
|
||||
&.qf-exceptions {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
&.qf-api-monitoring {
|
||||
height: calc(100vh - 45px);
|
||||
}
|
||||
|
||||
&.qf-traces-explorer {
|
||||
height: calc(100vh - 45px);
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { refreshLicense } from 'api/generated/services/licenses';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { RefreshCcw } from '@signozhq/icons';
|
||||
import AuthZTooltip from 'lib/authz/components/AuthZTooltip/AuthZTooltip';
|
||||
import { buildLicenseUpdatePermission } from 'lib/authz/hooks/useAuthZ/permissions/license.permissions';
|
||||
@@ -49,7 +49,7 @@ function RefreshPaymentStatus({
|
||||
>
|
||||
<Button
|
||||
variant="link"
|
||||
color={type === 'text' ? 'none' : 'secondary'}
|
||||
color="secondary"
|
||||
size="md"
|
||||
className={className}
|
||||
onClick={handleRefreshPaymentStatus}
|
||||
@@ -64,7 +64,7 @@ function RefreshPaymentStatus({
|
||||
return (
|
||||
<span className="refresh-payment-status-btn-wrapper">
|
||||
{type === 'tooltip' ? (
|
||||
<TooltipSimple title={t('refreshPaymentStatus')}>{button}</TooltipSimple>
|
||||
<Tooltip title={t('refreshPaymentStatus')}>{button}</Tooltip>
|
||||
) : (
|
||||
button
|
||||
)}
|
||||
|
||||
@@ -5,7 +5,10 @@ import type {
|
||||
TableColumnType as ColumnType,
|
||||
} from 'antd';
|
||||
import { Button, Flex } from 'antd';
|
||||
import { DropdownMenuSimple, type MenuItem } from '@signozhq/ui/dropdown-menu';
|
||||
import {
|
||||
DropdownMenuSimple,
|
||||
type MenuItem,
|
||||
} from 'components/DropdownMenu/DropdownMenuSimple';
|
||||
import { Switch } from '@signozhq/ui/switch';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
|
||||
@@ -95,6 +98,8 @@ function DynamicColumnTable({
|
||||
>
|
||||
<div>{column.title?.toString()}</div>
|
||||
<Switch
|
||||
color="primary"
|
||||
textPlacement="right"
|
||||
value={columnsData?.findIndex((c) => c.key === column.key) !== -1}
|
||||
onChange={onToggleHandler(index, column)}
|
||||
/>
|
||||
|
||||
@@ -152,7 +152,7 @@ function RolesSelect(props: RolesSelectProps): JSX.Element {
|
||||
optionFilterProp="label"
|
||||
optionRender={(option): JSX.Element => (
|
||||
<div style={{ pointerEvents: 'none' }}>
|
||||
<Checkbox value={value.includes(option.value as string)}>
|
||||
<Checkbox color="primary" value={value.includes(option.value as string)}>
|
||||
{option.label}
|
||||
</Checkbox>
|
||||
</div>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// Hands the parent's height down to the active pane and lets the pane scroll
|
||||
// its own content, so TopNav and the tab bar stay put. Child combinators only
|
||||
// (nested Tabs must not be caught).
|
||||
.routeTab {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.routeTab > :global(.ant-tabs-content-holder) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.routeTab > :global(.ant-tabs-content-holder) > :global(.ant-tabs-content) {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.routeTab
|
||||
> :global(.ant-tabs-content-holder)
|
||||
> :global(.ant-tabs-content)
|
||||
> :global(.ant-tabs-tabpane-active) {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.routeTab
|
||||
> :global(.ant-tabs-content-holder)
|
||||
> :global(.ant-tabs-content)
|
||||
> :global(.ant-tabs-tabpane-active)
|
||||
> :global(.overlay-scrollbar) {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
@@ -5,11 +5,6 @@ import { fireEvent, render, screen } from 'tests/test-utils';
|
||||
import RouteTab from './index';
|
||||
import { RouteTabProps } from './types';
|
||||
|
||||
jest.mock('./RouteTab.module.scss', () => ({
|
||||
__esModule: true,
|
||||
default: { routeTab: 'routeTab' },
|
||||
}));
|
||||
|
||||
function DummyComponent1(): JSX.Element {
|
||||
return <div>Dummy Component 1</div>;
|
||||
}
|
||||
@@ -79,36 +74,6 @@ describe('RouteTab component', () => {
|
||||
expect(history.location.pathname).toBe('/tab2');
|
||||
});
|
||||
|
||||
it('applies the layout class alongside a custom className', () => {
|
||||
const history = createMemoryHistory();
|
||||
const { container } = render(
|
||||
<Router history={history}>
|
||||
<RouteTab
|
||||
history={history}
|
||||
routes={testRoutes}
|
||||
activeKey="Tab1"
|
||||
className="custom-tabs"
|
||||
/>
|
||||
</Router>,
|
||||
);
|
||||
expect(container.querySelector('.ant-tabs')).toHaveClass(
|
||||
'routeTab',
|
||||
'custom-tabs',
|
||||
);
|
||||
});
|
||||
|
||||
it('renders the active tab content inside an overlay scrollbar', () => {
|
||||
const history = createMemoryHistory();
|
||||
const { container } = render(
|
||||
<Router history={history}>
|
||||
<RouteTab history={history} routes={testRoutes} activeKey="Tab1" />
|
||||
</Router>,
|
||||
);
|
||||
expect(
|
||||
container.querySelector('.ant-tabs-tabpane-active > .overlay-scrollbar'),
|
||||
).toHaveTextContent('Dummy Component 1');
|
||||
});
|
||||
|
||||
it('calls onChangeHandler on tab change', () => {
|
||||
const onChangeHandler = jest.fn();
|
||||
const history = createMemoryHistory();
|
||||
|
||||
@@ -5,32 +5,20 @@ import {
|
||||
useParams,
|
||||
} from 'react-router-dom';
|
||||
import { Tabs, TabsProps } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import HeaderRightSection from 'components/HeaderRightSection/HeaderRightSection';
|
||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||
|
||||
import { RouteTabProps } from './types';
|
||||
|
||||
import styles from './RouteTab.module.scss';
|
||||
|
||||
interface Params {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Each pane scrolls its own content inside an OverlayScrollbar, so the tab bar
|
||||
* stays put. Mounted as the page root the pane is bounded to the viewport; inside
|
||||
* a plain block wrapper the scroller is inert and the page scrolls as usual.
|
||||
* Pane content that needs a bounded box must size itself with `height: 100%`
|
||||
* (the scroller's viewport is block flow, so `flex: 1` has no effect there).
|
||||
*/
|
||||
function RouteTab({
|
||||
routes,
|
||||
activeKey,
|
||||
onChangeHandler,
|
||||
history,
|
||||
showRightSection,
|
||||
className,
|
||||
...rest
|
||||
}: RouteTabProps & TabsProps): JSX.Element {
|
||||
const params = useParams<Params>();
|
||||
@@ -62,16 +50,11 @@ function RouteTab({
|
||||
label: name,
|
||||
key,
|
||||
tabKey: route,
|
||||
children: (
|
||||
<OverlayScrollbar>
|
||||
<Component />
|
||||
</OverlayScrollbar>
|
||||
),
|
||||
children: <Component />,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
className={cx(styles.routeTab, className)}
|
||||
onChange={onChange}
|
||||
destroyInactiveTabPane
|
||||
activeKey={currentRoute?.key || activeKey}
|
||||
|
||||
@@ -24,6 +24,7 @@ function KeyCreatedPhase({
|
||||
<div className="add-key-modal__key-display">
|
||||
<span className="add-key-modal__key-text">{createdKey.key}</span>
|
||||
<Button
|
||||
size="md"
|
||||
variant="link"
|
||||
color="secondary"
|
||||
onClick={onCopy}
|
||||
@@ -36,7 +37,9 @@ function KeyCreatedPhase({
|
||||
|
||||
<div className="add-key-modal__expiry-meta">
|
||||
<span className="add-key-modal__expiry-label">Expiration</span>
|
||||
<Badge color="vanilla">{expiryLabel}</Badge>
|
||||
<Badge variant="solid" color="secondary">
|
||||
{expiryLabel}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="add-key-modal__callout-wrapper">
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Control, UseFormRegister } from 'react-hook-form';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
|
||||
import { ToggleGroup } from '@signozhq/ui/toggle-group';
|
||||
import { DatePicker } from 'antd';
|
||||
import AuthZButton from 'lib/authz/components/AuthZButton/AuthZButton';
|
||||
import { AuthZGuardContent } from 'lib/authz/components/AuthZGuard/AuthZGuardContent';
|
||||
@@ -68,7 +68,9 @@ function KeyFormPhase({
|
||||
name="expiryMode"
|
||||
control={control}
|
||||
render={({ field }): JSX.Element => (
|
||||
<ToggleGroupSimple
|
||||
<ToggleGroup
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
type="single"
|
||||
value={field.value}
|
||||
onChange={(val: string): void => {
|
||||
@@ -117,6 +119,7 @@ function KeyFormPhase({
|
||||
<div className="add-key-modal__footer">
|
||||
<div className="add-key-modal__footer-right">
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
onClick={onClose}
|
||||
@@ -125,16 +128,22 @@ function KeyFormPhase({
|
||||
Cancel
|
||||
</Button>
|
||||
<AuthZButton
|
||||
size="md"
|
||||
checks={checks}
|
||||
authZEnabled={!!accountId}
|
||||
withPortal={false}
|
||||
type="submit"
|
||||
form={FORM_ID}
|
||||
type="button"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
loading={isSubmitting}
|
||||
disabled={!isValid}
|
||||
testId="add-key-submit-btn"
|
||||
onClick={(): void => {
|
||||
const form = document.getElementById(FORM_ID);
|
||||
if (form instanceof HTMLFormElement) {
|
||||
form.requestSubmit();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Create Key
|
||||
</AuthZButton>
|
||||
|
||||
@@ -80,15 +80,16 @@ function DeleteAccountModal(): JSX.Element {
|
||||
|
||||
const footer = (
|
||||
<div className="sa-delete-dialog__footer">
|
||||
<Button variant="solid" color="secondary" onClick={handleCancel}>
|
||||
<Button size="md" variant="solid" color="secondary" onClick={handleCancel}>
|
||||
<X size={12} />
|
||||
Cancel
|
||||
</Button>
|
||||
<AuthZButton
|
||||
size="md"
|
||||
checks={[buildSADeletePermission(accountId ?? '')]}
|
||||
authZEnabled={!!accountId}
|
||||
variant="solid"
|
||||
color="destructive"
|
||||
color="danger"
|
||||
loading={isDeleting}
|
||||
onClick={handleConfirm}
|
||||
data-testid="confirm-delete-btn"
|
||||
|
||||
@@ -4,7 +4,7 @@ import { LockKeyhole, Trash2, X } from '@signozhq/icons';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
|
||||
import { ToggleGroup } from '@signozhq/ui/toggle-group';
|
||||
import { DatePicker } from 'antd';
|
||||
import type { ServiceaccounttypesGettableFactorAPIKeyDTO } from 'api/generated/services/sigNoz.schemas';
|
||||
import AuthZButton from 'lib/authz/components/AuthZButton/AuthZButton';
|
||||
@@ -103,7 +103,9 @@ function EditKeyForm({
|
||||
name="expiryMode"
|
||||
control={control}
|
||||
render={({ field }): JSX.Element => (
|
||||
<ToggleGroupSimple
|
||||
<ToggleGroup
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
type="single"
|
||||
value={field.value}
|
||||
onChange={(val: string): void => {
|
||||
@@ -113,6 +115,9 @@ function EditKeyForm({
|
||||
}}
|
||||
size="sm"
|
||||
disabled={!canUpdate}
|
||||
disabledTooltip={
|
||||
canUpdate ? undefined : 'You do not have permission to update this key'
|
||||
}
|
||||
className="edit-key-modal__expiry-toggle"
|
||||
items={[
|
||||
{ value: ExpiryMode.NONE, label: 'No Expiration' },
|
||||
@@ -150,7 +155,7 @@ function EditKeyForm({
|
||||
|
||||
<div className="edit-key-modal__meta">
|
||||
<span className="edit-key-modal__meta-label">Last Observed At</span>
|
||||
<Badge color="vanilla">
|
||||
<Badge variant="solid" color="secondary">
|
||||
{formatLastObservedAt(
|
||||
keyItem?.lastObservedAt ?? null,
|
||||
formatTimezoneAdjustedTimestamp,
|
||||
@@ -161,13 +166,14 @@ function EditKeyForm({
|
||||
|
||||
<div className="edit-key-modal__footer">
|
||||
<AuthZButton
|
||||
size="md"
|
||||
checks={[
|
||||
buildAPIKeyDeletePermission(keyItem?.id ?? ''),
|
||||
buildSADetachPermission(accountId ?? ''),
|
||||
]}
|
||||
authZEnabled={!!accountId && !!keyItem?.id}
|
||||
variant="link"
|
||||
color="destructive"
|
||||
color="danger"
|
||||
onClick={onRevokeClick}
|
||||
withPortal={false}
|
||||
>
|
||||
@@ -175,20 +181,26 @@ function EditKeyForm({
|
||||
Revoke Key
|
||||
</AuthZButton>
|
||||
<div className="edit-key-modal__footer-right">
|
||||
<Button variant="solid" color="secondary" onClick={onClose}>
|
||||
<Button size="md" variant="solid" color="secondary" onClick={onClose}>
|
||||
<X size={12} />
|
||||
Cancel
|
||||
</Button>
|
||||
<AuthZButton
|
||||
size="md"
|
||||
checks={[buildAPIKeyUpdatePermission(keyItem?.id ?? '')]}
|
||||
authZEnabled={!!accountId && !!keyItem?.id}
|
||||
type="submit"
|
||||
form={FORM_ID}
|
||||
type="button"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
loading={isSaving}
|
||||
disabled={!isDirty}
|
||||
withPortal={false}
|
||||
onClick={(): void => {
|
||||
const form = document.getElementById(FORM_ID);
|
||||
if (form instanceof HTMLFormElement) {
|
||||
form.requestSubmit();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Save Changes
|
||||
</AuthZButton>
|
||||
|
||||
@@ -122,9 +122,11 @@ function buildColumns({
|
||||
]}
|
||||
authZEnabled={!isDisabled && !!accountId}
|
||||
withPortal={false}
|
||||
variant="ghost"
|
||||
variant="solid"
|
||||
size="sm"
|
||||
color="destructive"
|
||||
color="danger"
|
||||
icon
|
||||
aria-label="Revoke Key"
|
||||
disabled={isDisabled}
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
@@ -213,6 +215,7 @@ function KeysTab({
|
||||
</a>
|
||||
</p>
|
||||
<AuthZButton
|
||||
size="md"
|
||||
checks={[APIKeyCreatePermission, buildSAAttachPermission(accountId)]}
|
||||
authZEnabled={!isDisabled && !!accountId}
|
||||
withPortal={false}
|
||||
|
||||
@@ -122,11 +122,12 @@ function OverviewTab({
|
||||
<span className="sa-drawer__input-text">{account.id || '—'}</span>
|
||||
{account.id && (
|
||||
<Button
|
||||
size="md"
|
||||
variant="link"
|
||||
color="secondary"
|
||||
onClick={handleCopyId}
|
||||
className="sa-drawer__copy-btn"
|
||||
data-testid="copy-id-btn"
|
||||
testId="copy-id-btn"
|
||||
>
|
||||
{hasCopiedId ? <Check size={14} /> : <Copy size={14} />}
|
||||
</Button>
|
||||
@@ -156,7 +157,7 @@ function OverviewTab({
|
||||
localRoles.map((roleId) => {
|
||||
const role = availableRoles.find((r) => r.id === roleId);
|
||||
return (
|
||||
<Badge key={roleId} color="vanilla">
|
||||
<Badge variant="solid" key={roleId} color="secondary">
|
||||
{role?.name ?? roleId}
|
||||
</Badge>
|
||||
);
|
||||
@@ -187,15 +188,15 @@ function OverviewTab({
|
||||
<div className="sa-drawer__meta-item">
|
||||
<span className="sa-drawer__meta-label">Status</span>
|
||||
{account.status?.toUpperCase() === 'ACTIVE' ? (
|
||||
<Badge color="forest" variant="outline">
|
||||
<Badge color="success" variant="outlined">
|
||||
ACTIVE
|
||||
</Badge>
|
||||
) : account.status?.toUpperCase() === 'DELETED' ? (
|
||||
<Badge color="cherry" variant="outline">
|
||||
<Badge color="danger" variant="outlined">
|
||||
DELETED
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge color="vanilla" variant="outline" className="sa-status-badge">
|
||||
<Badge color="secondary" variant="outlined" className="sa-status-badge">
|
||||
{account.status ? account.status.toUpperCase() : 'UNKNOWN'}
|
||||
</Badge>
|
||||
)}
|
||||
@@ -203,12 +204,16 @@ function OverviewTab({
|
||||
|
||||
<div className="sa-drawer__meta-item">
|
||||
<span className="sa-drawer__meta-label">Created At</span>
|
||||
<Badge color="vanilla">{formatTimestamp(account.createdAt)}</Badge>
|
||||
<Badge variant="solid" color="secondary">
|
||||
{formatTimestamp(account.createdAt)}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="sa-drawer__meta-item">
|
||||
<span className="sa-drawer__meta-label">Updated At</span>
|
||||
<Badge color="vanilla">{formatTimestamp(account.updatedAt)}</Badge>
|
||||
<Badge variant="solid" color="secondary">
|
||||
{formatTimestamp(account.updatedAt)}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -41,18 +41,19 @@ export function RevokeKeyFooter({
|
||||
}: RevokeKeyFooterProps): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<Button variant="solid" color="secondary" onClick={onCancel}>
|
||||
<Button size="md" variant="solid" color="secondary" onClick={onCancel}>
|
||||
<X size={12} />
|
||||
Cancel
|
||||
</Button>
|
||||
<AuthZButton
|
||||
size="md"
|
||||
checks={[
|
||||
buildAPIKeyDeletePermission(keyId ?? ''),
|
||||
buildSADetachPermission(accountId ?? ''),
|
||||
]}
|
||||
authZEnabled={!!accountId && !!keyId}
|
||||
variant="solid"
|
||||
color="destructive"
|
||||
color="danger"
|
||||
loading={isRevoking}
|
||||
onClick={onConfirm}
|
||||
withPortal={false}
|
||||
|
||||
@@ -40,8 +40,9 @@ function SaveErrorItem({
|
||||
</span>
|
||||
{onRetry && !isRetrying && (
|
||||
<Button
|
||||
size="md"
|
||||
variant="link"
|
||||
color="none"
|
||||
color="secondary"
|
||||
aria-label="Retry"
|
||||
onClick={async (e): Promise<void> => {
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Key, LayoutGrid, Plus, Trash2, X } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { DrawerWrapper } from '@signozhq/ui/drawer';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
|
||||
import { ToggleGroup } from '@signozhq/ui/toggle-group';
|
||||
import { Skeleton } from 'antd';
|
||||
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
|
||||
import {
|
||||
@@ -375,11 +375,12 @@ function ServiceAccountDrawer({
|
||||
activeTab === ServiceAccountDrawerTab.Overview && !isDeleted && open ? (
|
||||
<div className="sa-drawer__footer">
|
||||
<AuthZButton
|
||||
size="md"
|
||||
checks={[buildSADeletePermission(selectedAccountId ?? '')]}
|
||||
authZEnabled={!!selectedAccountId}
|
||||
withPortal={false}
|
||||
variant="link"
|
||||
color="destructive"
|
||||
color="danger"
|
||||
onClick={(): void => {
|
||||
void setIsDeleteOpen(true);
|
||||
}}
|
||||
@@ -388,11 +389,17 @@ function ServiceAccountDrawer({
|
||||
Delete Service Account
|
||||
</AuthZButton>
|
||||
<div className="sa-drawer__footer-right">
|
||||
<Button variant="outlined" color="secondary" onClick={handleClose}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<X size={14} />
|
||||
Cancel
|
||||
</Button>
|
||||
<AuthZButton
|
||||
size="md"
|
||||
checks={[
|
||||
buildSAReadPermission(selectedAccountId ?? ''),
|
||||
buildSAUpdatePermission(selectedAccountId ?? ''),
|
||||
@@ -426,7 +433,9 @@ function ServiceAccountDrawer({
|
||||
const body = (
|
||||
<div className="sa-drawer__layout">
|
||||
<div className="sa-drawer__tabs">
|
||||
<ToggleGroupSimple
|
||||
<ToggleGroup
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
type="single"
|
||||
value={activeTab}
|
||||
size="sm"
|
||||
|
||||
@@ -28,20 +28,20 @@ export function NameEmailCell({
|
||||
export function StatusBadge({ status }: { status: string }): JSX.Element {
|
||||
if (status?.toUpperCase() === 'ACTIVE') {
|
||||
return (
|
||||
<Badge color="forest" variant="outline">
|
||||
<Badge color="success" variant="outlined">
|
||||
ACTIVE
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
if (status?.toUpperCase() === 'DELETED') {
|
||||
return (
|
||||
<Badge color="cherry" variant="outline">
|
||||
<Badge color="danger" variant="outlined">
|
||||
DELETED
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Badge color="vanilla" variant="outline" className="sa-status-badge">
|
||||
<Badge color="secondary" variant="outlined" className="sa-status-badge">
|
||||
{status ? status.toUpperCase() : 'UNKNOWN'}
|
||||
</Badge>
|
||||
);
|
||||
|
||||
@@ -13,7 +13,13 @@ function ModalFixture(): JSX.Element {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button data-testid="open-signoz-modal" onClick={(): void => setOpen(true)}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
testId="open-signoz-modal"
|
||||
onClick={(): void => setOpen(true)}
|
||||
>
|
||||
Open modal
|
||||
</Button>
|
||||
<SignozModal
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
|
||||
import { ToggleGroup } from '@signozhq/ui/toggle-group';
|
||||
|
||||
import './SignozRadioGroup.styles.scss';
|
||||
|
||||
@@ -24,12 +24,16 @@ function SignozRadioGroup({
|
||||
disabled = false,
|
||||
}: SignozRadioGroupProps): JSX.Element {
|
||||
return (
|
||||
<ToggleGroupSimple
|
||||
<ToggleGroup
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
type="single"
|
||||
value={value}
|
||||
className={`signoz-radio-group ${className}`}
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
disabledTooltip={undefined}
|
||||
items={options.map((option) => ({
|
||||
value: option.value,
|
||||
label: (
|
||||
|
||||
@@ -12,7 +12,7 @@ function BadgeWithTooltip({
|
||||
return (
|
||||
<div key={label}>
|
||||
<Tooltip title={tooltipTitle}>
|
||||
<Badge className="label-column--tag" color="vanilla">
|
||||
<Badge variant="solid" className="label-column--tag" color="secondary">
|
||||
{getLabelRenderingValue(label, value && value[label])}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
|
||||
@@ -27,7 +27,11 @@ function LabelColumn({ labels, value }: LabelColumnProps): JSX.Element {
|
||||
{labels.map(
|
||||
(label: string): JSX.Element => (
|
||||
<div key={label}>
|
||||
<Badge className="label-column--tag" color="vanilla">
|
||||
<Badge
|
||||
variant="solid"
|
||||
className="label-column--tag"
|
||||
color="secondary"
|
||||
>
|
||||
{getLabelAndValueContent(label, value && value[label])}
|
||||
</Badge>
|
||||
</div>
|
||||
@@ -37,7 +41,7 @@ function LabelColumn({ labels, value }: LabelColumnProps): JSX.Element {
|
||||
}
|
||||
trigger="hover"
|
||||
>
|
||||
<Badge className="label-column--tag" color="vanilla">
|
||||
<Badge variant="solid" className="label-column--tag" color="secondary">
|
||||
+{remainingLabels.length}
|
||||
</Badge>
|
||||
</Popover>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type MouseEvent, type ReactNode } from 'react';
|
||||
import { X } from '@signozhq/icons';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import cx from 'classnames';
|
||||
|
||||
@@ -23,11 +24,22 @@ function TagBadge({
|
||||
}: TagBadgeProps): JSX.Element {
|
||||
return (
|
||||
<Badge
|
||||
color="sienna"
|
||||
variant="outline"
|
||||
color="archive"
|
||||
variant="outlined"
|
||||
className={cx(styles.static, className)}
|
||||
closable={closable}
|
||||
onClose={onClose}
|
||||
suffix={
|
||||
closable ? (
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Remove"
|
||||
onClick={(event): void => {
|
||||
onClose?.(event);
|
||||
}}
|
||||
>
|
||||
<X size={12} />
|
||||
</button>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Badge>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { type ChangeEvent, type KeyboardEvent, useState } from 'react';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import cx from 'classnames';
|
||||
@@ -138,16 +139,18 @@ function TagKeyValueInput({
|
||||
closable
|
||||
onClose={(): void => removeTag(tag)}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
className={styles.tagLabel}
|
||||
title="Double-click to edit"
|
||||
testId={`${testId}-chip`}
|
||||
onDoubleClick={(): void => startEdit(index)}
|
||||
>
|
||||
{tag}
|
||||
</Button>
|
||||
<Tooltip title="Double-click to edit">
|
||||
<Button
|
||||
size="md"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
className={styles.tagLabel}
|
||||
testId={`${testId}-chip`}
|
||||
onDoubleClick={(): void => startEdit(index)}
|
||||
>
|
||||
{tag}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</TagBadge>
|
||||
),
|
||||
)}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { type ReactNode, useLayoutEffect, useMemo } from 'react';
|
||||
import { type ReactNode, useLayoutEffect } from 'react';
|
||||
|
||||
import { chromePerformanceTanstackTableEndHover } from './perfDevtools';
|
||||
import { useIsRowHovered } from './TanStackTableStateContext';
|
||||
import {
|
||||
TooltipContentProps,
|
||||
TooltipSimple,
|
||||
TooltipSimpleProps,
|
||||
} from '@signozhq/ui/tooltip';
|
||||
import { Tooltip, TooltipProps, TooltipProvider } from '@signozhq/ui/tooltip';
|
||||
|
||||
export type HoverTooltipProps = Omit<TooltipSimpleProps, 'open'> & {
|
||||
export type HoverTooltipProps = Omit<TooltipProps, 'open'> & {
|
||||
rowId: string;
|
||||
children: ReactNode;
|
||||
};
|
||||
@@ -26,28 +22,13 @@ export function TanStackHoverTooltip({
|
||||
}
|
||||
}, [isHovered, rowId]);
|
||||
|
||||
const tooltipContentProps = useMemo(
|
||||
() =>
|
||||
({
|
||||
onPointerDownOutside: (e): void => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
},
|
||||
}) satisfies TooltipContentProps,
|
||||
[],
|
||||
);
|
||||
|
||||
if (!isHovered) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<TooltipSimple
|
||||
delayDuration={700}
|
||||
tooltipContentProps={tooltipContentProps}
|
||||
{...tooltipProps}
|
||||
>
|
||||
{children}
|
||||
</TooltipSimple>
|
||||
<TooltipProvider delay={700}>
|
||||
<Tooltip {...tooltipProps}>{children}</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Ellipsis } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
|
||||
import { DropdownMenuSimple } from 'components/DropdownMenu/DropdownMenuSimple';
|
||||
import { GroupedStatusCounts } from 'container/InfraMonitoringK8sV2/components/GroupedStatusCounts';
|
||||
import type { StatusCountItem } from 'container/InfraMonitoringK8sV2/components/GroupedStatusCounts';
|
||||
import { ValidateColumnValueWrapper } from 'container/InfraMonitoringK8sV2/components/ValidateColumnValueWrapper';
|
||||
@@ -104,7 +104,8 @@ const rowActions = (): JSX.Element => (
|
||||
<Button
|
||||
aria-label="Service actions"
|
||||
color="secondary"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
variant="outlined"
|
||||
>
|
||||
<Ellipsis size={16} />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Dispatch, SetStateAction, useCallback, useMemo } from 'react';
|
||||
import { ChevronDown, Globe } from '@signozhq/icons';
|
||||
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
|
||||
import { DropdownMenuSimple } from 'components/DropdownMenu/DropdownMenuSimple';
|
||||
import { Button } from 'antd';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import TimeItems, {
|
||||
|
||||
@@ -47,5 +47,4 @@ export enum LOCALSTORAGE {
|
||||
DASHBOARDS_LIST_VIEWS = 'DASHBOARDS_LIST_VIEWS',
|
||||
DASHBOARD_V2_PANEL_COLUMN_WIDTHS = 'DASHBOARD_V2_PANEL_COLUMN_WIDTHS',
|
||||
LLM_ATTRIBUTE_MAPPING_TEST_SPAN = 'LLM_ATTRIBUTE_MAPPING_TEST_SPAN',
|
||||
SAVED_VIEW_ENABLED = 'SAVED_VIEW_ENABLED',
|
||||
}
|
||||
|
||||
@@ -129,10 +129,6 @@ const themeColors = {
|
||||
salmon2: '#FFAB91',
|
||||
salmon3: '#E0876A',
|
||||
},
|
||||
/* Series palette (dark). Hues in the red band are deliberately absent: red is
|
||||
reserved for thresholds and error states, so an arbitrary series must never
|
||||
claim it. generateColor indexes by `hash % Object.keys(...).length`, so
|
||||
adding or removing an entry recolors every existing chart. */
|
||||
chartcolors: {
|
||||
// Blues (3)
|
||||
dodgerBlue: '#2F80ED',
|
||||
@@ -156,13 +152,13 @@ const themeColors = {
|
||||
|
||||
// Oranges (3)
|
||||
festivalOrange: '#F2994A',
|
||||
amber1: '#E1A155',
|
||||
coralOrange: '#E17055',
|
||||
pumpkin: '#FF7F50',
|
||||
|
||||
// Olives / Greens (3)
|
||||
olive1: '#DFC33A',
|
||||
olive2: '#D5E55D',
|
||||
green7: '#81C220',
|
||||
// Reds (3)
|
||||
radicalRed: '#FF1A66',
|
||||
crimsonRed: '#EB5757',
|
||||
fireRed: '#E10600',
|
||||
|
||||
// Pinks (3)
|
||||
hotPink: '#E84393',
|
||||
@@ -195,9 +191,9 @@ const themeColors = {
|
||||
orange1: '#D35400',
|
||||
orange2: '#E67E22',
|
||||
orange3: '#F5B041',
|
||||
green8: '#5AC02B',
|
||||
green9: '#48E043',
|
||||
green10: '#68E788',
|
||||
red1: '#C0392B',
|
||||
red2: '#E74C3C',
|
||||
red3: '#EC7063',
|
||||
pink1: '#D81B60',
|
||||
pink2: '#E91E63',
|
||||
pink3: '#F06292',
|
||||
@@ -216,9 +212,9 @@ const themeColors = {
|
||||
coral1: '#E67E22',
|
||||
coral2: '#F39C12',
|
||||
coral3: '#F5B041',
|
||||
teal7: '#2BC07B',
|
||||
teal8: '#43E0C5',
|
||||
teal9: '#68D9E7',
|
||||
crimson1: '#C0392B',
|
||||
crimson2: '#E74C3C',
|
||||
crimson3: '#EC7063',
|
||||
violet1: '#8E44AD',
|
||||
violet2: '#9B59B6',
|
||||
violet3: '#BB8FCE',
|
||||
@@ -228,18 +224,18 @@ const themeColors = {
|
||||
forest1: '#27AE60',
|
||||
forest2: '#2ECC71',
|
||||
forest3: '#58D68D',
|
||||
cyan4: '#83C2EB',
|
||||
blush1: '#FF6F91',
|
||||
blush2: '#FF85A2',
|
||||
blush3: '#FFA0B3',
|
||||
lavender1: '#9B59B6',
|
||||
lavender2: '#AF7AC5',
|
||||
lavender3: '#C39BD3',
|
||||
blue7: '#4375E0',
|
||||
blue8: '#686DE7',
|
||||
indigo1: '#A68EED',
|
||||
indigo2: '#B980EA',
|
||||
purple6: '#EE98D9',
|
||||
olive3: '#F2F0AE',
|
||||
tomato1: '#E74C3C',
|
||||
tomato2: '#EC7063',
|
||||
tomato3: '#F1948A',
|
||||
salmon1: '#FF6B6B',
|
||||
salmon2: '#FF8787',
|
||||
salmon3: '#FFA1A1',
|
||||
mustard1: '#F1C40F',
|
||||
mustard2: '#F7DC6F',
|
||||
mustard3: '#F9E79F',
|
||||
@@ -258,9 +254,9 @@ const themeColors = {
|
||||
blue4: '#2874A6',
|
||||
blue5: '#2E86C1',
|
||||
blue6: '#3498DB',
|
||||
purple4: '#A52BC0',
|
||||
purple5: '#E043D0',
|
||||
magenta4: '#E768B5',
|
||||
red4: '#C0392B',
|
||||
red5: '#E74C3C',
|
||||
red6: '#EC7063',
|
||||
orange4: '#D35400',
|
||||
orange5: '#E67E22',
|
||||
orange6: '#EB984E',
|
||||
@@ -271,19 +267,18 @@ const themeColors = {
|
||||
gold5: '#F1C40F',
|
||||
gold6: '#F4D03F',
|
||||
},
|
||||
/* Series palette (light). Same red-free constraint as chartcolors above. */
|
||||
lightModeColor: {
|
||||
magenta1: '#D81B60',
|
||||
radicalRed: '#D81B60',
|
||||
|
||||
dodgerBlueDark: '#1E5BD9',
|
||||
steelgrey: '#344B6B',
|
||||
steelpurple: '#5E548E',
|
||||
steelindigo: '#8E4A7C',
|
||||
steelpink: '#B63A6F',
|
||||
amber1: '#E1A14B',
|
||||
steelcoral: '#E14B5A',
|
||||
steelorange: '#E76F2F',
|
||||
steelgold: '#E09B00',
|
||||
olive1: '#C9BD3A',
|
||||
steelrust: '#C93A50',
|
||||
steelgreen: '#2F7D69',
|
||||
|
||||
mediumOrchidDark: '#8E24AA',
|
||||
@@ -291,17 +286,17 @@ const themeColors = {
|
||||
seaGreen: '#1E7F5A',
|
||||
turquoiseBlueDark: '#007EA7',
|
||||
silverDark: '#5F5F5F',
|
||||
green1: '#ACDB24',
|
||||
green2: '#66CC21',
|
||||
outrageousOrangeDark: '#E64A19',
|
||||
roseBudDark: '#D84315',
|
||||
deepSkyBlueDark: '#0277BD',
|
||||
royalBlue: '#2A4FDB',
|
||||
|
||||
avocadoDark: '#6B6B1E',
|
||||
mintGreenDark: '#2E9E55',
|
||||
green3: '#3F8B3A',
|
||||
chestnut: '#8B3A3A',
|
||||
limaDark: '#5C7F00',
|
||||
olive: '#6E7F00',
|
||||
green4: '#3CC964',
|
||||
beautyBushDark: '#C93C3C',
|
||||
|
||||
danube: '#4F6FB3',
|
||||
oliveDrab: '#4F7F1A',
|
||||
@@ -309,13 +304,13 @@ const themeColors = {
|
||||
electricLimeDark: '#6B8F00',
|
||||
robin: '#2F4FCC',
|
||||
|
||||
teal1: '#1FBF83',
|
||||
harleyOrange: '#CC2E12',
|
||||
gladeGreen: '#4F7F46',
|
||||
hemlock: '#5C5C45',
|
||||
vidaLoca: '#3D6B00',
|
||||
rust: '#993300',
|
||||
|
||||
teal2: '#28C6C1',
|
||||
red: '#C62828',
|
||||
blue: '#1A237E',
|
||||
green: '#1B7F3A',
|
||||
purple: '#6A1B9A',
|
||||
@@ -325,7 +320,7 @@ const themeColors = {
|
||||
brown: '#7A3A1E',
|
||||
teal: '#006D6F',
|
||||
limeDark: '#4C8C2B',
|
||||
cyan1: '#1B546D',
|
||||
maroon: '#6D1B1B',
|
||||
navy: '#0D1B5E',
|
||||
gray: '#616161',
|
||||
|
||||
@@ -333,25 +328,25 @@ const themeColors = {
|
||||
indigo: '#303F9F',
|
||||
slateGray: '#556B7C',
|
||||
chocolate: '#9C4A1A',
|
||||
blue1: '#3B74DF',
|
||||
tomato: '#E53935',
|
||||
steelBlue: '#3A6EA5',
|
||||
|
||||
peruDark: '#B35E00',
|
||||
darkOliveGreen: '#445B1F',
|
||||
blue2: '#4041B0',
|
||||
indianRed: '#B04040',
|
||||
mediumSlateBlue: '#5C6BC0',
|
||||
indigo1: '#6644A9',
|
||||
rosyBrownDark: '#A94444',
|
||||
darkSlateGray: '#2E4A4A',
|
||||
|
||||
fuchsia: '#C511C5',
|
||||
indigo2: '#AD42E0',
|
||||
purple1: '#C83AC5',
|
||||
salmonDark: '#E64A3C',
|
||||
darkSalmonDark: '#C85A3A',
|
||||
paleVioletRedDark: '#C2186A',
|
||||
|
||||
mediumPurple: '#7E57C2',
|
||||
darkOrchid: '#7B1FA2',
|
||||
mediumSeaGreenDark: '#2E8B57',
|
||||
purple2: '#E573BC',
|
||||
lightCoralDark: '#E57373',
|
||||
|
||||
gold: '#D4AF37',
|
||||
sandyBrownDark: '#C76A15',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { Drawer } from 'antd';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { Maximize2, Plus, X } from '@signozhq/icons';
|
||||
@@ -53,22 +53,25 @@ export default function AIAssistantDrawer(): JSX.Element {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<TooltipSimple title="New conversation">
|
||||
<Tooltip title="New conversation">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={handleNewConversation}
|
||||
aria-label="New conversation"
|
||||
>
|
||||
<Plus size={16} />
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
|
||||
<TooltipSimple title="Open full screen">
|
||||
<Tooltip title="Open full screen">
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={handleExpand}
|
||||
disabled={!activeConversationId}
|
||||
@@ -76,19 +79,20 @@ export default function AIAssistantDrawer(): JSX.Element {
|
||||
>
|
||||
<Maximize2 size={16} />
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
|
||||
<TooltipSimple title="Close">
|
||||
<Tooltip title="Close">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={closeDrawer}
|
||||
aria-label="Close drawer"
|
||||
>
|
||||
<X size={16} />
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useHistory, useLocation } from 'react-router-dom';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { History, Maximize2, Minus, Plus, X } from '@signozhq/icons';
|
||||
import Noz from 'components/Noz/Noz';
|
||||
@@ -159,62 +159,73 @@ export default function AIAssistantModal(): JSX.Element | null {
|
||||
</div>
|
||||
|
||||
<div className={styles.actions}>
|
||||
<TooltipSimple title={showHistory ? 'Back to chat' : 'Conversations'}>
|
||||
<Tooltip title={showHistory ? 'Back to chat' : 'Conversations'}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={(): void => setShowHistory((v) => !v)}
|
||||
aria-label="Toggle conversations"
|
||||
className={showHistory ? styles.toggleBtnActive : ''}
|
||||
prefix={<History size={14} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<History size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<TooltipSimple title="New conversation">
|
||||
<Tooltip title="New conversation">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={handleNew}
|
||||
aria-label="New conversation"
|
||||
prefix={<Plus size={14} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<Plus size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<TooltipSimple title="Open full screen">
|
||||
<Tooltip title="Open full screen">
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={handleExpand}
|
||||
disabled={!activeConversationId}
|
||||
aria-label="Open full screen"
|
||||
prefix={<Maximize2 size={14} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<Maximize2 size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<TooltipSimple title="Minimize to side panel">
|
||||
<Tooltip title="Minimize to side panel">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={handleMinimize}
|
||||
aria-label="Minimize to side panel"
|
||||
prefix={<Minus size={14} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<Minus size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<TooltipSimple title="Close">
|
||||
<Tooltip title="Close">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={closeModal}
|
||||
aria-label="Close"
|
||||
prefix={<X size={14} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<X size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { matchPath, useHistory, useLocation } from 'react-router-dom';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { History, Maximize2, Plus, X } from '@signozhq/icons';
|
||||
import Noz from 'components/Noz/Noz';
|
||||
@@ -144,50 +144,59 @@ export default function AIAssistantPanel(): JSX.Element | null {
|
||||
</div>
|
||||
|
||||
<div className={styles.actions}>
|
||||
<TooltipSimple title={showHistory ? 'Back to chat' : 'Conversations'}>
|
||||
<Tooltip title={showHistory ? 'Back to chat' : 'Conversations'}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={(): void => setShowHistory((v) => !v)}
|
||||
aria-label="Toggle conversations"
|
||||
prefix={<History size={14} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<History size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<TooltipSimple title="New conversation">
|
||||
<Tooltip title="New conversation">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={handleNew}
|
||||
aria-label="New conversation"
|
||||
prefix={<Plus size={14} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<Plus size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<TooltipSimple title="Open full screen">
|
||||
<Tooltip title="Open full screen">
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={handleExpand}
|
||||
disabled={!activeConversationId}
|
||||
aria-label="Open full screen"
|
||||
prefix={<Maximize2 size={14} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<Maximize2 size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<TooltipSimple title="Close">
|
||||
<Tooltip title="Close">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={closeDrawer}
|
||||
aria-label="Close panel"
|
||||
prefix={<X size={14} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<X size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { matchPath, useLocation } from 'react-router-dom';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import ROUTES from 'constants/routes';
|
||||
import Noz from 'components/Noz/Noz';
|
||||
@@ -43,15 +43,18 @@ export default function AIAssistantTrigger(): JSX.Element | null {
|
||||
}
|
||||
|
||||
return (
|
||||
<TooltipSimple title={NOZ_TOOLTIP_TITLE}>
|
||||
<Tooltip title={NOZ_TOOLTIP_TITLE}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
icon
|
||||
className={`${styles.trigger} noz-wave`}
|
||||
onClick={handleOpen}
|
||||
aria-label="Open Noz"
|
||||
prefix={<Noz size={24} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<Noz size={24} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import cx from 'classnames';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import type { MessageActionDTO } from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
|
||||
import {
|
||||
ApplyFilterSignalDTO,
|
||||
@@ -631,6 +631,7 @@ export default function ActionsSection({
|
||||
|
||||
const chip = (
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
@@ -645,9 +646,9 @@ export default function ActionsSection({
|
||||
);
|
||||
|
||||
return tooltip ? (
|
||||
<TooltipSimple key={key} title={tooltip}>
|
||||
<Tooltip key={key} title={tooltip}>
|
||||
{chip}
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span key={key}>{chip}</span>
|
||||
);
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
DialogSubtitle,
|
||||
DialogTitle,
|
||||
} from '@signozhq/ui/dialog';
|
||||
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { ToggleGroup } from '@signozhq/ui/toggle-group';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import type {
|
||||
ApprovalEventDTO,
|
||||
ApprovalEventDTODiff,
|
||||
@@ -101,16 +101,18 @@ export default function ApprovalCard({
|
||||
<div className={styles.diffSection}>
|
||||
<div className={styles.diffHeader}>
|
||||
<span className={styles.diffHeaderLabel}>Diff</span>
|
||||
<TooltipSimple title="Expand diff">
|
||||
<Tooltip title="Expand diff">
|
||||
<Button
|
||||
variant="link"
|
||||
size="sm"
|
||||
color="secondary"
|
||||
icon
|
||||
onClick={(): void => setDiffExpanded(true)}
|
||||
aria-label="Expand diff"
|
||||
prefix={<Maximize2 size={12} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<Maximize2 size={12} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<DiffView diff={approval.diff} />
|
||||
</div>
|
||||
@@ -132,7 +134,9 @@ export default function ApprovalCard({
|
||||
<div className={styles.diffModalBody}>
|
||||
<p className={styles.diffModalSummary}>{approval.summary}</p>
|
||||
<div className={styles.diffToolbarRow}>
|
||||
<ToggleGroupSimple
|
||||
<ToggleGroup
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
type="single"
|
||||
size="sm"
|
||||
value={viewMode}
|
||||
@@ -156,7 +160,9 @@ export default function ApprovalCard({
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<ToggleGroupSimple
|
||||
<ToggleGroup
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
type="multiple"
|
||||
size="sm"
|
||||
value={wrapText ? ['wrap'] : []}
|
||||
@@ -185,6 +191,8 @@ export default function ApprovalCard({
|
||||
|
||||
<div className={styles.actions}>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
color="primary"
|
||||
variant="solid"
|
||||
size="sm"
|
||||
onClick={handleApprove}
|
||||
@@ -194,6 +202,7 @@ export default function ApprovalCard({
|
||||
Approve
|
||||
</Button>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
variant="outlined"
|
||||
size="sm"
|
||||
color="secondary"
|
||||
@@ -465,7 +474,7 @@ function CopyButton({ text, label }: CopyButtonProps): JSX.Element {
|
||||
};
|
||||
|
||||
return (
|
||||
<TooltipSimple title={copied ? `Copied ${label}` : `Copy ${label}`}>
|
||||
<Tooltip title={copied ? `Copied ${label}` : `Copy ${label}`}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@@ -475,6 +484,6 @@ function CopyButton({ text, label }: CopyButtonProps): JSX.Element {
|
||||
>
|
||||
{copied ? <Check size={12} /> : <Copy size={12} />}
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Button } from '@signozhq/ui/button';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@signozhq/ui/popover';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import type { UploadFile } from 'antd';
|
||||
import getSessionStorage from 'api/browser/sessionstorage/get';
|
||||
import setSessionStorage from 'api/browser/sessionstorage/set';
|
||||
@@ -900,8 +900,10 @@ export default function ChatInput({
|
||||
<div key={f.uid} className={styles.attachmentChip}>
|
||||
<span className={styles.attachmentName}>{f.name}</span>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
className={styles.attachmentRemove}
|
||||
onClick={(): void => removeFile(f.uid)}
|
||||
aria-label={`Remove ${f.name}`}
|
||||
@@ -925,7 +927,7 @@ export default function ChatInput({
|
||||
<div className={styles.contextTagContent}>
|
||||
<Badge
|
||||
color="secondary"
|
||||
variant="outline"
|
||||
variant="outlined"
|
||||
className={styles.contextTagCategory}
|
||||
>
|
||||
{category}
|
||||
@@ -935,13 +937,15 @@ export default function ChatInput({
|
||||
{onDismissAutoContext && (
|
||||
<Button
|
||||
variant="link"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
className={styles.contextTagRemove}
|
||||
onClick={(): void => onDismissAutoContext(key)}
|
||||
aria-label={`Remove ${category}: ${label} context`}
|
||||
prefix={<X size={10} />}
|
||||
></Button>
|
||||
>
|
||||
<X size={10} />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -954,7 +958,7 @@ export default function ChatInput({
|
||||
<div className={styles.contextTagContent}>
|
||||
<Badge
|
||||
color="primary"
|
||||
variant="outline"
|
||||
variant="outlined"
|
||||
className={styles.contextTagCategory}
|
||||
>
|
||||
{contextItem.category}
|
||||
@@ -963,15 +967,17 @@ export default function ChatInput({
|
||||
</div>
|
||||
<Button
|
||||
variant="link"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
className={styles.contextTagRemove}
|
||||
onClick={(): void =>
|
||||
removeContext(contextItem.category, contextItem.entityId)
|
||||
}
|
||||
aria-label={`Remove ${contextItem.category}: ${contextItem.value} context`}
|
||||
prefix={<X size={10} />}
|
||||
></Button>
|
||||
>
|
||||
<X size={10} />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -1020,6 +1026,7 @@ export default function ChatInput({
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
@@ -1059,7 +1066,6 @@ export default function ChatInput({
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
role="tab"
|
||||
id={`ai-context-tab-${category}`}
|
||||
// Single stable panel id shared by every tab: only the
|
||||
// active category's tabpanel is rendered, so per-category
|
||||
@@ -1178,18 +1184,20 @@ export default function ChatInput({
|
||||
aria-live="polite"
|
||||
aria-label="Recording voice input"
|
||||
>
|
||||
<TooltipSimple title="Discard recording">
|
||||
<Tooltip title="Discard recording">
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
className={cx(styles.micDiscard, styles.secondary)}
|
||||
onClick={handleDiscard}
|
||||
aria-label="Discard recording"
|
||||
prefix={<X size={12} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<X size={12} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<span className={styles.micWaves} aria-hidden="true">
|
||||
<span />
|
||||
<span />
|
||||
@@ -1200,56 +1208,67 @@ export default function ChatInput({
|
||||
<span />
|
||||
<span />
|
||||
</span>
|
||||
<TooltipSimple title="Stop and send">
|
||||
<Tooltip title="Stop and send">
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
color="destructive"
|
||||
variant="solid"
|
||||
size="sm"
|
||||
icon
|
||||
color="danger"
|
||||
className={cx(styles.micStop, styles.destructive)}
|
||||
onClick={handleStopAndSend}
|
||||
aria-label="Stop and send"
|
||||
prefix={<Square size={9} fill="currentColor" strokeWidth={0} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<Square size={9} fill="currentColor" strokeWidth={0} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : (
|
||||
<TooltipSimple title="Voice input">
|
||||
<Tooltip title="Voice input">
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
color="secondary"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
onClick={(): void => startVoiceInput(VoiceInputSource.Button)}
|
||||
disabled={disabled}
|
||||
aria-label="Start voice input"
|
||||
className={styles.micBtn}
|
||||
prefix={<Mic size={14} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<Mic size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
))}
|
||||
|
||||
{isStreaming && onCancel ? (
|
||||
<TooltipSimple title="Stop generating">
|
||||
<Tooltip title="Stop generating">
|
||||
<Button
|
||||
variant="solid"
|
||||
size="icon"
|
||||
color="destructive"
|
||||
size="sm"
|
||||
icon
|
||||
color="danger"
|
||||
onClick={onCancel}
|
||||
aria-label="Stop generating"
|
||||
prefix={<Square size={10} fill="currentColor" strokeWidth={0} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<Square size={10} fill="currentColor" strokeWidth={0} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<TooltipSimple title="Send message">
|
||||
<Tooltip title="Send message">
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
variant="solid"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="primary"
|
||||
onClick={isListening ? handleStopAndSend : handleSend}
|
||||
disabled={disabled || (!text.trim() && pendingFiles.length === 0)}
|
||||
aria-label="Send message"
|
||||
prefix={<Send size={14} />}
|
||||
/>
|
||||
</TooltipSimple>
|
||||
>
|
||||
<Send size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -58,7 +58,7 @@ export default function ContextPickerEmptyState({
|
||||
color="primary"
|
||||
className={styles.contextPopoverEmptyCta}
|
||||
onClick={(): void => onPrefill(prefill)}
|
||||
data-testid={`ai-context-empty-cta-${category}`}
|
||||
testId={`ai-context-empty-cta-${category}`}
|
||||
prefix={<Sparkles size={14} />}
|
||||
>
|
||||
<span className={styles.contextPopoverEmptyCtaLabel}>{ctaLabel}</span>
|
||||
|
||||
@@ -140,6 +140,8 @@ export default function ClarificationForm({
|
||||
|
||||
<div className={styles.actions}>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
onClick={handleSubmit}
|
||||
@@ -149,6 +151,8 @@ export default function ClarificationForm({
|
||||
Submit
|
||||
</Button>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={handleCancel}
|
||||
@@ -310,6 +314,7 @@ function FieldInput({ field, value, onChange }: FieldInputProps): JSX.Element {
|
||||
return (
|
||||
<div className={styles.field}>
|
||||
<Checkbox
|
||||
color="primary"
|
||||
className={styles.checkboxLabel}
|
||||
value={checked}
|
||||
onChange={(): void => onChange(!checked)}
|
||||
@@ -382,6 +387,7 @@ function FieldInput({ field, value, onChange }: FieldInputProps): JSX.Element {
|
||||
<div className={styles.checkboxGroup}>
|
||||
{options?.map((opt) => (
|
||||
<Checkbox
|
||||
color="primary"
|
||||
key={opt}
|
||||
className={styles.checkboxLabel}
|
||||
value={regularSelected.includes(opt)}
|
||||
@@ -392,6 +398,7 @@ function FieldInput({ field, value, onChange }: FieldInputProps): JSX.Element {
|
||||
))}
|
||||
{allowCustom && (
|
||||
<Checkbox
|
||||
color="primary"
|
||||
className={styles.checkboxLabel}
|
||||
value={isCustom}
|
||||
onChange={toggleCustom}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { getAbsoluteUrl } from 'utils/basePath';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
|
||||
import { DropdownMenuSimple } from 'components/DropdownMenu/DropdownMenuSimple';
|
||||
|
||||
import {
|
||||
Archive,
|
||||
@@ -207,17 +207,18 @@ export default function ConversationItem({
|
||||
<DropdownMenuSimple
|
||||
menu={{ items: menuItems }}
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
className={styles.menu}
|
||||
>
|
||||
<Button
|
||||
variant="link"
|
||||
size="icon"
|
||||
color="none"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
className={styles.btn}
|
||||
aria-label="Conversation actions"
|
||||
prefix={<EllipsisVertical size={12} />}
|
||||
/>
|
||||
>
|
||||
<EllipsisVertical size={12} />
|
||||
</Button>
|
||||
</DropdownMenuSimple>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import cx from 'classnames';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Input } from '@signozhq/ui/input';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { Plus, Search } from '@signozhq/icons';
|
||||
|
||||
import logEvent from 'api/common/logEvent';
|
||||
@@ -171,7 +171,7 @@ export default function ConversationsList({
|
||||
{isLoadingThreads && <HeaderLoadingDots />}
|
||||
|
||||
{!isLoadingThreads && showAddNewConversation && (
|
||||
<TooltipSimple title="New conversation">
|
||||
<Tooltip title="New conversation">
|
||||
<Button
|
||||
variant="solid"
|
||||
size="sm"
|
||||
@@ -181,7 +181,7 @@ export default function ConversationsList({
|
||||
>
|
||||
<Plus size={12} />
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import cx from 'classnames';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { DialogWrapper } from '@signozhq/ui/dialog';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { Check, Copy, RefreshCw, ThumbsDown, ThumbsUp } from '@signozhq/icons';
|
||||
import { useTimezone } from 'providers/Timezone';
|
||||
@@ -174,10 +174,11 @@ export default function MessageFeedback({
|
||||
<>
|
||||
<div className={cx(styles.feedback, { [styles.visible]: isLastAssistant })}>
|
||||
<div className={styles.actions}>
|
||||
<TooltipSimple title={copied ? 'Copied!' : 'Copy'}>
|
||||
<Tooltip title={copied ? 'Copied!' : 'Copy'}>
|
||||
<Button
|
||||
className={styles.btn}
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
variant="ghost"
|
||||
onClick={handleCopy}
|
||||
color="secondary"
|
||||
@@ -185,14 +186,15 @@ export default function MessageFeedback({
|
||||
>
|
||||
{copied ? <Check size={12} /> : <Copy size={12} />}
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
|
||||
<TooltipSimple title={VOTE_LABEL[FeedbackRatingDTO.positive].tooltip}>
|
||||
<Tooltip title={VOTE_LABEL[FeedbackRatingDTO.positive].tooltip}>
|
||||
<Button
|
||||
className={cx(styles.btn, {
|
||||
[styles.votedUp]: vote === FeedbackRatingDTO.positive,
|
||||
})}
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
onClick={(): void => handleVote(FeedbackRatingDTO.positive)}
|
||||
@@ -201,14 +203,15 @@ export default function MessageFeedback({
|
||||
>
|
||||
<ThumbsUp size={12} />
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
|
||||
<TooltipSimple title={VOTE_LABEL[FeedbackRatingDTO.negative].tooltip}>
|
||||
<Tooltip title={VOTE_LABEL[FeedbackRatingDTO.negative].tooltip}>
|
||||
<Button
|
||||
className={cx(styles.btn, {
|
||||
[styles.votedDown]: vote === FeedbackRatingDTO.negative,
|
||||
})}
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
onClick={(): void => handleVote(FeedbackRatingDTO.negative)}
|
||||
@@ -217,13 +220,14 @@ export default function MessageFeedback({
|
||||
>
|
||||
<ThumbsDown size={12} />
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
|
||||
{onRegenerate && (
|
||||
<TooltipSimple title="Regenerate">
|
||||
<Tooltip title="Regenerate">
|
||||
<Button
|
||||
className={styles.btn}
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
onClick={onRegenerate}
|
||||
@@ -231,7 +235,7 @@ export default function MessageFeedback({
|
||||
>
|
||||
<RefreshCw size={12} />
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -249,13 +253,19 @@ export default function MessageFeedback({
|
||||
footer={
|
||||
<div className={styles.feedbackDialogFooter}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
onClick={(): void => setIsNegativeDialogOpen(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="solid" color="primary" onClick={handleSubmitNegative}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
onClick={handleSubmitNegative}
|
||||
>
|
||||
Send feedback
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { Check, Copy } from '@signozhq/icons';
|
||||
|
||||
import logEvent from 'api/common/logEvent';
|
||||
@@ -40,10 +40,11 @@ export default function UserMessageActions({
|
||||
|
||||
return (
|
||||
<div className={styles.actions}>
|
||||
<TooltipSimple title={copied ? 'Copied!' : 'Copy'}>
|
||||
<Tooltip title={copied ? 'Copied!' : 'Copy'}>
|
||||
<Button
|
||||
className={styles.btn}
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
onClick={handleCopy}
|
||||
@@ -51,7 +52,7 @@ export default function UserMessageActions({
|
||||
>
|
||||
{copied ? <Check size={12} /> : <Copy size={12} />}
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -189,11 +189,16 @@ export default function ActionBlock({
|
||||
)}
|
||||
|
||||
<div className={styles.actions}>
|
||||
<Button variant="solid" size="sm" onClick={execute}>
|
||||
<Button color="primary" variant="solid" size="sm" onClick={execute}>
|
||||
<Check size={12} />
|
||||
Apply
|
||||
</Button>
|
||||
<Button variant="outlined" size="sm" onClick={handleDismiss}>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
size="sm"
|
||||
onClick={handleDismiss}
|
||||
>
|
||||
<X size={12} />
|
||||
Dismiss
|
||||
</Button>
|
||||
|
||||
@@ -83,11 +83,17 @@ export default function ConfirmBlock({
|
||||
<div className={blockStyles.block}>
|
||||
{message && <p className={styles.message}>{message}</p>}
|
||||
<div className={styles.actions}>
|
||||
<Button variant="solid" size="sm" onClick={(): void => handle('accepted')}>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="solid"
|
||||
size="sm"
|
||||
onClick={(): void => handle('accepted')}
|
||||
>
|
||||
<Check size={12} />
|
||||
{acceptLabel}
|
||||
</Button>
|
||||
<Button
|
||||
color="secondary"
|
||||
variant="outlined"
|
||||
size="sm"
|
||||
onClick={(): void => handle('rejected')}
|
||||
|
||||
@@ -3,7 +3,7 @@ import cx from 'classnames';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { Checkbox } from '@signozhq/ui/checkbox';
|
||||
import { RadioGroup, RadioGroupItem } from '@signozhq/ui/radio-group';
|
||||
import { RadioGroup } from '@signozhq/ui/radio-group';
|
||||
|
||||
import { AIAssistantEvents } from '../../../events';
|
||||
import { useAIAssistantAnalyticsContext } from '../../../hooks/useAIAssistantAnalyticsContext';
|
||||
@@ -83,27 +83,24 @@ export default function InteractiveQuestion({
|
||||
|
||||
{type === 'radio' ? (
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
textOverflow="wrap"
|
||||
className={styles.options}
|
||||
onChange={(value): void => {
|
||||
setSelected([value]);
|
||||
handleSubmit([value]);
|
||||
}}
|
||||
>
|
||||
{normalized.map((opt) => (
|
||||
<RadioGroupItem
|
||||
key={opt.value}
|
||||
value={opt.value}
|
||||
className={styles.option}
|
||||
>
|
||||
{opt.label}
|
||||
</RadioGroupItem>
|
||||
))}
|
||||
</RadioGroup>
|
||||
items={normalized.map((opt) => ({
|
||||
value: opt.value,
|
||||
label: <span className={styles.option}>{opt.label}</span>,
|
||||
}))}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<div className={cx(styles.options, styles.checkbox)}>
|
||||
{normalized.map((opt) => (
|
||||
<Checkbox
|
||||
color="primary"
|
||||
key={opt.value}
|
||||
value={selected.includes(opt.value)}
|
||||
onChange={(checked): void => {
|
||||
@@ -120,6 +117,8 @@ export default function InteractiveQuestion({
|
||||
))}
|
||||
</div>
|
||||
<Button
|
||||
disabledTooltip={undefined}
|
||||
color="primary"
|
||||
variant="solid"
|
||||
size="sm"
|
||||
className={styles.submit}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user