From 64b3a5021382ec0e7c23af9e21bc6e16537bb18e Mon Sep 17 00:00:00 2001 From: Ayush Jhanwar Date: Wed, 26 Aug 2026 13:51:45 +0530 Subject: [PATCH] feat(dashboard): mark built-in properties with $ in PropertiesCombobox MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Built-in geo/device/session columns (country, os, region, browser…) and built-in profile fields render with the same name as a client-sent property (built-in geo-IP `country` vs a `properties.country` the SDK sends), so the picker shows two identical "country" rows with no way to tell which is which. Prefix the built-ins (anything not under `properties.` / `profile.properties.`) with a leading `$` (reserved-property convention) and label their subtext "OpenPanel". Display only: the selected value and the filter it produces are the raw property, unchanged; search still matches. --- .../report/sidebar/PropertiesCombobox.tsx | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/apps/start/src/components/report/sidebar/PropertiesCombobox.tsx b/apps/start/src/components/report/sidebar/PropertiesCombobox.tsx index 54c52e713..44550a781 100644 --- a/apps/start/src/components/report/sidebar/PropertiesCombobox.tsx +++ b/apps/start/src/components/report/sidebar/PropertiesCombobox.tsx @@ -93,6 +93,27 @@ const DEFAULT_CATEGORIES: PropertiesComboboxCategory[] = [ 'group', ]; +// Built-in ("reserved") properties — the geo/device/session columns and the +// built-in profile fields — carry no `properties.` / `profile.properties.` +// prefix. A client-sent property can share the same name (built-in geo-IP +// `country` vs a `properties.country` the SDK sends), so both render as just +// "country" with no way to tell them apart. Mark the built-ins with a leading +// `$` (the reserved-property convention). Display only — the selected `value`, +// and the filter it produces, is the raw property, unchanged. +function toPropertyAction(property: string): PropertiesComboboxAction { + const reserved = + !property.startsWith('properties.') && + !property.startsWith('profile.properties.'); + const name = property.split('.').pop() ?? property; + return { + value: property, + label: reserved ? `$${name}` : name, + description: reserved + ? 'OpenPanel' + : property.split('.').slice(0, -1).join('.'), + }; +} + function SearchHeader({ onBack, onSearch, @@ -189,21 +210,13 @@ export function PropertiesCombobox({ (property) => property.startsWith('profile') && shouldShowProperty(property) ) - .map((property) => ({ - value: property, - label: property.split('.').pop() ?? property, - description: property.split('.').slice(0, -1).join('.'), - })); + .map(toPropertyAction); const eventActions = allProperties .filter( (property) => !property.startsWith('profile') && shouldShowProperty(property) ) - .map((property) => ({ - value: property, - label: property.split('.').pop() ?? property, - description: property.split('.').slice(0, -1).join('.'), - })); + .map(toPropertyAction); const sessionActions = SESSION_ACTIONS.filter((a) => shouldShowProperty(a.value), );