From 058c3621df78746f89c81fd2f1e313bc7bf3790e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl-Gerhard=20Lindesva=CC=88rd?= Date: Fri, 6 Mar 2026 14:16:09 +0100 Subject: [PATCH] fixes --- packages/common/scripts/get-referrers.ts | 1 - packages/db/src/services/chart.service.ts | 12 ++++++++++-- packages/db/src/services/event.service.ts | 5 +++++ packages/db/src/services/group.service.ts | 16 ++++++++++------ packages/sdks/sdk/src/index.ts | 11 ++--------- packages/validation/src/track.validation.ts | 6 +++--- 6 files changed, 30 insertions(+), 21 deletions(-) diff --git a/packages/common/scripts/get-referrers.ts b/packages/common/scripts/get-referrers.ts index 761a153a..14808da0 100644 --- a/packages/common/scripts/get-referrers.ts +++ b/packages/common/scripts/get-referrers.ts @@ -74,7 +74,6 @@ const extraReferrers = { 'rytr.me': { type: 'ai', name: 'Rytr' }, 'notion.ai': { type: 'ai', name: 'Notion AI' }, 'grammarly.com': { type: 'ai', name: 'Grammarly' }, - 'bing.com': { type: 'ai', name: 'Bing AI' }, 'grok.com': { type: 'ai', name: 'Grok' }, 'x.ai': { type: 'ai', name: 'xAI' }, 'aistudio.google.com': { type: 'ai', name: 'Google AI Studio' }, diff --git a/packages/db/src/services/chart.service.ts b/packages/db/src/services/chart.service.ts index 9183b42f..29103e11 100644 --- a/packages/db/src/services/chart.service.ts +++ b/packages/db/src/services/chart.service.ts @@ -167,8 +167,12 @@ export function getChartSql({ const anyBreakdownOnGroup = breakdowns.some((breakdown) => breakdown.name.startsWith('group.') ); + const anyMetricOnGroup = !!event.property?.startsWith('group.'); const needsGroupArrayJoin = - anyFilterOnGroup || anyBreakdownOnGroup || event.segment === 'group'; + anyFilterOnGroup || + anyBreakdownOnGroup || + anyMetricOnGroup || + event.segment === 'group'; if (needsGroupArrayJoin) { addCte( @@ -453,8 +457,12 @@ export function getAggregateChartSql({ const anyBreakdownOnGroup = breakdowns.some((breakdown) => breakdown.name.startsWith('group.') ); + const anyMetricOnGroup = !!event.property?.startsWith('group.'); const needsGroupArrayJoin = - anyFilterOnGroup || anyBreakdownOnGroup || event.segment === 'group'; + anyFilterOnGroup || + anyBreakdownOnGroup || + anyMetricOnGroup || + event.segment === 'group'; if (needsGroupArrayJoin) { addCte( diff --git a/packages/db/src/services/event.service.ts b/packages/db/src/services/event.service.ts index 9c3b00d2..50d96b1c 100644 --- a/packages/db/src/services/event.service.ts +++ b/packages/db/src/services/event.service.ts @@ -676,6 +676,7 @@ export async function getEventList(options: GetEventListOptions) { export async function getEventsCount({ projectId, profileId, + groupId, events, filters, startDate, @@ -687,6 +688,10 @@ export async function getEventsCount({ sb.where.profileId = `profile_id = ${sqlstring.escape(profileId)}`; } + if (groupId) { + sb.where.groupId = `has(groups, ${sqlstring.escape(groupId)})`; + } + if (startDate && endDate) { sb.where.created_at = `toDate(created_at) BETWEEN toDate('${formatClickhouseDate(startDate)}') AND toDate('${formatClickhouseDate(endDate)}')`; } diff --git a/packages/db/src/services/group.service.ts b/packages/db/src/services/group.service.ts index 145e68e1..def8a1f0 100644 --- a/packages/db/src/services/group.service.ts +++ b/packages/db/src/services/group.service.ts @@ -137,7 +137,7 @@ export async function getGroupList({ WHERE ${conditions.join(' AND ')} ORDER BY created_at DESC LIMIT ${take} - OFFSET ${cursor ?? 0} + OFFSET ${Math.max(0, (cursor ?? 0) * take)} `); return rows.map(transformGroup); } @@ -194,15 +194,19 @@ export async function updateGroup( if (!existing) { throw new Error(`Group ${id} not found`); } + const mergedProperties = { + ...(existing.properties ?? {}), + ...(data.properties ?? {}), + }; + const normalizedProperties = toDots( + mergedProperties as Record + ); const updated = { id, projectId, type: data.type ?? existing.type, name: data.name ?? existing.name, - properties: (data.properties ?? existing.properties) as Record< - string, - string - >, + properties: normalizedProperties, createdAt: existing.createdAt, }; await writeGroupToCh(updated); @@ -314,7 +318,7 @@ export async function getGroupMemberProfiles({ take: number; search?: string; }): Promise<{ data: IServiceProfile[]; count: number }> { - const offset = Math.max(0, cursor ?? 0); + const offset = Math.max(0, (cursor ?? 0) * take); const searchCondition = search?.trim() ? `AND (email ILIKE ${sqlstring.escape(`%${search.trim()}%`)} OR first_name ILIKE ${sqlstring.escape(`%${search.trim()}%`)} OR last_name ILIKE ${sqlstring.escape(`%${search.trim()}%`)})` : ''; diff --git a/packages/sdks/sdk/src/index.ts b/packages/sdks/sdk/src/index.ts index e2b2bf76..373ce32d 100644 --- a/packages/sdks/sdk/src/index.ts +++ b/packages/sdks/sdk/src/index.ts @@ -277,10 +277,7 @@ export class OpenPanel { const mergedGroups = [...new Set([...this.groups, ...queuedGroups])]; return { ...item.payload, - profileId: - 'profileId' in item.payload - ? (item.payload.profileId ?? this.profileId) - : this.profileId, + profileId: item.payload.profileId ?? this.profileId, groups: mergedGroups.length > 0 ? mergedGroups : undefined, }; } @@ -291,11 +288,7 @@ export class OpenPanel { ) { return { ...item.payload, - profileId: String( - 'profileId' in item.payload - ? (item.payload.profileId ?? this.profileId) - : (this.profileId ?? '') - ), + profileId: item.payload.profileId ?? this.profileId, } as TrackHandlerPayload['payload']; } if (item.type === 'group') { diff --git a/packages/validation/src/track.validation.ts b/packages/validation/src/track.validation.ts index 99c295fc..43836f23 100644 --- a/packages/validation/src/track.validation.ts +++ b/packages/validation/src/track.validation.ts @@ -7,15 +7,15 @@ export const zGroupPayload = z.object({ type: z.string().min(1), name: z.string().min(1), properties: z.record(z.unknown()).optional(), - profileId: z.string().optional(), + profileId: z.union([z.string().min(1), z.number()]).optional(), }); export const zTrackPayload = z .object({ name: z.string().min(1), properties: z.record(z.string(), z.unknown()).optional(), - profileId: z.string().or(z.number()).optional(), - groups: z.array(z.string()).optional(), + profileId: z.union([z.string().min(1), z.number()]).optional(), + groups: z.array(z.string().min(1)).optional(), }) .refine((data) => !RESERVED_EVENT_NAMES.includes(data.name as any), { message: `Event name cannot be one of the reserved names: ${RESERVED_EVENT_NAMES.join(', ')}`,