Skip to content

Conversation

@PavelKopecky
Copy link
Contributor

Closes #144

@coderabbitai
Copy link

coderabbitai bot commented Oct 12, 2025

Walkthrough

This PR adds an admin-only probeType filter (values: all | hardware | software) across the probes filtering system. It updates useProbeFilters types, DEFAULT_FILTER, route query parsing/building, Directus mapping (hardwareDevice), and exposes PROBE_TYPE_OPTIONS and anyAdminFilterApplied. Templates render adoption and probe type values using upperFirst and wire PROBE_TYPE_OPTIONS into the Probe type control. AsyncCell gains a size prop.

Possibly related PRs

Suggested labels

`in progress`

Poem

New probe-type toggles, threefold light,
all, hardware, software — set just right.
Queries updated, options in view,
Admins choose paths both old and new.
Tiny UI polish, filters take flight ✨🎛️

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning This pull request includes extensive modifications to components and utilities for credits filtering, async cell sizing, presets, and tree-select logic that are unrelated to adding the probe type admin filter specified in issue #144. Please remove the unrelated changes to credits filters, AsyncCell props, aura presets, tree-select utilities, and pages/credits from this pull request and create separate pull requests for those features so that this PR focuses solely on adding the probe type admin filter.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly and concisely describes the main purpose of the changeset by stating that a probe type filter is being added to the admin interface, matching the core objective of the pull request.
Linked Issues Check ✅ Passed The pull request implements the addition of a probe type filter by introducing new probeType state, options, UI bindings, and data handling based on the hardwareDevice field, fully satisfying the requirements of issue #144.
Description Check ✅ Passed The description references the linked issue for adding the probe type admin filter and is directly related to the changeset by indicating that this pull request resolves that issue.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch gh-144

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.

@MartinKolarik MartinKolarik merged commit f817c71 into master Oct 14, 2025
2 of 5 checks passed
MartinKolarik pushed a commit that referenced this pull request Oct 14, 2025
@MartinKolarik MartinKolarik deleted the gh-144 branch October 14, 2025 15:29
Copy link

@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: 0

🧹 Nitpick comments (6)
app/components/probe/ProbeFilters/AdminFilterSettings.vue (2)

11-16: Avoid lodash for simple labels; use explicit label maps to ensure correct wording and enable i18n

upperFirst is brittle for hyphenated values and adds an extra dep. Define labels and render them, which also makes later i18n trivial.

Apply:

-	import upperFirst from 'lodash/upperFirst';
+	// prefer explicit labels over runtime capitalization (better control and i18n-ready)
+	const ADOPTION_LABELS = { all: 'All', adopted: 'Adopted', 'non-adopted': 'Non-adopted' } as const;
+	const PROBE_TYPE_LABELS = { all: 'All', hardware: 'Hardware', software: 'Software' } as const;

And update slots:

-				<template #value="{value}">
-					{{upperFirst(value)}}
-				</template>
-				<template #option="{option}">
-					{{upperFirst(option)}}
-				</template>
+				<template #value="{ value }">
+					{{ ADOPTION_LABELS[value] }}
+				</template>
+				<template #option="{ option }">
+					{{ ADOPTION_LABELS[option] }}
+				</template>
-				<template #value="{value}">
-					{{upperFirst(value)}}
-				</template>
-				<template #option="{option}">
-					{{upperFirst(option)}}
-				</template>
+				<template #value="{ value }">
+					{{ PROBE_TYPE_LABELS[value] }}
+				</template>
+				<template #option="{ option }">
+					{{ PROBE_TYPE_LABELS[option] }}
+				</template>

Also applies to: 27-32, 39-39


42-49: Check nested prop mutation of a v-modelled prop; emit updates if the parent expects update:filter

When filter is provided (custom filter mode), v-model="usedFilter.adoption" mutates a nested prop directly. If the parent relies on update:filter to react (instead of observing deep mutations), this may not propagate.

If needed, wrap fields with computed setters that replace the object (emitting update:filter) instead of mutating in place, e.g., adoptionModel/probeTypeModel computed get/set and bind v-model to those.

app/composables/useProbeFilters.ts (3)

86-87: Use an explicit query key (probeType) instead of generic type; update watcher accordingly

type is ambiguous and prone to collision. Prefer probeType in the URL and parsing.

Apply:

--- a/app/composables/useProbeFilters.ts
+++ b/app/composables/useProbeFilters.ts
@@
-		...auth.isAdmin && !isDefault('probeType') && { type: filter.value.probeType },
+		...auth.isAdmin && !isDefault('probeType') && { probeType: filter.value.probeType },
@@
-		() => route.query.type,
-	], async ([ search, by, desc, status, adoption, probeType ]) => {
+		() => route.query.probeType,
+	], ([ search, by, desc, status, adoption, probeTypeParam ]) => {
@@
-		if (typeof probeType === 'string' && PROBE_TYPE_OPTIONS.includes(probeType) && auth.isAdmin) {
-			filter.value.probeType = probeType as ProbeTypeOption;
+		if (typeof probeTypeParam === 'string' && PROBE_TYPE_OPTIONS.includes(probeTypeParam) && auth.isAdmin) {
+			filter.value.probeType = probeTypeParam as ProbeTypeOption;
 		} else {
 			filter.value.probeType = DEFAULT_FILTER.probeType;
 		}

Optional: after parsing, if the user is not admin but admin-only params are present, normalize the URL by calling onParamChange() to strip them.

Also applies to: 145-146, 181-185


39-42: Tighten option array typing to catch mismatches at compile time

Use readonly, strongly-typed arrays to improve includes type narrowing and IDE hints.

-export const ADOPTION_OPTIONS: string[] = [ 'all', 'adopted', 'non-adopted' ] as const;
-export const PROBE_TYPE_OPTIONS: string[] = [ 'all', 'hardware', 'software' ] as const;
+export const ADOPTION_OPTIONS: readonly AdoptionOption[] = [ 'all', 'adopted', 'non-adopted' ] as const;
+export const PROBE_TYPE_OPTIONS: readonly ProbeTypeOption[] = [ 'all', 'hardware', 'software' ] as const;

139-147: Remove unnecessary async and consider stripping admin-only params for non-admins

The watcher is marked async but does not await; drop async. Also, if a non-admin lands with adoption/probeType in the URL, consider normalizing the query to avoid exposing no-op admin params.

Example:

-	], async ([ search, by, desc, status, adoption, probeType ]) => {
+	], ([ search, by, desc, status, adoption, probeTypeParam ]) => {
 		if (!toValue(active)) {
 			return;
 		}
+		// Optionally normalize URL for non-admin users:
+		// if (!auth.isAdmin && (adoption || probeTypeParam)) onParamChange();
app/composables/useCreditsFilters.ts (1)

59-68: Consider alternative for 'other' handling.

Adding 'other' to the reason array when all permitted reasons are selected (line 66) could be confusing since it's not an actual reason value. Consider alternatives:

  • A separate includeOther boolean parameter
  • Omitting the filter entirely when all values are selected

However, if the backend API expects this format, the current implementation is acceptable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 017cce6 and 9abe9f5.

📒 Files selected for processing (12)
  • app/components/AsyncCell.vue (1 hunks)
  • app/components/credits/CreditsFilters.vue (1 hunks)
  • app/components/probe/ProbeFilters/AdminFilterSettings.vue (1 hunks)
  • app/components/probe/ProbeFilters/ProbeListFilters.vue (2 hunks)
  • app/composables/useCreditsFilters.ts (1 hunks)
  • app/composables/useProbeFilters.ts (8 hunks)
  • app/pages/credits.vue (5 hunks)
  • app/presets/aura/index.js (3 hunks)
  • app/presets/aura/skeleton/index.js (1 hunks)
  • app/presets/aura/tree/index.js (4 hunks)
  • app/presets/aura/treeselect/index.js (4 hunks)
  • app/utils/tree-select.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/components/probe/ProbeFilters/ProbeListFilters.vue
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{vue,ts,js}

⚙️ CodeRabbit configuration file

We use Nuxt with auto imports enabled. Don't warn about missing imports.

Files:

  • app/components/AsyncCell.vue
  • app/components/credits/CreditsFilters.vue
  • app/composables/useProbeFilters.ts
  • app/presets/aura/skeleton/index.js
  • app/components/probe/ProbeFilters/AdminFilterSettings.vue
  • app/pages/credits.vue
  • app/utils/tree-select.ts
  • app/presets/aura/tree/index.js
  • app/composables/useCreditsFilters.ts
  • app/presets/aura/treeselect/index.js
  • app/presets/aura/index.js
🧠 Learnings (3)
📚 Learning: 2025-10-12T14:51:10.711Z
Learnt from: PavelKopecky
PR: jsdelivr/globalping-dash#154
File: app/components/credits/CreditsFilters.vue:101-106
Timestamp: 2025-10-12T14:51:10.711Z
Learning: In app/components/credits/CreditsFilters.vue, the early return when selectedCount.value === 0 is intentional. Having nothing selected is considered an invalid state, so the component preserves the previous filter values rather than clearing or resetting them.

Applied to files:

  • app/components/credits/CreditsFilters.vue
📚 Learning: 2025-10-06T17:46:36.047Z
Learnt from: PavelKopecky
PR: jsdelivr/globalping-dash#146
File: app/pages/credits.vue:58-62
Timestamp: 2025-10-06T17:46:36.047Z
Learning: In `app/pages/credits.vue`, the `CreditsChange` items rendered in the mobile view (lines 58-62) do not have an `id` field available for use as a v-for key. Using the array index is acceptable in this scenario due to the short list length and pagination-only updates.

Applied to files:

  • app/pages/credits.vue
📚 Learning: 2025-07-17T14:33:26.596Z
Learnt from: PavelKopecky
PR: jsdelivr/globalping-dash#103
File: pages/probes/index.vue:416-428
Timestamp: 2025-07-17T14:33:26.596Z
Learning: In the GlobalPing Dashboard probes listing page, there's a performance optimization where an initial lightweight probe count check is performed in onMounted() using only getUserFilter('userId'). This avoids running the more expensive loadLazyData() function (which includes complex filtering and status aggregation) when a user has no probes at all. The loadLazyData() function is only called if the initial count check confirms probes exist.

Applied to files:

  • app/pages/credits.vue
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run e2e tests
🔇 Additional comments (12)
app/presets/aura/skeleton/index.js (1)

13-13: Clarify styling migration rationale
The update from dark:bg-surface-700 to dark:bg-dark-500 (token defined in tailwind.config.js:58) isn’t related to the probe type admin filter. Document this change’s intent in the PR description or split it into a dedicated styling PR.

app/presets/aura/tree/index.js (2)

44-47: Verify that the checked || selected logic matches the intended tree behavior.

The styling conditions changed from context.selected to context.checked || context.selected across multiple properties (text color, hover states, icon colors, label colors). This will highlight both checked and selected nodes identically.

Confirm this is the intended behavior for the tree component, especially if it's being used for the probe type filter where selection vs. checked states may have different semantics.

Also applies to: 69-71, 92-94, 100-102


32-32: Validate combined checked || selected condition (lines 44, 47, 69-70, 92-93, 100-101): ensure the new highlighting logic matches the intended UX.

app/presets/aura/index.js (1)

100-101: LGTM!

The tree and treeselect components are correctly imported and exported in their appropriate categories (forms and data).

Also applies to: 136-136, 152-152

app/presets/aura/treeselect/index.js (1)

46-46: Verify WCAG AA contrast ratio for text-bluegray-400 Ensure text-bluegray-400 meets at least a 4.5:1 contrast ratio against bg-surface-0 (light mode) and dark:bg-dark-900 (dark mode).

app/composables/useProbeFilters.ts (1)

129-131: Confirm Directus null-check semantics for hardwareDevice

Using { _eq: null } assumes empty/absent values are truly null. If the field can be empty-string or otherwise falsy, adjust to { _null: true } / { _nnull: true } or broaden the condition.

app/components/AsyncCell.vue (1)

7-18: LGTM: Size prop implementation is clean.

The addition of the size prop with a simple computed class mapping is straightforward and works correctly. The change from anonymous defineProps to const props = defineProps properly exposes the props for the computed property.

app/utils/tree-select.ts (1)

1-75: LGTM: Well-structured tree utilities.

The TreeNode type and utility functions are cleanly implemented:

  • renderTreeSelectValue correctly handles empty selections, full selections, and partial selections with proper subtree collapsing
  • buildNodesByKey properly constructs the tree hierarchy using dash-separated key prefixes
  • Edge cases are handled (e.g., keys without dashes at line 64)
app/components/credits/CreditsFilters.vue (1)

30-136: LGTM: Solid filtering implementation.

The component correctly implements tree-based filtering with:

  • Proper debounced change handling with cleanup on unmount
  • Correct recursive state management in buildSelection
  • Change detection using isEqual to prevent unnecessary updates
  • Early return when nothing is selected (intentional per project learnings)

Based on learnings

app/composables/useCreditsFilters.ts (1)

1-108: LGTM: Well-structured filter composable.

The composable correctly:

  • Validates route query parameters against permitted values
  • Synchronizes filter state bidirectionally with the route
  • Provides helpers for query construction and default comparison
  • Handles the business logic of clearing reasons when type excludes 'additions'
app/pages/credits.vue (2)

30-30: PR objectives mismatch with code changes.

The PR objectives state this adds "probe type to admin filter" for classifying probes as hardware/software based on hardwareDevice field (issue #144). However, all code changes in this review are implementing credits filtering with type/reason filters, not probe filtering.

Verify that:

  1. The correct files were included in this PR
  2. The PR objectives accurately describe these changes

1-276: LGTM: Comprehensive filtering integration.

The credits page successfully integrates filtering with:

  • Proper loading state management using initialLoading for the initial render and loading for individual cells
  • Consistent AsyncCell usage with appropriate size variants
  • Correct filter dependency tracking using computedDebounced
  • Well-structured empty states for both desktop and mobile views
  • Proper synchronization with the filter composable

The implementation provides good UX by maintaining table structure during filtered reloads while showing per-cell loading indicators.

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.

Add probe type to admin filter

3 participants