style: run prettier on entire codebase
This commit is contained in:
@@ -1,13 +1,23 @@
|
||||
import { FastifyInstance } from 'fastify';
|
||||
import prisma from '../../config/database';
|
||||
import { requireAuth } from '../../middleware/auth';
|
||||
import { success } from '../../utils/response';
|
||||
import { FastifyInstance } from "fastify";
|
||||
import prisma from "../../config/database";
|
||||
import { requireAuth } from "../../middleware/auth";
|
||||
import { success } from "../../utils/response";
|
||||
|
||||
export default async function dashboardRoutes(fastify: FastifyInstance): Promise<void> {
|
||||
fastify.get('/', { preHandler: requireAuth }, async (request, reply) => {
|
||||
export default async function dashboardRoutes(
|
||||
fastify: FastifyInstance,
|
||||
): Promise<void> {
|
||||
fastify.get("/", { preHandler: requireAuth }, async (request, reply) => {
|
||||
const now = new Date();
|
||||
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const todayEnd = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
|
||||
const todayStart = new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
now.getDate(),
|
||||
);
|
||||
const todayEnd = new Date(
|
||||
now.getFullYear(),
|
||||
now.getMonth(),
|
||||
now.getDate() + 1,
|
||||
);
|
||||
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 1);
|
||||
const authData = request.authData!;
|
||||
@@ -18,65 +28,87 @@ export default async function dashboardRoutes(fastify: FastifyInstance): Promise
|
||||
const result: Record<string, unknown> = {};
|
||||
|
||||
// My shift — always available for authenticated users with attendance.record
|
||||
if (has('attendance.record')) {
|
||||
if (has("attendance.record")) {
|
||||
const myShift = await prisma.attendance.findFirst({
|
||||
where: { user_id: userId, arrival_time: { not: null }, departure_time: null },
|
||||
orderBy: { created_at: 'desc' },
|
||||
where: {
|
||||
user_id: userId,
|
||||
arrival_time: { not: null },
|
||||
departure_time: null,
|
||||
},
|
||||
orderBy: { created_at: "desc" },
|
||||
});
|
||||
result.my_shift = { has_ongoing: myShift !== null };
|
||||
}
|
||||
|
||||
// Attendance admin — only for attendance.admin
|
||||
if (has('attendance.admin')) {
|
||||
if (has("attendance.admin")) {
|
||||
const [todayAttendance, onLeaveToday, usersCount] = await Promise.all([
|
||||
prisma.attendance.findMany({
|
||||
where: {
|
||||
shift_date: { gte: todayStart, lt: todayEnd },
|
||||
OR: [{ leave_type: null }, { leave_type: 'work' }],
|
||||
OR: [{ leave_type: null }, { leave_type: "work" }],
|
||||
},
|
||||
include: { users: { select: { id: true, first_name: true, last_name: true } } },
|
||||
orderBy: { arrival_time: 'asc' },
|
||||
include: {
|
||||
users: { select: { id: true, first_name: true, last_name: true } },
|
||||
},
|
||||
orderBy: { arrival_time: "asc" },
|
||||
}),
|
||||
prisma.attendance.findMany({
|
||||
where: {
|
||||
shift_date: { gte: todayStart, lt: todayEnd },
|
||||
leave_type: { in: ['vacation', 'sick', 'holiday', 'unpaid'] },
|
||||
leave_type: { in: ["vacation", "sick", "holiday", "unpaid"] },
|
||||
},
|
||||
include: {
|
||||
users: { select: { id: true, first_name: true, last_name: true } },
|
||||
},
|
||||
include: { users: { select: { id: true, first_name: true, last_name: true } } },
|
||||
}),
|
||||
prisma.users.count({ where: { is_active: true } }),
|
||||
]);
|
||||
|
||||
const userAttendanceMap = new Map<number, typeof todayAttendance[0]>();
|
||||
const userAttendanceMap = new Map<number, (typeof todayAttendance)[0]>();
|
||||
for (const a of todayAttendance) {
|
||||
const existing = userAttendanceMap.get(a.users.id);
|
||||
if (!existing || (a.arrival_time && existing.arrival_time && a.arrival_time > existing.arrival_time)) {
|
||||
if (
|
||||
!existing ||
|
||||
(a.arrival_time &&
|
||||
existing.arrival_time &&
|
||||
a.arrival_time > existing.arrival_time)
|
||||
) {
|
||||
userAttendanceMap.set(a.users.id, a);
|
||||
}
|
||||
}
|
||||
|
||||
let presentCount = 0;
|
||||
const attendanceUsers: Array<{
|
||||
user_id: number; name: string; initials: string;
|
||||
status: string; arrived_at: string | null; leave_type?: string;
|
||||
user_id: number;
|
||||
name: string;
|
||||
initials: string;
|
||||
status: string;
|
||||
arrived_at: string | null;
|
||||
leave_type?: string;
|
||||
}> = [];
|
||||
|
||||
for (const a of userAttendanceMap.values()) {
|
||||
const user = a.users;
|
||||
const firstInitial = user.first_name?.charAt(0) ?? '';
|
||||
const lastInitial = user.last_name?.charAt(0) ?? '';
|
||||
let status = 'out';
|
||||
const firstInitial = user.first_name?.charAt(0) ?? "";
|
||||
const lastInitial = user.last_name?.charAt(0) ?? "";
|
||||
let status = "out";
|
||||
if (a.arrival_time) {
|
||||
if (a.departure_time) status = 'out';
|
||||
else if (a.break_start && !a.break_end) status = 'away';
|
||||
else { status = 'in'; presentCount++; }
|
||||
if (a.departure_time) status = "out";
|
||||
else if (a.break_start && !a.break_end) status = "away";
|
||||
else {
|
||||
status = "in";
|
||||
presentCount++;
|
||||
}
|
||||
}
|
||||
attendanceUsers.push({
|
||||
user_id: user.id,
|
||||
name: `${user.first_name} ${user.last_name}`,
|
||||
initials: `${firstInitial}${lastInitial}`.toUpperCase(),
|
||||
status,
|
||||
arrived_at: a.arrival_time ? `${String(a.arrival_time.getHours()).padStart(2, '0')}:${String(a.arrival_time.getMinutes()).padStart(2, '0')}` : null,
|
||||
arrived_at: a.arrival_time
|
||||
? `${String(a.arrival_time.getHours()).padStart(2, "0")}:${String(a.arrival_time.getMinutes()).padStart(2, "0")}`
|
||||
: null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,10 +120,11 @@ export default async function dashboardRoutes(fastify: FastifyInstance): Promise
|
||||
attendanceUsers.push({
|
||||
user_id: user.id,
|
||||
name: `${user.first_name} ${user.last_name}`,
|
||||
initials: `${user.first_name?.charAt(0) ?? ''}${user.last_name?.charAt(0) ?? ''}`.toUpperCase(),
|
||||
status: 'leave',
|
||||
initials:
|
||||
`${user.first_name?.charAt(0) ?? ""}${user.last_name?.charAt(0) ?? ""}`.toUpperCase(),
|
||||
status: "leave",
|
||||
arrived_at: null,
|
||||
leave_type: (a.leave_type as string) || 'vacation',
|
||||
leave_type: (a.leave_type as string) || "vacation",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -105,39 +138,49 @@ export default async function dashboardRoutes(fastify: FastifyInstance): Promise
|
||||
}
|
||||
|
||||
// Offers — only for offers.view
|
||||
if (has('offers.view')) {
|
||||
const [openCount, convertedCount, expiredCount, createdThisMonth] = await Promise.all([
|
||||
prisma.quotations.count({ where: { status: 'active' } }),
|
||||
prisma.quotations.count({ where: { status: 'converted' } }),
|
||||
prisma.quotations.count({ where: { status: 'expired' } }),
|
||||
prisma.quotations.count({ where: { created_at: { gte: monthStart, lt: monthEnd } } }),
|
||||
]);
|
||||
result.offers = { open_count: openCount, converted_count: convertedCount, expired_count: expiredCount, created_this_month: createdThisMonth };
|
||||
if (has("offers.view")) {
|
||||
const [openCount, convertedCount, expiredCount, createdThisMonth] =
|
||||
await Promise.all([
|
||||
prisma.quotations.count({ where: { status: "active" } }),
|
||||
prisma.quotations.count({ where: { status: "converted" } }),
|
||||
prisma.quotations.count({ where: { status: "expired" } }),
|
||||
prisma.quotations.count({
|
||||
where: { created_at: { gte: monthStart, lt: monthEnd } },
|
||||
}),
|
||||
]);
|
||||
result.offers = {
|
||||
open_count: openCount,
|
||||
converted_count: convertedCount,
|
||||
expired_count: expiredCount,
|
||||
created_this_month: createdThisMonth,
|
||||
};
|
||||
}
|
||||
|
||||
// Projects — only for projects.view
|
||||
if (has('projects.view')) {
|
||||
if (has("projects.view")) {
|
||||
const [activeCount, activeList] = await Promise.all([
|
||||
prisma.projects.count({ where: { status: 'aktivni' } }),
|
||||
prisma.projects.count({ where: { status: "aktivni" } }),
|
||||
prisma.projects.findMany({
|
||||
where: { status: 'aktivni' },
|
||||
where: { status: "aktivni" },
|
||||
include: { customers: { select: { name: true } } },
|
||||
orderBy: { created_at: 'desc' },
|
||||
orderBy: { created_at: "desc" },
|
||||
take: 5,
|
||||
}),
|
||||
]);
|
||||
result.active_projects = activeCount;
|
||||
result.projects = {
|
||||
active_projects: activeList.map(p => ({
|
||||
id: p.id, name: p.name ?? '', customer_name: p.customers?.name ?? null,
|
||||
active_projects: activeList.map((p) => ({
|
||||
id: p.id,
|
||||
name: p.name ?? "",
|
||||
customer_name: p.customers?.name ?? null,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
// Invoices — only for invoices.view
|
||||
if (has('invoices.view')) {
|
||||
if (has("invoices.view")) {
|
||||
const [unpaidCount, issuedThisMonth] = await Promise.all([
|
||||
prisma.invoices.count({ where: { status: 'issued' } }),
|
||||
prisma.invoices.count({ where: { status: "issued" } }),
|
||||
prisma.invoices.findMany({
|
||||
where: { issue_date: { gte: monthStart, lt: monthEnd } },
|
||||
include: { invoice_items: true },
|
||||
@@ -146,48 +189,70 @@ export default async function dashboardRoutes(fastify: FastifyInstance): Promise
|
||||
|
||||
const revenueByCurrency: Record<string, number> = {};
|
||||
for (const inv of issuedThisMonth) {
|
||||
const currency = inv.currency ?? 'CZK';
|
||||
const currency = inv.currency ?? "CZK";
|
||||
let total = 0;
|
||||
for (const item of inv.invoice_items) {
|
||||
total += (Number(item.quantity) || 0) * (Number(item.unit_price) || 0);
|
||||
total +=
|
||||
(Number(item.quantity) || 0) * (Number(item.unit_price) || 0);
|
||||
}
|
||||
revenueByCurrency[currency] = (revenueByCurrency[currency] ?? 0) + total;
|
||||
revenueByCurrency[currency] =
|
||||
(revenueByCurrency[currency] ?? 0) + total;
|
||||
}
|
||||
|
||||
result.invoices = {
|
||||
revenue_this_month: Object.entries(revenueByCurrency).map(([currency, amount]) => ({
|
||||
amount: Math.round(amount * 100) / 100, currency,
|
||||
})),
|
||||
revenue_this_month: Object.entries(revenueByCurrency).map(
|
||||
([currency, amount]) => ({
|
||||
amount: Math.round(amount * 100) / 100,
|
||||
currency,
|
||||
}),
|
||||
),
|
||||
unpaid_count: unpaidCount,
|
||||
revenue_czk: revenueByCurrency['CZK'] != null ? Math.round(revenueByCurrency['CZK'] * 100) / 100 : null,
|
||||
revenue_czk:
|
||||
revenueByCurrency["CZK"] != null
|
||||
? Math.round(revenueByCurrency["CZK"] * 100) / 100
|
||||
: null,
|
||||
};
|
||||
result.unpaid_invoices = unpaidCount;
|
||||
}
|
||||
|
||||
// Orders — only for orders.view
|
||||
if (has('orders.view')) {
|
||||
result.pending_orders = await prisma.orders.count({ where: { status: 'prijata' } });
|
||||
if (has("orders.view")) {
|
||||
result.pending_orders = await prisma.orders.count({
|
||||
where: { status: "prijata" },
|
||||
});
|
||||
}
|
||||
|
||||
// Leave pending — only for attendance.approve
|
||||
if (has('attendance.approve')) {
|
||||
const count = await prisma.leave_requests.count({ where: { status: 'pending' } });
|
||||
if (has("attendance.approve")) {
|
||||
const count = await prisma.leave_requests.count({
|
||||
where: { status: "pending" },
|
||||
});
|
||||
result.leave_pending = { count };
|
||||
result.pending_leave_requests = count;
|
||||
}
|
||||
|
||||
// Recent activity — only for settings.audit (admin)
|
||||
if (has('settings.audit')) {
|
||||
if (has("settings.audit")) {
|
||||
const logs = await prisma.audit_logs.findMany({
|
||||
orderBy: { created_at: 'desc' },
|
||||
orderBy: { created_at: "desc" },
|
||||
take: 8,
|
||||
where: { action: { in: ['create', 'update', 'delete', 'login'] } },
|
||||
select: { id: true, action: true, entity_type: true, description: true, username: true, created_at: true },
|
||||
where: { action: { in: ["create", "update", "delete", "login"] } },
|
||||
select: {
|
||||
id: true,
|
||||
action: true,
|
||||
entity_type: true,
|
||||
description: true,
|
||||
username: true,
|
||||
created_at: true,
|
||||
},
|
||||
});
|
||||
result.recent_activity = logs.map(log => ({
|
||||
id: log.id, action: log.action, entity_type: log.entity_type ?? '',
|
||||
description: log.description ?? '', username: log.username ?? null,
|
||||
created_at: log.created_at ? log.created_at.toISOString() : '',
|
||||
result.recent_activity = logs.map((log) => ({
|
||||
id: log.id,
|
||||
action: log.action,
|
||||
entity_type: log.entity_type ?? "",
|
||||
description: log.description ?? "",
|
||||
username: log.username ?? null,
|
||||
created_at: log.created_at ? log.created_at.toISOString() : "",
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user