refactor: cut off the old customre entity

This commit is contained in:
ghost2023
2026-06-18 14:41:16 +03:00
parent a9aa4876af
commit 81a495ec61
11 changed files with 233 additions and 863 deletions

View File

@@ -1,25 +1,25 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Employee } from '@tria-plc/iamapi-common';
import { User } from '@tria-plc/iamapi-common/entities/iam/user/user.entity';
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Employee } from "@tria-plc/iamapi-common";
import { User } from "@tria-plc/iamapi-common/entities/iam/user/user.entity";
import { Booking } from '../bookings/entities/booking.entity';
import { Cargo } from '../cargoes/entities/cargoes.entity';
import { Container } from '../container-management/entities/container.entity';
import { Customer } from '../customers/entities/customer.entity';
import { PaymentEntity } from '../payment/entities/payment.entity';
import { Train } from '../trains/entities/train.entity';
import { Wagon } from '../wagons/entities/wagon.entity';
import { OverviewController } from './overview.controller';
import { OverviewRepository } from './overview.repository';
import { OverviewService } from './overview.service';
import { Booking } from "../bookings/entities/booking.entity";
import { Cargo } from "../cargoes/entities/cargoes.entity";
import { Container } from "../container-management/entities/container.entity";
import { Company } from "../companies/entities/company.entity";
import { PaymentEntity } from "../payment/entities/payment.entity";
import { Train } from "../trains/entities/train.entity";
import { Wagon } from "../wagons/entities/wagon.entity";
import { OverviewController } from "./overview.controller";
import { OverviewRepository } from "./overview.repository";
import { OverviewService } from "./overview.service";
@Module({
imports: [
TypeOrmModule.forFeature([
Booking,
PaymentEntity,
Customer,
Company,
Train,
Wagon,
Container,
@@ -31,4 +31,4 @@ import { OverviewService } from './overview.service';
controllers: [OverviewController],
providers: [OverviewService, OverviewRepository],
})
export class OverviewModule {}
export class OverviewModule { }

View File

@@ -1,24 +1,24 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { EUserStatus } from '@tria-plc/api-common/utils/enums/user.enum';
import { Employee } from '@tria-plc/iamapi-common';
import { User } from '@tria-plc/iamapi-common/entities/iam/user/user.entity';
import { Freight } from '@edr/types';
import { Repository, ObjectLiteral } from 'typeorm';
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { EUserStatus } from "@tria-plc/api-common/utils/enums/user.enum";
import { Employee } from "@tria-plc/iamapi-common";
import { User } from "@tria-plc/iamapi-common/entities/iam/user/user.entity";
import { Freight } from "@edr/types";
import { Repository, ObjectLiteral } from "typeorm";
import { Booking } from '../bookings/entities/booking.entity';
import { Cargo } from '../cargoes/entities/cargoes.entity';
import { Container } from '../container-management/entities/container.entity';
import { Customer } from '../customers/entities/customer.entity';
import { PaymentEntity } from '../payment/entities/payment.entity';
import { Train } from '../trains/entities/train.entity';
import { Wagon } from '../wagons/entities/wagon.entity';
import { Booking } from "../bookings/entities/booking.entity";
import { Cargo } from "../cargoes/entities/cargoes.entity";
import { Container } from "../container-management/entities/container.entity";
import { PaymentEntity } from "../payment/entities/payment.entity";
import { Train } from "../trains/entities/train.entity";
import { Wagon } from "../wagons/entities/wagon.entity";
import {
OVERVIEW_CLOSED_STATUSES,
OVERVIEW_IN_APPROVAL_STATUSES,
OVERVIEW_NEEDS_ACTION_STATUSES,
OVERVIEW_URGENT_PRIORITY_THRESHOLD,
} from './overview.constants';
} from "./overview.constants";
import { Company } from "../companies/entities/company.entity";
export type OverviewBookingKpisRow = {
totalActive: number;
@@ -46,8 +46,8 @@ export class OverviewRepository {
private readonly bookingRepository: Repository<Booking>,
@InjectRepository(PaymentEntity)
private readonly paymentRepository: Repository<PaymentEntity>,
@InjectRepository(Customer)
private readonly customerRepository: Repository<Customer>,
@InjectRepository(Company)
private readonly companyRepository: Repository<Company>,
@InjectRepository(Train)
private readonly trainRepository: Repository<Train>,
@InjectRepository(Wagon)
@@ -60,32 +60,32 @@ export class OverviewRepository {
private readonly employeeRepository: Repository<Employee>,
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
) { }
async getBookingKpis(): Promise<OverviewBookingKpisRow> {
const row = await this.bookingRepository
.createQueryBuilder('booking')
.createQueryBuilder("booking")
.select(
`COUNT(*) FILTER (WHERE booking.status NOT IN (:...closedStatuses) AND booking.status != 'DRAFT')::int`,
'totalActive',
"totalActive",
)
.addSelect(
`COUNT(*) FILTER (WHERE booking.status IN (:...needsActionStatuses))::int`,
'needsAction',
"needsAction",
)
.addSelect(
`COUNT(*) FILTER (WHERE booking.priority_score >= :urgentThreshold)::int`,
'urgent',
"urgent",
)
.addSelect(
`COUNT(*) FILTER (WHERE booking.status IN (:...inApprovalStatuses))::int`,
'inApproval',
"inApproval",
)
.addSelect(
`COUNT(*) FILTER (WHERE booking.created_at >= CURRENT_DATE AND booking.status != 'DRAFT')::int`,
'submittedToday',
"submittedToday",
)
.where('booking.deleted_at IS NULL')
.where("booking.deleted_at IS NULL")
.setParameters({
closedStatuses: [...OVERVIEW_CLOSED_STATUSES],
needsActionStatuses: [...OVERVIEW_NEEDS_ACTION_STATUSES],
@@ -112,9 +112,9 @@ export class OverviewRepository {
const [trainsActive, wagonsAvailable, containersInTransit, cargoesLoaded] =
await Promise.all([
this.trainRepository
.createQueryBuilder('train')
.where('train.deleted_at IS NULL')
.andWhere('train.status IN (:...statuses)', {
.createQueryBuilder("train")
.where("train.deleted_at IS NULL")
.andWhere("train.status IN (:...statuses)", {
statuses: [
Freight.TrainStatus.InService,
Freight.TrainStatus.Scheduled,
@@ -122,39 +122,46 @@ export class OverviewRepository {
})
.getCount(),
this.wagonRepository
.createQueryBuilder('wagon')
.where('wagon.deleted_at IS NULL')
.andWhere('wagon.status = :status', { status: Freight.WagonStatus.Available })
.createQueryBuilder("wagon")
.where("wagon.deleted_at IS NULL")
.andWhere("wagon.status = :status", {
status: Freight.WagonStatus.Available,
})
.getCount(),
this.containerRepository
.createQueryBuilder('container')
.where('container.deleted_at IS NULL')
.andWhere('container.status = :status', { status: 'IN_TRANSIT' })
.createQueryBuilder("container")
.where("container.deleted_at IS NULL")
.andWhere("container.status = :status", { status: "IN_TRANSIT" })
.getCount(),
this.cargoRepository
.createQueryBuilder('cargo')
.where('cargo.deleted_at IS NULL')
.andWhere('cargo.status IN (:...statuses)', {
statuses: ['LOADED', 'IN_TRANSIT'],
.createQueryBuilder("cargo")
.where("cargo.deleted_at IS NULL")
.andWhere("cargo.status IN (:...statuses)", {
statuses: ["LOADED", "IN_TRANSIT"],
})
.getCount(),
]);
return { trainsActive, wagonsAvailable, containersInTransit, cargoesLoaded };
return {
trainsActive,
wagonsAvailable,
containersInTransit,
cargoesLoaded,
};
}
async getCustomerKpis(): Promise<{
totalCustomers: number;
newCustomersThisMonth: number;
}> {
const row = await this.customerRepository
.createQueryBuilder('customer')
.select('COUNT(*)::int', 'totalCustomers')
const row = await this.companyRepository
.createQueryBuilder("customer")
.select("COUNT(*)::int", "totalCustomers")
.addSelect(
`COUNT(*) FILTER (WHERE customer.created_at >= date_trunc('month', CURRENT_DATE))::int`,
'newCustomersThisMonth',
"newCustomersThisMonth",
)
.where('customer.deleted_at IS NULL')
.where("customer.deleted_at IS NULL")
.getRawOne<Record<string, string>>();
return {
@@ -170,26 +177,26 @@ export class OverviewRepository {
successfulPaymentsMtd: number;
}> {
const revenueRow = await this.paymentRepository
.createQueryBuilder('payment')
.createQueryBuilder("payment")
.select(
`COALESCE(SUM(payment.amount) FILTER (WHERE payment.currency = 'ETB'), 0)`,
'revenueMtdEtb',
"revenueMtdEtb",
)
.addSelect(
`COALESCE(SUM(payment.amount) FILTER (WHERE payment.currency = 'USD'), 0)`,
'revenueMtdUsd',
"revenueMtdUsd",
)
.addSelect(`COUNT(*)::int`, 'successfulPaymentsMtd')
.where('payment.status = :status', { status: 'success' })
.addSelect(`COUNT(*)::int`, "successfulPaymentsMtd")
.where("payment.status = :status", { status: "success" })
.andWhere(
`COALESCE(payment.paid_at, payment.created_at) >= date_trunc('month', CURRENT_DATE)`,
)
.getRawOne<Record<string, string>>();
const pendingPayments = await this.paymentRepository
.createQueryBuilder('payment')
.where('payment.status IN (:...statuses)', {
statuses: ['action-required', 'processing'],
.createQueryBuilder("payment")
.where("payment.status IN (:...statuses)", {
statuses: ["action-required", "processing"],
})
.getCount();
@@ -201,7 +208,10 @@ export class OverviewRepository {
};
}
async getStaffKpis(): Promise<{ activeEmployees: number; activeUsers: number }> {
async getStaffKpis(): Promise<{
activeEmployees: number;
activeUsers: number;
}> {
const [activeEmployees, activeUsers] = await Promise.all([
this.employeeRepository.count({
where: { isCurrent: true },
@@ -217,15 +227,17 @@ export class OverviewRepository {
return { activeEmployees, activeUsers };
}
async getBookingTrend(days: number): Promise<{ date: string; count: number }[]> {
async getBookingTrend(
days: number,
): Promise<{ date: string; count: number }[]> {
const rows = await this.bookingRepository
.createQueryBuilder('booking')
.select(`to_char(booking.created_at::date, 'YYYY-MM-DD')`, 'date')
.addSelect('COUNT(*)::int', 'count')
.where('booking.deleted_at IS NULL')
.createQueryBuilder("booking")
.select(`to_char(booking.created_at::date, 'YYYY-MM-DD')`, "date")
.addSelect("COUNT(*)::int", "count")
.where("booking.deleted_at IS NULL")
.andWhere(`booking.created_at >= CURRENT_DATE - :days::int + 1`, { days })
.groupBy('booking.created_at::date')
.orderBy('booking.created_at::date', 'ASC')
.groupBy("booking.created_at::date")
.orderBy("booking.created_at::date", "ASC")
.getRawMany<{ date: string; count: string }>();
return rows.map((row) => ({
@@ -236,11 +248,11 @@ export class OverviewRepository {
async getStatusCounts(): Promise<Record<string, number>> {
const rows = await this.bookingRepository
.createQueryBuilder('booking')
.select('booking.status', 'status')
.addSelect('COUNT(*)::int', 'count')
.where('booking.deleted_at IS NULL')
.groupBy('booking.status')
.createQueryBuilder("booking")
.select("booking.status", "status")
.addSelect("COUNT(*)::int", "count")
.where("booking.deleted_at IS NULL")
.groupBy("booking.status")
.getRawMany<{ status: string; count: string }>();
return Object.fromEntries(
@@ -252,26 +264,26 @@ export class OverviewRepository {
days: number,
): Promise<{ date: string; amountEtb: number; amountUsd: number }[]> {
const rows = await this.paymentRepository
.createQueryBuilder('payment')
.createQueryBuilder("payment")
.select(
`to_char(COALESCE(payment.paid_at, payment.created_at)::date, 'YYYY-MM-DD')`,
'date',
"date",
)
.addSelect(
`COALESCE(SUM(payment.amount) FILTER (WHERE payment.currency = 'ETB'), 0)`,
'amountEtb',
"amountEtb",
)
.addSelect(
`COALESCE(SUM(payment.amount) FILTER (WHERE payment.currency = 'USD'), 0)`,
'amountUsd',
"amountUsd",
)
.where('payment.status = :status', { status: 'success' })
.where("payment.status = :status", { status: "success" })
.andWhere(
`COALESCE(payment.paid_at, payment.created_at) >= CURRENT_DATE - :days::int + 1`,
{ days },
)
.groupBy(`COALESCE(payment.paid_at, payment.created_at)::date`)
.orderBy(`COALESCE(payment.paid_at, payment.created_at)::date`, 'ASC')
.orderBy(`COALESCE(payment.paid_at, payment.created_at)::date`, "ASC")
.getRawMany<{ date: string; amountEtb: string; amountUsd: string }>();
return rows.map((row) => ({
@@ -283,18 +295,18 @@ export class OverviewRepository {
async getRecentBookings(limit: number): Promise<OverviewRecentBookingRow[]> {
const rows = await this.bookingRepository
.createQueryBuilder('booking')
.leftJoin('booking.company', 'company')
.select('booking.id', 'id')
.addSelect('booking.reference', 'reference')
.addSelect('COALESCE(company.name, \'—\')', 'customerLabel')
.addSelect('booking.status', 'status')
.addSelect('booking.priority_score', 'priorityScore')
.addSelect('booking.total_amount', 'totalAmount')
.addSelect('booking.payment_currency', 'paymentCurrency')
.addSelect('booking.created_at', 'createdAt')
.where('booking.deleted_at IS NULL')
.orderBy('booking.created_at', 'DESC')
.createQueryBuilder("booking")
.leftJoin("booking.company", "company")
.select("booking.id", "id")
.addSelect("booking.reference", "reference")
.addSelect("COALESCE(company.name, '—')", "customerLabel")
.addSelect("booking.status", "status")
.addSelect("booking.priority_score", "priorityScore")
.addSelect("booking.total_amount", "totalAmount")
.addSelect("booking.payment_currency", "paymentCurrency")
.addSelect("booking.created_at", "createdAt")
.where("booking.deleted_at IS NULL")
.orderBy("booking.created_at", "DESC")
.limit(limit)
.getRawMany<{
id: string;
@@ -319,15 +331,17 @@ export class OverviewRepository {
}));
}
async getBookingsByFreightType(): Promise<{ label: string; count: number }[]> {
async getBookingsByFreightType(): Promise<
{ label: string; count: number }[]
> {
const rows = await this.bookingRepository
.createQueryBuilder('booking')
.select('booking.freight_type', 'label')
.addSelect('COUNT(*)::int', 'count')
.where('booking.deleted_at IS NULL')
.createQueryBuilder("booking")
.select("booking.freight_type", "label")
.addSelect("COUNT(*)::int", "count")
.where("booking.deleted_at IS NULL")
.andWhere("booking.status != 'DRAFT'")
.groupBy('booking.freight_type')
.orderBy('count', 'DESC')
.groupBy("booking.freight_type")
.orderBy("count", "DESC")
.getRawMany<{ label: string; count: string }>();
return rows.map((row) => ({
@@ -338,13 +352,13 @@ export class OverviewRepository {
async getBookingsByCurrency(): Promise<{ label: string; count: number }[]> {
const rows = await this.bookingRepository
.createQueryBuilder('booking')
.select('booking.payment_currency', 'label')
.addSelect('COUNT(*)::int', 'count')
.where('booking.deleted_at IS NULL')
.createQueryBuilder("booking")
.select("booking.payment_currency", "label")
.addSelect("COUNT(*)::int", "count")
.where("booking.deleted_at IS NULL")
.andWhere("booking.status != 'DRAFT'")
.groupBy('booking.payment_currency')
.orderBy('count', 'DESC')
.groupBy("booking.payment_currency")
.orderBy("count", "DESC")
.getRawMany<{ label: string; count: string }>();
return rows.map((row) => ({
@@ -355,11 +369,11 @@ export class OverviewRepository {
async getPaymentsByStatus(): Promise<{ status: string; count: number }[]> {
const rows = await this.paymentRepository
.createQueryBuilder('payment')
.select('payment.status', 'status')
.addSelect('COUNT(*)::int', 'count')
.groupBy('payment.status')
.orderBy('count', 'DESC')
.createQueryBuilder("payment")
.select("payment.status", "status")
.addSelect("COUNT(*)::int", "count")
.groupBy("payment.status")
.orderBy("count", "DESC")
.getRawMany<{ status: string; count: string }>();
return rows.map((row) => ({
@@ -372,20 +386,25 @@ export class OverviewRepository {
{ method: string; count: number; amountEtb: number; amountUsd: number }[]
> {
const rows = await this.paymentRepository
.createQueryBuilder('payment')
.select('payment.method', 'method')
.addSelect('COUNT(*)::int', 'count')
.createQueryBuilder("payment")
.select("payment.method", "method")
.addSelect("COUNT(*)::int", "count")
.addSelect(
`COALESCE(SUM(payment.amount) FILTER (WHERE payment.currency = 'ETB' AND payment.status = 'success'), 0)`,
'amountEtb',
"amountEtb",
)
.addSelect(
`COALESCE(SUM(payment.amount) FILTER (WHERE payment.currency = 'USD' AND payment.status = 'success'), 0)`,
'amountUsd',
"amountUsd",
)
.groupBy('payment.method')
.orderBy('count', 'DESC')
.getRawMany<{ method: string; count: string; amountEtb: string; amountUsd: string }>();
.groupBy("payment.method")
.orderBy("count", "DESC")
.getRawMany<{
method: string;
count: string;
amountEtb: string;
amountUsd: string;
}>();
return rows.map((row) => ({
method: row.method,
@@ -395,16 +414,18 @@ export class OverviewRepository {
}));
}
async getRevenueByCurrency(): Promise<{ currency: string; amount: number }[]> {
async getRevenueByCurrency(): Promise<
{ currency: string; amount: number }[]
> {
const rows = await this.paymentRepository
.createQueryBuilder('payment')
.select('payment.currency', 'currency')
.addSelect('COALESCE(SUM(payment.amount), 0)', 'amount')
.where('payment.status = :status', { status: 'success' })
.createQueryBuilder("payment")
.select("payment.currency", "currency")
.addSelect("COALESCE(SUM(payment.amount), 0)", "amount")
.where("payment.status = :status", { status: "success" })
.andWhere(
`COALESCE(payment.paid_at, payment.created_at) >= date_trunc('month', CURRENT_DATE)`,
)
.groupBy('payment.currency')
.groupBy("payment.currency")
.getRawMany<{ currency: string; amount: string }>();
return rows.map((row) => ({
@@ -413,20 +434,28 @@ export class OverviewRepository {
}));
}
async getTrainStatusBreakdown(): Promise<{ status: string; count: number }[]> {
return this.statusBreakdown(this.trainRepository, 'train');
async getTrainStatusBreakdown(): Promise<
{ status: string; count: number }[]
> {
return this.statusBreakdown(this.trainRepository, "train");
}
async getWagonStatusBreakdown(): Promise<{ status: string; count: number }[]> {
return this.statusBreakdown(this.wagonRepository, 'wagon');
async getWagonStatusBreakdown(): Promise<
{ status: string; count: number }[]
> {
return this.statusBreakdown(this.wagonRepository, "wagon");
}
async getContainerStatusBreakdown(): Promise<{ status: string; count: number }[]> {
return this.statusBreakdown(this.containerRepository, 'container');
async getContainerStatusBreakdown(): Promise<
{ status: string; count: number }[]
> {
return this.statusBreakdown(this.containerRepository, "container");
}
async getCargoStatusBreakdown(): Promise<{ status: string; count: number }[]> {
return this.statusBreakdown(this.cargoRepository, 'cargo');
async getCargoStatusBreakdown(): Promise<
{ status: string; count: number }[]
> {
return this.statusBreakdown(this.cargoRepository, "cargo");
}
private async statusBreakdown(
@@ -435,11 +464,11 @@ export class OverviewRepository {
): Promise<{ status: string; count: number }[]> {
const rows = await repository
.createQueryBuilder(alias)
.select(`${alias}.status`, 'status')
.addSelect('COUNT(*)::int', 'count')
.select(`${alias}.status`, "status")
.addSelect("COUNT(*)::int", "count")
.where(`${alias}.deleted_at IS NULL`)
.groupBy(`${alias}.status`)
.orderBy('count', 'DESC')
.orderBy("count", "DESC")
.getRawMany<{ status: string; count: string }>();
return rows.map((row) => ({
@@ -448,15 +477,19 @@ export class OverviewRepository {
}));
}
async getCustomerGrowthTrend(days: number): Promise<{ date: string; count: number }[]> {
const rows = await this.customerRepository
.createQueryBuilder('customer')
.select(`to_char(customer.created_at::date, 'YYYY-MM-DD')`, 'date')
.addSelect('COUNT(*)::int', 'count')
.where('customer.deleted_at IS NULL')
.andWhere(`customer.created_at >= CURRENT_DATE - :days::int + 1`, { days })
.groupBy('customer.created_at::date')
.orderBy('customer.created_at::date', 'ASC')
async getCustomerGrowthTrend(
days: number,
): Promise<{ date: string; count: number }[]> {
const rows = await this.companyRepository
.createQueryBuilder("customer")
.select(`to_char(customer.created_at::date, 'YYYY-MM-DD')`, "date")
.addSelect("COUNT(*)::int", "count")
.where("customer.deleted_at IS NULL")
.andWhere(`customer.created_at >= CURRENT_DATE - :days::int + 1`, {
days,
})
.groupBy("customer.created_at::date")
.orderBy("customer.created_at::date", "ASC")
.getRawMany<{ date: string; count: string }>();
return rows.map((row) => ({
@@ -466,13 +499,16 @@ export class OverviewRepository {
}
async getCustomersByType(): Promise<{ label: string; count: number }[]> {
const rows = await this.customerRepository
.createQueryBuilder('customer')
.select(`COALESCE(NULLIF(customer.customer_type, ''), 'Unknown')`, 'label')
.addSelect('COUNT(*)::int', 'count')
.where('customer.deleted_at IS NULL')
.groupBy('customer.customer_type')
.orderBy('count', 'DESC')
const rows = await this.companyRepository
.createQueryBuilder("customer")
.select(
`COALESCE(NULLIF(customer.type, ''), 'Unknown')`,
"label",
)
.addSelect("COUNT(*)::int", "count")
.where("customer.deleted_at IS NULL")
.groupBy("customer.type")
.orderBy("count", "DESC")
.getRawMany<{ label: string; count: string }>();
return rows.map((row) => ({
@@ -481,16 +517,18 @@ export class OverviewRepository {
}));
}
async getTopCustomersByBookings(limit: number): Promise<{ label: string; count: number }[]> {
async getTopCustomersByBookings(
limit: number,
): Promise<{ label: string; count: number }[]> {
const rows = await this.bookingRepository
.createQueryBuilder('booking')
.leftJoin('booking.company', 'company')
.select(`COALESCE(company.name, 'Unknown')`, 'label')
.addSelect('COUNT(*)::int', 'count')
.where('booking.deleted_at IS NULL')
.createQueryBuilder("booking")
.leftJoin("booking.company", "company")
.select(`COALESCE(company.name, 'Unknown')`, "label")
.addSelect("COUNT(*)::int", "count")
.where("booking.deleted_at IS NULL")
.andWhere("booking.status != 'DRAFT'")
.groupBy('company.name')
.orderBy('count', 'DESC')
.groupBy("company.name")
.orderBy("count", "DESC")
.limit(limit)
.getRawMany<{ label: string; count: string }>();
@@ -502,11 +540,11 @@ export class OverviewRepository {
async getUsersByStatus(): Promise<{ status: string; count: number }[]> {
const rows = await this.userRepository
.createQueryBuilder('user')
.select('user.status', 'status')
.addSelect('COUNT(*)::int', 'count')
.groupBy('user.status')
.orderBy('count', 'DESC')
.createQueryBuilder("user")
.select("user.status", "status")
.addSelect("COUNT(*)::int", "count")
.groupBy("user.status")
.orderBy("count", "DESC")
.getRawMany<{ status: string; count: string }>();
return rows.map((row) => ({
@@ -515,15 +553,19 @@ export class OverviewRepository {
}));
}
async getEmployeeGrowthTrend(days: number): Promise<{ date: string; count: number }[]> {
async getEmployeeGrowthTrend(
days: number,
): Promise<{ date: string; count: number }[]> {
const rows = await this.employeeRepository
.createQueryBuilder('employee')
.select(`to_char(employee.created_at::date, 'YYYY-MM-DD')`, 'date')
.addSelect('COUNT(*)::int', 'count')
.where('employee.is_current = true')
.andWhere(`employee.created_at >= CURRENT_DATE - :days::int + 1`, { days })
.groupBy('employee.created_at::date')
.orderBy('employee.created_at::date', 'ASC')
.createQueryBuilder("employee")
.select(`to_char(employee.created_at::date, 'YYYY-MM-DD')`, "date")
.addSelect("COUNT(*)::int", "count")
.where("employee.is_current = true")
.andWhere(`employee.created_at >= CURRENT_DATE - :days::int + 1`, {
days,
})
.groupBy("employee.created_at::date")
.orderBy("employee.created_at::date", "ASC")
.getRawMany<{ date: string; count: string }>();
return rows.map((row) => ({
@@ -538,16 +580,16 @@ export class OverviewRepository {
where: { isActive: true, status: EUserStatus.ACCEPTED },
}),
this.userRepository
.createQueryBuilder('user')
.where('user.is_active = false OR user.status != :status', {
.createQueryBuilder("user")
.where("user.is_active = false OR user.status != :status", {
status: EUserStatus.ACCEPTED,
})
.getCount(),
]);
return [
{ label: 'Active', count: active },
{ label: 'Inactive', count: inactive },
{ label: "Active", count: active },
{ label: "Inactive", count: inactive },
];
}
}