{showAvatar && (
-
+
)}
diff --git a/frontend/src/components/ui/StatsCard.tsx b/frontend/src/components/ui/StatsCard.tsx
index 54711ff3..03fe7413 100644
--- a/frontend/src/components/ui/StatsCard.tsx
+++ b/frontend/src/components/ui/StatsCard.tsx
@@ -1,5 +1,6 @@
-import * as React from 'react'
import { TrendingUp, TrendingDown, Minus } from 'lucide-react'
+import * as React from 'react'
+
import { cn } from '../../utils/cn'
export interface StatsCardChange {
diff --git a/frontend/src/components/ui/Switch.tsx b/frontend/src/components/ui/Switch.tsx
index 2466a95d..89214db1 100644
--- a/frontend/src/components/ui/Switch.tsx
+++ b/frontend/src/components/ui/Switch.tsx
@@ -1,4 +1,5 @@
import * as React from 'react'
+
import { cn } from '../../utils/cn'
interface SwitchProps extends React.InputHTMLAttributes {
@@ -34,7 +35,7 @@ const Switch = React.forwardRef(
'bg-surface-muted',
'peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-brand-500 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-surface-base',
'peer-checked:bg-brand-500',
- "after:content-[''] after:absolute after:top-[2px] after:start-[2px]",
+ "after:content-[''] after:absolute after:top-0.5 after:start-[2px]",
'after:bg-white after:border after:border-border after:rounded-full',
'after:h-5 after:w-5 after:transition-all after:duration-fast',
'peer-checked:after:translate-x-full peer-checked:after:border-white',
diff --git a/frontend/src/components/ui/Tabs.test.tsx b/frontend/src/components/ui/Tabs.test.tsx
index 2774bb51..36168b11 100644
--- a/frontend/src/components/ui/Tabs.test.tsx
+++ b/frontend/src/components/ui/Tabs.test.tsx
@@ -1,7 +1,8 @@
import '@testing-library/jest-dom/vitest'
import { render, screen } from '@testing-library/react'
-import { describe, it, expect } from 'vitest'
import userEvent from '@testing-library/user-event'
+import { describe, it, expect } from 'vitest'
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from './Tabs'
describe('Tabs', () => {
diff --git a/frontend/src/components/ui/Tabs.tsx b/frontend/src/components/ui/Tabs.tsx
index 40882376..ed7b2b7d 100644
--- a/frontend/src/components/ui/Tabs.tsx
+++ b/frontend/src/components/ui/Tabs.tsx
@@ -1,5 +1,6 @@
-import * as React from 'react'
import * as TabsPrimitive from '@radix-ui/react-tabs'
+import * as React from 'react'
+
import { cn } from '../../utils/cn'
const Tabs = TabsPrimitive.Root
diff --git a/frontend/src/components/ui/Textarea.tsx b/frontend/src/components/ui/Textarea.tsx
index 4dc8d6e9..764c04fa 100644
--- a/frontend/src/components/ui/Textarea.tsx
+++ b/frontend/src/components/ui/Textarea.tsx
@@ -1,4 +1,5 @@
import * as React from 'react'
+
import { cn } from '../../utils/cn'
export interface TextareaProps
diff --git a/frontend/src/components/ui/Tooltip.tsx b/frontend/src/components/ui/Tooltip.tsx
index 5d799612..855a6de6 100644
--- a/frontend/src/components/ui/Tooltip.tsx
+++ b/frontend/src/components/ui/Tooltip.tsx
@@ -1,5 +1,6 @@
-import * as React from 'react'
import * as TooltipPrimitive from '@radix-ui/react-tooltip'
+import * as React from 'react'
+
import { cn } from '../../utils/cn'
const TooltipProvider = TooltipPrimitive.Provider
diff --git a/frontend/src/components/ui/__tests__/Alert.test.tsx b/frontend/src/components/ui/__tests__/Alert.test.tsx
index a6cb9bea..254989aa 100644
--- a/frontend/src/components/ui/__tests__/Alert.test.tsx
+++ b/frontend/src/components/ui/__tests__/Alert.test.tsx
@@ -1,7 +1,8 @@
import '@testing-library/jest-dom/vitest'
import { render, screen, fireEvent } from '@testing-library/react'
-import { describe, it, expect, vi } from 'vitest'
import { AlertCircle } from 'lucide-react'
+import { describe, it, expect, vi } from 'vitest'
+
import { Alert, AlertTitle, AlertDescription } from '../Alert'
describe('Alert', () => {
diff --git a/frontend/src/components/ui/__tests__/DataTable.test.tsx b/frontend/src/components/ui/__tests__/DataTable.test.tsx
index 3d96eeb0..beddcf71 100644
--- a/frontend/src/components/ui/__tests__/DataTable.test.tsx
+++ b/frontend/src/components/ui/__tests__/DataTable.test.tsx
@@ -1,5 +1,6 @@
import { render, screen, fireEvent } from '@testing-library/react'
import { describe, it, expect, vi } from 'vitest'
+
import { DataTable, type Column } from '../DataTable'
interface TestRow {
diff --git a/frontend/src/components/ui/__tests__/Input.test.tsx b/frontend/src/components/ui/__tests__/Input.test.tsx
index 3f956f44..d66074ba 100644
--- a/frontend/src/components/ui/__tests__/Input.test.tsx
+++ b/frontend/src/components/ui/__tests__/Input.test.tsx
@@ -1,8 +1,9 @@
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import { describe, it, expect, vi } from 'vitest'
-import { createRef } from 'react'
import { Search, Mail, Lock } from 'lucide-react'
+import { createRef } from 'react'
+import { describe, it, expect, vi } from 'vitest'
+
import { Input } from '../Input'
describe('Input', () => {
diff --git a/frontend/src/components/ui/__tests__/Skeleton.test.tsx b/frontend/src/components/ui/__tests__/Skeleton.test.tsx
index 6abb0f79..41e5bf3b 100644
--- a/frontend/src/components/ui/__tests__/Skeleton.test.tsx
+++ b/frontend/src/components/ui/__tests__/Skeleton.test.tsx
@@ -1,5 +1,6 @@
import { render, screen } from '@testing-library/react'
import { describe, it, expect } from 'vitest'
+
import {
Skeleton,
SkeletonCard,
diff --git a/frontend/src/components/ui/__tests__/StatsCard.test.tsx b/frontend/src/components/ui/__tests__/StatsCard.test.tsx
index 42befffc..13141052 100644
--- a/frontend/src/components/ui/__tests__/StatsCard.test.tsx
+++ b/frontend/src/components/ui/__tests__/StatsCard.test.tsx
@@ -1,6 +1,7 @@
import { render, screen } from '@testing-library/react'
-import { describe, it, expect } from 'vitest'
import { Users } from 'lucide-react'
+import { describe, it, expect } from 'vitest'
+
import { StatsCard, type StatsCardChange } from '../StatsCard'
describe('StatsCard', () => {
diff --git a/frontend/src/context/AuthContext.tsx b/frontend/src/context/AuthContext.tsx
index 44a9c333..4fd416bb 100644
--- a/frontend/src/context/AuthContext.tsx
+++ b/frontend/src/context/AuthContext.tsx
@@ -1,6 +1,7 @@
import { useState, useEffect, useCallback, useRef, type ReactNode, type FC } from 'react';
+
+import { AuthContext, type User } from './AuthContextValue';
import client, { setAuthToken, setAuthErrorHandler } from '../api/client';
-import { AuthContext, User } from './AuthContextValue';
export const AuthProvider: FC<{ children: ReactNode }> = ({ children }) => {
const [user, setUser] = useState(null);
@@ -164,15 +165,15 @@ export const AuthProvider: FC<{ children: ReactNode }> = ({ children }) => {
const events = ['mousedown', 'keydown', 'scroll', 'touchstart'];
const handleActivity = () => resetTimer();
- events.forEach(event => {
+ for (const event of events) {
window.addEventListener(event, handleActivity);
- });
+ }
return () => {
if (timeoutId) clearTimeout(timeoutId);
- events.forEach(event => {
+ for (const event of events) {
window.removeEventListener(event, handleActivity);
- });
+ }
};
}, [user, logout]);
diff --git a/frontend/src/context/LanguageContext.tsx b/frontend/src/context/LanguageContext.tsx
index dcb7d6eb..590671df 100644
--- a/frontend/src/context/LanguageContext.tsx
+++ b/frontend/src/context/LanguageContext.tsx
@@ -1,6 +1,7 @@
-import { ReactNode, useState, useEffect } from 'react'
+import { type ReactNode, useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
-import { LanguageContext, Language } from './LanguageContextValue'
+
+import { LanguageContext, type Language } from './LanguageContextValue'
export function LanguageProvider({ children }: { children: ReactNode }) {
const { i18n } = useTranslation()
diff --git a/frontend/src/context/ThemeContext.tsx b/frontend/src/context/ThemeContext.tsx
index 98f0a3db..5e17057b 100644
--- a/frontend/src/context/ThemeContext.tsx
+++ b/frontend/src/context/ThemeContext.tsx
@@ -1,5 +1,6 @@
-import { useEffect, useState, ReactNode } from 'react'
-import { ThemeContext, Theme } from './ThemeContextValue'
+import { useEffect, useState, type ReactNode } from 'react'
+
+import { ThemeContext, type Theme } from './ThemeContextValue'
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState(() => {
diff --git a/frontend/src/data/__tests__/crowdsecPresets.test.ts b/frontend/src/data/__tests__/crowdsecPresets.test.ts
index 6ca204c0..330fc3a1 100644
--- a/frontend/src/data/__tests__/crowdsecPresets.test.ts
+++ b/frontend/src/data/__tests__/crowdsecPresets.test.ts
@@ -1,4 +1,5 @@
import { describe, it, expect } from 'vitest'
+
import { CROWDSEC_PRESETS, findCrowdsecPreset, type CrowdsecPreset } from '../crowdsecPresets'
describe('crowdsecPresets', () => {
@@ -13,35 +14,35 @@ describe('crowdsecPresets', () => {
})
it('should have valid YAML content for each preset', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
+ for (const preset of CROWDSEC_PRESETS) {
expect(preset.content).toContain('configs:')
expect(preset.content).toMatch(/collections:|parsers:|scenarios:|postoverflows:/)
- })
+ }
})
it('should have required metadata fields', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
+ for (const preset of CROWDSEC_PRESETS) {
expect(preset).toHaveProperty('slug')
expect(preset).toHaveProperty('title')
expect(preset).toHaveProperty('description')
expect(preset).toHaveProperty('content')
expect(preset.slug).toMatch(/^[a-z0-9-]+$/) // Slug format validation
- })
+ }
})
it('should have descriptive titles and descriptions', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
+ for (const preset of CROWDSEC_PRESETS) {
expect(preset.title.length).toBeGreaterThan(5)
expect(preset.description.length).toBeGreaterThan(10)
- })
+ }
})
it('should have tags for each preset', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
+ for (const preset of CROWDSEC_PRESETS) {
expect(preset.tags).toBeDefined()
expect(Array.isArray(preset.tags)).toBe(true)
expect(preset.tags!.length).toBeGreaterThan(0)
- })
+ }
})
it('should have warnings for production-critical presets', () => {
@@ -61,32 +62,32 @@ describe('crowdsecPresets', () => {
describe('preset content integrity', () => {
it('should have valid CrowdSec YAML structure', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
+ for (const preset of CROWDSEC_PRESETS) {
const lines = preset.content.split('\n')
expect(lines[0]).toMatch(/^configs:/)
- })
+ }
})
it('should reference valid CrowdSec hub items', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
+ for (const preset of CROWDSEC_PRESETS) {
// Extract collection references
const collections = preset.content.match(/- crowdsecurity\/[\w-]+/g) || []
- collections.forEach((item) => {
+ for (const item of collections) {
// Hub items can contain underscores (e.g., http-crawl-non_statics)
expect(item).toMatch(/^- crowdsecurity\/[a-z0-9-_]+$/)
- })
- })
+ }
+ }
})
it('should have proper YAML indentation', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
+ for (const preset of CROWDSEC_PRESETS) {
const lines = preset.content.split('\n')
// Check that collection/parser/scenario items are indented with spaces
const itemLines = lines.filter((line) => line.trim().startsWith('- crowdsecurity/'))
- itemLines.forEach((line) => {
+ for (const line of itemLines) {
expect(line).toMatch(/^\s{4,}- crowdsecurity\//)
- })
- })
+ }
+ }
})
it('should reference known CrowdSec collections', () => {
@@ -191,11 +192,11 @@ describe('crowdsecPresets', () => {
describe('preset tag consistency', () => {
it('should have consistent tag naming (lowercase, hyphenated)', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
- preset.tags?.forEach((tag) => {
+ for (const preset of CROWDSEC_PRESETS) {
+ for (const tag of preset.tags ?? []) {
expect(tag).toMatch(/^[a-z0-9-]+$/)
- })
- })
+ }
+ }
})
it('should have descriptive tags', () => {
@@ -207,100 +208,96 @@ describe('crowdsecPresets', () => {
describe('slug format validation', () => {
it('should use lowercase slugs', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
+ for (const preset of CROWDSEC_PRESETS) {
expect(preset.slug).toBe(preset.slug.toLowerCase())
- })
+ }
})
it('should use hyphens as separators', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
+ for (const preset of CROWDSEC_PRESETS) {
expect(preset.slug).not.toContain('_')
expect(preset.slug).not.toContain(' ')
- })
+ }
})
it('should not have leading or trailing hyphens', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
+ for (const preset of CROWDSEC_PRESETS) {
expect(preset.slug).not.toMatch(/^-/)
expect(preset.slug).not.toMatch(/-$/)
- })
+ }
})
it('should not have consecutive hyphens', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
+ for (const preset of CROWDSEC_PRESETS) {
expect(preset.slug).not.toContain('--')
- })
+ }
})
})
describe('content safety', () => {
it('should not contain executable code', () => {
- CROWDSEC_PRESETS.forEach((preset) => {
+ for (const preset of CROWDSEC_PRESETS) {
expect(preset.content).not.toContain('