- Backend: Node.js/TypeScript with Prisma ORM - Frontend: Vite + TypeScript - Project configuration and documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
334 lines
8.7 KiB
TypeScript
334 lines
8.7 KiB
TypeScript
import { Response } from 'express';
|
|
import prisma from '../config/database';
|
|
import { successResponse, errorResponse } from '../utils/helpers';
|
|
import { AuthRequest } from '../middleware/auth.middleware';
|
|
|
|
// Hlavný dashboard endpoint - štatistiky pre karty
|
|
export const getDashboard = async (_req: AuthRequest, res: Response): Promise<void> => {
|
|
try {
|
|
const today = new Date();
|
|
const nextMonth = new Date(today);
|
|
nextMonth.setDate(nextMonth.getDate() + 30);
|
|
|
|
const [
|
|
totalProjects,
|
|
activeProjects,
|
|
totalTasks,
|
|
pendingTasks,
|
|
inProgressTasks,
|
|
totalCustomers,
|
|
activeCustomers,
|
|
totalEquipment,
|
|
upcomingRevisions,
|
|
totalRMAs,
|
|
pendingRMAs,
|
|
] = await Promise.all([
|
|
prisma.project.count(),
|
|
prisma.project.count({ where: { status: { isFinal: false } } }),
|
|
prisma.task.count(),
|
|
prisma.task.count({ where: { status: { code: 'NEW' } } }),
|
|
prisma.task.count({ where: { status: { code: 'IN_PROGRESS' } } }),
|
|
prisma.customer.count(),
|
|
prisma.customer.count({ where: { active: true } }),
|
|
prisma.equipment.count({ where: { active: true } }),
|
|
prisma.revision.count({
|
|
where: {
|
|
nextDueDate: {
|
|
gte: today,
|
|
lte: nextMonth,
|
|
},
|
|
},
|
|
}),
|
|
prisma.rMA.count(),
|
|
prisma.rMA.count({ where: { status: { isFinal: false } } }),
|
|
]);
|
|
|
|
successResponse(res, {
|
|
projects: {
|
|
total: totalProjects,
|
|
active: activeProjects,
|
|
},
|
|
tasks: {
|
|
total: totalTasks,
|
|
pending: pendingTasks,
|
|
inProgress: inProgressTasks,
|
|
},
|
|
customers: {
|
|
total: totalCustomers,
|
|
active: activeCustomers,
|
|
},
|
|
equipment: {
|
|
total: totalEquipment,
|
|
upcomingRevisions,
|
|
},
|
|
rma: {
|
|
total: totalRMAs,
|
|
pending: pendingRMAs,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('Error fetching dashboard:', error);
|
|
errorResponse(res, 'Chyba pri načítaní dashboardu.', 500);
|
|
}
|
|
};
|
|
|
|
export const getDashboardToday = async (req: AuthRequest, res: Response): Promise<void> => {
|
|
try {
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
const tomorrow = new Date(today);
|
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
|
|
const userId = req.user!.userId;
|
|
|
|
const [myTasks, myProjects, recentRMAs] = await Promise.all([
|
|
// Tasks assigned to me that are not completed
|
|
prisma.task.findMany({
|
|
where: {
|
|
assignees: { some: { userId } },
|
|
status: { isFinal: false },
|
|
},
|
|
include: {
|
|
status: true,
|
|
priority: true,
|
|
project: { select: { id: true, name: true } },
|
|
createdBy: { select: { id: true, name: true } },
|
|
assignees: { include: { user: { select: { id: true, name: true } } } },
|
|
},
|
|
orderBy: [{ priority: { level: 'desc' } }, { deadline: 'asc' }],
|
|
take: 10,
|
|
}),
|
|
|
|
// My active projects
|
|
prisma.project.findMany({
|
|
where: {
|
|
OR: [
|
|
{ ownerId: userId },
|
|
{ members: { some: { userId } } },
|
|
],
|
|
status: { isFinal: false },
|
|
},
|
|
include: {
|
|
status: true,
|
|
_count: { select: { tasks: true } },
|
|
},
|
|
take: 5,
|
|
}),
|
|
|
|
// Recent RMAs
|
|
prisma.rMA.findMany({
|
|
where: {
|
|
OR: [
|
|
{ assignedToId: userId },
|
|
{ createdById: userId },
|
|
],
|
|
},
|
|
include: {
|
|
status: true,
|
|
},
|
|
orderBy: { createdAt: 'desc' },
|
|
take: 5,
|
|
}),
|
|
]);
|
|
|
|
successResponse(res, {
|
|
myTasks,
|
|
myProjects,
|
|
recentRMAs,
|
|
});
|
|
} catch (error) {
|
|
console.error('Error fetching dashboard today:', error);
|
|
errorResponse(res, 'Chyba pri načítaní dashboardu.', 500);
|
|
}
|
|
};
|
|
|
|
export const getDashboardWeek = async (req: AuthRequest, res: Response): Promise<void> => {
|
|
try {
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
const nextWeek = new Date(today);
|
|
nextWeek.setDate(nextWeek.getDate() + 7);
|
|
|
|
const userId = req.user!.userId;
|
|
|
|
const [tasksDeadlineThisWeek, upcomingRevisions] = await Promise.all([
|
|
// Tasks with deadline this week
|
|
prisma.task.findMany({
|
|
where: {
|
|
deadline: {
|
|
gte: today,
|
|
lte: nextWeek,
|
|
},
|
|
status: { isFinal: false },
|
|
OR: [
|
|
{ createdById: userId },
|
|
{ assignees: { some: { userId } } },
|
|
],
|
|
},
|
|
include: {
|
|
status: true,
|
|
priority: true,
|
|
project: { select: { id: true, name: true } },
|
|
assignees: { include: { user: { select: { id: true, name: true } } } },
|
|
},
|
|
orderBy: { deadline: 'asc' },
|
|
}),
|
|
|
|
// Upcoming equipment revisions
|
|
prisma.revision.findMany({
|
|
where: {
|
|
nextDueDate: {
|
|
gte: today,
|
|
lte: nextWeek,
|
|
},
|
|
},
|
|
include: {
|
|
equipment: {
|
|
include: {
|
|
type: true,
|
|
customer: { select: { id: true, name: true } },
|
|
},
|
|
},
|
|
type: true,
|
|
},
|
|
orderBy: { nextDueDate: 'asc' },
|
|
}),
|
|
]);
|
|
|
|
successResponse(res, {
|
|
tasksDeadlineThisWeek,
|
|
upcomingRevisions,
|
|
});
|
|
} catch (error) {
|
|
console.error('Error fetching dashboard week:', error);
|
|
errorResponse(res, 'Chyba pri načítaní týždenného prehľadu.', 500);
|
|
}
|
|
};
|
|
|
|
export const getDashboardStats = async (req: AuthRequest, res: Response): Promise<void> => {
|
|
try {
|
|
const [
|
|
totalProjects,
|
|
activeProjects,
|
|
totalTasks,
|
|
completedTasks,
|
|
totalCustomers,
|
|
totalEquipment,
|
|
totalRMAs,
|
|
openRMAs,
|
|
] = await Promise.all([
|
|
prisma.project.count(),
|
|
prisma.project.count({ where: { status: { isFinal: false } } }),
|
|
prisma.task.count(),
|
|
prisma.task.count({ where: { status: { isFinal: true } } }),
|
|
prisma.customer.count({ where: { active: true } }),
|
|
prisma.equipment.count({ where: { active: true } }),
|
|
prisma.rMA.count(),
|
|
prisma.rMA.count({ where: { status: { isFinal: false } } }),
|
|
]);
|
|
|
|
successResponse(res, {
|
|
projects: {
|
|
total: totalProjects,
|
|
active: activeProjects,
|
|
},
|
|
tasks: {
|
|
total: totalTasks,
|
|
completed: completedTasks,
|
|
completionRate: totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0,
|
|
},
|
|
customers: {
|
|
total: totalCustomers,
|
|
},
|
|
equipment: {
|
|
total: totalEquipment,
|
|
},
|
|
rmas: {
|
|
total: totalRMAs,
|
|
open: openRMAs,
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error('Error fetching dashboard stats:', error);
|
|
errorResponse(res, 'Chyba pri načítaní štatistík.', 500);
|
|
}
|
|
};
|
|
|
|
export const getDashboardReminders = async (req: AuthRequest, res: Response): Promise<void> => {
|
|
try {
|
|
const today = new Date();
|
|
const nextMonth = new Date(today);
|
|
nextMonth.setDate(nextMonth.getDate() + 30);
|
|
|
|
const userId = req.user!.userId;
|
|
|
|
const [taskReminders, equipmentRevisions, overdueRMAs] = await Promise.all([
|
|
// Task reminders
|
|
prisma.reminder.findMany({
|
|
where: {
|
|
userId,
|
|
dismissed: false,
|
|
remindAt: {
|
|
lte: nextMonth,
|
|
},
|
|
},
|
|
include: {
|
|
task: {
|
|
include: {
|
|
status: true,
|
|
project: { select: { id: true, name: true } },
|
|
},
|
|
},
|
|
},
|
|
orderBy: { remindAt: 'asc' },
|
|
}),
|
|
|
|
// Equipment revision reminders
|
|
prisma.revision.findMany({
|
|
where: {
|
|
nextDueDate: {
|
|
gte: today,
|
|
lte: nextMonth,
|
|
},
|
|
},
|
|
include: {
|
|
equipment: {
|
|
include: {
|
|
type: true,
|
|
customer: { select: { id: true, name: true } },
|
|
},
|
|
},
|
|
type: true,
|
|
},
|
|
orderBy: { nextDueDate: 'asc' },
|
|
take: 10,
|
|
}),
|
|
|
|
// Overdue or old RMAs
|
|
prisma.rMA.findMany({
|
|
where: {
|
|
status: { isFinal: false },
|
|
createdAt: {
|
|
lte: new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000), // Older than 7 days
|
|
},
|
|
},
|
|
include: {
|
|
status: true,
|
|
customer: { select: { id: true, name: true } },
|
|
},
|
|
orderBy: { createdAt: 'asc' },
|
|
take: 10,
|
|
}),
|
|
]);
|
|
|
|
successResponse(res, {
|
|
taskReminders,
|
|
equipmentRevisions,
|
|
overdueRMAs,
|
|
});
|
|
} catch (error) {
|
|
console.error('Error fetching dashboard reminders:', error);
|
|
errorResponse(res, 'Chyba pri načítaní upomienok.', 500);
|
|
}
|
|
};
|