ts
This commit is contained in:
@@ -1,21 +1,15 @@
|
||||
import { slug } from '@openpanel/common';
|
||||
import { alphabetIds } from '@openpanel/constants';
|
||||
import type {
|
||||
IChartBreakdown,
|
||||
IChartEvent,
|
||||
IChartEventItem,
|
||||
} from '@openpanel/validation';
|
||||
import type { IChartEventItem } from '@openpanel/validation';
|
||||
import { getSettingsForProject } from '../services/organization.service';
|
||||
import type { ConcreteSeries, Plan } from './types';
|
||||
import type { NormalizedInput } from './normalize';
|
||||
import type { ConcreteSeries, Plan } from './types';
|
||||
|
||||
/**
|
||||
* Create an execution plan from normalized input
|
||||
* This sets up ConcreteSeries placeholders - actual breakdown expansion happens during fetch
|
||||
*/
|
||||
export async function plan(
|
||||
normalized: NormalizedInput,
|
||||
): Promise<Plan> {
|
||||
export async function plan(normalized: NormalizedInput): Promise<Plan> {
|
||||
const { timezone } = await getSettingsForProject(normalized.projectId);
|
||||
|
||||
const concreteSeries: ConcreteSeries[] = [];
|
||||
@@ -24,7 +18,7 @@ export async function plan(
|
||||
normalized.series.forEach((definition, index) => {
|
||||
if (definition.type === 'event') {
|
||||
const event = definition as IChartEventItem & { type: 'event' };
|
||||
|
||||
|
||||
// For events, create a placeholder
|
||||
// If breakdowns exist, fetch will return multiple series (one per breakdown value)
|
||||
// If no breakdowns, fetch will return one series
|
||||
@@ -54,6 +48,3 @@ export async function plan(
|
||||
timezone,
|
||||
};
|
||||
}
|
||||
|
||||
export type NormalizedInput = Awaited<ReturnType<typeof import('./normalize').normalize>>;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getEventFiltersWhereClause,
|
||||
getSelectPropertyKey,
|
||||
} from './chart.service';
|
||||
import { onlyReportEvents } from './reports.service';
|
||||
|
||||
export class ConversionService {
|
||||
constructor(private client: typeof ch) {}
|
||||
@@ -18,7 +19,6 @@ export class ConversionService {
|
||||
funnelGroup,
|
||||
funnelWindow = 24,
|
||||
series,
|
||||
events, // Backward compatibility - use series if available
|
||||
breakdowns = [],
|
||||
interval,
|
||||
timezone,
|
||||
@@ -31,12 +31,9 @@ export class ConversionService {
|
||||
);
|
||||
const breakdownGroupBy = breakdowns.map((b, index) => `b_${index}`);
|
||||
|
||||
// Use series if available, otherwise fall back to events (backward compat)
|
||||
const eventSeries = (series ?? events ?? []).filter(
|
||||
(item): item is IChartEvent => item.type === 'event',
|
||||
) as IChartEvent[];
|
||||
const events = onlyReportEvents(series);
|
||||
|
||||
if (eventSeries.length !== 2) {
|
||||
if (events.length !== 2) {
|
||||
throw new Error('events must be an array of two events');
|
||||
}
|
||||
|
||||
@@ -44,8 +41,8 @@ export class ConversionService {
|
||||
throw new Error('startDate and endDate are required');
|
||||
}
|
||||
|
||||
const eventA = eventSeries[0]!;
|
||||
const eventB = eventSeries[1]!;
|
||||
const eventA = events[0]!;
|
||||
const eventB = events[1]!;
|
||||
const whereA = Object.values(
|
||||
getEventFiltersWhereClause(eventA.filters),
|
||||
).join(' AND ');
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
getEventFiltersWhereClause,
|
||||
getSelectPropertyKey,
|
||||
} from './chart.service';
|
||||
import { onlyReportEvents } from './reports.service';
|
||||
|
||||
export class FunnelService {
|
||||
constructor(private client: typeof ch) {}
|
||||
@@ -179,7 +180,6 @@ export class FunnelService {
|
||||
startDate,
|
||||
endDate,
|
||||
series,
|
||||
events, // Backward compatibility - use series if available
|
||||
funnelWindow = 24,
|
||||
funnelGroup,
|
||||
breakdowns = [],
|
||||
@@ -189,12 +189,7 @@ export class FunnelService {
|
||||
throw new Error('startDate and endDate are required');
|
||||
}
|
||||
|
||||
// Use series if available, otherwise fall back to events (backward compat)
|
||||
const rawSeries = (series ?? events ?? []) as IChartEventItem[];
|
||||
const eventSeries = rawSeries.filter(
|
||||
(item): item is IChartEventItem & { type: 'event' } =>
|
||||
item.type === 'event',
|
||||
) as IChartEvent[];
|
||||
const eventSeries = onlyReportEvents(series);
|
||||
|
||||
if (eventSeries.length === 0) {
|
||||
throw new Error('events are required');
|
||||
|
||||
@@ -5,21 +5,25 @@ import {
|
||||
} from '@openpanel/constants';
|
||||
import type {
|
||||
IChartBreakdown,
|
||||
IChartEvent,
|
||||
IChartEventFilter,
|
||||
IChartEventItem,
|
||||
IChartFormula,
|
||||
IChartLineType,
|
||||
IChartProps,
|
||||
IChartRange,
|
||||
ICriteria,
|
||||
} from '@openpanel/validation';
|
||||
|
||||
import { db } from '../prisma-client';
|
||||
import type { Report as DbReport, ReportLayout } from '../prisma-client';
|
||||
import { db } from '../prisma-client';
|
||||
|
||||
export type IServiceReport = Awaited<ReturnType<typeof getReportById>>;
|
||||
|
||||
export const onlyReportEvents = (
|
||||
series: NonNullable<IServiceReport>['series'],
|
||||
) => {
|
||||
return series.filter((item) => item.type === 'event');
|
||||
};
|
||||
|
||||
export function transformFilter(
|
||||
filter: Partial<IChartEventFilter>,
|
||||
index: number,
|
||||
@@ -34,72 +38,39 @@ export function transformFilter(
|
||||
}
|
||||
|
||||
export function transformReportEventItem(
|
||||
item: Partial<IChartEventItem> | Partial<IChartEvent>,
|
||||
item: IChartEventItem,
|
||||
index: number,
|
||||
): IChartEventItem {
|
||||
// If item already has type field, it's the new format
|
||||
if (item && typeof item === 'object' && 'type' in item) {
|
||||
if (item.type === 'formula') {
|
||||
// Transform formula
|
||||
const formula = item as Partial<IChartFormula>;
|
||||
return {
|
||||
type: 'formula',
|
||||
id: formula.id ?? alphabetIds[index]!,
|
||||
formula: formula.formula || '',
|
||||
displayName: formula.displayName,
|
||||
};
|
||||
}
|
||||
// Transform event with type field
|
||||
const event = item as Partial<IChartEvent>;
|
||||
if (item.type === 'formula') {
|
||||
// Transform formula
|
||||
return {
|
||||
type: 'event',
|
||||
segment: event.segment ?? 'event',
|
||||
filters: (event.filters ?? []).map(transformFilter),
|
||||
id: event.id ?? alphabetIds[index]!,
|
||||
name: event.name || 'unknown_event',
|
||||
displayName: event.displayName,
|
||||
property: event.property,
|
||||
type: 'formula',
|
||||
id: item.id ?? alphabetIds[index]!,
|
||||
formula: item.formula || '',
|
||||
displayName: item.displayName,
|
||||
};
|
||||
}
|
||||
|
||||
// Old format without type field - assume it's an event
|
||||
const event = item as Partial<IChartEvent>;
|
||||
// Transform event with type field
|
||||
return {
|
||||
type: 'event',
|
||||
segment: event.segment ?? 'event',
|
||||
filters: (event.filters ?? []).map(transformFilter),
|
||||
id: event.id ?? alphabetIds[index]!,
|
||||
name: event.name || 'unknown_event',
|
||||
displayName: event.displayName,
|
||||
property: event.property,
|
||||
segment: item.segment ?? 'event',
|
||||
filters: (item.filters ?? []).map(transformFilter),
|
||||
id: item.id ?? alphabetIds[index]!,
|
||||
name: item.name || 'unknown_event',
|
||||
displayName: item.displayName,
|
||||
property: item.property,
|
||||
};
|
||||
}
|
||||
|
||||
// Keep the old function for backward compatibility, but it now uses the new transformer
|
||||
export function transformReportEvent(
|
||||
event: Partial<IChartEvent>,
|
||||
index: number,
|
||||
): IChartEvent {
|
||||
const transformed = transformReportEventItem(event, index);
|
||||
if (transformed.type === 'event') {
|
||||
return transformed;
|
||||
}
|
||||
// This shouldn't happen for old code, but handle it gracefully
|
||||
throw new Error('transformReportEvent called on a formula');
|
||||
}
|
||||
|
||||
export function transformReport(
|
||||
report: DbReport & { layout?: ReportLayout | null },
|
||||
): IChartProps & { id: string; layout?: ReportLayout | null } {
|
||||
// Events can be either old format (IChartEvent[]) or new format (IChartEventItem[])
|
||||
const eventsData = report.events as unknown as Array<
|
||||
Partial<IChartEventItem> | Partial<IChartEvent>
|
||||
>;
|
||||
|
||||
return {
|
||||
id: report.id,
|
||||
projectId: report.projectId,
|
||||
series: eventsData.map(transformReportEventItem),
|
||||
series:
|
||||
(report.events as IChartEventItem[]).map(transformReportEventItem) ?? [],
|
||||
breakdowns: report.breakdowns as IChartBreakdown[],
|
||||
chartType: report.chartType,
|
||||
lineType: (report.lineType as IChartLineType) ?? lineTypes.monotone,
|
||||
|
||||
Reference in New Issue
Block a user