Initial commit
This commit is contained in:
281
api/admin/dashboard.php
Normal file
281
api/admin/dashboard.php
Normal file
@@ -0,0 +1,281 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Dashboard API - agregovaná data pro dashboard
|
||||
*
|
||||
* GET /api/admin/dashboard.php
|
||||
* Vrací sekce dle oprávnění přihlášeného uživatele.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once dirname(__DIR__) . '/config.php';
|
||||
require_once dirname(__DIR__) . '/includes/JWTAuth.php';
|
||||
require_once dirname(__DIR__) . '/includes/AuditLog.php';
|
||||
require_once dirname(__DIR__) . '/includes/CnbRates.php';
|
||||
|
||||
setCorsHeaders();
|
||||
setSecurityHeaders();
|
||||
setNoCacheHeaders();
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$authData = JWTAuth::requireAuth();
|
||||
AuditLog::setUser($authData['user_id'], $authData['user']['username'] ?? 'unknown');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
http_response_code(204);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
errorResponse('Method not allowed', 405);
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
$result = [];
|
||||
|
||||
// --- Stav smeny aktualniho uzivatele (attendance.record) ---
|
||||
$userId = $authData['user_id'];
|
||||
if (hasPermission($authData, 'attendance.record')) {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id FROM attendance
|
||||
WHERE user_id = ? AND departure_time IS NULL AND (leave_type IS NULL OR leave_type = 'work')
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
");
|
||||
$stmt->execute([$userId]);
|
||||
$result['my_shift'] = [
|
||||
'has_ongoing' => (bool) $stmt->fetch(),
|
||||
];
|
||||
}
|
||||
|
||||
// --- Docházka dnes (attendance.admin) ---
|
||||
if (hasPermission($authData, 'attendance.admin')) {
|
||||
// Poslední pracovní záznam per uživatel (vyloučit ty co mají leave dnes)
|
||||
$stmt = $pdo->query("
|
||||
SELECT u.id, CONCAT(u.first_name, ' ', u.last_name) as name,
|
||||
CONCAT(LEFT(u.first_name, 1), LEFT(u.last_name, 1)) as initials,
|
||||
a.arrival_time, a.departure_time, a.break_start, a.break_end
|
||||
FROM users u
|
||||
LEFT JOIN (
|
||||
SELECT a1.*
|
||||
FROM attendance a1
|
||||
INNER JOIN (
|
||||
SELECT user_id, MAX(id) as max_id
|
||||
FROM attendance
|
||||
WHERE shift_date = CURDATE()
|
||||
AND (leave_type IS NULL OR leave_type = 'work')
|
||||
GROUP BY user_id
|
||||
) a2 ON a1.id = a2.max_id
|
||||
) a ON u.id = a.user_id
|
||||
WHERE u.is_active = 1
|
||||
AND u.id NOT IN (
|
||||
SELECT user_id FROM attendance
|
||||
WHERE shift_date = CURDATE() AND leave_type IN ('vacation', 'sick', 'holiday', 'unpaid')
|
||||
)
|
||||
ORDER BY a.arrival_time IS NULL, a.arrival_time ASC
|
||||
");
|
||||
$users = $stmt->fetchAll();
|
||||
|
||||
$present = 0;
|
||||
$away = 0;
|
||||
$attendanceUsers = [];
|
||||
|
||||
foreach ($users as $u) {
|
||||
$status = 'out';
|
||||
$arrivedAt = null;
|
||||
|
||||
if ($u['arrival_time'] !== null) {
|
||||
if ($u['departure_time'] !== null) {
|
||||
$status = 'out';
|
||||
} elseif ($u['break_start'] !== null && $u['break_end'] === null) {
|
||||
$status = 'away';
|
||||
$away++;
|
||||
} else {
|
||||
$status = 'in';
|
||||
$present++;
|
||||
}
|
||||
$arrivedAt = date('H:i', strtotime($u['arrival_time']));
|
||||
}
|
||||
|
||||
$attendanceUsers[] = [
|
||||
'name' => $u['name'],
|
||||
'initials' => $u['initials'],
|
||||
'status' => $status,
|
||||
'arrived_at' => $arrivedAt,
|
||||
];
|
||||
}
|
||||
|
||||
// Dnes na dovolene/nemocenske
|
||||
$stmtLeave = $pdo->query("
|
||||
SELECT CONCAT(u.first_name, ' ', u.last_name) as name,
|
||||
CONCAT(LEFT(u.first_name, 1), LEFT(u.last_name, 1)) as initials,
|
||||
a.leave_type
|
||||
FROM attendance a
|
||||
JOIN users u ON a.user_id = u.id
|
||||
WHERE a.shift_date = CURDATE() AND a.leave_type IN ('vacation', 'sick', 'holiday', 'unpaid')
|
||||
");
|
||||
$onLeave = $stmtLeave->fetchAll();
|
||||
|
||||
foreach ($onLeave as $leave) {
|
||||
$attendanceUsers[] = [
|
||||
'name' => $leave['name'],
|
||||
'initials' => $leave['initials'],
|
||||
'status' => 'leave',
|
||||
'arrived_at' => null,
|
||||
'leave_type' => $leave['leave_type'],
|
||||
];
|
||||
}
|
||||
|
||||
$result['attendance'] = [
|
||||
'present_today' => $present,
|
||||
'away_today' => $away,
|
||||
'total_active' => count($users),
|
||||
'on_leave' => count($onLeave),
|
||||
'users' => $attendanceUsers,
|
||||
];
|
||||
}
|
||||
|
||||
// --- Nabídky (offers.view) ---
|
||||
if (hasPermission($authData, 'offers.view')) {
|
||||
$stmt = $pdo->query("
|
||||
SELECT
|
||||
COUNT(*) as total,
|
||||
SUM(CASE WHEN q.order_id IS NULL
|
||||
AND (q.valid_until IS NULL OR q.valid_until >= CURDATE())
|
||||
THEN 1 ELSE 0 END) as open_count,
|
||||
SUM(CASE WHEN q.order_id IS NULL
|
||||
AND q.valid_until < CURDATE()
|
||||
THEN 1 ELSE 0 END) as expired_count,
|
||||
SUM(CASE WHEN q.order_id IS NOT NULL
|
||||
THEN 1 ELSE 0 END) as converted_count
|
||||
FROM quotations q
|
||||
");
|
||||
$counts = $stmt->fetch();
|
||||
|
||||
$stmtMonth = $pdo->query("
|
||||
SELECT COUNT(*) as count FROM quotations
|
||||
WHERE YEAR(created_at) = YEAR(CURDATE()) AND MONTH(created_at) = MONTH(CURDATE())
|
||||
");
|
||||
$monthData = $stmtMonth->fetch();
|
||||
|
||||
$result['offers'] = [
|
||||
'total' => (int) $counts['total'],
|
||||
'open_count' => (int) $counts['open_count'],
|
||||
'expired_count' => (int) $counts['expired_count'],
|
||||
'converted_count' => (int) $counts['converted_count'],
|
||||
'created_this_month' => (int) $monthData['count'],
|
||||
];
|
||||
}
|
||||
|
||||
// --- Projekty (projects.view) ---
|
||||
if (hasPermission($authData, 'projects.view')) {
|
||||
$stmt = $pdo->query("
|
||||
SELECT p.id, p.name, p.status, c.name as customer_name
|
||||
FROM projects p
|
||||
LEFT JOIN customers c ON p.customer_id = c.id
|
||||
WHERE p.status = 'aktivni'
|
||||
ORDER BY p.modified_at DESC
|
||||
LIMIT 5
|
||||
");
|
||||
$activeProjects = $stmt->fetchAll();
|
||||
|
||||
$stmtCounts = $pdo->query("
|
||||
SELECT
|
||||
SUM(CASE WHEN status = 'aktivni' THEN 1 ELSE 0 END) as active_count,
|
||||
SUM(CASE WHEN status = 'dokonceny' THEN 1 ELSE 0 END) as completed_count
|
||||
FROM projects WHERE status != 'deleted'
|
||||
");
|
||||
$projectCounts = $stmtCounts->fetch();
|
||||
|
||||
$result['projects'] = [
|
||||
'active_count' => (int) ($projectCounts['active_count'] ?? 0),
|
||||
'completed_count' => (int) ($projectCounts['completed_count'] ?? 0),
|
||||
'active_projects' => $activeProjects,
|
||||
];
|
||||
}
|
||||
|
||||
// --- Faktury (invoices.view) ---
|
||||
if (hasPermission($authData, 'invoices.view')) {
|
||||
$stmt = $pdo->query("
|
||||
SELECT
|
||||
COUNT(*) as total,
|
||||
SUM(CASE WHEN i.status = 'paid'
|
||||
AND YEAR(i.paid_date) = YEAR(CURDATE())
|
||||
AND MONTH(i.paid_date) = MONTH(CURDATE())
|
||||
THEN 1 ELSE 0 END) as paid_this_month,
|
||||
SUM(CASE WHEN i.status IN ('issued', 'overdue')
|
||||
THEN 1 ELSE 0 END) as unpaid_count
|
||||
FROM invoices i
|
||||
");
|
||||
$invCounts = $stmt->fetch();
|
||||
|
||||
// Tržby tento měsíc per faktura (pro kurz k datu vystaveni)
|
||||
$stmtRevenue = $pdo->query("
|
||||
SELECT i.id, i.currency, i.issue_date,
|
||||
COALESCE(SUM(ii.quantity * ii.unit_price), 0) as revenue
|
||||
FROM invoices i
|
||||
JOIN invoice_items ii ON i.id = ii.invoice_id
|
||||
WHERE i.status = 'paid'
|
||||
AND YEAR(i.paid_date) = YEAR(CURDATE())
|
||||
AND MONTH(i.paid_date) = MONTH(CURDATE())
|
||||
GROUP BY i.id, i.currency, i.issue_date
|
||||
ORDER BY revenue DESC
|
||||
");
|
||||
|
||||
$revByCurrency = [];
|
||||
$revCzkItems = [];
|
||||
foreach ($stmtRevenue->fetchAll() as $row) {
|
||||
$cur = $row['currency'];
|
||||
$amt = (float) $row['revenue'];
|
||||
$revByCurrency[$cur] = ($revByCurrency[$cur] ?? 0) + $amt;
|
||||
$revCzkItems[] = [
|
||||
'amount' => $amt,
|
||||
'currency' => $cur,
|
||||
'date' => $row['issue_date'],
|
||||
];
|
||||
}
|
||||
|
||||
$revenueByCurrency = [];
|
||||
foreach ($revByCurrency as $cur => $total) {
|
||||
$revenueByCurrency[] = [
|
||||
'currency' => $cur,
|
||||
'amount' => round($total, 2),
|
||||
];
|
||||
}
|
||||
|
||||
$cnb = CnbRates::getInstance();
|
||||
|
||||
$result['invoices'] = [
|
||||
'total' => (int) $invCounts['total'],
|
||||
'paid_this_month' => (int) $invCounts['paid_this_month'],
|
||||
'unpaid_count' => (int) $invCounts['unpaid_count'],
|
||||
'revenue_this_month' => $revenueByCurrency,
|
||||
'revenue_czk' => $cnb->sumToCzk($revCzkItems),
|
||||
];
|
||||
}
|
||||
|
||||
// --- Čekající žádosti (attendance.approve) ---
|
||||
if (hasPermission($authData, 'attendance.approve')) {
|
||||
$stmt = $pdo->query("
|
||||
SELECT COUNT(*) as count FROM leave_requests WHERE status = 'pending'
|
||||
");
|
||||
$pending = $stmt->fetch();
|
||||
|
||||
$result['leave_pending'] = [
|
||||
'count' => (int) $pending['count'],
|
||||
];
|
||||
}
|
||||
|
||||
// --- Poslední aktivita (settings.roles = admin přehled) ---
|
||||
if (hasPermission($authData, 'settings.roles')) {
|
||||
$stmt = $pdo->query("
|
||||
SELECT username, action, entity_type, description, created_at
|
||||
FROM audit_logs
|
||||
WHERE action IN ('create', 'update', 'delete', 'login')
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 8
|
||||
");
|
||||
$result['recent_activity'] = $stmt->fetchAll();
|
||||
}
|
||||
|
||||
jsonResponse($result);
|
||||
Reference in New Issue
Block a user