mirror of
https://github.com/SigNoz/signoz.git
synced 2026-02-27 10:42:53 +00:00
Compare commits
36 Commits
nv/4074
...
feat/azure
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf078e906a | ||
|
|
ac40cc4f5c | ||
|
|
49a90e79f9 | ||
|
|
14cfc31c88 | ||
|
|
376d650a8c | ||
|
|
0ab179b5df | ||
|
|
a9b1dd8510 | ||
|
|
9beff5a527 | ||
|
|
3bbde37f08 | ||
|
|
b4eb5f9df5 | ||
|
|
3adbf92a8e | ||
|
|
ca006bc851 | ||
|
|
534ceac3d7 | ||
|
|
89e64fde70 | ||
|
|
b52bfb16d8 | ||
|
|
1facf20561 | ||
|
|
048de52246 | ||
|
|
644480c4c3 | ||
|
|
08748dfe7f | ||
|
|
9dce854255 | ||
|
|
b2539b337e | ||
|
|
9d45e75d52 | ||
|
|
9c7a54b549 | ||
|
|
763e13df21 | ||
|
|
5cb81fe17a | ||
|
|
3ecd0a662c | ||
|
|
b062a8a463 | ||
|
|
82a67b62e2 | ||
|
|
9a70da858f | ||
|
|
74a548e2a2 | ||
|
|
be68b71bd8 | ||
|
|
5119a62a77 | ||
|
|
5203a9f177 | ||
|
|
09ac5abe33 | ||
|
|
bea4f32fe9 | ||
|
|
1e07714075 |
77
frontend/.cursor/rules/jest-mocking-strategy.mdc
Normal file
77
frontend/.cursor/rules/jest-mocking-strategy.mdc
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
description: Global vs local mock strategy for Jest tests
|
||||
globs:
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/__tests__/**/*.ts"
|
||||
- "**/__tests__/**/*.tsx"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Mock Decision Strategy
|
||||
|
||||
## Global Mocks (20+ test files)
|
||||
|
||||
- Core infrastructure: react-router-dom, react-query, antd
|
||||
- Browser APIs: ResizeObserver, matchMedia, localStorage
|
||||
- Utility libraries: date-fns, lodash
|
||||
- Available: `uplot` → `__mocks__/uplotMock.ts`
|
||||
|
||||
## Local Mocks (5–15 test files)
|
||||
|
||||
- Business logic dependencies
|
||||
- API endpoints with specific responses
|
||||
- Domain-specific components
|
||||
- Error scenarios and edge cases
|
||||
|
||||
## Decision Tree
|
||||
|
||||
```
|
||||
Is it used in 20+ test files?
|
||||
├─ YES → Use Global Mock
|
||||
│ ├─ react-router-dom
|
||||
│ ├─ react-query
|
||||
│ ├─ antd components
|
||||
│ └─ browser APIs
|
||||
│
|
||||
└─ NO → Is it business logic?
|
||||
├─ YES → Use Local Mock
|
||||
│ ├─ API endpoints
|
||||
│ ├─ Custom hooks
|
||||
│ └─ Domain components
|
||||
│
|
||||
└─ NO → Is it test-specific?
|
||||
├─ YES → Use Local Mock
|
||||
│ ├─ Error scenarios
|
||||
│ ├─ Loading states
|
||||
│ └─ Specific data
|
||||
│
|
||||
└─ NO → Consider Global Mock
|
||||
└─ If it becomes frequently used
|
||||
```
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
❌ Don't mock global dependencies locally:
|
||||
```ts
|
||||
jest.mock('react-router-dom', () => ({ ... })); // Already globally mocked
|
||||
```
|
||||
|
||||
❌ Don't create global mocks for test-specific data:
|
||||
```ts
|
||||
jest.mock('../api/tracesService', () => ({
|
||||
getTraces: jest.fn(() => specificTestData) // BAD - should be local
|
||||
}));
|
||||
```
|
||||
|
||||
✅ Do use global mocks for infrastructure:
|
||||
```ts
|
||||
import { useLocation } from 'react-router-dom';
|
||||
```
|
||||
|
||||
✅ Do create local mocks for business logic:
|
||||
```ts
|
||||
jest.mock('../api/tracesService', () => ({
|
||||
getTraces: jest.fn(() => mockTracesData)
|
||||
}));
|
||||
```
|
||||
124
frontend/.cursor/rules/jest-test-conventions.mdc
Normal file
124
frontend/.cursor/rules/jest-test-conventions.mdc
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
description: Core Jest/React Testing Library conventions - harness, MSW, interactions, timers
|
||||
globs:
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/__tests__/**/*.ts"
|
||||
- "**/__tests__/**/*.tsx"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Jest Test Conventions
|
||||
|
||||
Expert developer with Jest, React Testing Library, MSW, and TypeScript. Focus on critical functionality, mock dependencies before imports, test multiple scenarios, write maintainable tests.
|
||||
|
||||
**Auto-detect TypeScript**: Check for TypeScript in the project through tsconfig.json or package.json dependencies. Adjust syntax based on this detection.
|
||||
|
||||
## Imports
|
||||
|
||||
Always import from our harness:
|
||||
```ts
|
||||
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
||||
```
|
||||
For API mocks:
|
||||
```ts
|
||||
import { server, rest } from 'mocks-server/server';
|
||||
```
|
||||
❌ Do not import directly from `@testing-library/react`.
|
||||
|
||||
## Router
|
||||
|
||||
Use the router built into render:
|
||||
```ts
|
||||
render(<Page />, undefined, { initialRoute: '/traces-explorer' });
|
||||
```
|
||||
Only mock `useLocation` / `useParams` if the test depends on them.
|
||||
|
||||
## Hook Mocks
|
||||
|
||||
```ts
|
||||
import useFoo from 'hooks/useFoo';
|
||||
jest.mock('hooks/useFoo');
|
||||
const mockUseFoo = jest.mocked(useFoo);
|
||||
mockUseFoo.mockReturnValue(/* minimal shape */ as any);
|
||||
```
|
||||
Prefer helpers (`rqSuccess`, `rqLoading`, `rqError`) for React Query results.
|
||||
|
||||
## MSW
|
||||
|
||||
Global MSW server runs automatically. Override per-test:
|
||||
```ts
|
||||
server.use(
|
||||
rest.get('*/api/v1/foo', (_req, res, ctx) => res(ctx.status(200), ctx.json({ ok: true })))
|
||||
);
|
||||
```
|
||||
Keep large responses in `mocks-server/__mockdata__/`.
|
||||
|
||||
## Interactions
|
||||
|
||||
- Prefer `userEvent` for real user interactions (click, type, select, tab).
|
||||
- Use `fireEvent` only for low-level/programmatic events not covered by `userEvent` (e.g., scroll, resize, setting `element.scrollTop` for virtualization). Wrap in `act(...)` if needed.
|
||||
- Always await interactions:
|
||||
```ts
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
await user.click(screen.getByRole('button', { name: /save/i }));
|
||||
```
|
||||
|
||||
```ts
|
||||
// Example: virtualized list scroll (no userEvent helper)
|
||||
const scroller = container.querySelector('[data-test-id="virtuoso-scroller"]') as HTMLElement;
|
||||
scroller.scrollTop = targetScrollTop;
|
||||
act(() => { fireEvent.scroll(scroller); });
|
||||
```
|
||||
|
||||
## Timers
|
||||
|
||||
❌ No global fake timers. ✅ Per-test only:
|
||||
```ts
|
||||
jest.useFakeTimers();
|
||||
const user = userEvent.setup({ advanceTimers: (ms) => jest.advanceTimersByTime(ms) });
|
||||
await user.type(screen.getByRole('textbox'), 'query');
|
||||
jest.advanceTimersByTime(400);
|
||||
jest.useRealTimers();
|
||||
```
|
||||
|
||||
## Queries
|
||||
|
||||
Prefer accessible queries (`getByRole`, `findByRole`, `getByLabelText`). Fallback: visible text. Last resort: `data-testid`.
|
||||
|
||||
## Best Practices
|
||||
|
||||
- **Critical Functionality**: Prioritize testing business logic and utilities
|
||||
- **Dependency Mocking**: Global mocks for infra, local mocks for business logic
|
||||
- **Data Scenarios**: Always test valid, invalid, and edge cases
|
||||
- **Descriptive Names**: Make test intent clear
|
||||
- **Organization**: Group related tests in describe
|
||||
- **Consistency**: Match repo conventions
|
||||
- **Edge Cases**: Test null, undefined, unexpected values
|
||||
- **Limit Scope**: 3–5 focused tests per file
|
||||
- **Use Helpers**: `rqSuccess`, `makeUser`, etc.
|
||||
- **No Any**: Enforce type safety
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
❌ Importing RTL directly | ❌ Global fake timers | ❌ Wrapping render in `act(...)` | ❌ Mocking infra locally
|
||||
✅ Use harness | ✅ MSW for API | ✅ userEvent + await | ✅ Pin time only for relative-date tests
|
||||
|
||||
## Example
|
||||
|
||||
```ts
|
||||
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
|
||||
import { server, rest } from 'mocks-server/server';
|
||||
import MyComponent from '../MyComponent';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('renders and interacts', async () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
server.use(rest.get('*/api/v1/example', (_req, res, ctx) => res(ctx.status(200), ctx.json({ value: 42 }))));
|
||||
render(<MyComponent />, undefined, { initialRoute: '/foo' });
|
||||
expect(await screen.findByText(/value: 42/i)).toBeInTheDocument();
|
||||
await user.click(screen.getByRole('button', { name: /refresh/i }));
|
||||
await waitFor(() => expect(screen.getByText(/loading/i)).toBeInTheDocument());
|
||||
});
|
||||
});
|
||||
```
|
||||
168
frontend/.cursor/rules/jest-typescript-type-safety.mdc
Normal file
168
frontend/.cursor/rules/jest-typescript-type-safety.mdc
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
description: TypeScript type safety for Jest tests - mocks, interfaces, no any
|
||||
globs:
|
||||
- "**/*.test.ts"
|
||||
- "**/*.test.tsx"
|
||||
- "**/__tests__/**/*.ts"
|
||||
- "**/__tests__/**/*.tsx"
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# TypeScript Type Safety for Jest Tests
|
||||
|
||||
**CRITICAL**: All Jest tests MUST be fully type-safe.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Use proper TypeScript interfaces for all mock data
|
||||
- Type all Jest mock functions with `jest.MockedFunction<T>`
|
||||
- Use generic types for React components and hooks
|
||||
- Define proper return types for mock functions
|
||||
- Use `as const` for literal types when needed
|
||||
- Avoid `any` type – use proper typing instead
|
||||
|
||||
## Mock Function Typing
|
||||
|
||||
```ts
|
||||
// ✅ GOOD
|
||||
const mockFetchUser = jest.fn() as jest.MockedFunction<(id: number) => Promise<ApiResponse<User>>>;
|
||||
const mockEventHandler = jest.fn() as jest.MockedFunction<(event: Event) => void>;
|
||||
|
||||
// ❌ BAD
|
||||
const mockFetchUser = jest.fn() as any;
|
||||
```
|
||||
|
||||
## Mock Data with Interfaces
|
||||
|
||||
```ts
|
||||
interface User { id: number; name: string; email: string; }
|
||||
interface ApiResponse<T> { data: T; status: number; message: string; }
|
||||
|
||||
const mockUser: User = { id: 1, name: 'John Doe', email: 'john@example.com' };
|
||||
mockFetchUser.mockResolvedValue({ data: mockUser, status: 200, message: 'Success' });
|
||||
```
|
||||
|
||||
## Component Props Typing
|
||||
|
||||
```ts
|
||||
interface ComponentProps { title: string; data: User[]; onUserSelect: (user: User) => void; }
|
||||
|
||||
const mockProps: ComponentProps = {
|
||||
title: 'Test',
|
||||
data: [{ id: 1, name: 'John', email: 'john@example.com' }],
|
||||
onUserSelect: jest.fn() as jest.MockedFunction<(user: User) => void>,
|
||||
};
|
||||
render(<TestComponent {...mockProps} />);
|
||||
```
|
||||
|
||||
## Hook Testing with Types
|
||||
|
||||
```ts
|
||||
interface UseUserDataReturn {
|
||||
user: User | null;
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
refetch: () => void;
|
||||
}
|
||||
|
||||
describe('useUserData', () => {
|
||||
it('should return user data with proper typing', () => {
|
||||
const mockUser: User = { id: 1, name: 'John', email: 'john@example.com' };
|
||||
mockFetchUser.mockResolvedValue({ data: mockUser, status: 200, message: 'Success' });
|
||||
const { result } = renderHook(() => useUserData(1));
|
||||
expect(result.current.user).toEqual(mockUser);
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(result.current.error).toBeNull();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Generic Mock Typing
|
||||
|
||||
```ts
|
||||
interface MockApiResponse<T> { data: T; status: number; }
|
||||
|
||||
const mockFetchData = jest.fn() as jest.MockedFunction<
|
||||
<T>(endpoint: string) => Promise<MockApiResponse<T>>
|
||||
>;
|
||||
mockFetchData<User>('/users').mockResolvedValue({ data: { id: 1, name: 'John' }, status: 200 });
|
||||
```
|
||||
|
||||
## React Testing Library with Types
|
||||
|
||||
```ts
|
||||
type TestComponentProps = ComponentProps<typeof TestComponent>;
|
||||
|
||||
const renderTestComponent = (props: Partial<TestComponentProps> = {}): RenderResult => {
|
||||
const defaultProps: TestComponentProps = { title: 'Test', data: [], onSelect: jest.fn(), ...props };
|
||||
return render(<TestComponent {...defaultProps} />);
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling with Types
|
||||
|
||||
```ts
|
||||
interface ApiError { message: string; code: number; details?: Record<string, unknown>; }
|
||||
const mockApiError: ApiError = { message: 'API Error', code: 500, details: { endpoint: '/users' } };
|
||||
mockFetchUser.mockRejectedValue(new Error(JSON.stringify(mockApiError)));
|
||||
```
|
||||
|
||||
## Global Mock Type Safety
|
||||
|
||||
```ts
|
||||
// In __mocks__/routerMock.ts
|
||||
export const mockUseLocation = (overrides: Partial<Location> = {}): Location => ({
|
||||
pathname: '/traces',
|
||||
search: '',
|
||||
hash: '',
|
||||
state: null,
|
||||
key: 'test-key',
|
||||
...overrides,
|
||||
});
|
||||
// In test files: const location = useLocation(); // Properly typed from global mock
|
||||
```
|
||||
|
||||
## TypeScript Configuration for Jest
|
||||
|
||||
```json
|
||||
// jest.config.ts
|
||||
{
|
||||
"preset": "ts-jest/presets/js-with-ts-esm",
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"useESM": true,
|
||||
"isolatedModules": true,
|
||||
"tsconfig": "<rootDir>/tsconfig.jest.json"
|
||||
}
|
||||
},
|
||||
"extensionsToTreatAsEsm": [".ts", ".tsx"],
|
||||
"moduleFileExtensions": ["ts", "tsx", "js", "json"]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
// tsconfig.jest.json
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": ["jest", "@testing-library/jest-dom"],
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["src/**/*", "**/*.test.ts", "**/*.test.tsx", "__mocks__/**/*"]
|
||||
}
|
||||
```
|
||||
|
||||
## Type Safety Checklist
|
||||
|
||||
- [ ] All mock functions use `jest.MockedFunction<T>`
|
||||
- [ ] All mock data has proper interfaces
|
||||
- [ ] No `any` types in test files
|
||||
- [ ] Generic types are used where appropriate
|
||||
- [ ] Error types are properly defined
|
||||
- [ ] Component props are typed
|
||||
- [ ] Hook return types are defined
|
||||
- [ ] API response types are defined
|
||||
- [ ] Global mocks are type-safe
|
||||
- [ ] Test utilities are properly typed
|
||||
@@ -78,7 +78,7 @@ module.exports = {
|
||||
// TODO: Change to 'error' after fixing ~80 empty function placeholders in providers/contexts
|
||||
'@typescript-eslint/no-empty-function': 'off', // Disallows empty function bodies
|
||||
'@typescript-eslint/no-var-requires': 'error', // Disallows require() in TypeScript (use import instead)
|
||||
'@typescript-eslint/ban-ts-comment': 'off', // Allows @ts-ignore comments (sometimes needed for third-party libs)
|
||||
'@typescript-eslint/ban-ts-comment': 'warn', // Allows @ts-ignore comments (sometimes needed for third-party libs)
|
||||
'no-empty-function': 'off', // Disabled in favor of TypeScript version above
|
||||
|
||||
// React rules
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
"@signozhq/combobox": "0.0.2",
|
||||
"@signozhq/command": "0.0.0",
|
||||
"@signozhq/design-tokens": "2.1.1",
|
||||
"@signozhq/dialog": "0.0.2",
|
||||
"@signozhq/drawer": "0.0.4",
|
||||
"@signozhq/icons": "0.1.0",
|
||||
"@signozhq/input": "0.0.2",
|
||||
"@signozhq/popover": "0.0.0",
|
||||
@@ -60,10 +62,12 @@
|
||||
"@signozhq/sonner": "0.1.0",
|
||||
"@signozhq/switch": "0.0.2",
|
||||
"@signozhq/table": "0.3.7",
|
||||
"@signozhq/tabs": "0.0.11",
|
||||
"@signozhq/tooltip": "0.0.2",
|
||||
"@tanstack/react-table": "8.20.6",
|
||||
"@tanstack/react-virtual": "3.11.2",
|
||||
"@uiw/codemirror-theme-copilot": "4.23.11",
|
||||
"@uiw/codemirror-theme-dracula": "4.25.4",
|
||||
"@uiw/codemirror-theme-github": "4.24.1",
|
||||
"@uiw/react-codemirror": "4.23.10",
|
||||
"@uiw/react-md-editor": "3.23.5",
|
||||
@@ -135,6 +139,7 @@
|
||||
"react-full-screen": "1.1.1",
|
||||
"react-grid-layout": "^1.3.4",
|
||||
"react-helmet-async": "1.3.0",
|
||||
"react-hook-form": "7.71.2",
|
||||
"react-i18next": "^11.16.1",
|
||||
"react-lottie": "1.2.10",
|
||||
"react-markdown": "8.0.7",
|
||||
@@ -289,4 +294,4 @@
|
||||
"on-headers": "^1.1.0",
|
||||
"tmp": "0.2.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
312
frontend/public/svgs/dotted-double-line.svg
Normal file
312
frontend/public/svgs/dotted-double-line.svg
Normal file
@@ -0,0 +1,312 @@
|
||||
<svg width="929" height="8" viewBox="0 0 929 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="12" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="18" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="24" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="30" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="36" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="42" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="48" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="54" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="60" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="66" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="72" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="78" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="84" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="90" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="96" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="102" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="108" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="114" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="120" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="126" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="132" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="138" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="144" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="150" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="156" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="162" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="168" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="174" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="180" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="186" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="192" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="198" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="204" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="210" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="216" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="222" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="228" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="234" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="240" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="246" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="252" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="258" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="264" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="270" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="276" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="282" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="288" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="294" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="300" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="306" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="312" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="318" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="324" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="330" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="336" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="342" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="348" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="354" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="360" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="366" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="372" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="378" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="384" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="390" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="396" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="402" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="408" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="414" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="420" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="426" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="432" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="438" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="444" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="450" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="456" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="462" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="468" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="474" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="480" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="486" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="492" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="498" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="504" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="510" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="516" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="522" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="528" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="534" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="540" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="546" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="552" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="558" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="564" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="570" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="576" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="582" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="588" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="594" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="600" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="606" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="612" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="618" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="624" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="630" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="636" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="642" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="648" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="654" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="660" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="666" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="672" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="678" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="684" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="690" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="696" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="702" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="708" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="714" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="720" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="726" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="732" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="738" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="744" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="750" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="756" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="762" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="768" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="774" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="780" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="786" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="792" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="798" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="804" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="810" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="816" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="822" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="828" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="834" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="840" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="846" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="852" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="858" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="864" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="870" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="876" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="882" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="888" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="894" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="900" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="906" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="912" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="918" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="924" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="6" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="12" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="18" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="24" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="30" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="36" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="42" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="48" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="54" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="60" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="66" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="72" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="78" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="84" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="90" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="96" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="102" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="108" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="114" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="120" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="126" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="132" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="138" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="144" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="150" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="156" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="162" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="168" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="174" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="180" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="186" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="192" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="198" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="204" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="210" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="216" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="222" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="228" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="234" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="240" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="246" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="252" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="258" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="264" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="270" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="276" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="282" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="288" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="294" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="300" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="306" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="312" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="318" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="324" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="330" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="336" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="342" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="348" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="354" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="360" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="366" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="372" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="378" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="384" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="390" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="396" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="402" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="408" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="414" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="420" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="426" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="432" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="438" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="444" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="450" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="456" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="462" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="468" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="474" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="480" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="486" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="492" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="498" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="504" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="510" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="516" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="522" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="528" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="534" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="540" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="546" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="552" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="558" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="564" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="570" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="576" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="582" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="588" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="594" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="600" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="606" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="612" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="618" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="624" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="630" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="636" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="642" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="648" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="654" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="660" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="666" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="672" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="678" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="684" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="690" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="696" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="702" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="708" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="714" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="720" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="726" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="732" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="738" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="744" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="750" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="756" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="762" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="768" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="774" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="780" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="786" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="792" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="798" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="804" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="810" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="816" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="822" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="828" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="834" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="840" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="846" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="852" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="858" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="864" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="870" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="876" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="882" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="888" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="894" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="900" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="906" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="912" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="918" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
<rect x="924" y="6" width="2" height="2" rx="1" fill="#242834"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 19 KiB |
@@ -297,7 +297,6 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
}, [isLoggedInState, pathname, user, isOldRoute, currentRoute, location]);
|
||||
|
||||
// NOTE: disabling this rule as there is no need to have div
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
|
||||
@@ -218,12 +218,8 @@ function App(): JSX.Element {
|
||||
pathname === ROUTES.ONBOARDING ||
|
||||
pathname.startsWith('/public/dashboard/')
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
window.Pylon('hideChatBubble');
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
window.Pylon('showChatBubble');
|
||||
}
|
||||
}, [pathname]);
|
||||
|
||||
@@ -253,12 +253,18 @@ export const ShortcutsPage = Loadable(
|
||||
() => import(/* webpackChunkName: "ShortcutsPage" */ 'pages/Settings'),
|
||||
);
|
||||
|
||||
export const InstalledIntegrations = Loadable(
|
||||
export const Integrations = Loadable(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "InstalledIntegrations" */ 'pages/IntegrationsModulePage'
|
||||
),
|
||||
);
|
||||
export const IntegrationsDetailsPage = Loadable(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "IntegrationsDetailsPage" */ 'pages/IntegrationsDetailsPage'
|
||||
),
|
||||
);
|
||||
|
||||
export const MessagingQueuesMainPage = Loadable(
|
||||
() =>
|
||||
|
||||
@@ -18,7 +18,8 @@ import {
|
||||
ForgotPassword,
|
||||
Home,
|
||||
InfrastructureMonitoring,
|
||||
InstalledIntegrations,
|
||||
Integrations,
|
||||
IntegrationsDetailsPage,
|
||||
LicensePage,
|
||||
ListAllALertsPage,
|
||||
LiveLogs,
|
||||
@@ -389,10 +390,17 @@ const routes: AppRoutes[] = [
|
||||
isPrivate: true,
|
||||
key: 'WORKSPACE_ACCESS_RESTRICTED',
|
||||
},
|
||||
{
|
||||
path: ROUTES.INTEGRATIONS_DETAIL,
|
||||
exact: true,
|
||||
component: IntegrationsDetailsPage,
|
||||
isPrivate: true,
|
||||
key: 'INTEGRATIONS_DETAIL',
|
||||
},
|
||||
{
|
||||
path: ROUTES.INTEGRATIONS,
|
||||
exact: true,
|
||||
component: InstalledIntegrations,
|
||||
component: Integrations,
|
||||
isPrivate: true,
|
||||
key: 'INTEGRATIONS',
|
||||
},
|
||||
|
||||
@@ -44,7 +44,6 @@ const dashboardVariablesQuery = async (
|
||||
} catch (error) {
|
||||
const formattedError = ErrorResponseHandler(error as AxiosError);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||
throw { message: 'Error fetching data', details: formattedError };
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import axios from 'api';
|
||||
|
||||
import { getFieldKeys } from '../getFieldKeys';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import axios from 'api';
|
||||
|
||||
import { getFieldValues } from '../getFieldValues';
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { QueryClient } from 'react-query';
|
||||
import getLocalStorageApi from 'api/browser/localstorage/get';
|
||||
import post from 'api/v2/sessions/rotate/post';
|
||||
|
||||
@@ -5,13 +5,13 @@ import {
|
||||
ServiceData,
|
||||
UpdateServiceConfigPayload,
|
||||
UpdateServiceConfigResponse,
|
||||
} from 'container/CloudIntegrationPage/ServicesSection/types';
|
||||
} from 'container/Integrations/CloudIntegration/AmazonWebServices/types';
|
||||
import {
|
||||
AccountConfigPayload,
|
||||
AccountConfigResponse,
|
||||
ConnectionParams,
|
||||
AWSAccountConfigPayload,
|
||||
ConnectionUrlResponse,
|
||||
} from 'types/api/integrations/aws';
|
||||
import { ConnectionParams } from 'types/api/integrations/types';
|
||||
|
||||
export const getAwsAccounts = async (): Promise<CloudAccount[]> => {
|
||||
const response = await axios.get('/cloud-integrations/aws/accounts');
|
||||
@@ -60,7 +60,7 @@ export const generateConnectionUrl = async (params: {
|
||||
|
||||
export const updateAccountConfig = async (
|
||||
accountId: string,
|
||||
payload: AccountConfigPayload,
|
||||
payload: AWSAccountConfigPayload,
|
||||
): Promise<AccountConfigResponse> => {
|
||||
const response = await axios.post<AccountConfigResponse>(
|
||||
`/cloud-integrations/aws/accounts/${accountId}/config`,
|
||||
|
||||
122
frontend/src/api/integration/index.ts
Normal file
122
frontend/src/api/integration/index.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import axios from 'api';
|
||||
import {
|
||||
CloudAccount,
|
||||
ServiceData,
|
||||
} from 'container/Integrations/CloudIntegration/AmazonWebServices/types';
|
||||
import {
|
||||
AzureCloudAccountConfig,
|
||||
AzureService,
|
||||
AzureServiceConfigPayload,
|
||||
} from 'container/Integrations/types';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import {
|
||||
AccountConfigResponse,
|
||||
AWSAccountConfigPayload,
|
||||
} from 'types/api/integrations/aws';
|
||||
import {
|
||||
AzureAccountConfig,
|
||||
ConnectionParams,
|
||||
IAzureDeploymentCommands,
|
||||
} from 'types/api/integrations/types';
|
||||
|
||||
export const getCloudIntegrationAccounts = async (
|
||||
cloudServiceId: string,
|
||||
): Promise<CloudAccount[]> => {
|
||||
const response = await axios.get(
|
||||
`/cloud-integrations/${cloudServiceId}/accounts`,
|
||||
);
|
||||
|
||||
return response.data.data.accounts;
|
||||
};
|
||||
|
||||
export const getCloudIntegrationServices = async (
|
||||
cloudServiceId: string,
|
||||
cloudAccountId?: string,
|
||||
): Promise<AzureService[]> => {
|
||||
const params = cloudAccountId
|
||||
? { cloud_account_id: cloudAccountId }
|
||||
: undefined;
|
||||
|
||||
const response = await axios.get(
|
||||
`/cloud-integrations/${cloudServiceId}/services`,
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
|
||||
return response.data.data.services;
|
||||
};
|
||||
|
||||
export const getCloudIntegrationServiceDetails = async (
|
||||
cloudServiceId: string,
|
||||
serviceId: string,
|
||||
cloudAccountId?: string,
|
||||
): Promise<ServiceData> => {
|
||||
const params = cloudAccountId
|
||||
? { cloud_account_id: cloudAccountId }
|
||||
: undefined;
|
||||
const response = await axios.get(
|
||||
`/cloud-integrations/${cloudServiceId}/services/${serviceId}`,
|
||||
{ params },
|
||||
);
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
export const updateAccountConfig = async (
|
||||
cloudServiceId: string,
|
||||
accountId: string,
|
||||
payload: AWSAccountConfigPayload | AzureAccountConfig,
|
||||
): Promise<AccountConfigResponse> => {
|
||||
const response = await axios.post<AccountConfigResponse>(
|
||||
`/cloud-integrations/${cloudServiceId}/accounts/${accountId}/config`,
|
||||
payload,
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const updateServiceConfig = async (
|
||||
cloudServiceId: string,
|
||||
serviceId: string,
|
||||
payload: AzureServiceConfigPayload,
|
||||
): Promise<AzureServiceConfigPayload> => {
|
||||
const response = await axios.post<AzureServiceConfigPayload>(
|
||||
`/cloud-integrations/${cloudServiceId}/services/${serviceId}/config`,
|
||||
payload,
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getConnectionParams = async (
|
||||
cloudServiceId: string,
|
||||
): Promise<ConnectionParams> => {
|
||||
const response = await axios.get(
|
||||
`/cloud-integrations/${cloudServiceId}/accounts/generate-connection-params`,
|
||||
);
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
export const getAzureDeploymentCommands = async (params: {
|
||||
agent_config: ConnectionParams;
|
||||
account_config: AzureCloudAccountConfig;
|
||||
}): Promise<IAzureDeploymentCommands> => {
|
||||
const response = await axios.post(
|
||||
`/cloud-integrations/azure/accounts/generate-connection-url`,
|
||||
params,
|
||||
);
|
||||
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
export const removeIntegrationAccount = async ({
|
||||
cloudServiceId,
|
||||
accountId,
|
||||
}: {
|
||||
cloudServiceId: string;
|
||||
accountId: string;
|
||||
}): Promise<SuccessResponse<Record<string, never>> | ErrorResponse> => {
|
||||
const response = await axios.post(
|
||||
`/cloud-integrations/${cloudServiceId}/accounts/${accountId}/disconnect`,
|
||||
);
|
||||
|
||||
return response.data;
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import {
|
||||
MetricRangePayloadV5,
|
||||
|
||||
@@ -274,7 +274,6 @@ function convertDistributionData(
|
||||
distributionData: DistributionData,
|
||||
legendMap: Record<string, string>,
|
||||
): any {
|
||||
// eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
// Convert V5 distribution format to legacy histogram format
|
||||
return {
|
||||
...distributionData,
|
||||
@@ -415,7 +414,6 @@ export function convertV5ResponseToLegacy(
|
||||
if (legacyResponse.payload?.data?.result) {
|
||||
legacyResponse.payload.data.result = legacyResponse.payload.data.result.map(
|
||||
(queryData: any) => {
|
||||
// eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
const newQueryData = cloneDeep(queryData);
|
||||
newQueryData.legend = legendMap[queryData.queryName];
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string, simple-import-sort/imports, @typescript-eslint/indent, no-mixed-spaces-and-tabs */
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import {
|
||||
IBuilderFormula,
|
||||
IBuilderQuery,
|
||||
} from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import {
|
||||
ClickHouseQuery,
|
||||
LogAggregation,
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
} from 'types/api/v5/queryRange';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { DataSource, ReduceOperators } from 'types/common/queryBuilder';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
import { prepareQueryRangePayloadV5 } from './prepareQueryRangePayloadV5';
|
||||
|
||||
|
||||
@@ -308,7 +308,7 @@ export function createAggregation(
|
||||
* Converts query builder data to V5 builder queries
|
||||
*/
|
||||
export function convertBuilderQueriesToV5(
|
||||
builderQueries: Record<string, any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
builderQueries: Record<string, any>,
|
||||
requestType: RequestType,
|
||||
panelType?: PANEL_TYPES,
|
||||
): QueryEnvelope[] {
|
||||
@@ -467,7 +467,7 @@ export function convertTraceOperatorToV5(
|
||||
* Converts PromQL queries to V5 format
|
||||
*/
|
||||
export function convertPromQueriesToV5(
|
||||
promQueries: Record<string, any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
promQueries: Record<string, any>,
|
||||
): QueryEnvelope[] {
|
||||
return Object.entries(promQueries).map(
|
||||
([queryName, queryData]): QueryEnvelope => ({
|
||||
@@ -488,7 +488,7 @@ export function convertPromQueriesToV5(
|
||||
* Converts ClickHouse queries to V5 format
|
||||
*/
|
||||
export function convertClickHouseQueriesToV5(
|
||||
chQueries: Record<string, any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
chQueries: Record<string, any>,
|
||||
): QueryEnvelope[] {
|
||||
return Object.entries(chQueries).map(
|
||||
([queryName, queryData]): QueryEnvelope => ({
|
||||
@@ -508,9 +508,8 @@ export function convertClickHouseQueriesToV5(
|
||||
* Helper function to reduce query arrays to objects
|
||||
*/
|
||||
function reduceQueriesToObject(
|
||||
queryArray: any[], // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
queryArray: any[],
|
||||
): { queries: Record<string, any>; legends: Record<string, string> } {
|
||||
// eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
const legends: Record<string, string> = {};
|
||||
const queries = queryArray.reduce((acc, queryItem) => {
|
||||
if (!queryItem.query) {
|
||||
@@ -519,7 +518,7 @@ function reduceQueriesToObject(
|
||||
acc[queryItem.name] = queryItem;
|
||||
legends[queryItem.name] = queryItem.legend;
|
||||
return acc;
|
||||
}, {} as Record<string, any>); // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
}, {} as Record<string, any>);
|
||||
|
||||
return { queries, legends };
|
||||
}
|
||||
@@ -589,7 +588,6 @@ export const prepareQueryRangePayloadV5 = ({
|
||||
limit: formulaData.limit ?? undefined,
|
||||
legend: isEmpty(formulaData.legend) ? undefined : formulaData.legend,
|
||||
order: formulaData.orderBy?.map(
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
(order: any): OrderBy => ({
|
||||
key: {
|
||||
name: order.columnName,
|
||||
|
||||
@@ -10,7 +10,6 @@ function ErrorIcon({ ...props }: ErrorIconProps): JSX.Element {
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
|
||||
3
frontend/src/auto-import-registry.d.ts
vendored
3
frontend/src/auto-import-registry.d.ts
vendored
@@ -18,6 +18,8 @@ import '@signozhq/checkbox';
|
||||
import '@signozhq/combobox';
|
||||
import '@signozhq/command';
|
||||
import '@signozhq/design-tokens';
|
||||
import '@signozhq/dialog';
|
||||
import '@signozhq/drawer';
|
||||
import '@signozhq/icons';
|
||||
import '@signozhq/input';
|
||||
import '@signozhq/popover';
|
||||
@@ -26,4 +28,5 @@ import '@signozhq/resizable';
|
||||
import '@signozhq/sonner';
|
||||
import '@signozhq/switch';
|
||||
import '@signozhq/table';
|
||||
import '@signozhq/tabs';
|
||||
import '@signozhq/tooltip';
|
||||
|
||||
@@ -96,7 +96,6 @@ export function FilterSelect({
|
||||
key={filterType.toString()}
|
||||
placeholder={placeholder}
|
||||
showSearch
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...(isMultiple ? { mode: 'multiple' } : {})}
|
||||
options={mergedOptions}
|
||||
loading={isFetching}
|
||||
|
||||
@@ -90,7 +90,6 @@ const getColumnSearchProps = (
|
||||
clearFilters,
|
||||
close,
|
||||
}): JSX.Element => (
|
||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
||||
<div style={{ padding: 8 }} onKeyDown={(e): void => e.stopPropagation()}>
|
||||
<Input
|
||||
ref={searchInput}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
import { useQuery } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { DefaultOptionType } from 'antd/es/select';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
|
||||
import { getWidgetQuery } from 'pages/MessagingQueues/MQDetails/MetricPage/MetricPageUtil';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-nested-ternary */
|
||||
import { Dispatch, SetStateAction, useMemo } from 'react';
|
||||
import { Col, Row } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
/* eslint-disable sonarjs/no-identical-functions */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
/* eslint-disable sonarjs/no-identical-functions */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
.cloud-integration-accounts {
|
||||
padding: 0px 16px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
.selected-cloud-integration-account-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
.selected-cloud-integration-account-section-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--l3-background);
|
||||
background: var(--l1-background);
|
||||
|
||||
.selected-cloud-integration-account-section-header-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.selected-cloud-integration-account-status {
|
||||
display: flex;
|
||||
border-right: 1px solid var(--l3-background);
|
||||
border-radius: none;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.selected-cloud-integration-account-section-header-title-text {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
.azure-cloud-account-selector {
|
||||
.ant-select {
|
||||
background-color: transparent !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
|
||||
.ant-select-selector {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selected-cloud-integration-account-settings {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
line-height: 32px;
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.account-settings-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.add-new-cloud-integration-account-button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
line-height: 32px;
|
||||
margin-right: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cloud-integration-accounts-drawer-content {
|
||||
height: 100%;
|
||||
max-height: calc(100vh - 120px); // Account for drawer header and padding
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0.2rem;
|
||||
}
|
||||
|
||||
.edit-account-content,
|
||||
.add-new-account-content {
|
||||
flex: 1;
|
||||
min-height: 0; // Allows flex children to shrink below content size
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@signozhq/button';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { DrawerWrapper } from '@signozhq/drawer';
|
||||
import { Select } from 'antd';
|
||||
import ConnectNewAzureAccount from 'container/Integrations/CloudIntegration/AzureServices/AzureAccount/ConnectNewAzureAccount';
|
||||
import EditAzureAccount from 'container/Integrations/CloudIntegration/AzureServices/AzureAccount/EditAzureAccount';
|
||||
import { INTEGRATION_TYPES } from 'container/Integrations/constants';
|
||||
import { CloudAccount } from 'container/Integrations/types';
|
||||
import { useGetConnectionParams } from 'hooks/integration/useGetConnectionParams';
|
||||
import useAxiosError from 'hooks/useAxiosError';
|
||||
import { Dot, PencilLine, Plus } from 'lucide-react';
|
||||
|
||||
import './CloudIntegrationAccounts.styles.scss';
|
||||
|
||||
export type DrawerMode = 'edit' | 'add';
|
||||
|
||||
export default function CloudIntegrationAccounts({
|
||||
selectedAccount,
|
||||
accounts,
|
||||
isLoadingAccounts,
|
||||
onSelectAccount,
|
||||
refetchAccounts,
|
||||
}: {
|
||||
selectedAccount: CloudAccount | null;
|
||||
accounts: CloudAccount[];
|
||||
isLoadingAccounts: boolean;
|
||||
onSelectAccount: (account: CloudAccount) => void;
|
||||
refetchAccounts: () => void;
|
||||
}): JSX.Element {
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
const [mode, setMode] = useState<DrawerMode>('add');
|
||||
|
||||
const handleDrawerOpenChange = (open: boolean): void => {
|
||||
setIsDrawerOpen(open);
|
||||
};
|
||||
|
||||
const handleEditAccount = (): void => {
|
||||
setMode('edit');
|
||||
setIsDrawerOpen(true);
|
||||
};
|
||||
|
||||
const handleAddNewAccount = (): void => {
|
||||
setMode('add');
|
||||
setIsDrawerOpen(true);
|
||||
};
|
||||
|
||||
const handleError = useAxiosError();
|
||||
|
||||
const {
|
||||
data: connectionParams,
|
||||
isLoading: isConnectionParamsLoading,
|
||||
} = useGetConnectionParams({
|
||||
cloudServiceId: INTEGRATION_TYPES.AZURE,
|
||||
options: { onError: handleError },
|
||||
});
|
||||
|
||||
const handleSelectAccount = (value: string): void => {
|
||||
const account = accounts.find(
|
||||
(account) => account.cloud_account_id === value,
|
||||
);
|
||||
|
||||
if (account) {
|
||||
onSelectAccount(account);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAccountConnected = (): void => {
|
||||
refetchAccounts();
|
||||
};
|
||||
|
||||
const handleAccountUpdated = (): void => {
|
||||
refetchAccounts();
|
||||
};
|
||||
|
||||
const renderDrawerContent = (): JSX.Element => {
|
||||
return (
|
||||
<div className="cloud-integration-accounts-drawer-content">
|
||||
{mode === 'edit' ? (
|
||||
<div className="edit-account-content">
|
||||
<EditAzureAccount
|
||||
selectedAccount={selectedAccount as CloudAccount}
|
||||
connectionParams={connectionParams || {}}
|
||||
isConnectionParamsLoading={isConnectionParamsLoading}
|
||||
onAccountUpdated={handleAccountUpdated}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="add-new-account-content">
|
||||
<ConnectNewAzureAccount
|
||||
connectionParams={connectionParams || {}}
|
||||
isConnectionParamsLoading={isConnectionParamsLoading}
|
||||
onAccountConnected={handleAccountConnected}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="cloud-integration-accounts">
|
||||
{selectedAccount && (
|
||||
<div className="selected-cloud-integration-account-section">
|
||||
<div className="selected-cloud-integration-account-section-header">
|
||||
<div className="selected-cloud-integration-account-section-header-title">
|
||||
<div className="selected-cloud-integration-account-status">
|
||||
<Dot size={24} color={Color.BG_FOREST_500} />
|
||||
</div>
|
||||
<div className="selected-cloud-integration-account-section-header-title-text">
|
||||
Subscription ID :
|
||||
<span className="azure-cloud-account-selector">
|
||||
<Select
|
||||
value={selectedAccount?.cloud_account_id}
|
||||
options={accounts.map((account) => ({
|
||||
label: account.cloud_account_id,
|
||||
value: account.cloud_account_id,
|
||||
}))}
|
||||
onChange={handleSelectAccount}
|
||||
loading={isLoadingAccounts}
|
||||
placeholder="Select Account"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="selected-cloud-integration-account-settings">
|
||||
<Button
|
||||
variant="link"
|
||||
color="secondary"
|
||||
prefixIcon={<PencilLine size={14} />}
|
||||
onClick={handleEditAccount}
|
||||
>
|
||||
Edit Account
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="link"
|
||||
color="secondary"
|
||||
prefixIcon={<Plus size={14} />}
|
||||
onClick={handleAddNewAccount}
|
||||
>
|
||||
Add New Account
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="account-settings-container">
|
||||
<DrawerWrapper
|
||||
open={isDrawerOpen}
|
||||
onOpenChange={handleDrawerOpenChange}
|
||||
type="panel"
|
||||
header={{
|
||||
title: mode === 'add' ? 'Connect with Azure' : 'Edit Azure Account',
|
||||
}}
|
||||
content={renderDrawerContent()}
|
||||
showCloseButton
|
||||
allowOutsideClick={mode === 'edit'}
|
||||
direction="right"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import CloudIntegrationAccounts from './CloudIntegrationAccounts';
|
||||
|
||||
export default CloudIntegrationAccounts;
|
||||
@@ -0,0 +1,50 @@
|
||||
.cloud-integrations-header-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
border-bottom: 1px solid var(--l3-background);
|
||||
|
||||
.cloud-integrations-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
padding-bottom: 0px;
|
||||
gap: 16px;
|
||||
|
||||
.cloud-integrations-title-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
|
||||
.cloud-integrations-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
.cloud-integrations-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
color: var(--l1-foreground);
|
||||
font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 32px; /* 200% */
|
||||
letter-spacing: -0.08px;
|
||||
}
|
||||
|
||||
.cloud-integrations-description {
|
||||
color: var(--l2-foreground);
|
||||
font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
AWS_INTEGRATION,
|
||||
AZURE_INTEGRATION,
|
||||
} from 'container/Integrations/constants';
|
||||
import { CloudAccount, IntegrationType } from 'container/Integrations/types';
|
||||
|
||||
import CloudIntegrationAccounts from '../CloudIntegrationAccounts';
|
||||
|
||||
import './CloudIntegrationsHeader.styles.scss';
|
||||
|
||||
export default function CloudIntegrationsHeader({
|
||||
cloudServiceId,
|
||||
selectedAccount,
|
||||
accounts,
|
||||
isLoadingAccounts,
|
||||
onSelectAccount,
|
||||
refetchAccounts,
|
||||
}: {
|
||||
selectedAccount: CloudAccount | null;
|
||||
accounts: CloudAccount[] | [];
|
||||
isLoadingAccounts: boolean;
|
||||
onSelectAccount: (account: CloudAccount) => void;
|
||||
cloudServiceId: IntegrationType;
|
||||
refetchAccounts: () => void;
|
||||
}): JSX.Element {
|
||||
const INTEGRATION_DATA =
|
||||
cloudServiceId === IntegrationType.AWS_SERVICES
|
||||
? AWS_INTEGRATION
|
||||
: AZURE_INTEGRATION;
|
||||
|
||||
return (
|
||||
<div className="cloud-integrations-header-section">
|
||||
<div className="cloud-integrations-header">
|
||||
<div className="cloud-integrations-title-section">
|
||||
<div className="cloud-integrations-title">
|
||||
<img
|
||||
className="cloud-integrations-icon"
|
||||
src={INTEGRATION_DATA.icon}
|
||||
alt={INTEGRATION_DATA.icon_alt}
|
||||
/>
|
||||
|
||||
{INTEGRATION_DATA.title}
|
||||
</div>
|
||||
<div className="cloud-integrations-description">
|
||||
{INTEGRATION_DATA.description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="cloud-integrations-accounts-list">
|
||||
<CloudIntegrationAccounts
|
||||
selectedAccount={selectedAccount}
|
||||
accounts={accounts}
|
||||
isLoadingAccounts={isLoadingAccounts}
|
||||
onSelectAccount={onSelectAccount}
|
||||
refetchAccounts={refetchAccounts}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import CloudIntegrationsHeader from './CloudIntegrationsHeader';
|
||||
|
||||
export default CloudIntegrationsHeader;
|
||||
@@ -0,0 +1,34 @@
|
||||
.cloud-service-data-collected {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
.cloud-service-data-collected-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.cloud-service-data-collected-table-heading {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
color: var(--l2-foreground);
|
||||
|
||||
/* Bifrost (Ancient)/Content/sm */
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 20px; /* 142.857% */
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
.cloud-service-data-collected-table-logs {
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--l3-background);
|
||||
background: var(--l1-background);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Table } from 'antd';
|
||||
import { ServiceData } from 'container/Integrations/CloudIntegration/AmazonWebServices/types';
|
||||
import { BarChart2, ScrollText } from 'lucide-react';
|
||||
|
||||
import { ServiceData } from './types';
|
||||
import './CloudServiceDataCollected.styles.scss';
|
||||
|
||||
function CloudServiceDataCollected({
|
||||
logsData,
|
||||
@@ -61,26 +63,30 @@ function CloudServiceDataCollected({
|
||||
return (
|
||||
<div className="cloud-service-data-collected">
|
||||
{logsData && logsData.length > 0 && (
|
||||
<div className="cloud-service-data-collected__table">
|
||||
<div className="cloud-service-data-collected__table-heading">Logs</div>
|
||||
<div className="cloud-service-data-collected-table">
|
||||
<div className="cloud-service-data-collected-table-heading">
|
||||
<ScrollText size={14} />
|
||||
Logs
|
||||
</div>
|
||||
<Table
|
||||
columns={logsColumns}
|
||||
dataSource={logsData}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...tableProps}
|
||||
className="cloud-service-data-collected__table-logs"
|
||||
className="cloud-service-data-collected-table-logs"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{metricsData && metricsData.length > 0 && (
|
||||
<div className="cloud-service-data-collected__table">
|
||||
<div className="cloud-service-data-collected__table-heading">Metrics</div>
|
||||
<div className="cloud-service-data-collected-table">
|
||||
<div className="cloud-service-data-collected-table-heading">
|
||||
<BarChart2 size={14} />
|
||||
Metrics
|
||||
</div>
|
||||
<Table
|
||||
columns={metricsColumns}
|
||||
dataSource={metricsData}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...tableProps}
|
||||
className="cloud-service-data-collected__table-metrics"
|
||||
className="cloud-service-data-collected-table-metrics"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
39
frontend/src/components/CodeBlock/CodeBlock.styles.scss
Normal file
39
frontend/src/components/CodeBlock/CodeBlock.styles.scss
Normal file
@@ -0,0 +1,39 @@
|
||||
.code-block-container {
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
.code-block-copy-btn {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 8px;
|
||||
transform: translateY(-50%);
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px 8px;
|
||||
color: var(--bg-vanilla-100);
|
||||
transition: color 0.15s ease;
|
||||
|
||||
&.copied {
|
||||
background-color: var(--bg-robin-500);
|
||||
}
|
||||
}
|
||||
|
||||
// CodeMirror wrapper
|
||||
.code-block-editor {
|
||||
border-radius: 4px;
|
||||
|
||||
.cm-editor {
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
font-family: 'Space Mono', monospace;
|
||||
}
|
||||
|
||||
.cm-scroller {
|
||||
font-family: 'Space Mono', monospace;
|
||||
}
|
||||
}
|
||||
}
|
||||
145
frontend/src/components/CodeBlock/CodeBlock.tsx
Normal file
145
frontend/src/components/CodeBlock/CodeBlock.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { CheckOutlined, CopyOutlined } from '@ant-design/icons';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { Button } from '@signozhq/button';
|
||||
import { dracula } from '@uiw/codemirror-theme-dracula';
|
||||
import { githubLight } from '@uiw/codemirror-theme-github';
|
||||
import CodeMirror, {
|
||||
EditorState,
|
||||
EditorView,
|
||||
Extension,
|
||||
} from '@uiw/react-codemirror';
|
||||
import cx from 'classnames';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
|
||||
import './CodeBlock.styles.scss';
|
||||
|
||||
export type CodeBlockLanguage =
|
||||
| 'javascript'
|
||||
| 'typescript'
|
||||
| 'js'
|
||||
| 'ts'
|
||||
| 'json'
|
||||
| 'bash'
|
||||
| 'shell'
|
||||
| 'text';
|
||||
|
||||
export type CodeBlockTheme = 'light' | 'dark' | 'auto';
|
||||
|
||||
interface CodeBlockProps {
|
||||
/** The code content to display */
|
||||
value: string;
|
||||
/** Language for syntax highlighting */
|
||||
language?: CodeBlockLanguage;
|
||||
/** Theme: 'light' | 'dark' | 'auto' (follows app dark mode when 'auto') */
|
||||
theme?: CodeBlockTheme;
|
||||
/** Show line numbers */
|
||||
lineNumbers?: boolean;
|
||||
/** Show copy button */
|
||||
showCopyButton?: boolean;
|
||||
/** Custom class name for the container */
|
||||
className?: string;
|
||||
/** Max height in pixels - enables scrolling when content exceeds */
|
||||
maxHeight?: number | string;
|
||||
/** Callback when copy is clicked */
|
||||
onCopy?: (copiedText: string) => void;
|
||||
}
|
||||
|
||||
const LANGUAGE_EXTENSION_MAP: Record<
|
||||
CodeBlockLanguage,
|
||||
ReturnType<typeof javascript> | undefined
|
||||
> = {
|
||||
javascript: javascript({ jsx: true }),
|
||||
typescript: javascript({ jsx: true }),
|
||||
js: javascript({ jsx: true }),
|
||||
ts: javascript({ jsx: true }),
|
||||
json: javascript(), // JSON is valid JS; proper json() would require @codemirror/lang-json
|
||||
bash: undefined,
|
||||
shell: undefined,
|
||||
text: undefined,
|
||||
};
|
||||
|
||||
function CodeBlock({
|
||||
value,
|
||||
language = 'text',
|
||||
theme: themeProp = 'auto',
|
||||
lineNumbers = true,
|
||||
showCopyButton = true,
|
||||
className,
|
||||
maxHeight,
|
||||
onCopy,
|
||||
}: CodeBlockProps): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
|
||||
const resolvedDark = themeProp === 'auto' ? isDarkMode : themeProp === 'dark';
|
||||
const theme = resolvedDark ? dracula : githubLight;
|
||||
|
||||
const extensions = useMemo((): Extension[] => {
|
||||
const langExtension = LANGUAGE_EXTENSION_MAP[language];
|
||||
return [
|
||||
EditorState.readOnly.of(true),
|
||||
EditorView.editable.of(false),
|
||||
EditorView.lineWrapping,
|
||||
...(langExtension ? [langExtension] : []),
|
||||
];
|
||||
}, [language]);
|
||||
|
||||
const handleCopy = useCallback((): void => {
|
||||
navigator.clipboard.writeText(value).then(() => {
|
||||
setIsCopied(true);
|
||||
onCopy?.(value);
|
||||
setTimeout(() => setIsCopied(false), 2000);
|
||||
});
|
||||
}, [value, onCopy]);
|
||||
|
||||
return (
|
||||
<div className={cx('code-block-container', className)}>
|
||||
{showCopyButton && (
|
||||
<Button
|
||||
variant="solid"
|
||||
size="xs"
|
||||
color="secondary"
|
||||
className={cx('code-block-copy-btn', { copied: isCopied })}
|
||||
onClick={handleCopy}
|
||||
aria-label={isCopied ? 'Copied' : 'Copy code'}
|
||||
title={isCopied ? 'Copied' : 'Copy code'}
|
||||
>
|
||||
{isCopied ? <CheckOutlined /> : <CopyOutlined />}
|
||||
</Button>
|
||||
)}
|
||||
<CodeMirror
|
||||
className="code-block-editor"
|
||||
value={value}
|
||||
theme={theme}
|
||||
readOnly
|
||||
editable={false}
|
||||
extensions={extensions}
|
||||
basicSetup={{
|
||||
lineNumbers,
|
||||
highlightActiveLineGutter: false,
|
||||
highlightActiveLine: false,
|
||||
highlightSelectionMatches: true,
|
||||
drawSelection: true,
|
||||
syntaxHighlighting: true,
|
||||
bracketMatching: true,
|
||||
history: false,
|
||||
foldGutter: false,
|
||||
autocompletion: false,
|
||||
defaultKeymap: false,
|
||||
searchKeymap: true,
|
||||
historyKeymap: false,
|
||||
foldKeymap: false,
|
||||
completionKeymap: false,
|
||||
closeBrackets: false,
|
||||
indentOnInput: false,
|
||||
}}
|
||||
style={{
|
||||
maxHeight: maxHeight ?? 'auto',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CodeBlock;
|
||||
2
frontend/src/components/CodeBlock/index.ts
Normal file
2
frontend/src/components/CodeBlock/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export type { CodeBlockLanguage, CodeBlockTheme } from './CodeBlock';
|
||||
export { default as CodeBlock } from './CodeBlock';
|
||||
@@ -1,6 +1,3 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
import {
|
||||
ChangeEvent,
|
||||
Dispatch,
|
||||
|
||||
@@ -92,7 +92,6 @@ const getDateRange = (
|
||||
return { from, to };
|
||||
};
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
function CustomTimePickerPopoverContent({
|
||||
isLiveLogsEnabled,
|
||||
minTime,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
import { Dispatch, SetStateAction, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { DatePicker } from 'antd';
|
||||
@@ -45,7 +44,6 @@ function RangePickerModal(props: RangePickerModalProps): JSX.Element {
|
||||
|
||||
// Using any type here because antd's DatePicker expects its own internal Dayjs type
|
||||
// which conflicts with our project's Dayjs type that has additional plugins (tz, utc etc).
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
||||
const disabledDate = (current: any): boolean => {
|
||||
const currentDay = dayjs(current);
|
||||
return currentDay.isAfter(dayjs());
|
||||
|
||||
@@ -124,7 +124,6 @@ const filterAndSortTimezones = (
|
||||
export const generateTimezoneData = (
|
||||
includeEtcTimezones = false,
|
||||
): Timezone[] => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const allTimezones = (Intl as any).supportedValuesOf('timeZone');
|
||||
const timezones: Timezone[] = [];
|
||||
|
||||
|
||||
@@ -37,13 +37,7 @@ function DraggableTableRow({
|
||||
drop(drag(ref));
|
||||
|
||||
return (
|
||||
<tr
|
||||
ref={ref}
|
||||
className={className}
|
||||
style={{ ...style }}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...restProps}
|
||||
/>
|
||||
<tr ref={ref} className={className} style={{ ...style }} {...restProps} />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,6 @@ function withErrorBoundary<P extends Record<string, unknown>>(
|
||||
}}
|
||||
onError={onError}
|
||||
>
|
||||
{/* eslint-disable-next-line react/jsx-props-no-spreading */}
|
||||
<WrappedComponent {...props} />
|
||||
</Sentry.ErrorBoundary>
|
||||
);
|
||||
|
||||
@@ -19,11 +19,8 @@ jest.mock('react-query', () => ({
|
||||
const mockError: APIError = new APIError({
|
||||
httpStatusCode: 400,
|
||||
error: {
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
message: 'Something went wrong while processing your request.',
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
code: 'An error occurred',
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
url: 'https://example.com/docs',
|
||||
errors: [
|
||||
{ message: 'First error detail' },
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
import { ReactNode } from 'react';
|
||||
import { Popover, PopoverProps } from 'antd';
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import ExplorerCard from '../ExplorerCard';
|
||||
|
||||
const historyReplace = jest.fn();
|
||||
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useLocation: (): { pathname: string } => ({
|
||||
|
||||
@@ -37,7 +37,6 @@ export const getViewDetailsUsingViewKey: GetViewDetailsUsingViewKey = (
|
||||
return undefined;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const omitIdFromQuery = (query: Query | null): any => ({
|
||||
...query,
|
||||
builder: {
|
||||
|
||||
@@ -310,7 +310,6 @@ export const createDragSelectPlugin = (): Plugin<
|
||||
const top = chart.chartArea.top - 5;
|
||||
const bottom = chart.chartArea.bottom + 5;
|
||||
|
||||
/* eslint-disable-next-line no-param-reassign */
|
||||
chart.ctx.fillStyle = pluginOptions.color;
|
||||
chart.ctx.fillRect(left, top, right - left, bottom - top);
|
||||
}
|
||||
|
||||
@@ -142,7 +142,6 @@ export const createIntersectionCursorPlugin = (): Plugin<
|
||||
const { top, bottom, left, right } = chart.chartArea;
|
||||
|
||||
chart.ctx.beginPath();
|
||||
/* eslint-disable-next-line no-param-reassign */
|
||||
chart.ctx.strokeStyle = pluginOptions.color;
|
||||
chart.ctx.setLineDash(lineDashData);
|
||||
chart.ctx.moveTo(left, positionY);
|
||||
@@ -151,7 +150,6 @@ export const createIntersectionCursorPlugin = (): Plugin<
|
||||
|
||||
chart.ctx.beginPath();
|
||||
chart.ctx.setLineDash(lineDashData);
|
||||
/* eslint-disable-next-line no-param-reassign */
|
||||
chart.ctx.strokeStyle = pluginOptions.color;
|
||||
chart.ctx.moveTo(positionX, top);
|
||||
chart.ctx.lineTo(positionX, bottom);
|
||||
|
||||
@@ -55,7 +55,6 @@ export const legend = (id: string, isLonger: boolean): Plugin<ChartType> => ({
|
||||
)
|
||||
: null;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
items?.forEach((item: Record<any, any>, index: number) => {
|
||||
const li = document.createElement('li');
|
||||
li.style.alignItems = 'center';
|
||||
@@ -65,7 +64,6 @@ export const legend = (id: string, isLonger: boolean): Plugin<ChartType> => ({
|
||||
// li.style.marginTop = '5px';
|
||||
|
||||
li.onclick = (): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const { type } = chart.config;
|
||||
if (type === 'pie' || type === 'doughnut') {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { PrecisionOptionsEnum } from '../types';
|
||||
import { getYAxisFormattedValue } from '../yAxisConfig';
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
import { ChartData } from 'chart.js';
|
||||
|
||||
export const hasData = (data: ChartData): boolean => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { MutableRefObject } from 'react';
|
||||
import { Chart, ChartConfiguration, ChartData, Color } from 'chart.js';
|
||||
// eslint-disable-next-line import/namespace -- side-effect import that registers Chart.js date adapter
|
||||
import * as chartjsAdapter from 'chartjs-adapter-date-fns';
|
||||
import { Timezone } from 'components/CustomTimePicker/timezoneUtils';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
@@ -208,7 +207,6 @@ export const getGraphOptions = (
|
||||
cubicInterpolationMode: 'monotone',
|
||||
},
|
||||
point: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
hoverBackgroundColor: (ctx: any): string => {
|
||||
if (ctx?.element?.options?.borderColor) {
|
||||
return ctx.element.options.borderColor;
|
||||
@@ -235,7 +233,6 @@ export const getGraphOptions = (
|
||||
);
|
||||
|
||||
if (interactions[0]) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
nearestDatasetIndex.current = interactions[0].datasetIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ import { useLocation } from 'react-router-dom';
|
||||
import { toast } from '@signozhq/sonner';
|
||||
import { Button, Input, Radio, RadioChangeEvent, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { handleContactSupport } from 'container/Integrations/utils';
|
||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||
import { handleContactSupport } from 'pages/Integrations/utils';
|
||||
|
||||
function FeedbackModal({ onClose }: { onClose: () => void }): JSX.Element {
|
||||
const [activeTab, setActiveTab] = useState('feedback');
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
// Mock dependencies before imports
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { toast } from '@signozhq/sonner';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { handleContactSupport } from 'container/Integrations/utils';
|
||||
import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
|
||||
import { handleContactSupport } from 'pages/Integrations/utils';
|
||||
|
||||
import FeedbackModal from '../FeedbackModal';
|
||||
|
||||
@@ -31,7 +30,7 @@ jest.mock('hooks/useGetTenantLicense', () => ({
|
||||
useGetTenantLicense: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('pages/Integrations/utils', () => ({
|
||||
jest.mock('container/Integrations/utils', () => ({
|
||||
handleContactSupport: jest.fn(),
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
// Mock dependencies before imports
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
@@ -143,7 +143,6 @@ function HostMetricsDetails({
|
||||
page: InfraMonitoringEvents.DetailedPage,
|
||||
});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [host]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -207,7 +206,6 @@ function HostMetricsDetails({
|
||||
page: InfraMonitoringEvents.DetailedPage,
|
||||
});
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[],
|
||||
);
|
||||
|
||||
@@ -490,7 +488,6 @@ function HostMetricsDetails({
|
||||
>
|
||||
<Radio.Button
|
||||
className={
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
selectedView === VIEW_TYPES.METRICS ? 'selected_view tab' : 'tab'
|
||||
}
|
||||
value={VIEW_TYPES.METRICS}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-nested-ternary */
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
|
||||
@@ -122,7 +121,6 @@ function HostMetricsLogs({ timeRange, filters }: Props): JSX.Element {
|
||||
|
||||
const renderFooter = useCallback(
|
||||
(): JSX.Element | null => (
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
<>
|
||||
{isFetching ? (
|
||||
<div className="logs-loading-skeleton"> Loading more logs ... </div>
|
||||
|
||||
@@ -34,7 +34,6 @@ function InputComponent({
|
||||
addonBefore={addonBefore}
|
||||
onBlur={onBlurHandler}
|
||||
onPressEnter={onPressEnterHandler}
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...props}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -112,8 +112,6 @@ function LaunchChatSupport({
|
||||
} else {
|
||||
logEvent(eventName, attributes);
|
||||
if (window.pylon && !chatMessageDisabled) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
window.Pylon('showNewMessage', defaultTo(message, ''));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ function QueryBuilderSearchWrapper({
|
||||
setContextQuery({ ...nextQuery });
|
||||
};
|
||||
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
if (!contextQuery || !isEdit) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
@@ -424,7 +424,6 @@ function LogDetailInner({
|
||||
>
|
||||
<Radio.Button
|
||||
className={
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
selectedView === VIEW_TYPES.OVERVIEW ? 'selected_view tab' : 'tab'
|
||||
}
|
||||
value={VIEW_TYPES.OVERVIEW}
|
||||
@@ -573,11 +572,9 @@ function LogDetailInner({
|
||||
function LogDetail(props: LogDetailProps): JSX.Element {
|
||||
const { log } = props;
|
||||
if (!log) {
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
return <></>;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
return <LogDetailInner {...(props as LogDetailInnerProps)} />;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ function AddToQueryHOC({
|
||||
]);
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
||||
<div className={cx('addToQueryContainer', fontSize)} onClick={handleQueryAdd}>
|
||||
<Popover
|
||||
overlayClassName="drawer-popover"
|
||||
|
||||
@@ -70,9 +70,6 @@
|
||||
padding-left: 0;
|
||||
}
|
||||
transition: background-color 0.2s ease-in;
|
||||
&:hover {
|
||||
background-color: rgba(171, 189, 255, 0.04) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.log-selected-fields {
|
||||
@@ -183,11 +180,6 @@
|
||||
.log-value {
|
||||
color: var(--text-slate-400);
|
||||
}
|
||||
.log-line {
|
||||
&:hover {
|
||||
background-color: var(--text-vanilla-200) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-nested-ternary */
|
||||
import { Card } from 'antd';
|
||||
import { FontSize } from 'container/OptionsMenu/types';
|
||||
import styled from 'styled-components';
|
||||
@@ -49,6 +48,12 @@ export const Container = styled(Card)<{
|
||||
|
||||
${({ $isActiveLog, $isDarkMode, $logType }): string =>
|
||||
getActiveLogBackground($isActiveLog, $isDarkMode, $logType)}
|
||||
}
|
||||
|
||||
&:hover .ant-card-body {
|
||||
${({ $isDarkMode, $logType }): string =>
|
||||
getActiveLogBackground(true, $isDarkMode, $logType)}
|
||||
}
|
||||
`;
|
||||
|
||||
export const LogContainer = styled.div<LogContainerProps>`
|
||||
|
||||
@@ -78,7 +78,6 @@ const SEVERITY_VARIANT_CLASSES: Record<string, string> = {
|
||||
Wrn: 'severity-warn-4',
|
||||
|
||||
// Error variants - cherry-600 to cherry-200
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
ERROR: 'severity-error-0',
|
||||
Error: 'severity-error-1',
|
||||
error: 'severity-error-2',
|
||||
@@ -90,11 +89,9 @@ const SEVERITY_VARIANT_CLASSES: Record<string, string> = {
|
||||
FAIL: 'severity-error-0',
|
||||
|
||||
// Fatal variants - sakura-600 to sakura-200
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
FATAL: 'severity-fatal-0',
|
||||
Fatal: 'severity-fatal-1',
|
||||
fatal: 'severity-fatal-2',
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
critical: 'severity-fatal-3',
|
||||
Critical: 'severity-fatal-4',
|
||||
CRITICAL: 'severity-fatal-0',
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
|
||||
import { getLogIndicatorType, getLogIndicatorTypeForTable } from './utils';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-nested-ternary */
|
||||
import { blue } from '@ant-design/colors';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Col, Row, Space } from 'antd';
|
||||
@@ -8,7 +7,6 @@ import styled from 'styled-components';
|
||||
import {
|
||||
getActiveLogBackground,
|
||||
getCustomHighlightBackground,
|
||||
getDefaultLogBackground,
|
||||
} from 'utils/logs';
|
||||
|
||||
import { RawLogContentProps } from './types';
|
||||
@@ -48,7 +46,9 @@ export const RawLogViewContainer = styled(Row)<{
|
||||
${({ $isReadOnly, $isActiveLog, $isDarkMode, $logType }): string =>
|
||||
$isActiveLog
|
||||
? getActiveLogBackground($isActiveLog, $isDarkMode, $logType)
|
||||
: getDefaultLogBackground($isReadOnly, $isDarkMode)}
|
||||
: !$isReadOnly
|
||||
? `&:hover { ${getActiveLogBackground(true, $isDarkMode, $logType)} }`
|
||||
: ''}
|
||||
|
||||
${({ $isHightlightedLog, $isDarkMode }): string =>
|
||||
$isHightlightedLog
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-nested-ternary */
|
||||
import { FontSize } from 'container/OptionsMenu/types';
|
||||
import styled from 'styled-components';
|
||||
|
||||
|
||||
@@ -84,7 +84,6 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
|
||||
// We do not need any title and data index for the log state indicator
|
||||
title: '',
|
||||
dataIndex: '',
|
||||
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||
key: 'state-indicator',
|
||||
accessorKey: 'state-indicator',
|
||||
id: 'state-indicator',
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Button, Input, InputNumber, Popover, Tooltip, Typography } from 'antd';
|
||||
import { DefaultOptionType } from 'antd/es/select';
|
||||
@@ -80,7 +77,6 @@ function OptionsMenu({
|
||||
};
|
||||
|
||||
const handleSearchValueChange = useDebouncedFn((event): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const value = event?.target?.value || '';
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable prefer-destructuring */
|
||||
import React, { useState } from 'react';
|
||||
import { CheckOutlined, CopyOutlined } from '@ant-design/icons';
|
||||
import cx from 'classnames';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
@@ -53,7 +51,6 @@ function Code({
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
return !inline && match ? (
|
||||
<SyntaxHighlighter
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
style={a11yDark}
|
||||
language={match[1]}
|
||||
@@ -116,7 +113,6 @@ function MarkdownRenderer({
|
||||
<ReactMarkdown
|
||||
rehypePlugins={[rehypeRaw as any]}
|
||||
components={{
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
a: Link,
|
||||
pre: ({ children }) =>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
import { ReactNode, useEffect, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { CaretDownOutlined, LoadingOutlined } from '@ant-design/icons';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Button } from 'antd';
|
||||
import cx from 'classnames';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react/destructuring-assignment */
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Tooltip } from 'antd';
|
||||
import { DefaultOptionType } from 'antd/es/select';
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
/* eslint-disable no-nested-ternary */
|
||||
/* eslint-disable react/function-component-definition */
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
/* eslint-disable no-nested-ternary */
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
/* eslint-disable react/function-component-definition */
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-identical-functions */
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { VirtuosoMockContext } from 'react-virtuoso';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-identical-functions */
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import { Provider } from 'react-redux';
|
||||
import { VirtuosoMockContext } from 'react-virtuoso';
|
||||
@@ -65,7 +64,6 @@ function TestWrapper({ children }: { children: React.ReactNode }): JSX.Element {
|
||||
<Provider store={mockStore}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<VirtuosoMockContext.Provider
|
||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
||||
value={{ viewportHeight: 300, itemHeight: 40 }}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
import { uniqueOptions } from 'container/DashboardContainer/DashboardVariablesSelection/util';
|
||||
|
||||
import { OptionData } from './types';
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
/* eslint-disable react/require-default-props */
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { Input, InputProps, InputRef, Tooltip } from 'antd';
|
||||
import cx from 'classnames';
|
||||
|
||||
@@ -78,14 +78,10 @@ const MetricsAggregateSection = memo(function MetricsAggregateSection({
|
||||
[handleChangeQueryData],
|
||||
);
|
||||
|
||||
const showAggregationInterval = useMemo(() => {
|
||||
// eslint-disable-next-line sonarjs/prefer-single-boolean-return
|
||||
if (panelType === PANEL_TYPES.VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}, [panelType]);
|
||||
const showAggregationInterval = useMemo(
|
||||
() => panelType !== PANEL_TYPES.VALUE,
|
||||
[panelType],
|
||||
);
|
||||
|
||||
const disableOperatorSelector =
|
||||
!queryAggregation.metricName || queryAggregation.metricName === '';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react/require-default-props */
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Button, Radio, RadioChangeEvent, Tooltip } from 'antd';
|
||||
import InputWithLabel from 'components/InputWithLabel/InputWithLabel';
|
||||
@@ -248,8 +247,6 @@ function QueryAddOns({
|
||||
filteredAddOns.some((addOn) => addOn.key === view.key),
|
||||
),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [panelType, isListViewPanel, query, showReduceTo]);
|
||||
|
||||
const handleOptionClick = (e: RadioChangeEvent): void => {
|
||||
|
||||
@@ -26,7 +26,6 @@ function QueryAggregationOptions({
|
||||
queryData: IBuilderQuery | IBuilderTraceOperator;
|
||||
}): JSX.Element {
|
||||
const showAggregationInterval = useMemo(() => {
|
||||
// eslint-disable-next-line sonarjs/prefer-single-boolean-return
|
||||
if (panelType === PANEL_TYPES.VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
/* eslint-disable no-cond-assign */
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
/* eslint-disable class-methods-use-this */
|
||||
/* eslint-disable react/no-this-in-sfc */
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
@@ -149,7 +145,6 @@ const stopEventsExtension = EditorView.domEventHandlers({
|
||||
},
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react/no-this-in-sfc
|
||||
function QueryAggregationSelect({
|
||||
onChange,
|
||||
queryData,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useMemo } from 'react';
|
||||
/* eslint-disable react/require-default-props */
|
||||
import { Button, Tooltip, Typography } from 'antd';
|
||||
import WarningPopover from 'components/WarningPopover/WarningPopover';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-identical-functions */
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { CheckCircleFilled } from '@ant-design/icons';
|
||||
@@ -392,7 +391,6 @@ function QuerySearch({
|
||||
|
||||
// Use callback to prevent dependency changes on each render
|
||||
const fetchValueSuggestions = useCallback(
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
async ({
|
||||
key,
|
||||
searchText,
|
||||
@@ -671,7 +669,6 @@ function QuerySearch({
|
||||
};
|
||||
|
||||
// Enhanced myCompletions function to better use context including query pairs
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
function autoSuggestions(context: CompletionContext): CompletionResult | null {
|
||||
// This matches words before the cursor position
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
@@ -1089,7 +1086,6 @@ function QuerySearch({
|
||||
!(isLoadingSuggestions && lastKeyRef.current === keyName);
|
||||
|
||||
if (shouldFetch) {
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
debouncedFetchValueSuggestions({
|
||||
key: keyName,
|
||||
searchText,
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
/* eslint-disable react/require-default-props */
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import { Button, Tooltip, Typography } from 'antd';
|
||||
import cx from 'classnames';
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
|
||||
import { Token } from 'antlr4';
|
||||
import TraceOperatorGrammarLexer from 'parser/TraceOperatorParser/TraceOperatorGrammarLexer';
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
/* eslint-disable no-continue */
|
||||
|
||||
import { CharStreams, CommonTokenStream, Token } from 'antlr4';
|
||||
import TraceOperatorGrammarLexer from 'parser/TraceOperatorParser/TraceOperatorGrammarLexer';
|
||||
@@ -290,7 +289,6 @@ export function getCurrentTraceExpressionPair(
|
||||
// Find the rightmost pair whose end position is before or at the cursor
|
||||
let bestMatch: ITraceExpressionPair | null = null;
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const pair of expressionPairs) {
|
||||
const { position } = pair;
|
||||
const pairEnd =
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable sonarjs/cognitive-complexity */
|
||||
/* eslint-disable import/named */
|
||||
import { EditorView } from '@uiw/react-codemirror';
|
||||
import { getKeySuggestions } from 'api/querySuggestions/getKeySuggestions';
|
||||
import { getValueSuggestions } from 'api/querySuggestions/getValueSuggestion';
|
||||
import { initialQueriesMap } from 'constants/queryBuilder';
|
||||
import { fireEvent, render, userEvent, waitFor } from 'tests/test-utils';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import type { QueryKeyDataSuggestionsProps } from 'types/api/querySuggestions/types';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
@@ -367,4 +365,36 @@ describe('QuerySearch (Integration with Real CodeMirror)', () => {
|
||||
|
||||
dispatchSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('fetches key suggestions for metrics even without aggregateAttribute.key when showFilterSuggestionsWithoutMetric is true', async () => {
|
||||
const mockedGetKeys = getKeySuggestions as jest.MockedFunction<
|
||||
typeof getKeySuggestions
|
||||
>;
|
||||
mockedGetKeys.mockClear();
|
||||
|
||||
const queryData = {
|
||||
...initialQueriesMap.metrics.builder.queryData[0],
|
||||
aggregateAttribute: {
|
||||
key: '',
|
||||
dataType: DataTypes.String,
|
||||
type: 'string',
|
||||
},
|
||||
};
|
||||
|
||||
render(
|
||||
<QuerySearch
|
||||
onChange={jest.fn()}
|
||||
queryData={queryData}
|
||||
dataSource={DataSource.METRICS}
|
||||
showFilterSuggestionsWithoutMetric
|
||||
/>,
|
||||
);
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(mockedGetKeys).toHaveBeenCalled();
|
||||
},
|
||||
{ timeout: 2000 },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
/* eslint-disable react/display-name */
|
||||
import { jest } from '@jest/globals';
|
||||
import { fireEvent, waitFor } from '@testing-library/react';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
@@ -28,14 +27,14 @@ import { QueryBuilderV2 } from '../../QueryBuilderV2';
|
||||
jest.mock(
|
||||
'../QueryAggregation/QueryAggregation',
|
||||
() =>
|
||||
function () {
|
||||
function QueryAggregation() {
|
||||
return <div>QueryAggregation</div>;
|
||||
},
|
||||
);
|
||||
jest.mock(
|
||||
'../MerticsAggregateSection/MetricsAggregateSection',
|
||||
() =>
|
||||
function () {
|
||||
function MetricsAggregateSection() {
|
||||
return <div>MetricsAggregateSection</div>;
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable */
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import {
|
||||
fireEvent,
|
||||
render,
|
||||
@@ -7,40 +7,46 @@ import {
|
||||
waitFor,
|
||||
within,
|
||||
} from 'tests/test-utils';
|
||||
import { DataSource, ReduceOperators } from 'types/common/queryBuilder';
|
||||
|
||||
import QueryAddOns from '../QueryV2/QueryAddOns/QueryAddOns';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { DataSource, ReduceOperators } from 'types/common/queryBuilder';
|
||||
|
||||
// Mocks: only what is required for this component to render and for us to assert handler calls
|
||||
const mockHandleChangeQueryData = jest.fn();
|
||||
const mockHandleSetQueryData = jest.fn();
|
||||
|
||||
jest.mock('hooks/queryBuilder/useQueryBuilderOperations', () => ({
|
||||
useQueryOperations: () => ({
|
||||
useQueryOperations: (): {
|
||||
handleChangeQueryData: typeof mockHandleChangeQueryData;
|
||||
} => ({
|
||||
handleChangeQueryData: mockHandleChangeQueryData,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('hooks/queryBuilder/useQueryBuilder', () => ({
|
||||
useQueryBuilder: () => ({
|
||||
useQueryBuilder: (): {
|
||||
handleSetQueryData: typeof mockHandleSetQueryData;
|
||||
} => ({
|
||||
handleSetQueryData: mockHandleSetQueryData,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('container/QueryBuilder/filters/GroupByFilter/GroupByFilter', () => ({
|
||||
GroupByFilter: ({ onChange }: any) => (
|
||||
<button data-testid="groupby" onClick={() => onChange(['service.name'])}>
|
||||
GroupByFilter: ({ onChange }: any): JSX.Element => (
|
||||
<button
|
||||
data-testid="groupby"
|
||||
onClick={(): void => onChange(['service.name'])}
|
||||
>
|
||||
GroupByFilter
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
jest.mock('container/QueryBuilder/filters/OrderByFilter/OrderByFilter', () => ({
|
||||
OrderByFilter: ({ onChange }: any) => (
|
||||
OrderByFilter: ({ onChange }: any): JSX.Element => (
|
||||
<button
|
||||
data-testid="orderby"
|
||||
onClick={() => onChange([{ columnName: 'duration', order: 'desc' }])}
|
||||
onClick={(): void => onChange([{ columnName: 'duration', order: 'desc' }])}
|
||||
>
|
||||
OrderByFilter
|
||||
</button>
|
||||
@@ -49,9 +55,12 @@ jest.mock('container/QueryBuilder/filters/OrderByFilter/OrderByFilter', () => ({
|
||||
|
||||
jest.mock('../QueryV2/QueryAddOns/HavingFilter/HavingFilter', () => ({
|
||||
__esModule: true,
|
||||
default: ({ onChange, onClose }: any) => (
|
||||
default: ({ onChange, onClose }: any): JSX.Element => (
|
||||
<div>
|
||||
<button data-testid="having-change" onClick={() => onChange('p99 > 500')}>
|
||||
<button
|
||||
data-testid="having-change"
|
||||
onClick={(): void => onChange('p99 > 500')}
|
||||
>
|
||||
HavingFilter
|
||||
</button>
|
||||
<button data-testid="having-close" onClick={onClose}>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user