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
+11
View File
@@ -14,6 +14,7 @@ import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { RequireBusinessPermission } from '../auth/decorators/require-business-permission.decorator';
import { BusinessPermissionGuard } from '../auth/guards/business-permission.guard';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { DailyActivityQueryDto } from '../common/dto/daily-activity-query.dto';
import { CustomersService } from './customers.service';
import { CreateCustomerDto } from './dto/create-customer.dto';
import { ListCustomersDto } from './dto/list-customers.dto';
@@ -35,6 +36,16 @@ export class CustomersController {
return this.service.list(businessId, query, user);
}
@Get('activity')
@RequireBusinessPermission('orders.read')
activity(
@Param('businessId') businessId: string,
@Query() query: DailyActivityQueryDto,
@CurrentUser() user: AuthUser,
) {
return this.service.dailyActivity(businessId, query.days, user);
}
@Get('search')
@RequireBusinessPermission('orders.create')
search(
+46
View File
@@ -3,6 +3,10 @@ import { Prisma } from '@prisma/client';
import * as bcrypt from 'bcrypt';
import { AuthUser } from '../auth/auth.types';
import { PermissionsService } from '../auth/permissions.service';
import {
buildDualDailyActivitySeries,
startOfLocalDay,
} from '../common/daily-activity';
import { PrismaService } from '../prisma/prisma.service';
import { CreateCustomerDto } from './dto/create-customer.dto';
import { ListCustomersDto } from './dto/list-customers.dto';
@@ -94,6 +98,48 @@ export class CustomersService {
};
}
/** Lightweight daily registration + active-login counts for dashboard charts. */
async dailyActivity(
businessIdRaw: string,
daysRaw: number | undefined,
actor: AuthUser,
) {
const businessId = BigInt(businessIdRaw);
await this.assertPermission(businessId, actor.id, 'orders.read');
const days = Math.min(Math.max(daysRaw ?? 30, 1), 90);
const from = startOfLocalDay(days - 1);
const [registeredRows, activeRows] = await Promise.all([
this.prisma.$queryRaw<{ day: Date; count: number }[]>(
Prisma.sql`
SELECT created_at::date AS day,
COUNT(*)::int AS count
FROM business_customers
WHERE business_id = ${businessId}
AND created_at >= ${from}
GROUP BY 1
ORDER BY 1
`,
),
this.prisma.$queryRaw<{ day: Date; count: number }[]>(
Prisma.sql`
SELECT u.last_login_at::date AS day,
COUNT(*)::int AS count
FROM business_customers bc
JOIN users u ON u.id = bc.user_id
WHERE bc.business_id = ${businessId}
AND u.last_login_at IS NOT NULL
AND u.last_login_at >= ${from}
GROUP BY 1
ORDER BY 1
`,
),
]);
return buildDualDailyActivitySeries(days, registeredRows, activeRows);
}
async search(
businessIdRaw: string,
query: SearchCustomersDto,