Skip to content

Commit 318876f

Browse files
committed
Merge branch 'staging' of https://github.com/databuddy-analytics/Databuddy into staging
2 parents 9bf04a4 + de65eed commit 318876f

File tree

10 files changed

+47
-72
lines changed

10 files changed

+47
-72
lines changed

apps/api/src/routes/query.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -205,15 +205,16 @@ export const query = new Elysia({ prefix: '/v1/query' })
205205
query: queryParams,
206206
}: {
207207
body: CompileRequestType;
208-
query: { website_id?: string };
208+
query: { website_id?: string; timezone?: string };
209209
}) => {
210210
try {
211211
const { website_id } = queryParams;
212+
const timezone = queryParams.timezone || 'UTC';
212213
const websiteDomain = website_id
213214
? await getWebsiteDomain(website_id)
214215
: null;
215216

216-
const result = compileQuery(body as QueryRequest, websiteDomain);
217+
const result = compileQuery(body as QueryRequest, websiteDomain, timezone);
217218
return {
218219
success: true,
219220
...result,
@@ -235,12 +236,12 @@ export const query = new Elysia({ prefix: '/v1/query' })
235236
async ({
236237
body,
237238
query: queryParams,
238-
timezone,
239239
}: {
240240
body: DynamicQueryRequestType | DynamicQueryRequestType[];
241-
query: { website_id?: string };
242-
timezone: string;
241+
query: { website_id?: string; timezone?: string };
243242
}) => {
243+
const timezone = queryParams.timezone || 'UTC';
244+
244245
try {
245246
if (Array.isArray(body)) {
246247
const uniqueWebsiteIds = [

apps/basket/src/lib/kafka.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,18 @@ describe('Kafka Producer and Consumer', () => {
2020
initialRetryTime: 100,
2121
retries: 8,
2222
},
23+
sasl: {
24+
mechanism: 'scram-sha-256',
25+
username: process.env.KAFKA_USER as string,
26+
password: process.env.KAFKA_PASSWORD as string,
27+
},
2328
});
2429

2530
producer = kafka.producer();
2631
consumer = kafka.consumer({
2732
groupId: TEST_GROUP_ID,
2833
sessionTimeout: 10000,
34+
allowAutoTopicCreation: true,
2935
heartbeatInterval: 3000,
3036
});
3137

apps/basket/src/lib/producer.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,17 @@ describe('Producer Module', () => {
2020
initialRetryTime: 100,
2121
retries: 8,
2222
},
23+
sasl: {
24+
mechanism: 'scram-sha-256',
25+
username: process.env.KAFKA_USER as string,
26+
password: process.env.KAFKA_PASSWORD as string,
27+
},
2328
});
2429

2530
consumer = kafka.consumer({
2631
groupId: TEST_GROUP_ID,
2732
sessionTimeout: 10000,
33+
allowAutoTopicCreation: true,
2834
});
2935

3036
await consumer.connect();

apps/basket/src/lib/producer.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ if (BROKER) {
6666
brokers: [BROKER],
6767
connectionTimeout: 5000,
6868
requestTimeout: KAFKA_TIMEOUT,
69+
sasl: {
70+
mechanism: 'scram-sha-256',
71+
username: process.env.KAFKA_USER as string,
72+
password: process.env.KAFKA_PASSWORD as string,
73+
},
6974
});
7075
producer = kafka.producer({
7176
allowAutoTopicCreation: true,

apps/dashboard/app/(main)/websites/[id]/_components/tabs/overview-tab.tsx

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import {
1111
} from '@phosphor-icons/react';
1212
import type { ColumnDef } from '@tanstack/react-table';
1313
import dayjs from 'dayjs';
14-
import timezone from 'dayjs/plugin/timezone';
15-
import utc from 'dayjs/plugin/utc';
1614
import { useAtom } from 'jotai';
1715
import dynamic from 'next/dynamic';
1816
import { useCallback, useMemo } from 'react';
@@ -31,7 +29,6 @@ import {
3129
createReferrerColumns,
3230
} from '@/components/table/rows';
3331
import { useBatchDynamicQuery } from '@/hooks/use-dynamic-query';
34-
import { getUserTimezone } from '@/lib/timezone';
3532
import { useDateFilters } from '@/hooks/use-date-filters';
3633
import {
3734
metricVisibilityAtom,
@@ -322,15 +319,13 @@ export function WebsiteOverviewTab({
322319
const processedEventsData = useMemo(() => {
323320
if (!analytics.events_by_date?.length) return [];
324321

325-
const userTimezone = getUserTimezone();
326-
const now = dayjs().tz(userTimezone);
322+
const now = dayjs();
327323
const isHourly = dateRange.granularity === 'hourly';
328324

329-
// Step 1: Filter future events
330325
const filteredEvents = analytics.events_by_date.filter((event: MetricPoint) => {
331-
const eventDate = dayjs.utc(event.date).tz(userTimezone);
326+
const eventDate = dayjs(event.date);
332327

333-
if (isHourly) {
328+
if (isHourly) {
334329
return eventDate.isBefore(now);
335330
}
336331

@@ -346,8 +341,8 @@ export function WebsiteOverviewTab({
346341
}
347342

348343
// Step 3: Fill missing dates
349-
const startDate = dayjs(dateRange.start_date).tz(userTimezone);
350-
const endDate = dayjs(dateRange.end_date).tz(userTimezone);
344+
const startDate = dayjs(dateRange.start_date);
345+
const endDate = dayjs(dateRange.end_date);
351346
const filled: MetricPoint[] = [];
352347
let current = startDate;
353348

@@ -676,13 +671,10 @@ export function WebsiteOverviewTab({
676671
},
677672
];
678673

679-
const userTimezone = getUserTimezone();
680-
dayjs.extend(utc);
681-
dayjs.extend(timezone);
682-
const todayDate = dayjs().tz(userTimezone).format('YYYY-MM-DD');
674+
const todayDate = dayjs().format('YYYY-MM-DD');
683675
const todayEvent = analytics.events_by_date.find(
684676
(event: MetricPoint) =>
685-
dayjs(event.date).tz(userTimezone).format('YYYY-MM-DD') === todayDate
677+
dayjs(event.date).format('YYYY-MM-DD') === todayDate
686678
);
687679
const todayVisitors = todayEvent?.visitors ?? 0;
688680
const todaySessions = todayEvent?.sessions ?? 0;

apps/dashboard/app/(main)/websites/[id]/_components/utils/analytics-helpers.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,11 @@ export const safeParseDate = (
6666
}
6767
};
6868

69-
// Format date for display based on granularity with timezone conversion
7069
export const formatDateByGranularity = (
7170
date: string | Date,
7271
granularity: Granularity = 'daily'
7372
): string => {
74-
const userTimezone = getUserTimezone();
75-
const dateObj = dayjs.utc(date).tz(userTimezone);
73+
const dateObj = dayjs(date);
7674
return granularity === 'hourly'
7775
? dateObj.format('MMM D, h:mm A')
7876
: dateObj.format('MMM D');

apps/dashboard/hooks/use-date-filters.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@ import {
55
formattedDateRangeAtom,
66
setDateRangeAndAdjustGranularityAtom,
77
timeGranularityAtom,
8-
timezoneAtom,
98
} from '@/stores/jotai/filterAtoms';
109

1110
export function useDateFilters() {
1211
const [currentDateRange, setCurrentDateRange] = useAtom(dateRangeAtom);
13-
const [timezone] = useAtom(timezoneAtom);
1412
const [formattedDateRangeState] = useAtom(formattedDateRangeAtom);
1513
const [currentGranularity, setCurrentGranularityAtomState] =
1614
useAtom(timeGranularityAtom);
@@ -21,9 +19,8 @@ export function useDateFilters() {
2119
start_date: formattedDateRangeState.startDate,
2220
end_date: formattedDateRangeState.endDate,
2321
granularity: currentGranularity,
24-
timezone,
2522
}),
26-
[formattedDateRangeState, currentGranularity, timezone]
23+
[formattedDateRangeState, currentGranularity]
2724
);
2825

2926
return {

apps/dashboard/hooks/use-dynamic-query.ts

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
} from '@tanstack/react-query';
1919
import { useCallback, useMemo } from 'react';
2020
import { formatDuration } from '@/lib/utils';
21-
import { usePreferences } from './use-preferences';
21+
import { getUserTimezone } from '@/lib/timezone';
2222

2323
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';
2424

@@ -70,31 +70,6 @@ const defaultQueryOptions = {
7070
placeholderData: undefined, // Don't use placeholder data to ensure loading states show
7171
};
7272

73-
/**
74-
* Hook to get the user's effective timezone
75-
*/
76-
function useUserTimezone(): string {
77-
const { preferences } = usePreferences();
78-
79-
// Get browser timezone as fallback
80-
const browserTimezone = useMemo(() => {
81-
try {
82-
return Intl.DateTimeFormat().resolvedOptions().timeZone;
83-
} catch {
84-
return 'UTC';
85-
}
86-
}, []);
87-
88-
// Return user's preferred timezone or browser timezone if 'auto'
89-
if (!preferences) {
90-
return browserTimezone;
91-
}
92-
93-
return preferences.timezone === 'auto'
94-
? browserTimezone
95-
: preferences.timezone;
96-
}
97-
9873
function transformFilters(filters?: DynamicQueryRequest['filters']) {
9974
return filters?.map(({ field, operator, value }) => ({
10075
field,
@@ -108,10 +83,9 @@ async function fetchDynamicQuery(
10883
websiteId: string,
10984
dateRange: DateRange,
11085
queryData: DynamicQueryRequest | DynamicQueryRequest[],
111-
signal?: AbortSignal,
112-
userTimezone?: string
86+
signal?: AbortSignal
11387
): Promise<DynamicQueryResponse | BatchQueryResponse> {
114-
const timezone = userTimezone || 'UTC';
88+
const timezone = getUserTimezone();
11589
const params = buildParams(websiteId, dateRange, { timezone });
11690
const url = `${API_BASE_URL}/v1/query?${params}`;
11791

@@ -174,24 +148,21 @@ export function useDynamicQuery<T extends (keyof ParameterDataMap)[]>(
174148
queryData: DynamicQueryRequest,
175149
options?: Partial<UseQueryOptions<DynamicQueryResponse>>
176150
) {
177-
const userTimezone = useUserTimezone();
178-
179151
const fetchData = useCallback(
180152
async ({ signal }: { signal?: AbortSignal }) => {
181153
const result = await fetchDynamicQuery(
182154
websiteId,
183155
dateRange,
184156
queryData,
185-
signal,
186-
userTimezone
157+
signal
187158
);
188159
return result as DynamicQueryResponse;
189160
},
190-
[websiteId, dateRange, queryData, userTimezone]
161+
[websiteId, dateRange, queryData]
191162
);
192163

193164
const query = useQuery({
194-
queryKey: ['dynamic-query', websiteId, dateRange, queryData, userTimezone],
165+
queryKey: ['dynamic-query', websiteId, dateRange, queryData],
195166
queryFn: fetchData,
196167
...defaultQueryOptions,
197168
...options,
@@ -250,21 +221,18 @@ export function useBatchDynamicQuery(
250221
queries: DynamicQueryRequest[],
251222
options?: Partial<UseQueryOptions<BatchQueryResponse>>
252223
) {
253-
const userTimezone = useUserTimezone();
254-
255224
const fetchData = useCallback(
256225
async ({ signal }: { signal?: AbortSignal }) => {
257226
const result = await fetchDynamicQuery(
258227
websiteId,
259228
dateRange,
260229
queries,
261-
signal,
262-
userTimezone
230+
signal
263231
);
264232
// Ensure we return a batch query response
265233
return result as BatchQueryResponse;
266234
},
267-
[websiteId, dateRange, queries, userTimezone]
235+
[websiteId, dateRange, queries]
268236
);
269237

270238
const query = useQuery({

apps/dashboard/lib/timezone.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
* Shared timezone utilities for dashboard components
33
*/
44

5-
// Get user's timezone
65
export const getUserTimezone = (): string => {
7-
// Hardcode GMT+2 for testing
8-
return 'Europe/Athens';
6+
try {
7+
const browserTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
8+
return browserTz;
9+
} catch {
10+
return 'UTC';
11+
}
912
};

infra/ingest/docker-compose.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ services:
2929
--smp 2 \
3030
--memory 2G \
3131
--reserve-memory 0M \
32-
--default-log-level=info \
33-
--set "redpanda.superusers=['admin']"
32+
--default-log-level=info
3433
networks:
3534
- databuddy
3635
healthcheck:

0 commit comments

Comments
 (0)