diff --git a/.github/workflows/frontend-ci.yml b/.github/workflows/frontend-ci.yml
index 078b2812..81312d87 100644
--- a/.github/workflows/frontend-ci.yml
+++ b/.github/workflows/frontend-ci.yml
@@ -39,6 +39,14 @@ jobs:
working-directory: frontend
run: npm run stylelint
+ - name: Check formatting (Prettier)
+ working-directory: frontend
+ run: npm run format:check
+
- name: Run svelte-check
working-directory: frontend
run: npm run check
+
+ - name: Verify production build
+ working-directory: frontend
+ run: npm run build
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index c77fcbda..389bd80c 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -46,10 +46,18 @@ repos:
files: ^frontend/src/.*\.css$
pass_filenames: false
+ # Prettier - matches CI: cd frontend && npx prettier --check
+ - id: prettier-frontend
+ name: prettier (frontend)
+ entry: bash -c 'cd frontend && npx prettier --check "src/**/*.{ts,svelte,json}"'
+ language: system
+ files: ^frontend/src/.*\.(ts|svelte|json)$
+ pass_filenames: false
+
# Svelte Check - matches CI: cd frontend && npx svelte-check
- id: svelte-check-frontend
name: svelte-check (frontend)
- entry: bash -c 'cd frontend && npx svelte-check --tsconfig ./tsconfig.json'
+ entry: bash -c 'cd frontend && npx svelte-check --tsconfig ./tsconfig.json --fail-on-warnings'
language: system
files: ^frontend/src/.*\.(ts|svelte)$
pass_filenames: false
diff --git a/frontend/.prettierignore b/frontend/.prettierignore
new file mode 100644
index 00000000..1ae07bbb
--- /dev/null
+++ b/frontend/.prettierignore
@@ -0,0 +1,8 @@
+node_modules/
+public/build/
+dist/
+coverage/
+test-results/
+playwright-report/
+src/lib/api/
+*.css
diff --git a/frontend/.prettierrc b/frontend/.prettierrc
new file mode 100644
index 00000000..e0a93da8
--- /dev/null
+++ b/frontend/.prettierrc
@@ -0,0 +1,8 @@
+{
+ "singleQuote": true,
+ "tabWidth": 4,
+ "printWidth": 120,
+ "trailingComma": "all",
+ "plugins": ["prettier-plugin-svelte"],
+ "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
+}
diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js
index 9ba89eea..10fb20ac 100644
--- a/frontend/eslint.config.js
+++ b/frontend/eslint.config.js
@@ -64,7 +64,7 @@ export default [
},
rules: {
...svelte.configs.recommended.rules,
- 'svelte/button-has-type': 'warn',
+ 'svelte/button-has-type': 'error',
'no-unused-vars': 'off',
'no-undef': 'off',
},
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 7f622b6a..381e22bd 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -64,6 +64,8 @@
"monocart-reporter": "^2.10.0",
"postcss": "^8.4.47",
"postcss-lightningcss": "^1.0.2",
+ "prettier": "^3.8.1",
+ "prettier-plugin-svelte": "^3.5.1",
"rollup": "^4.59.0",
"rollup-plugin-css-only": "^4.3.0",
"rollup-plugin-livereload": "^2.0.0",
@@ -7978,6 +7980,31 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
+ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
+ "dev": true,
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-plugin-svelte": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.5.1.tgz",
+ "integrity": "sha512-65+fr5+cgIKWKiqM1Doum4uX6bY8iFCdztvvp2RcF+AJoieaw9kJOFMNcJo/bkmKYsxFaM9OsVZK/gWauG/5mg==",
+ "dev": true,
+ "peerDependencies": {
+ "prettier": "^3.0.0",
+ "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
+ }
+ },
"node_modules/pretty-format": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 22de3249..88cd8215 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -9,13 +9,15 @@
"start": "sirv public --single --no-clear --dev --host",
"generate:api": "openapi-ts",
"lint": "eslint src --ext .ts,.svelte",
- "check": "svelte-check --tsconfig ./tsconfig.json",
+ "check": "svelte-check --tsconfig ./tsconfig.json --fail-on-warnings",
"test": "vitest run",
"test:watch": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
"test:e2e": "playwright test",
- "stylelint": "stylelint \"src/**/*.css\""
+ "stylelint": "stylelint \"src/**/*.css\"",
+ "format": "prettier --write \"src/**/*.{ts,svelte,json}\"",
+ "format:check": "prettier --check \"src/**/*.{ts,svelte,json}\""
},
"dependencies": {
"@codemirror/autocomplete": "^6.17.0",
@@ -69,20 +71,22 @@
"eslint-plugin-svelte": "^3.15.0",
"express": "^5.2.1",
"globals": "^17.3.0",
- "jsdom": "^28.1.0",
"http-proxy": "^1.18.1",
+ "jsdom": "^28.1.0",
"monocart-reporter": "^2.10.0",
"postcss": "^8.4.47",
"postcss-lightningcss": "^1.0.2",
+ "prettier": "^3.8.1",
+ "prettier-plugin-svelte": "^3.5.1",
"rollup": "^4.59.0",
"rollup-plugin-css-only": "^4.3.0",
"rollup-plugin-livereload": "^2.0.0",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-serve": "^3.0.0",
"rollup-plugin-svelte": "^7.2.2",
+ "sirv-cli": "^3.0.1",
"stylelint": "^17.0.0",
"stylelint-config-standard": "^40.0.0",
- "sirv-cli": "^3.0.1",
"svelte-check": "^4.3.6",
"svelte-eslint-parser": "^1.4.1",
"svelte-preprocess": "^6.0.3",
diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte
index f71ba4eb..c197c8bc 100644
--- a/frontend/src/App.svelte
+++ b/frontend/src/App.svelte
@@ -1,11 +1,11 @@
@@ -83,7 +83,7 @@
{:else}
-
+
{#if !authInitialized}
@@ -94,6 +94,6 @@
{/if}
-
+
{/if}
diff --git a/frontend/src/__tests__/test-utils.ts b/frontend/src/__tests__/test-utils.ts
index 8bc33704..ae049433 100644
--- a/frontend/src/__tests__/test-utils.ts
+++ b/frontend/src/__tests__/test-utils.ts
@@ -10,25 +10,25 @@ import { vi, type Mock } from 'vitest';
import userEvent from '@testing-library/user-event';
import { EVENT_TYPES } from '$lib/admin/events/eventTypes';
import type {
- ExecutionCompletedEvent,
- EventBrowseResponse,
- EventDetailResponse,
- EventStatsResponse,
- AdminUserOverview,
- NotificationResponse,
- EventMetadata,
- EventType,
- AdminExecutionResponse,
- QueueStatusResponse,
- SagaStatusResponse,
- UserResponse,
+ ExecutionCompletedEvent,
+ EventBrowseResponse,
+ EventDetailResponse,
+ EventStatsResponse,
+ AdminUserOverview,
+ NotificationResponse,
+ EventMetadata,
+ EventType,
+ AdminExecutionResponse,
+ QueueStatusResponse,
+ SagaStatusResponse,
+ UserResponse,
} from '$lib/api';
export type UserEventInstance = ReturnType;
export const user: UserEventInstance = userEvent.setup({
- delay: null,
- pointerEventsCheck: 0,
+ delay: null,
+ pointerEventsCheck: 0,
});
/**
@@ -36,17 +36,17 @@ export const user: UserEventInstance = userEvent.setup({
* Use this for type annotations in test files.
*/
export interface MockStore {
- set(v: T): void;
- subscribe(fn: (v: T) => void): () => void;
- update(fn: (v: T) => T): void;
- _getValue?(): T;
+ set(v: T): void;
+ subscribe(fn: (v: T) => void): () => void;
+ update(fn: (v: T) => T): void;
+ _getValue?(): T;
}
/**
* Type definition for mock derived stores.
*/
export interface MockDerivedStore {
- subscribe(fn: (v: T) => void): () => void;
+ subscribe(fn: (v: T) => void): () => void;
}
/**
@@ -54,8 +54,8 @@ export interface MockDerivedStore {
* Returns a function to restore the original behavior.
*/
export function suppressConsoleError(): () => void {
- const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
- return () => spy.mockRestore();
+ const spy = vi.spyOn(console, 'error').mockImplementation(() => {});
+ return () => spy.mockRestore();
}
/**
@@ -63,8 +63,8 @@ export function suppressConsoleError(): () => void {
* Returns a function to restore the original behavior.
*/
export function suppressConsoleWarn(): () => void {
- const spy = vi.spyOn(console, 'warn').mockImplementation(() => {});
- return () => spy.mockRestore();
+ const spy = vi.spyOn(console, 'warn').mockImplementation(() => {});
+ return () => spy.mockRestore();
}
/**
@@ -74,258 +74,281 @@ export function suppressConsoleWarn(): () => void {
* @param components - Record mapping export names to HTML strings
*/
export function createMockNamedComponents(components: Record): Record {
- const module: Record = {};
- for (const [name, html] of Object.entries(components)) {
- const Mock = function () {
- return {};
- } as unknown as { new (): object; render: () => { html: string; css: { code: string; map: null }; head: string } };
- Mock.render = () => ({ html, css: { code: '', map: null }, head: '' });
- module[name] = Mock;
- }
- return module;
+ const module: Record = {};
+ for (const [name, html] of Object.entries(components)) {
+ const Mock = function () {
+ return {};
+ } as unknown as {
+ new (): object;
+ render: () => { html: string; css: { code: string; map: null }; head: string };
+ };
+ Mock.render = () => ({ html, css: { code: '', map: null }, head: '' });
+ module[name] = Mock;
+ }
+ return module;
}
/**
* Creates a mock notification for testing.
*/
export function createMockNotification(overrides: Partial = {}): NotificationResponse {
- return {
- notification_id: 'notif-1',
- subject: 'Test Notification',
- body: 'This is a test notification body',
- channel: 'in_app',
- status: 'delivered',
- severity: 'medium',
- tags: [],
- created_at: new Date().toISOString(),
- action_url: '',
- read_at: null,
- ...overrides,
- };
+ return {
+ notification_id: 'notif-1',
+ subject: 'Test Notification',
+ body: 'This is a test notification body',
+ channel: 'in_app',
+ status: 'delivered',
+ severity: 'medium',
+ tags: [],
+ created_at: new Date().toISOString(),
+ action_url: '',
+ read_at: null,
+ ...overrides,
+ };
}
/**
* Creates multiple mock notifications for testing.
*/
export function createMockNotifications(count: number): NotificationResponse[] {
- return Array.from({ length: count }, (_, i) =>
- createMockNotification({
- notification_id: `notif-${i + 1}`,
- subject: `Notification ${i + 1}`,
- body: `Body for notification ${i + 1}`,
- status: i % 2 === 0 ? 'delivered' : 'read',
- })
- );
+ return Array.from({ length: count }, (_, i) =>
+ createMockNotification({
+ notification_id: `notif-${i + 1}`,
+ subject: `Notification ${i + 1}`,
+ body: `Body for notification ${i + 1}`,
+ status: i % 2 === 0 ? 'delivered' : 'read',
+ }),
+ );
}
export function mockWindowGlobals(openMock: Mock, confirmMock: Mock): void {
- vi.stubGlobal('open', openMock);
- vi.stubGlobal('confirm', confirmMock);
+ vi.stubGlobal('open', openMock);
+ vi.stubGlobal('confirm', confirmMock);
}
export type MockEventOverrides = Omit, 'event_type' | 'metadata'> & {
- event_type?: EventType;
- metadata?: Partial;
+ event_type?: EventType;
+ metadata?: Partial;
};
export const DEFAULT_EVENT: ExecutionCompletedEvent = {
- event_id: 'evt-1',
- event_type: 'execution_completed',
- event_version: '1',
- timestamp: '2024-01-15T10:30:00Z',
- aggregate_id: 'exec-456',
- metadata: {
- service_name: 'test-service',
- service_version: '1.0.0',
- user_id: 'user-1',
- },
- execution_id: 'exec-456',
- exit_code: 0,
- stdout: 'hello',
+ event_id: 'evt-1',
+ event_type: 'execution_completed',
+ event_version: '1',
+ timestamp: '2024-01-15T10:30:00Z',
+ aggregate_id: 'exec-456',
+ metadata: {
+ service_name: 'test-service',
+ service_version: '1.0.0',
+ user_id: 'user-1',
+ },
+ execution_id: 'exec-456',
+ exit_code: 0,
+ stdout: 'hello',
};
export { EVENT_TYPES };
export const createMockEvent = (overrides: MockEventOverrides = {}): ExecutionCompletedEvent => {
- const { metadata: metadataOverrides, ...rest } = overrides;
- return {
- ...DEFAULT_EVENT,
- ...rest,
- metadata: { ...DEFAULT_EVENT.metadata, ...metadataOverrides },
- } as ExecutionCompletedEvent;
+ const { metadata: metadataOverrides, ...rest } = overrides;
+ return {
+ ...DEFAULT_EVENT,
+ ...rest,
+ metadata: { ...DEFAULT_EVENT.metadata, ...metadataOverrides },
+ } as ExecutionCompletedEvent;
};
export const createMockEvents = (count: number): EventBrowseResponse['events'] =>
- Array.from({ length: count }, (_, i) => ({
- ...createMockEvent({
- event_id: `evt-${i + 1}`,
- aggregate_id: `exec-${i + 1}`,
- metadata: {
- user_id: `user-${(i % 3) + 1}`,
- service_name: 'execution-service',
- },
- }),
- event_type: EVENT_TYPES[i % EVENT_TYPES.length],
- timestamp: new Date(Date.now() - i * 60000).toISOString(),
- } as EventBrowseResponse['events'][number]));
+ Array.from(
+ { length: count },
+ (_, i) =>
+ ({
+ ...createMockEvent({
+ event_id: `evt-${i + 1}`,
+ aggregate_id: `exec-${i + 1}`,
+ metadata: {
+ user_id: `user-${(i % 3) + 1}`,
+ service_name: 'execution-service',
+ },
+ }),
+ event_type: EVENT_TYPES[i % EVENT_TYPES.length],
+ timestamp: new Date(Date.now() - i * 60000).toISOString(),
+ }) as EventBrowseResponse['events'][number],
+ );
export function createMockStats(overrides: Partial = {}): EventStatsResponse {
- return {
- total_events: 150,
- error_rate: 2.5,
- avg_processing_time: 1.23,
- top_users: [{ user_id: 'user-1', event_count: 50 }],
- events_by_type: [],
- events_by_hour: [],
- ...overrides,
- };
+ return {
+ total_events: 150,
+ error_rate: 2.5,
+ avg_processing_time: 1.23,
+ top_users: [{ user_id: 'user-1', event_count: 50 }],
+ events_by_type: [],
+ events_by_hour: [],
+ ...overrides,
+ };
}
export function createMockEventDetail(event = createMockEvent()): EventDetailResponse {
- return {
- event: event as EventDetailResponse['event'],
- related_events: [
- { event_id: 'rel-1', event_type: 'execution_started', timestamp: '2024-01-15T10:29:00Z' },
- { event_id: 'rel-2', event_type: 'pod_created', timestamp: '2024-01-15T10:29:30Z' },
- ],
- timeline: [],
- };
+ return {
+ event: event as EventDetailResponse['event'],
+ related_events: [
+ { event_id: 'rel-1', event_type: 'execution_started', timestamp: '2024-01-15T10:29:00Z' },
+ { event_id: 'rel-2', event_type: 'pod_created', timestamp: '2024-01-15T10:29:30Z' },
+ ],
+ timeline: [],
+ };
}
export function createMockUserOverview(): AdminUserOverview {
- return {
- user: {
- user_id: 'user-1',
- username: 'testuser',
- email: 'test@example.com',
- role: 'user',
- is_active: true,
- is_superuser: false,
- created_at: '2024-01-01T00:00:00Z',
- updated_at: '2024-01-01T00:00:00Z',
- bypass_rate_limit: null,
- global_multiplier: null,
- has_custom_limits: null,
- },
- stats: {
- total_events: 100,
- events_by_type: [],
- events_by_service: [],
- events_by_hour: [],
- top_users: [],
- error_rate: 0,
- avg_processing_time: 0,
- start_time: null,
- end_time: null,
- },
- derived_counts: { succeeded: 80, failed: 10, timeout: 5, cancelled: 5, terminal_total: 100 },
- rate_limit_summary: { bypass_rate_limit: false, global_multiplier: 1, has_custom_limits: false },
- recent_events: [createMockEvent() as AdminUserOverview['recent_events'][number]],
- };
+ return {
+ user: {
+ user_id: 'user-1',
+ username: 'testuser',
+ email: 'test@example.com',
+ role: 'user',
+ is_active: true,
+ is_superuser: false,
+ created_at: '2024-01-01T00:00:00Z',
+ updated_at: '2024-01-01T00:00:00Z',
+ bypass_rate_limit: null,
+ global_multiplier: null,
+ has_custom_limits: null,
+ },
+ stats: {
+ total_events: 100,
+ events_by_type: [],
+ events_by_service: [],
+ events_by_hour: [],
+ top_users: [],
+ error_rate: 0,
+ avg_processing_time: 0,
+ start_time: null,
+ end_time: null,
+ },
+ derived_counts: { succeeded: 80, failed: 10, timeout: 5, cancelled: 5, terminal_total: 100 },
+ rate_limit_summary: { bypass_rate_limit: false, global_multiplier: 1, has_custom_limits: false },
+ recent_events: [createMockEvent() as AdminUserOverview['recent_events'][number]],
+ };
}
export const DEFAULT_EXECUTION: AdminExecutionResponse = {
- execution_id: 'exec-1',
- script: 'print("hi")',
- status: 'queued',
- lang: 'python',
- lang_version: '3.11',
- priority: 'normal',
- user_id: 'user-1',
- stdout: null,
- stderr: null,
- exit_code: null,
- error_type: null,
- created_at: '2024-01-15T10:30:00Z',
- updated_at: '2024-01-15T10:30:00Z',
+ execution_id: 'exec-1',
+ script: 'print("hi")',
+ status: 'queued',
+ lang: 'python',
+ lang_version: '3.11',
+ priority: 'normal',
+ user_id: 'user-1',
+ stdout: null,
+ stderr: null,
+ exit_code: null,
+ error_type: null,
+ created_at: '2024-01-15T10:30:00Z',
+ updated_at: '2024-01-15T10:30:00Z',
};
export const createMockExecution = (overrides: Partial = {}): AdminExecutionResponse => ({
- ...DEFAULT_EXECUTION,
- ...overrides,
+ ...DEFAULT_EXECUTION,
+ ...overrides,
});
const EXECUTION_STATUSES: AdminExecutionResponse['status'][] = [
- 'queued', 'scheduled', 'running', 'completed', 'failed', 'timeout', 'cancelled', 'error',
-];
-const EXECUTION_PRIORITIES: AdminExecutionResponse['priority'][] = [
- 'critical', 'high', 'normal', 'low', 'background',
+ 'queued',
+ 'scheduled',
+ 'running',
+ 'completed',
+ 'failed',
+ 'timeout',
+ 'cancelled',
+ 'error',
];
+const EXECUTION_PRIORITIES: AdminExecutionResponse['priority'][] = ['critical', 'high', 'normal', 'low', 'background'];
export const createMockExecutions = (count: number): AdminExecutionResponse[] =>
- Array.from({ length: count }, (_, i) => createMockExecution({
- execution_id: `exec-${i + 1}`,
- status: EXECUTION_STATUSES[i % EXECUTION_STATUSES.length],
- priority: EXECUTION_PRIORITIES[i % EXECUTION_PRIORITIES.length],
- user_id: `user-${(i % 3) + 1}`,
- created_at: new Date(Date.now() - i * 60000).toISOString(),
- }));
+ Array.from({ length: count }, (_, i) =>
+ createMockExecution({
+ execution_id: `exec-${i + 1}`,
+ status: EXECUTION_STATUSES[i % EXECUTION_STATUSES.length],
+ priority: EXECUTION_PRIORITIES[i % EXECUTION_PRIORITIES.length],
+ user_id: `user-${(i % 3) + 1}`,
+ created_at: new Date(Date.now() - i * 60000).toISOString(),
+ }),
+ );
export const createMockQueueStatus = (overrides: Partial = {}): QueueStatusResponse => ({
- queue_depth: 5,
- active_count: 2,
- max_concurrent: 10,
- by_priority: { normal: 3, high: 2 },
- ...overrides,
+ queue_depth: 5,
+ active_count: 2,
+ max_concurrent: 10,
+ by_priority: { normal: 3, high: 2 },
+ ...overrides,
});
export const DEFAULT_SAGA: SagaStatusResponse = {
- saga_id: 'saga-1',
- saga_name: 'execution_saga',
- execution_id: 'exec-123',
- state: 'running',
- current_step: 'create_pod',
- completed_steps: ['validate_execution', 'allocate_resources', 'queue_execution'],
- compensated_steps: [],
- retry_count: 0,
- error_message: null,
- created_at: '2024-01-15T10:30:00Z',
- updated_at: '2024-01-15T10:31:00Z',
- completed_at: null,
+ saga_id: 'saga-1',
+ saga_name: 'execution_saga',
+ execution_id: 'exec-123',
+ state: 'running',
+ current_step: 'create_pod',
+ completed_steps: ['validate_execution', 'allocate_resources', 'queue_execution'],
+ compensated_steps: [],
+ retry_count: 0,
+ error_message: null,
+ created_at: '2024-01-15T10:30:00Z',
+ updated_at: '2024-01-15T10:31:00Z',
+ completed_at: null,
};
export const createMockSaga = (overrides: Partial = {}): SagaStatusResponse => ({
- ...DEFAULT_SAGA,
- ...overrides,
+ ...DEFAULT_SAGA,
+ ...overrides,
});
const SAGA_STATES: SagaStatusResponse['state'][] = [
- 'created', 'running', 'completed', 'failed', 'compensating', 'timeout',
+ 'created',
+ 'running',
+ 'completed',
+ 'failed',
+ 'compensating',
+ 'timeout',
];
export const createMockSagas = (count: number): SagaStatusResponse[] =>
- Array.from({ length: count }, (_, i) => createMockSaga({
- saga_id: `saga-${i + 1}`,
- execution_id: `exec-${i + 1}`,
- state: SAGA_STATES[i % SAGA_STATES.length],
- created_at: new Date(Date.now() - i * 60000).toISOString(),
- updated_at: new Date(Date.now() - i * 30000).toISOString(),
- }));
+ Array.from({ length: count }, (_, i) =>
+ createMockSaga({
+ saga_id: `saga-${i + 1}`,
+ execution_id: `exec-${i + 1}`,
+ state: SAGA_STATES[i % SAGA_STATES.length],
+ created_at: new Date(Date.now() - i * 60000).toISOString(),
+ updated_at: new Date(Date.now() - i * 30000).toISOString(),
+ }),
+ );
export const DEFAULT_USER: UserResponse = {
- user_id: 'user-1',
- username: 'testuser',
- email: 'test@example.com',
- role: 'user',
- is_active: true,
- is_superuser: false,
- created_at: '2024-01-15T10:30:00Z',
- updated_at: '2024-01-15T10:30:00Z',
- bypass_rate_limit: false,
- global_multiplier: 1.0,
- has_custom_limits: false,
+ user_id: 'user-1',
+ username: 'testuser',
+ email: 'test@example.com',
+ role: 'user',
+ is_active: true,
+ is_superuser: false,
+ created_at: '2024-01-15T10:30:00Z',
+ updated_at: '2024-01-15T10:30:00Z',
+ bypass_rate_limit: false,
+ global_multiplier: 1.0,
+ has_custom_limits: false,
};
export const createMockUser = (overrides: Partial = {}): UserResponse => ({
- ...DEFAULT_USER,
- ...overrides,
+ ...DEFAULT_USER,
+ ...overrides,
});
export const createMockUsers = (count: number): UserResponse[] =>
- Array.from({ length: count }, (_, i) => createMockUser({
- user_id: `user-${i + 1}`,
- username: `user${i + 1}`,
- email: `user${i + 1}@example.com`,
- role: i === 0 ? 'admin' : 'user',
- is_active: i % 3 !== 0,
- }));
+ Array.from({ length: count }, (_, i) =>
+ createMockUser({
+ user_id: `user-${i + 1}`,
+ username: `user${i + 1}`,
+ email: `user${i + 1}@example.com`,
+ role: i === 0 ? 'admin' : 'user',
+ is_active: i % 3 !== 0,
+ }),
+ );
diff --git a/frontend/src/components/ErrorDisplay.svelte b/frontend/src/components/ErrorDisplay.svelte
index 77868c12..3870960f 100644
--- a/frontend/src/components/ErrorDisplay.svelte
+++ b/frontend/src/components/ErrorDisplay.svelte
@@ -1,73 +1,78 @@
-
-
-
+
+
+
-
-
- {title}
-
+
+
+ {title}
+
-
-
- {userMessage}
-
+
+
+ {userMessage}
+
-
-
-
-
-
+
+
+
+
+
-
-
- If this problem persists, please contact support.
-
-
+
+
+ If this problem persists, please contact support.
+
+
diff --git a/frontend/src/components/EventTypeIcon.svelte b/frontend/src/components/EventTypeIcon.svelte
index f00b03ac..0ac70e42 100644
--- a/frontend/src/components/EventTypeIcon.svelte
+++ b/frontend/src/components/EventTypeIcon.svelte
@@ -1,51 +1,51 @@
diff --git a/frontend/src/components/Footer.svelte b/frontend/src/components/Footer.svelte
index ff1f9ca2..f203502a 100644
--- a/frontend/src/components/Footer.svelte
+++ b/frontend/src/components/Footer.svelte
@@ -2,15 +2,13 @@
import { Github, Send } from '@lucide/svelte';
-