Skip to content

Feat/better frontend#240

Merged
HardMax71 merged 9 commits intomainfrom
feat/better-frontend
Feb 28, 2026
Merged

Feat/better frontend#240
HardMax71 merged 9 commits intomainfrom
feat/better-frontend

Conversation

@HardMax71
Copy link
Copy Markdown
Owner

@HardMax71 HardMax71 commented Feb 28, 2026


Summary by cubic

Adds a tagged consola logger and date-fns time formatters, simplifies admin auto‑refresh with plain intervals, cleans up dead code, and improves accessibility and menu behavior. Also auto‑marks notifications as read after a short delay and adds Stylelint.

  • New Features

    • Tagged logging via consola across modules (App, API, auth/theme/editor, notifications); Rollup injects isProduction to set level; Stylelint added (config, CI job, pre-commit).
    • Consistent timestamps via date-fns: formatTimestamp, formatRelativeTime (“just now”, “3m/5h/2d ago”), formatDateOnly/TimeOnly; used in notifications, admin tables, and pages. Notification dropdown auto-marks unread after 2s.
    • UX/accessibility: reduced-motion styles, Escape-to-close Modal with svelte:window, responsive Header via derived width, and outside-click closes menus (e.g., admin export). Safer localStorage writes in Editor.
  • Refactors

    • Replaced custom autoRefresh with setInterval in admin stores (Executions 5s, Events 30s, Sagas configurable via refreshEnabled/refreshRate); tests simplified.
    • Removed unused code and constants: ProtectedRoute, admin ActionButtons/StatsCard/StatusBadge, autoRefresh utilities, role/status color constants, pagination reset helpers, and related tests. Build keeps console logs; consolidated $lib imports; notifications list no longer capped.

Written for commit 02937c1. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Auto-mark unread notifications as read shortly after opening the dropdown.
  • Improvements

    • Unified, clearer date/time displays (e.g., "just now", "3m ago", "2d ago").
    • Improved form controls, button and editor styling; consistent animation names and reduced-motion support.
    • Mobile menu and dropdowns now close more reliably on outside clicks.
  • Refactor

    • Admin refresh/pagination behavior simplified and refresh controls unified.
  • Removals

    • Several admin UI components and related exports/tests removed; notification list cap lifted.

Copilot AI review requested due to automatic review settings February 28, 2026 20:35
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 28, 2026

📝 Walkthrough

Walkthrough

Adds a centralized consola-based logger, standardizes date handling with date-fns and new formatter APIs, inlines/removes the auto-refresh abstraction across admin stores, deletes several admin UI components and related tests, and replaces manual lifecycle listeners with Svelte reactive/global bindings.

Changes

Cohort / File(s) Summary
Build & deps
frontend/package.json, frontend/rollup.config.js
Added consola and date-fns; rollup now injects real production flag; terser no longer drops console calls; added stylelint scripts.
Logger core & adopters
frontend/src/lib/logger.ts, frontend/src/App.svelte, frontend/src/main.ts, frontend/src/lib/api-interceptors.ts, frontend/src/lib/notifications/stream.svelte.ts, frontend/src/routes/..., frontend/src/stores/...
New logger module; replaced many console.* usages with scoped logger calls; adjusted NotificationStream.connect signature (removed onError).
Formatters & date-fns
frontend/src/lib/formatters.ts, frontend/src/lib/__tests__/formatters.test.ts
Removed formatDate, formatBytes, formatNumber; added formatTimestamp, formatDateOnly, formatTimeOnly, formatDuration; switched to date-fns + parseISO and changed relative-time thresholds.
Formatter callsites & tests
frontend/src/components/admin/events/EventsTable.svelte, frontend/src/components/admin/events/__tests__/EventsTable.test.ts, frontend/src/routes/Notifications.svelte, frontend/src/routes/Settings.svelte, frontend/src/routes/admin/AdminExecutions.svelte, frontend/src/routes/__tests__/Notifications.test.ts
Rewired UI and tests to use new formatter APIs (formatTimestamp, formatDateOnly, formatTimeOnly, parseISO); updated test expectations to short relative strings.
Auto-refresh removal
frontend/src/lib/admin/autoRefresh.svelte.ts, frontend/src/lib/admin/index.ts, frontend/src/lib/admin/constants.ts, frontend/src/lib/admin/__tests__/*
Deleted autoRefresh module and its tests; removed STATUS_/ROLE_/ACTIVE_ color exports and StatusColor type; admin index no longer re-exports autoRefresh.
Store auto-refresh → inline intervals
frontend/src/lib/admin/stores/eventsStore.svelte.ts, frontend/src/lib/admin/stores/executionsStore.svelte.ts, frontend/src/lib/admin/stores/sagasStore.svelte.ts, frontend/src/lib/admin/stores/__tests__/*
Replaced createAutoRefresh with constructor/$effect-based intervals inside stores; SagasStore exposes refreshEnabled/refreshRate; cleanup semantics changed and tests updated.
Removed admin UI components & tests
frontend/src/components/admin/ActionButtons.svelte, frontend/src/components/admin/StatsCard.svelte, frontend/src/components/admin/StatusBadge.svelte, frontend/src/components/admin/__tests__/*, frontend/src/components/admin/index.ts
Deleted three admin components, their tests, and removed their exports from the admin index barrel.
Component lifecycle → reactive/global bindings
frontend/src/components/Header.svelte, frontend/src/components/Modal.svelte
Replaced manual onMount/onDestroy listeners with $effect and <svelte:window>/<svelte:document> bindings for resize, outside clicks, and Escape key handling.
NotificationCenter changes
frontend/src/components/NotificationCenter.svelte, frontend/src/components/__tests__/NotificationCenter.test.ts
Added logger, AUTO_READ_DELAY_MS auto-read effect, replaced local formatter with formatRelativeTime, consolidated click/keyboard handling, and adjusted tests to relative-time assertions.
Auth & stores adjustments
frontend/src/stores/auth.svelte.ts, frontend/src/stores/errorStore.svelte.ts, frontend/src/stores/notificationStore.svelte.ts, frontend/src/stores/theme.svelte.ts
Auth: removed fetchUserProfile() public method and uses verifyAuth; ErrorStore: removed timestamp from AppError and integrated logger; NotificationStore: removed 100-item cap; Theme: logs save failures.
Pagination API change
frontend/src/lib/admin/pagination.svelte.ts, frontend/src/lib/admin/__tests__/pagination.test.ts
Removed reset() from PaginationState and its tests; public reset API removed.
Styling & CSS utilities
frontend/src/app.css, frontend/src/styles/components.css, frontend/src/styles/pages.css
Renamed animation utilities, added form-control-base, prefers-reduced-motion, moved some media queries to width-based syntax, removed many legacy style blocks; CodeMirror and theme tweaks.
CI / linters
.github/workflows/frontend-ci.yml, .pre-commit-config.yaml, frontend/.stylelintrc.json
Added Stylelint step to CI, added pre-commit stylelint hook, and added Stylelint config extending stylelint-config-standard.
Test utilities & updates
frontend/src/__tests__/test-utils.ts, multiple __tests__ files
Removed header docs in test-utils; updated mocks to use shared PROFILE_RESPONSE; migrated test imports to $lib aliases; adjusted logging/time-format assertions.
Other UI refinements
frontend/src/components/Header.svelte, frontend/src/routes/AdminEvents.svelte, frontend/src/routes/admin/AdminSagas.svelte, frontend/src/routes/admin/AdminExecutions.svelte, frontend/src/routes/Editor.svelte, frontend/src/routes/Login.svelte, frontend/src/routes/Settings.svelte
Added logger usage, switched click-outside to document-level handlers, used shared formatters, replaced local cleanup with untrack/reactive patterns, hardened localStorage writes.

Sequence Diagram(s)

(omitted)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • fix: frontend fixes #212 — modifies notification stream connect signature and centralizes logging; strongly related to NotificationStream and logger changes.
  • chore: frontend tests #42 — refactors formatter APIs and date handling (introduces formatTimestamp variants); closely related to formatters work.
  • new frontend e2e tests #86 — touches frontend CI/linting and related scripts; related to added stylelint integration and CI step.

Poem

🐰 I hopped through logs and date-fns glen,

Consola now hums where console prints had been,
Old badges shelved, auto-refresh trimmed small,
Reactive bindings answered every call,
A rabbit cheers: tidy changes, one and all!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feat/better frontend' is vague and uses non-descriptive phrasing that does not convey meaningful information about the primary changes in the changeset. Revise the title to be more specific and descriptive of the main changes, such as 'Add centralized logging and date formatting utilities' or 'Refactor frontend logging, date formatting, and admin refresh mechanisms'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/better-frontend

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Feb 28, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 81.69935% with 28 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...rontend/src/lib/admin/stores/eventsStore.svelte.ts 36.36% 2 Missing and 5 partials ⚠️
frontend/src/main.ts 0.00% 4 Missing ⚠️
frontend/src/App.svelte 0.00% 3 Missing ⚠️
frontend/src/components/NotificationCenter.svelte 86.36% 1 Missing and 2 partials ⚠️
frontend/src/stores/auth.svelte.ts 70.00% 3 Missing ⚠️
frontend/src/lib/formatters.ts 93.54% 1 Missing and 1 partial ⚠️
frontend/src/routes/admin/AdminEvents.svelte 50.00% 1 Missing and 1 partial ⚠️
frontend/src/lib/logger.ts 66.66% 0 Missing and 1 partial ⚠️
frontend/src/lib/notifications/stream.svelte.ts 50.00% 1 Missing ⚠️
frontend/src/routes/Settings.svelte 75.00% 1 Missing ⚠️
... and 1 more
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Flag Coverage Δ
backend-e2e 82.53% <ø> (ø)
backend-unit 67.26% <ø> (ø)
frontend-unit 86.78% <81.69%> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
frontend/src/components/Header.svelte 88.88% <100.00%> (+1.73%) ⬆️
frontend/src/components/Modal.svelte 91.66% <100.00%> (+4.16%) ⬆️
...end/src/components/admin/events/EventsTable.svelte 81.48% <100.00%> (ø)
frontend/src/components/editor/OutputPanel.svelte 86.30% <100.00%> (ø)
frontend/src/lib/admin/constants.ts 100.00% <ø> (ø)
frontend/src/lib/admin/pagination.svelte.ts 100.00% <ø> (ø)
frontend/src/lib/admin/rate-limits/rateLimits.ts 100.00% <100.00%> (ø)
...end/src/lib/admin/stores/executionsStore.svelte.ts 96.66% <100.00%> (ø)
frontend/src/lib/admin/stores/sagasStore.svelte.ts 100.00% <100.00%> (ø)
frontend/src/lib/api-interceptors.ts 95.83% <100.00%> (+0.05%) ⬆️
... and 19 more
Components Coverage Δ
Backend 89.52% <ø> (ø)
Frontend 86.78% <81.69%> (-0.05%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 issues found across 56 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="frontend/src/lib/formatters.ts">

<violation number="1" location="frontend/src/lib/formatters.ts:22">
P2: `formatTimestamp` is no longer actually locale-aware; it now uses `date-fns` default locale instead of the user/browser locale.</violation>
</file>

<file name="frontend/src/lib/admin/stores/__tests__/eventsStore.test.ts">

<violation number="1" location="frontend/src/lib/admin/stores/__tests__/eventsStore.test.ts:400">
P2: `vi.clearAllTimers()` masks whether teardown really stops auto-refresh, so this test can pass even when teardown is broken.</violation>
</file>

<file name="frontend/src/stores/__tests__/errorStore.test.ts">

<violation number="1" location="frontend/src/stores/__tests__/errorStore.test.ts:54">
P2: This assertion is too permissive and no longer verifies the expected log content. Use `toHaveBeenCalledWith(...)` (or equivalent argument assertions) so the test fails when the logged title/message format regresses.</violation>
</file>

<file name="frontend/src/lib/admin/stores/executionsStore.svelte.ts">

<violation number="1" location="frontend/src/lib/admin/stores/executionsStore.svelte.ts:28">
P2: The interval triggers `loadData` even when a previous refresh is still in flight, which can cause overlapping requests and race-prone state updates.</violation>
</file>

<file name="frontend/src/stores/auth.svelte.ts">

<violation number="1" location="frontend/src/stores/auth.svelte.ts:166">
P1: `login()` can return success even when forced post-login verification fails and clears auth state, causing callers to treat a failed/invalid session as a successful login.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread frontend/src/stores/auth.svelte.ts Outdated
Comment thread frontend/src/lib/formatters.ts Outdated
Comment thread frontend/src/lib/admin/stores/__tests__/eventsStore.test.ts Outdated
Comment thread frontend/src/stores/__tests__/errorStore.test.ts Outdated
Comment thread frontend/src/lib/admin/stores/executionsStore.svelte.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
frontend/src/stores/__tests__/errorStore.test.ts (1)

49-55: Tighten logging assertions to avoid false positives.

toHaveBeenCalled() is permissive and may pass from unrelated console activity. A call-count delta assertion around setError(...) is more deterministic.

Suggested test assertion tightening
 it('logs error via consola', async () => {
   const { appError } = await import('$stores/errorStore.svelte');
   const consoleSpy = vi.spyOn(console, 'error');
+  const before = consoleSpy.mock.calls.length;
   appError.setError('Logged error', 'Error Title');

-  expect(consoleSpy).toHaveBeenCalled();
+  expect(consoleSpy.mock.calls.length).toBe(before + 1);
 });

 it('logs error without title via consola', async () => {
   const { appError } = await import('$stores/errorStore.svelte');
   const consoleSpy = vi.spyOn(console, 'error');
+  const before = consoleSpy.mock.calls.length;
   appError.setError('Logged error without title');

-  expect(consoleSpy).toHaveBeenCalled();
+  expect(consoleSpy.mock.calls.length).toBe(before + 1);
 });

Also applies to: 57-63

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/stores/__tests__/errorStore.test.ts` around lines 49 - 55, The
test uses a permissive expect(consoleSpy).toHaveBeenCalled() which can pass from
unrelated console activity; update the assertion around appError.setError to
assert a call-count delta instead (e.g., record initial
consoleSpy.mock.calls.length or clear the spy, invoke appError.setError('Logged
error','Error Title'), then assert the spy was called exactly once more using
toHaveBeenCalledTimes(1) or by comparing before/after counts). Apply the same
change to the second test covering lines 57-63 so both use deterministic
call-count assertions against console.error when calling appError.setError.
frontend/src/routes/admin/AdminExecutions.svelte (1)

29-39: Debounce search-driven reloads to prevent request bursts.

The effect currently reloads on every userSearch keystroke, which can generate excessive API calls and race responses.

♻️ Suggested debounce for search-triggered reloads
     let prevFilters = $state({ status: store.statusFilter, priority: store.priorityFilter, search: store.userSearch });
+    let searchReloadTimer: ReturnType<typeof setTimeout> | null = null;

     $effect(() => {
         const current = { status: store.statusFilter, priority: store.priorityFilter, search: store.userSearch };
-        if (current.status !== prevFilters.status || current.priority !== prevFilters.priority || current.search !== prevFilters.search) {
+        const changed =
+            current.status !== prevFilters.status ||
+            current.priority !== prevFilters.priority ||
+            current.search !== prevFilters.search;
+
+        if (changed) {
+            const searchChanged = current.search !== prevFilters.search;
             prevFilters = current;
             untrack(() => {
                 store.pagination.currentPage = 1;
-                store.loadExecutions();
+                if (searchReloadTimer) clearTimeout(searchReloadTimer);
+                if (searchChanged) {
+                    searchReloadTimer = setTimeout(() => {
+                        void store.loadExecutions();
+                    }, 250);
+                } else {
+                    void store.loadExecutions();
+                }
             });
         }
+
+        return () => {
+            if (searchReloadTimer) clearTimeout(searchReloadTimer);
+        };
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/routes/admin/AdminExecutions.svelte` around lines 29 - 39, The
effect currently calls store.loadExecutions on every change including each
keystroke in store.userSearch; debounce the search-driven reloads so rapid
keystrokes don't trigger multiple API calls: inside the $effect that compares
current vs prevFilters (variables: prevFilters, current), detect if the only
changed key is search (compare status and priority unchanged but search changed)
and in that branch schedule a debounced reload (use a timeout/clearTimeout
stored in a closure or component-level variable) to set
store.pagination.currentPage = 1 and call store.loadExecutions after a short
delay (e.g., 300ms); keep immediate reload behavior for status/priority changes
(call untrack + store.loadExecutions immediately). Ensure you clear the timeout
on component destroy to avoid leaks.
frontend/src/lib/admin/stores/eventsStore.svelte.ts (1)

42-46: Guard interval refreshes to avoid overlapping loadAll() calls.

This interval can trigger a new refresh before the previous one finishes, causing concurrent API calls and potential state races.

♻️ Suggested in-flight guard for interval refresh
 class EventsStore {
+    private refreshInFlight = false;
+
     constructor() {
         $effect(() => {
-            const id = setInterval(() => this.loadAll(), 30_000);
+            const id = setInterval(() => {
+                if (this.refreshInFlight) return;
+                this.refreshInFlight = true;
+                void this.loadAll().finally(() => {
+                    this.refreshInFlight = false;
+                });
+            }, 30_000);
             return () => { clearInterval(id); };
         });
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/lib/admin/stores/eventsStore.svelte.ts` around lines 42 - 46,
The interval in the constructor that calls this.loadAll() can start a new
refresh while a previous one is still running; add an in-flight guard (e.g. a
boolean like this._isLoading or a simple Promise lock) checked inside the
$effect callback before invoking loadAll(), set the guard just before the
asynchronous work starts and clear it in a finally block after loadAll()
completes (or await loadAll() inside the guarded section), so the interval
handler skips starting a new call when a previous call is still in-flight;
update references in constructor/$effect and the loadAll-related logic to use
this guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/app.css`:
- Around line 114-143: Add Tailwind directives to the stylelint config and
replace the invalid :where dark nesting inside the `@utility` block: update
.stylelintrc.json to include ignoreAtRules entries for "utility", "apply",
"variant", and "theme" so `@utility/`@apply/@variant are not flagged, then modify
the `@utility` form-control-base block in frontend/src/app.css to remove the
:where(.dark, .dark *) selector and instead nest the dark styles using
Tailwind's `@variant` dark (e.g., move the dark-specific
border-color/background-color/color/hover rules under an `@variant` dark block
within the same file); also update other places that use `@apply`
form-control-base (lines noted in the review) to remain compatible with the new
`@variant-based` dark styles.

In `@frontend/src/components/NotificationCenter.svelte`:
- Around line 163-165: The custom button element in NotificationCenter.svelte
(role="button", tabindex="0") only handles Enter activation; update the keyboard
handler so handleNotificationClick(notification) is invoked for both Enter and
Space key presses—modify the onkeydown callback that references
handleNotificationClick(notification) to check for e.key === 'Enter' OR e.key
=== ' ' OR e.code === 'Space' (and/or 'Spacebar' for legacy) and call
handleNotificationClick accordingly to ensure full keyboard accessibility.

In `@frontend/src/stores/notificationStore.svelte.ts`:
- Line 42: The add() method currently prepends new notifications with
"this.notifications = [notification, ...this.notifications]" causing unbounded
growth; change it to keep prepend behavior but clamp length by defining a
MAX_NOTIFICATIONS (e.g., 50) and after prepending assign only the first
MAX_NOTIFICATIONS items (e.g., this.notifications = [notification,
...this.notifications].slice(0, MAX_NOTIFICATIONS)). Update the
notificationStore export to include the MAX_NOTIFICATIONS constant so it’s easy
to tune and test.

In `@frontend/src/styles/components.css`:
- Line 104: The stylelint violation is caused by a missing empty line before the
background-image declaration; open the CSS block containing the
background-image: url('data:image/svg+xml;...') declaration and insert a single
blank line immediately above that declaration (i.e., add an empty line before
the "background-image: url(...)" rule) so the declaration-empty-line-before rule
is satisfied, or alternatively update the stylelint rule configuration to allow
this pattern if preferred.

---

Nitpick comments:
In `@frontend/src/lib/admin/stores/eventsStore.svelte.ts`:
- Around line 42-46: The interval in the constructor that calls this.loadAll()
can start a new refresh while a previous one is still running; add an in-flight
guard (e.g. a boolean like this._isLoading or a simple Promise lock) checked
inside the $effect callback before invoking loadAll(), set the guard just before
the asynchronous work starts and clear it in a finally block after loadAll()
completes (or await loadAll() inside the guarded section), so the interval
handler skips starting a new call when a previous call is still in-flight;
update references in constructor/$effect and the loadAll-related logic to use
this guard.

In `@frontend/src/routes/admin/AdminExecutions.svelte`:
- Around line 29-39: The effect currently calls store.loadExecutions on every
change including each keystroke in store.userSearch; debounce the search-driven
reloads so rapid keystrokes don't trigger multiple API calls: inside the $effect
that compares current vs prevFilters (variables: prevFilters, current), detect
if the only changed key is search (compare status and priority unchanged but
search changed) and in that branch schedule a debounced reload (use a
timeout/clearTimeout stored in a closure or component-level variable) to set
store.pagination.currentPage = 1 and call store.loadExecutions after a short
delay (e.g., 300ms); keep immediate reload behavior for status/priority changes
(call untrack + store.loadExecutions immediately). Ensure you clear the timeout
on component destroy to avoid leaks.

In `@frontend/src/stores/__tests__/errorStore.test.ts`:
- Around line 49-55: The test uses a permissive
expect(consoleSpy).toHaveBeenCalled() which can pass from unrelated console
activity; update the assertion around appError.setError to assert a call-count
delta instead (e.g., record initial consoleSpy.mock.calls.length or clear the
spy, invoke appError.setError('Logged error','Error Title'), then assert the spy
was called exactly once more using toHaveBeenCalledTimes(1) or by comparing
before/after counts). Apply the same change to the second test covering lines
57-63 so both use deterministic call-count assertions against console.error when
calling appError.setError.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae620eb and eccdef7.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (55)
  • frontend/package.json
  • frontend/rollup.config.js
  • frontend/src/App.svelte
  • frontend/src/__tests__/test-utils.ts
  • frontend/src/app.css
  • frontend/src/components/Header.svelte
  • frontend/src/components/Modal.svelte
  • frontend/src/components/NotificationCenter.svelte
  • frontend/src/components/ProtectedRoute.svelte
  • frontend/src/components/__tests__/Header.test.ts
  • frontend/src/components/__tests__/NotificationCenter.test.ts
  • frontend/src/components/__tests__/ProtectedRoute.test.ts
  • frontend/src/components/admin/ActionButtons.svelte
  • frontend/src/components/admin/StatsCard.svelte
  • frontend/src/components/admin/StatusBadge.svelte
  • frontend/src/components/admin/__tests__/ActionButtons.test.ts
  • frontend/src/components/admin/__tests__/StatsCard.test.ts
  • frontend/src/components/admin/__tests__/StatusBadge.test.ts
  • frontend/src/components/admin/events/EventsTable.svelte
  • frontend/src/components/admin/events/__tests__/EventsTable.test.ts
  • frontend/src/components/admin/index.ts
  • frontend/src/lib/__tests__/formatters.test.ts
  • frontend/src/lib/admin/__tests__/autoRefresh.test.ts
  • frontend/src/lib/admin/__tests__/constants.test.ts
  • frontend/src/lib/admin/autoRefresh.svelte.ts
  • frontend/src/lib/admin/constants.ts
  • frontend/src/lib/admin/index.ts
  • frontend/src/lib/admin/stores/__tests__/eventsStore.test.ts
  • frontend/src/lib/admin/stores/__tests__/executionsStore.test.ts
  • frontend/src/lib/admin/stores/__tests__/sagasStore.test.ts
  • frontend/src/lib/admin/stores/eventsStore.svelte.ts
  • frontend/src/lib/admin/stores/executionsStore.svelte.ts
  • frontend/src/lib/admin/stores/sagasStore.svelte.ts
  • frontend/src/lib/api-interceptors.ts
  • frontend/src/lib/formatters.ts
  • frontend/src/lib/logger.ts
  • frontend/src/lib/notifications/stream.svelte.ts
  • frontend/src/main.ts
  • frontend/src/routes/Editor.svelte
  • frontend/src/routes/Login.svelte
  • frontend/src/routes/Notifications.svelte
  • frontend/src/routes/Settings.svelte
  • frontend/src/routes/__tests__/Notifications.test.ts
  • frontend/src/routes/admin/AdminEvents.svelte
  • frontend/src/routes/admin/AdminExecutions.svelte
  • frontend/src/routes/admin/AdminSagas.svelte
  • frontend/src/routes/admin/__tests__/AdminUsers.test.ts
  • frontend/src/stores/__tests__/auth.test.ts
  • frontend/src/stores/__tests__/errorStore.test.ts
  • frontend/src/stores/__tests__/notificationStore.test.ts
  • frontend/src/stores/auth.svelte.ts
  • frontend/src/stores/errorStore.svelte.ts
  • frontend/src/stores/notificationStore.svelte.ts
  • frontend/src/stores/theme.svelte.ts
  • frontend/src/styles/components.css
💤 Files with no reviewable changes (16)
  • frontend/src/components/tests/Header.test.ts
  • frontend/src/lib/admin/tests/constants.test.ts
  • frontend/src/components/admin/StatsCard.svelte
  • frontend/src/lib/admin/constants.ts
  • frontend/src/lib/admin/index.ts
  • frontend/src/lib/admin/autoRefresh.svelte.ts
  • frontend/src/components/admin/StatusBadge.svelte
  • frontend/src/components/admin/tests/StatusBadge.test.ts
  • frontend/src/components/tests/ProtectedRoute.test.ts
  • frontend/src/lib/admin/tests/autoRefresh.test.ts
  • frontend/src/components/admin/ActionButtons.svelte
  • frontend/src/components/admin/tests/StatsCard.test.ts
  • frontend/src/components/admin/index.ts
  • frontend/src/components/ProtectedRoute.svelte
  • frontend/src/components/admin/tests/ActionButtons.test.ts
  • frontend/src/tests/test-utils.ts

Comment thread frontend/src/app.css
Comment thread frontend/src/components/NotificationCenter.svelte
Comment thread frontend/src/stores/notificationStore.svelte.ts
Comment thread frontend/src/styles/components.css
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes the frontend by introducing a unified logging + formatting layer, refactoring admin auto-refresh behavior, and tightening up several UI interaction patterns (dropdowns, modal escape handling, shared form control styles).

Changes:

  • Add a shared browser logger ($lib/logger) and replace many console.* calls with tagged logging.
  • Centralize timestamp/relative-time formatting in $lib/formatters (now using date-fns) and update UI/tests accordingly.
  • Refactor admin auto-refresh from a shared helper to store-local $effect intervals; remove several unused admin UI components/utilities.

Reviewed changes

Copilot reviewed 55 out of 56 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
frontend/src/styles/components.css Adds data-URI dropdown arrow styling for the shared pagination selector class.
frontend/src/stores/theme.svelte.ts Adds logger usage and error handling when persisting theme settings.
frontend/src/stores/notificationStore.svelte.ts Removes notification list size cap when adding new notifications.
frontend/src/stores/errorStore.svelte.ts Switches from console.error to shared logger.
frontend/src/stores/auth.svelte.ts Switches to shared logger; replaces post-login profile fetch with verifyAuth(true).
frontend/src/stores/tests/notificationStore.test.ts Removes tests asserting the 100-notification cap; renames unreadCount suite.
frontend/src/stores/tests/errorStore.test.ts Updates logging assertions after switching to logger/consola.
frontend/src/stores/tests/auth.test.ts Adjusts fixtures/expectations around profile response and removed fetchUserProfile.
frontend/src/routes/admin/tests/AdminUsers.test.ts Updates mocks to use $lib/* aliases for API imports.
frontend/src/routes/admin/AdminSagas.svelte Updates filter-change behavior using untrack; binds auto-refresh to new store fields.
frontend/src/routes/admin/AdminExecutions.svelte Uses shared formatters and improves filter-change paging/load behavior.
frontend/src/routes/admin/AdminEvents.svelte Adds document click handler to close export dropdown; uses shared pagination selector class.
frontend/src/routes/tests/Notifications.test.ts Updates relative-time expectations to the new compact format.
frontend/src/routes/Settings.svelte Uses shared formatter and date-fns parsing; replaces manual click-outside handler with <svelte:document>.
frontend/src/routes/Notifications.svelte Replaces local timestamp formatter with shared formatRelativeTime.
frontend/src/routes/Login.svelte Switches warning logging to shared logger.
frontend/src/routes/Editor.svelte Adds logger; guards localStorage persistence for quota/private-mode errors.
frontend/src/main.ts Routes global error handling through shared logger.
frontend/src/lib/notifications/stream.svelte.ts Switches notification stream connection error logging to shared logger.
frontend/src/lib/logger.ts Introduces browser logger based on consola/browser with build-time prod level replacement.
frontend/src/lib/formatters.ts Refactors timestamp/duration/relative-time formatting using date-fns; removes bytes/number helpers.
frontend/src/lib/api-interceptors.ts Switches API error logging to shared logger.
frontend/src/lib/admin/stores/sagasStore.svelte.ts Replaces createAutoRefresh with store-local $effect interval + enable/rate state.
frontend/src/lib/admin/stores/executionsStore.svelte.ts Replaces createAutoRefresh with store-local fixed interval refresh.
frontend/src/lib/admin/stores/eventsStore.svelte.ts Replaces createAutoRefresh with store-local interval refresh; improves SSE parsing logging.
frontend/src/lib/admin/stores/tests/sagasStore.test.ts Updates tests for the new refreshEnabled behavior and removes cleanup usage.
frontend/src/lib/admin/stores/tests/executionsStore.test.ts Updates tests to rely on teardown/timer clearing instead of autoRefresh cleanup.
frontend/src/lib/admin/stores/tests/eventsStore.test.ts Updates tests to rely on teardown/timer clearing instead of autoRefresh cleanup.
frontend/src/lib/admin/index.ts Removes re-export of the deleted autoRefresh helper.
frontend/src/lib/admin/constants.ts Removes unused badge/status/role color constants and types.
frontend/src/lib/admin/autoRefresh.svelte.ts Removes the shared auto-refresh helper implementation.
frontend/src/lib/admin/tests/constants.test.ts Removes tests for deleted constants.
frontend/src/lib/admin/tests/autoRefresh.test.ts Removes tests for the deleted autoRefresh helper.
frontend/src/lib/tests/formatters.test.ts Updates formatter tests to reflect new date-fns-based formatting API.
frontend/src/components/admin/index.ts Stops exporting removed admin components (StatsCard/StatusBadge/ActionButtons).
frontend/src/components/admin/events/tests/EventsTable.test.ts Updates formatting assertions to match date-fns formatting outputs.
frontend/src/components/admin/events/EventsTable.svelte Uses shared date-only/time-only formatters instead of toLocale*.
frontend/src/components/admin/tests/StatusBadge.test.ts Removes tests for deleted StatusBadge component.
frontend/src/components/admin/tests/StatsCard.test.ts Removes tests for deleted StatsCard component.
frontend/src/components/admin/tests/ActionButtons.test.ts Removes tests for deleted ActionButtons component.
frontend/src/components/admin/StatusBadge.svelte Removes StatusBadge component.
frontend/src/components/admin/StatsCard.svelte Removes StatsCard component.
frontend/src/components/admin/ActionButtons.svelte Removes ActionButtons component.
frontend/src/components/tests/ProtectedRoute.test.ts Removes tests for deleted ProtectedRoute component.
frontend/src/components/tests/NotificationCenter.test.ts Updates expectations for new relative-time output.
frontend/src/components/tests/Header.test.ts Removes mock method no longer present/used (fetchUserProfile).
frontend/src/components/ProtectedRoute.svelte Removes ProtectedRoute component.
frontend/src/components/NotificationCenter.svelte Uses shared formatRelativeTime, adds auto-read effect and logger; simplifies click handling.
frontend/src/components/Modal.svelte Moves Escape handling to <svelte:window> keydown handler.
frontend/src/components/Header.svelte Uses <svelte:window bind:innerWidth> + <svelte:document onclick> for responsive + click-outside behavior.
frontend/src/app.css Adds reduced-motion media query and consolidates form control styles into a utility.
frontend/src/tests/test-utils.ts Removes section divider comments; keeps utilities intact.
frontend/src/App.svelte Switches app init logging to shared logger.
frontend/rollup.config.js Injects build-time isProduction into logger; stops dropping console at minification.
frontend/package.json Adds consola and date-fns dependencies.
frontend/package-lock.json Locks new dependencies and updates consola dev/prod flags.
Files not reviewed (1)
  • frontend/package-lock.json: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread frontend/src/stores/notificationStore.svelte.ts
Comment thread frontend/src/stores/auth.svelte.ts
Comment thread frontend/src/routes/Settings.svelte
Comment thread frontend/src/routes/admin/AdminEvents.svelte
Comment thread frontend/src/lib/formatters.ts
Comment thread frontend/src/lib/admin/stores/eventsStore.svelte.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/components/__tests__/Header.test.ts`:
- Around line 191-193: The forEach callback in the test that iterates over
expectedContent should use a block body instead of an expression to avoid an
implicit return; update the call around expectedContent.forEach(text =>
expect(mobileMenu.textContent).toContain(text)) to use a block callback (e.g.,
expectedContent.forEach(text => {
expect(mobileMenu.textContent).toContain(text); })) so the callback does not
return a value and the Biome lint rule
(lint/suspicious/useIterableCallbackReturn) is satisfied.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eccdef7 and b78fc49.

📒 Files selected for processing (6)
  • frontend/src/components/Header.svelte
  • frontend/src/components/__tests__/Header.test.ts
  • frontend/src/lib/admin/__tests__/pagination.test.ts
  • frontend/src/lib/admin/pagination.svelte.ts
  • frontend/src/stores/__tests__/errorStore.test.ts
  • frontend/src/stores/errorStore.svelte.ts
💤 Files with no reviewable changes (2)
  • frontend/src/lib/admin/tests/pagination.test.ts
  • frontend/src/lib/admin/pagination.svelte.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/stores/tests/errorStore.test.ts

Comment thread frontend/src/components/__tests__/Header.test.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/routes/Home.svelte (1)

3-3: ⚠️ Potential issue | 🟡 Minor

Remove unused fade and fly imports.

Line 3 imports fade and fly from svelte/transition, but they are not used anywhere in the template. All animations use the hero-animate-fly-in CSS class instead. Remove these unused imports to keep the code clean.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/routes/Home.svelte` at line 3, Remove the unused imports "fade"
and "fly" from the import statement that currently reads import { fade, fly }
from "svelte/transition"; in Home.svelte — update the import to remove those
symbols so only used exports remain (or remove the entire import if nothing else
is imported), ensuring no unused variables named fade or fly remain in the
module.
🧹 Nitpick comments (3)
frontend/src/lib/admin/rate-limits/__tests__/rateLimits.test.ts (1)

1-8: Consider importing EndpointGroup type at the top.

The inline type assertion as import('$lib/api').EndpointGroup on line 13 is valid but could be cleaner. Per coding guidelines, prefer import type at the top of the file, which also enables type-checking for the EXPECTED_GROUPS array.

♻️ Proposed refactor
 import { describe, it, expect } from 'vitest';
+import type { EndpointGroup } from '$lib/api';
 import {
   getGroupColor,
   detectGroupFromEndpoint,
   createEmptyRule
 } from '$lib/admin/rate-limits/rateLimits';

-const EXPECTED_GROUPS = ['execution', 'admin', 'sse', 'websocket', 'auth', 'api', 'public'];
+const EXPECTED_GROUPS: EndpointGroup[] = ['execution', 'admin', 'sse', 'websocket', 'auth', 'api', 'public'];

Then on line 13:

-      const color = getGroupColor(group as import('$lib/api').EndpointGroup);
+      const color = getGroupColor(group);

As per coding guidelines: "use import type for type-only imports".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/lib/admin/rate-limits/__tests__/rateLimits.test.ts` around lines
1 - 8, Add a top-level type-only import for EndpointGroup (e.g., import type {
EndpointGroup } from '$lib/api') and use it to annotate EXPECTED_GROUPS instead
of the inline assertion; update the EXPECTED_GROUPS declaration to be typed as
EndpointGroup[] and remove the inline "as import('$lib/api').EndpointGroup"
usage so the array is type-checked; ensure the import is a type import to avoid
runtime overhead and reference EXPECTED_GROUPS, getGroupColor,
detectGroupFromEndpoint, and createEmptyRule when locating the change.
frontend/src/styles/pages.css (1)

296-303: Consider using width < 1024px for cleaner breakpoint boundary.

The width <= 1023.98px approach works but relies on a decimal to avoid overlap. Using width < 1024px would be cleaner and more explicit:

♻️ Suggested improvement
-  `@media` (width >= 640px) and (width <= 1023.98px) {
+  `@media` (width >= 640px) and (width < 1024px) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/styles/pages.css` around lines 296 - 303, Replace the media
query condition that uses the decimal upper bound with a clearer strict
less-than breakpoint: change the media rule using "(width >= 640px) and (width
<= 1023.98px)" to use "(width >= 640px) and (width < 1024px)" so the selector
.features-grid > :last-child:nth-child(2n + 1) keeps the same styles but the
breakpoint boundary is explicit and non-overlapping.
frontend/src/lib/formatters.ts (1)

102-121: Minor optimization opportunity in formatRelativeTime.

Multiple differenceIn* calls recalculate differences from the same now and date values. This is functionally correct but slightly redundant since these functions traverse the same interval. Given the simplicity of the operations, this is acceptable, but if performance becomes a concern, you could compute only the necessary difference based on magnitude.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/lib/formatters.ts` around lines 102 - 121, The
formatRelativeTime function is calling differenceInSeconds, differenceInMinutes,
differenceInHours, and differenceInDays redundantly; change it to compute a
single diffSecs = differenceInSeconds(now, date) and derive diffMins =
Math.floor(diffSecs/60), diffHrs = Math.floor(diffSecs/3600), diffDys =
Math.floor(diffSecs/86400) (keep the existing early-return logic: check <60,
<3600, <86400, <7*86400) so you avoid repeated interval calculations while
preserving output; update the function name references (formatRelativeTime,
diffSecs/diffMins/diffHrs/diffDys) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@frontend/src/routes/Home.svelte`:
- Line 3: Remove the unused imports "fade" and "fly" from the import statement
that currently reads import { fade, fly } from "svelte/transition"; in
Home.svelte — update the import to remove those symbols so only used exports
remain (or remove the entire import if nothing else is imported), ensuring no
unused variables named fade or fly remain in the module.

---

Nitpick comments:
In `@frontend/src/lib/admin/rate-limits/__tests__/rateLimits.test.ts`:
- Around line 1-8: Add a top-level type-only import for EndpointGroup (e.g.,
import type { EndpointGroup } from '$lib/api') and use it to annotate
EXPECTED_GROUPS instead of the inline assertion; update the EXPECTED_GROUPS
declaration to be typed as EndpointGroup[] and remove the inline "as
import('$lib/api').EndpointGroup" usage so the array is type-checked; ensure the
import is a type import to avoid runtime overhead and reference EXPECTED_GROUPS,
getGroupColor, detectGroupFromEndpoint, and createEmptyRule when locating the
change.

In `@frontend/src/lib/formatters.ts`:
- Around line 102-121: The formatRelativeTime function is calling
differenceInSeconds, differenceInMinutes, differenceInHours, and
differenceInDays redundantly; change it to compute a single diffSecs =
differenceInSeconds(now, date) and derive diffMins = Math.floor(diffSecs/60),
diffHrs = Math.floor(diffSecs/3600), diffDys = Math.floor(diffSecs/86400) (keep
the existing early-return logic: check <60, <3600, <86400, <7*86400) so you
avoid repeated interval calculations while preserving output; update the
function name references (formatRelativeTime, diffSecs/diffMins/diffHrs/diffDys)
accordingly.

In `@frontend/src/styles/pages.css`:
- Around line 296-303: Replace the media query condition that uses the decimal
upper bound with a clearer strict less-than breakpoint: change the media rule
using "(width >= 640px) and (width <= 1023.98px)" to use "(width >= 640px) and
(width < 1024px)" so the selector .features-grid > :last-child:nth-child(2n + 1)
keeps the same styles but the breakpoint boundary is explicit and
non-overlapping.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b78fc49 and d93b79e.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (21)
  • .github/workflows/frontend-ci.yml
  • .pre-commit-config.yaml
  • frontend/.stylelintrc.json
  • frontend/package.json
  • frontend/src/app.css
  • frontend/src/components/Header.svelte
  • frontend/src/components/__tests__/Header.test.ts
  • frontend/src/components/admin/events/__tests__/EventsTable.test.ts
  • frontend/src/components/editor/OutputPanel.svelte
  • frontend/src/lib/__tests__/formatters.test.ts
  • frontend/src/lib/admin/rate-limits/__tests__/rateLimits.test.ts
  • frontend/src/lib/admin/rate-limits/rateLimits.ts
  • frontend/src/lib/admin/stores/__tests__/eventsStore.test.ts
  • frontend/src/lib/formatters.ts
  • frontend/src/lib/notifications/stream.svelte.ts
  • frontend/src/routes/Home.svelte
  • frontend/src/stores/__tests__/auth.test.ts
  • frontend/src/stores/__tests__/errorStore.test.ts
  • frontend/src/stores/auth.svelte.ts
  • frontend/src/styles/components.css
  • frontend/src/styles/pages.css
🚧 Files skipped from review as they are similar to previous changes (5)
  • frontend/src/lib/tests/formatters.test.ts
  • frontend/src/stores/tests/auth.test.ts
  • frontend/src/lib/notifications/stream.svelte.ts
  • frontend/src/stores/tests/errorStore.test.ts
  • frontend/package.json

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/lib/admin/rate-limits/__tests__/rateLimits.test.ts`:
- Around line 1-11: The test file is missing explicit lifecycle hooks required
by repo policy; add beforeEach(() => vi.clearAllMocks()) to clear mocks and
afterEach(() => cleanup()) to run DOM cleanup, placing the beforeEach at the top
of the describe('rateLimits', ...) block and afterEach inside the same block;
ensure imports for vi/cleanup are available in the test environment (vitest
provides vi and cleanup from testing-library if needed) and keep existing tests
that reference getGroupColor, detectGroupFromEndpoint, and createEmptyRule
unchanged.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d93b79e and 02937c1.

📒 Files selected for processing (3)
  • frontend/src/components/NotificationCenter.svelte
  • frontend/src/lib/admin/rate-limits/__tests__/rateLimits.test.ts
  • frontend/src/styles/pages.css
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/components/NotificationCenter.svelte

Comment thread frontend/src/lib/admin/rate-limits/__tests__/rateLimits.test.ts
@HardMax71 HardMax71 merged commit 921666b into main Feb 28, 2026
17 checks passed
@HardMax71 HardMax71 deleted the feat/better-frontend branch February 28, 2026 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants