Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Brian Cao 2023-08-16 13:56:19 -07:00
commit 2e2b1411bf
25 changed files with 196 additions and 100 deletions

View File

@ -19,5 +19,6 @@ jobs:
close-issue-message: 'This issue was closed because it has been inactive for 7 days since being marked as stale.' close-issue-message: 'This issue was closed because it has been inactive for 7 days since being marked as stale.'
days-before-pr-stale: -1 days-before-pr-stale: -1
days-before-pr-close: -1 days-before-pr-close: -1
operations-per-run: 500 operations-per-run: 200
ascending: true
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -3,7 +3,7 @@ import { Icon, Modal, Dropdown, Item, Text, Flexbox } from 'react-basics';
import { endOfYear, isSameDay } from 'date-fns'; import { endOfYear, isSameDay } from 'date-fns';
import DatePickerForm from 'components/metrics/DatePickerForm'; import DatePickerForm from 'components/metrics/DatePickerForm';
import useLocale from 'hooks/useLocale'; import useLocale from 'hooks/useLocale';
import { dateFormat } from 'lib/date'; import { formatDate } from 'lib/date';
import Icons from 'components/icons'; import Icons from 'components/icons';
import useMessages from 'hooks/useMessages'; import useMessages from 'hooks/useMessages';
@ -135,8 +135,8 @@ const CustomRange = ({ startDate, endDate, onClick }) => {
<Icons.Calendar /> <Icons.Calendar />
</Icon> </Icon>
<Text> <Text>
{dateFormat(startDate, 'd LLL y', locale)} {formatDate(startDate, 'd LLL y', locale)}
{!isSameDay(startDate, endDate) && `${dateFormat(endDate, 'd LLL y', locale)}`} {!isSameDay(startDate, endDate) && `${formatDate(endDate, 'd LLL y', locale)}`}
</Text> </Text>
</Flexbox> </Flexbox>
); );

View File

@ -0,0 +1,51 @@
import { useRef, useState } from 'react';
import { Text, Icon, CalendarMonthSelect, CalendarYearSelect, Button } from 'react-basics';
import { startOfMonth, endOfMonth } from 'date-fns';
import Icons from 'components/icons';
import { useLocale } from 'hooks';
import { formatDate } from 'lib/date';
import { getDateLocale } from 'lib/lang';
import styles from './MonthSelect.module.css';
const MONTH = 'month';
const YEAR = 'year';
export function MonthSelect({ date = new Date(), onChange }) {
const { locale } = useLocale();
const [select, setSelect] = useState(null);
const month = formatDate(date, 'MMMM', locale);
const year = date.getFullYear();
const ref = useRef();
const handleSelect = value => {
setSelect(state => (state !== value ? value : null));
};
const handleChange = date => {
onChange(`range:${startOfMonth(date).getTime()}:${endOfMonth(date).getTime()}`);
setSelect(null);
};
return (
<>
<div ref={ref} className={styles.container}>
<Button className={styles.input} variant="quiet" onClick={() => handleSelect(MONTH)}>
<Text>{month}</Text>
<Icon size="sm">{select === MONTH ? <Icons.Close /> : <Icons.ChevronDown />}</Icon>
</Button>
<Button className={styles.input} variant="quiet" onClick={() => handleSelect(YEAR)}>
<Text>{year}</Text>
<Icon size="sm">{select === YEAR ? <Icons.Close /> : <Icons.ChevronDown />}</Icon>
</Button>
</div>
{select === MONTH && (
<CalendarMonthSelect date={date} locale={getDateLocale(locale)} onSelect={handleChange} />
)}
{select === YEAR && (
<CalendarYearSelect date={date} locale={getDateLocale(locale)} onSelect={handleChange} />
)}
</>
);
}
export default MonthSelect;

View File

@ -0,0 +1,12 @@
.container {
display: flex;
align-items: center;
justify-content: center;
}
.input {
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
}

View File

@ -2,6 +2,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding-top: 40px; padding-top: 40px;
padding-right: 20px;
} }
.content { .content {

View File

@ -36,6 +36,7 @@ export function EventDataValueTable({ data = [], event }) {
<GridColumn name="dataType" label={formatMessage(labels.type)}> <GridColumn name="dataType" label={formatMessage(labels.type)}>
{row => DATA_TYPES[row.dataType]} {row => DATA_TYPES[row.dataType]}
</GridColumn> </GridColumn>
<GridColumn name="fieldValue" label={formatMessage(labels.value)} />
<GridColumn name="total" label={formatMessage(labels.totalRecords)} width="200px"> <GridColumn name="total" label={formatMessage(labels.totalRecords)} width="200px">
{({ total }) => total.toLocaleString()} {({ total }) => total.toLocaleString()}
</GridColumn> </GridColumn>

View File

@ -8,7 +8,7 @@ import useLocale from 'hooks/useLocale';
import useCountryNames from 'hooks/useCountryNames'; import useCountryNames from 'hooks/useCountryNames';
import { BROWSERS } from 'lib/constants'; import { BROWSERS } from 'lib/constants';
import { stringToColor } from 'lib/format'; import { stringToColor } from 'lib/format';
import { dateFormat } from 'lib/date'; import { formatDate } from 'lib/date';
import { safeDecodeURI } from 'next-basics'; import { safeDecodeURI } from 'next-basics';
import Icons from 'components/icons'; import Icons from 'components/icons';
import styles from './RealtimeLog.module.css'; import styles from './RealtimeLog.module.css';
@ -50,7 +50,7 @@ export function RealtimeLog({ data, websiteDomain }) {
}, },
]; ];
const getTime = ({ createdAt }) => dateFormat(new Date(createdAt), 'pp', locale); const getTime = ({ createdAt }) => formatDate(new Date(createdAt), 'pp', locale);
const getColor = ({ id, sessionId }) => stringToColor(sessionId || id); const getColor = ({ id, sessionId }) => stringToColor(sessionId || id);

View File

@ -6,7 +6,12 @@ import { useContext } from 'react';
import { ReportContext } from './Report'; import { ReportContext } from './Report';
import { useMessages } from 'hooks'; import { useMessages } from 'hooks';
export function BaseParameters() { export function BaseParameters({
showWebsiteSelect = true,
allowWebsiteSelect = true,
showDateSelect = true,
allowDateSelect = true,
}) {
const { report, updateReport } = useContext(ReportContext); const { report, updateReport } = useContext(ReportContext);
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
@ -24,17 +29,25 @@ export function BaseParameters() {
return ( return (
<> <>
<FormRow label={formatMessage(labels.website)}> {showWebsiteSelect && (
<WebsiteSelect websiteId={websiteId} onSelect={handleWebsiteSelect} /> <FormRow label={formatMessage(labels.website)}>
</FormRow> {allowWebsiteSelect && (
<FormRow label={formatMessage(labels.dateRange)}> <WebsiteSelect websiteId={websiteId} onSelect={handleWebsiteSelect} />
<DateFilter )}
value={value} </FormRow>
startDate={startDate} )}
endDate={endDate} {showDateSelect && (
onChange={handleDateChange} <FormRow label={formatMessage(labels.dateRange)}>
/> {allowDateSelect && (
</FormRow> <DateFilter
value={value}
startDate={startDate}
endDate={endDate}
onChange={handleDateChange}
/>
)}
</FormRow>
)}
</> </>
); );
} }

View File

@ -1,21 +1,19 @@
import { useContext, useRef } from 'react'; import { useContext, useRef } from 'react';
import { useMessages } from 'hooks'; import { useMessages } from 'hooks';
import { Form, FormButtons, FormInput, FormRow, SubmitButton, TextField } from 'react-basics'; import { Form, FormButtons, FormRow, SubmitButton } from 'react-basics';
import { ReportContext } from 'components/pages/reports/Report'; import { ReportContext } from 'components/pages/reports/Report';
import { MonthSelect } from 'components/input/MonthSelect';
import BaseParameters from '../BaseParameters'; import BaseParameters from '../BaseParameters';
import { parseDateRange } from 'lib/date';
const fieldOptions = [
{ name: 'daily', type: 'string' },
{ name: 'weekly', type: 'string' },
];
export function RetentionParameters() { export function RetentionParameters() {
const { report, runReport, isRunning } = useContext(ReportContext); const { report, runReport, isRunning, updateReport } = useContext(ReportContext);
const { formatMessage, labels } = useMessages(); const { formatMessage, labels } = useMessages();
const ref = useRef(null); const ref = useRef(null);
const { parameters } = report || {}; const { parameters } = report || {};
const { websiteId, dateRange } = parameters || {}; const { websiteId, dateRange } = parameters || {};
const { startDate } = dateRange || {};
const queryDisabled = !websiteId || !dateRange; const queryDisabled = !websiteId || !dateRange;
const handleSubmit = (data, e) => { const handleSubmit = (data, e) => {
@ -26,9 +24,16 @@ export function RetentionParameters() {
} }
}; };
const handleDateChange = value => {
updateReport({ parameters: { dateRange: { ...parseDateRange(value) } } });
};
return ( return (
<Form ref={ref} values={parameters} onSubmit={handleSubmit} preventSubmit={true}> <Form ref={ref} values={parameters} onSubmit={handleSubmit} preventSubmit={true}>
<BaseParameters /> <BaseParameters showDateSelect={false} />
<FormRow label={formatMessage(labels.dateRange)}>
<MonthSelect date={startDate} onChange={handleDateChange} />
</FormRow>
<FormButtons> <FormButtons>
<SubmitButton variant="primary" disabled={queryDisabled} loading={isRunning}> <SubmitButton variant="primary" disabled={queryDisabled} loading={isRunning}>
{formatMessage(labels.runQuery)} {formatMessage(labels.runQuery)}

View File

@ -1,9 +1,10 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { GridTable, GridColumn } from 'react-basics'; import { GridTable, GridColumn } from 'react-basics';
import classNames from 'classnames';
import { ReportContext } from '../Report'; import { ReportContext } from '../Report';
import EmptyPlaceholder from 'components/common/EmptyPlaceholder'; import EmptyPlaceholder from 'components/common/EmptyPlaceholder';
import { useMessages } from 'hooks'; import { useMessages } from 'hooks';
import { dateFormat } from 'lib/date'; import { formatDate } from 'lib/date';
import styles from './RetentionTable.module.css'; import styles from './RetentionTable.module.css';
export function RetentionTable() { export function RetentionTable() {
@ -15,34 +16,32 @@ export function RetentionTable() {
return <EmptyPlaceholder />; return <EmptyPlaceholder />;
} }
const dates = data.reduce((arr, { date }) => { const rows = data.reduce((arr, { date, visitors }) => {
if (!arr.includes(date)) { if (!arr.find(a => a.date === date)) {
return arr.concat(date); return arr.concat({ date, visitors });
} }
return arr; return arr;
}, []); }, []);
const days = Array(32).fill(null); const days = [1, 2, 3, 4, 5, 6, 7, 14, 21, 30];
return ( return (
<> <>
<div className={styles.table}> <div className={styles.table}>
<div className={styles.row}> <div className={classNames(styles.row, styles.header)}>
<div className={styles.date}>{formatMessage(labels.date)}</div> <div className={styles.date}>{formatMessage(labels.date)}</div>
{days.map((n, i) => ( <div className={styles.visitors}>{formatMessage(labels.visitors)}</div>
<div key={i} className={styles.header}> {days.map(n => (
{formatMessage(labels.day)} {i} <div key={n} className={styles.day}>
{formatMessage(labels.day)} {n}
</div> </div>
))} ))}
</div> </div>
{dates.map((date, i) => { {rows.map(({ date, visitors }, i) => {
return ( return (
<div key={i} className={styles.row}> <div key={i} className={styles.row}>
<div className={styles.date}> <div className={styles.date}>{formatDate(`${date} 00:00:00`, 'PP')}</div>
{dateFormat(date, 'P')} <div className={styles.visitors}>{visitors}</div>
<br />
{date}
</div>
{days.map((n, day) => { {days.map((n, day) => {
return ( return (
<div key={day} className={styles.cell}> <div key={day} className={styles.cell}>

View File

@ -4,10 +4,7 @@
} }
.header { .header {
width: 60px; font-weight: 700;
height: 40px;
text-align: center;
font-size: var(--font-size-sm);
} }
.row { .row {
@ -28,5 +25,24 @@
} }
.date { .date {
min-width: 200px; display: flex;
align-items: center;
min-width: 160px;
}
.visitors {
display: flex;
align-items: center;
min-width: 80px;
}
.day {
display: flex;
align-items: center;
justify-content: center;
width: 60px;
height: 60px;
text-align: center;
font-size: var(--font-size-sm);
font-weight: 400;
} }

View File

@ -5,18 +5,18 @@ import { EventDataMetricsBar } from 'components/pages/event-data/EventDataMetric
import { useDateRange, useApi, usePageQuery } from 'hooks'; import { useDateRange, useApi, usePageQuery } from 'hooks';
import styles from './WebsiteEventData.module.css'; import styles from './WebsiteEventData.module.css';
function useData(websiteId, eventName) { function useData(websiteId, event) {
const [dateRange] = useDateRange(websiteId); const [dateRange] = useDateRange(websiteId);
const { startDate, endDate } = dateRange; const { startDate, endDate } = dateRange;
const { get, useQuery } = useApi(); const { get, useQuery } = useApi();
const { data, error, isLoading } = useQuery( const { data, error, isLoading } = useQuery(
['event-data:events', { websiteId, startDate, endDate, eventName }], ['event-data:events', { websiteId, startDate, endDate, event }],
() => () =>
get('/event-data/events', { get('/event-data/events', {
websiteId, websiteId,
startAt: +startDate, startAt: +startDate,
endAt: +endDate, endAt: +endDate,
eventName, event,
}), }),
{ enabled: !!(websiteId && startDate && endDate) }, { enabled: !!(websiteId && startDate && endDate) },
); );

View File

@ -1,5 +1,5 @@
import { StatusLight } from 'react-basics'; import { StatusLight } from 'react-basics';
import { dateFormat } from 'lib/date'; import { formatDate } from 'lib/date';
import { formatLongNumber } from 'lib/format'; import { formatLongNumber } from 'lib/format';
export function renderNumberLabels(label) { export function renderNumberLabels(label) {
@ -12,15 +12,15 @@ export function renderDateLabels(unit, locale) {
switch (unit) { switch (unit) {
case 'minute': case 'minute':
return dateFormat(d, 'h:mm', locale); return formatDate(d, 'h:mm', locale);
case 'hour': case 'hour':
return dateFormat(d, 'p', locale); return formatDate(d, 'p', locale);
case 'day': case 'day':
return dateFormat(d, 'MMM d', locale); return formatDate(d, 'MMM d', locale);
case 'month': case 'month':
return dateFormat(d, 'MMM', locale); return formatDate(d, 'MMM', locale);
case 'year': case 'year':
return dateFormat(d, 'YYY', locale); return formatDate(d, 'YYY', locale);
default: default:
return label; return label;
} }
@ -50,7 +50,7 @@ export function renderStatusTooltipPopup(unit, locale) {
setTooltipPopup( setTooltipPopup(
<> <>
<div>{dateFormat(new Date(dataPoints[0].raw.x), formats[unit], locale)}</div> <div>{formatDate(new Date(dataPoints[0].raw.x), formats[unit], locale)}</div>
<div> <div>
<StatusLight color={labelColors?.[0]?.backgroundColor}> <StatusLight color={labelColors?.[0]?.backgroundColor}>
{formatLongNumber(dataPoints[0].raw.y)} {dataPoints[0].dataset.label} {formatLongNumber(dataPoints[0].raw.y)} {dataPoints[0].dataset.label}

View File

@ -63,12 +63,12 @@ function getDateFormat(date) {
return `'${dateFormat(date, 'UTC:yyyy-mm-dd HH:MM:ss')}'`; return `'${dateFormat(date, 'UTC:yyyy-mm-dd HH:MM:ss')}'`;
} }
function mapFilter(column, operator, name) { function mapFilter(column, operator, name, type = 'String') {
switch (operator) { switch (operator) {
case OPERATORS.equals: case OPERATORS.equals:
return `${column} = {${name}:String}`; return `${column} = {${name}:${type}`;
case OPERATORS.notEquals: case OPERATORS.notEquals:
return `${column} != {${name}:String}`; return `${column} != {${name}:${type}}`;
default: default:
return ''; return '';
} }

View File

@ -249,7 +249,7 @@ export const customFormats = {
}, },
}; };
export function dateFormat(date, str, locale = 'en-US') { export function formatDate(date, str, locale = 'en-US') {
return format( return format(
typeof date === 'string' ? new Date(date) : date, typeof date === 'string' ? new Date(date) : date,
customFormats?.[locale]?.[str] || str, customFormats?.[locale]?.[str] || str,

View File

@ -92,12 +92,12 @@ function getTimestampIntervalQuery(field: string): string {
} }
} }
function mapFilter(column, operator, name) { function mapFilter(column, operator, name, type = 'String') {
switch (operator) { switch (operator) {
case OPERATORS.equals: case OPERATORS.equals:
return `${column} = {{${name}}}`; return `${column} = {${name}:${type}`;
case OPERATORS.notEquals: case OPERATORS.notEquals:
return `${column} != {{${name}}}`; return `${column} != {${name}:${type}}`;
default: default:
return ''; return '';
} }

View File

@ -126,13 +126,8 @@ export interface WebsiteEventMetric {
y: number; y: number;
} }
export interface WebsiteEventDataStats { export interface WebsiteEventData {
fieldName: string; eventName?: string;
dataType: number;
total: number;
}
export interface WebsiteEventDataFields {
fieldName: string; fieldName: string;
dataType: number; dataType: number;
fieldValue?: string; fieldValue?: string;

View File

@ -94,7 +94,7 @@
"node-fetch": "^3.2.8", "node-fetch": "^3.2.8",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"react": "^18.2.0", "react": "^18.2.0",
"react-basics": "^0.92.0", "react-basics": "^0.94.0",
"react-beautiful-dnd": "^13.1.0", "react-beautiful-dnd": "^13.1.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-error-boundary": "^4.0.4", "react-error-boundary": "^4.0.4",

View File

@ -5,16 +5,17 @@ import { NextApiResponse } from 'next';
import { ok, methodNotAllowed, unauthorized } from 'next-basics'; import { ok, methodNotAllowed, unauthorized } from 'next-basics';
import { getEventDataEvents } from 'queries'; import { getEventDataEvents } from 'queries';
export interface EventDataFieldsRequestBody { export interface EventDataEventsRequestQuery {
websiteId: string; websiteId: string;
dateRange: { dateRange: {
startDate: string; startDate: string;
endDate: string; endDate: string;
}; };
event?: string;
} }
export default async ( export default async (
req: NextApiRequestQueryBody<any, EventDataFieldsRequestBody>, req: NextApiRequestQueryBody<EventDataEventsRequestQuery>,
res: NextApiResponse<any>, res: NextApiResponse<any>,
) => { ) => {
await useCors(req, res); await useCors(req, res);

View File

@ -5,7 +5,7 @@ import { NextApiResponse } from 'next';
import { ok, methodNotAllowed, unauthorized } from 'next-basics'; import { ok, methodNotAllowed, unauthorized } from 'next-basics';
import { getEventDataFields } from 'queries'; import { getEventDataFields } from 'queries';
export interface EventDataFieldsRequestBody { export interface EventDataFieldsRequestQuery {
websiteId: string; websiteId: string;
dateRange: { dateRange: {
startDate: string; startDate: string;
@ -15,7 +15,7 @@ export interface EventDataFieldsRequestBody {
} }
export default async ( export default async (
req: NextApiRequestQueryBody<any, EventDataFieldsRequestBody>, req: NextApiRequestQueryBody<EventDataFieldsRequestQuery>,
res: NextApiResponse<any>, res: NextApiResponse<any>,
) => { ) => {
await useCors(req, res); await useCors(req, res);

View File

@ -5,7 +5,7 @@ import { NextApiResponse } from 'next';
import { ok, methodNotAllowed, unauthorized } from 'next-basics'; import { ok, methodNotAllowed, unauthorized } from 'next-basics';
import { getEventDataFields } from 'queries'; import { getEventDataFields } from 'queries';
export interface EventDataRequestBody { export interface EventDataStatsRequestQuery {
websiteId: string; websiteId: string;
dateRange: { dateRange: {
startDate: string; startDate: string;
@ -15,7 +15,7 @@ export interface EventDataRequestBody {
} }
export default async ( export default async (
req: NextApiRequestQueryBody<any, EventDataRequestBody>, req: NextApiRequestQueryBody<EventDataStatsRequestQuery>,
res: NextApiResponse<any>, res: NextApiResponse<any>,
) => { ) => {
await useCors(req, res); await useCors(req, res);
@ -32,18 +32,18 @@ export default async (
const endDate = new Date(+endAt); const endDate = new Date(+endAt);
const results = await getEventDataFields(websiteId, { startDate, endDate }); const results = await getEventDataFields(websiteId, { startDate, endDate });
const events = new Set(); const fields = new Set();
const data = results.reduce( const data = results.reduce(
(obj, row) => { (obj, row) => {
events.add(row.fieldName); fields.add(row.fieldName);
obj.records += Number(row.total); obj.records += Number(row.total);
return obj; return obj;
}, },
{ fields: results.length, records: 0 }, { events: results.length, records: 0 },
); );
return ok(res, { ...data, events: events.size }); return ok(res, { ...data, fields: fields.size });
} }
return methodNotAllowed(res); return methodNotAllowed(res);

View File

@ -1,11 +1,11 @@
import prisma from 'lib/prisma'; import prisma from 'lib/prisma';
import clickhouse from 'lib/clickhouse'; import clickhouse from 'lib/clickhouse';
import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db';
import { QueryFilters, WebsiteEventDataFields } from 'lib/types'; import { QueryFilters, WebsiteEventData } from 'lib/types';
export async function getEventDataEvents( export async function getEventDataEvents(
...args: [websiteId: string, filters: QueryFilters] ...args: [websiteId: string, filters: QueryFilters]
): Promise<WebsiteEventDataFields[]> { ): Promise<WebsiteEventData[]> {
return runQuery({ return runQuery({
[PRISMA]: () => relationalQuery(...args), [PRISMA]: () => relationalQuery(...args),
[CLICKHOUSE]: () => clickhouseQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args),
@ -24,7 +24,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
website_event.event_name as "eventName", website_event.event_name as "eventName",
event_data.event_key as "fieldName", event_data.event_key as "fieldName",
event_data.data_type as "dataType", event_data.data_type as "dataType",
event_data.string_value as "value", event_data.string_value as "fieldValue",
count(*) as "total" count(*) as "total"
from event_data from event_data
inner join website_event inner join website_event
@ -71,7 +71,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
event_name as eventName, event_name as eventName,
event_key as fieldName, event_key as fieldName,
data_type as dataType, data_type as dataType,
string_value as value, string_value as fieldValue,
count(*) as total count(*) as total
from event_data from event_data
where website_id = {websiteId:UUID} where website_id = {websiteId:UUID}

View File

@ -1,11 +1,11 @@
import prisma from 'lib/prisma'; import prisma from 'lib/prisma';
import clickhouse from 'lib/clickhouse'; import clickhouse from 'lib/clickhouse';
import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db'; import { CLICKHOUSE, PRISMA, runQuery } from 'lib/db';
import { QueryFilters, WebsiteEventDataFields } from 'lib/types'; import { QueryFilters, WebsiteEventData } from 'lib/types';
export async function getEventDataFields( export async function getEventDataFields(
...args: [websiteId: string, filters: QueryFilters & { field?: string }] ...args: [websiteId: string, filters: QueryFilters & { field?: string }]
): Promise<WebsiteEventDataFields[]> { ): Promise<WebsiteEventData[]> {
return runQuery({ return runQuery({
[PRISMA]: () => relationalQuery(...args), [PRISMA]: () => relationalQuery(...args),
[CLICKHOUSE]: () => clickhouseQuery(...args), [CLICKHOUSE]: () => clickhouseQuery(...args),

View File

@ -5,9 +5,10 @@ import prisma from 'lib/prisma';
export async function getRetention( export async function getRetention(
...args: [ ...args: [
websiteId: string, websiteId: string,
dateRange: { filters: {
startDate: Date; startDate: Date;
endDate: Date; endDate: Date;
timezone: string;
}, },
] ]
) { ) {
@ -19,9 +20,10 @@ export async function getRetention(
async function relationalQuery( async function relationalQuery(
websiteId: string, websiteId: string,
dateRange: { filters: {
startDate: Date; startDate: Date;
endDate: Date; endDate: Date;
timezone: string;
}, },
): Promise< ): Promise<
{ {
@ -32,9 +34,8 @@ async function relationalQuery(
percentage: number; percentage: number;
}[] }[]
> { > {
const { startDate, endDate } = dateRange; const { startDate, endDate, timezone = 'UTC' } = filters;
const { getDateQuery, getDayDiffQuery, getCastColumnQuery, rawQuery } = prisma; const { getDateQuery, getDayDiffQuery, getCastColumnQuery, rawQuery } = prisma;
const timezone = 'utc';
const unit = 'day'; const unit = 'day';
return rawQuery( return rawQuery(
@ -85,7 +86,7 @@ async function relationalQuery(
from cohort_date c from cohort_date c
join cohort_size s join cohort_size s
on c.cohort_date = s.cohort_date on c.cohort_date = s.cohort_date
where c.day_number IN (0,1,2,3,4,5,6,7,14,21,30) where c.day_number <= 31
order by 1, 2`, order by 1, 2`,
{ {
websiteId, websiteId,
@ -99,9 +100,10 @@ async function relationalQuery(
async function clickhouseQuery( async function clickhouseQuery(
websiteId: string, websiteId: string,
dateRange: { filters: {
startDate: Date; startDate: Date;
endDate: Date; endDate: Date;
timezone: string;
}, },
): Promise< ): Promise<
{ {
@ -112,9 +114,8 @@ async function clickhouseQuery(
percentage: number; percentage: number;
}[] }[]
> { > {
const { startDate, endDate } = dateRange; const { startDate, endDate, timezone = 'UTC' } = filters;
const { getDateQuery, getDateStringQuery, rawQuery } = clickhouse; const { getDateQuery, getDateStringQuery, rawQuery } = clickhouse;
const timezone = 'UTC';
const unit = 'day'; const unit = 'day';
return rawQuery( return rawQuery(
@ -164,7 +165,7 @@ async function clickhouseQuery(
from cohort_date c from cohort_date c
join cohort_size s join cohort_size s
on c.cohort_date = s.cohort_date on c.cohort_date = s.cohort_date
where c.day_number IN (0,1,2,3,4,5,6,7,14,21,30) where c.day_number <= 31
order by 1, 2`, order by 1, 2`,
{ {
websiteId, websiteId,

View File

@ -7557,10 +7557,10 @@ rc@^1.2.7:
minimist "^1.2.0" minimist "^1.2.0"
strip-json-comments "~2.0.1" strip-json-comments "~2.0.1"
react-basics@^0.92.0: react-basics@^0.94.0:
version "0.92.0" version "0.94.0"
resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.92.0.tgz#02bc6e88bdaf189c30cc6cbd8bbb1c9d12cd089b" resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.94.0.tgz#c15698148b959f40c6b451088f36f5735eb82815"
integrity sha512-BVUWg5a7R88konA9NedYMBx1hl50d6h/MD7qlKOEO/Cnm8cOC7AYTRKAKhO6kHMWjY4ZpUuvlg0UcF+SJP/uXA== integrity sha512-OlUHWrGRctRGEm+yL9iWSC9HRnxZhlm3enP2iCKytVmt7LvaPtsK4RtZ27qp4irNvuzg79aqF+h5IFnG+Vi7WA==
dependencies: dependencies:
classnames "^2.3.1" classnames "^2.3.1"
date-fns "^2.29.3" date-fns "^2.29.3"