Add defaultLocale, EN names, and dashboard activity endpoints.

Expose branding.defaultLocale, optional user EN name fields, and lightweight dual daily activity for orders/customers; update project context and rules.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-01 09:35:27 +03:30
co-authored by Cursor
parent 4598add88c
commit 43e3912662
22 changed files with 331 additions and 15 deletions
+80
View File
@@ -0,0 +1,80 @@
export type DailyCountRow = { day: Date | string; count: number };
export type DailyActivityPoint = {
date: string;
count: number;
};
export type DailyActivityResponse = {
days: number;
total: number;
items: DailyActivityPoint[];
};
/** Local calendar day at 00:00:00, `offsetDays` before today (0 = today). */
export function startOfLocalDay(offsetDays = 0): Date {
const date = new Date();
date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() - offsetDays);
return date;
}
function pad2(n: number) {
return String(n).padStart(2, '0');
}
/** Calendar date key from a PG `date` (UTC midnight) or ISO string. */
function toDateKey(value: Date | string): string {
if (typeof value === 'string') {
return value.slice(0, 10);
}
return `${value.getUTCFullYear()}-${pad2(value.getUTCMonth() + 1)}-${pad2(value.getUTCDate())}`;
}
function toLocalDateKey(date: Date): string {
return `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
}
/** Fill a continuous series of the last `days` local calendar days. */
export function buildDailyActivitySeries(
days: number,
rows: DailyCountRow[],
): DailyActivityResponse {
const byDay = new Map<string, number>();
for (const row of rows) {
byDay.set(toDateKey(row.day), Number(row.count) || 0);
}
const items: DailyActivityPoint[] = [];
let total = 0;
const end = startOfLocalDay(0);
for (let i = days - 1; i >= 0; i -= 1) {
const date = new Date(end);
date.setDate(end.getDate() - i);
const key = toLocalDateKey(date);
const count = byDay.get(key) ?? 0;
total += count;
items.push({ date: key, count });
}
return { days, total, items };
}
export type DualDailyActivityResponse = {
days: number;
primary: DailyActivityResponse;
secondary: DailyActivityResponse;
};
export function buildDualDailyActivitySeries(
days: number,
primaryRows: DailyCountRow[],
secondaryRows: DailyCountRow[],
): DualDailyActivityResponse {
return {
days,
primary: buildDailyActivitySeries(days, primaryRows),
secondary: buildDailyActivitySeries(days, secondaryRows),
};
}
@@ -0,0 +1,11 @@
import { Type } from 'class-transformer';
import { IsInt, IsOptional, Max, Min } from 'class-validator';
export class DailyActivityQueryDto {
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
@Max(90)
days?: number;
}