diff --git a/api/get_labour_stats.php b/api/get_labour_stats.php new file mode 100644 index 0000000..a90a72c --- /dev/null +++ b/api/get_labour_stats.php @@ -0,0 +1,26 @@ +prepare(" + SELECT entry_date, SUM(hours) as total_hours + FROM labour_entries + WHERE employee_id = ? AND entry_date BETWEEN ? AND ? + GROUP BY entry_date + "); + $stmt->execute([$employee_id, $start_date, $end_date]); + echo json_encode($stmt->fetchAll()); +} catch (Exception $e) { + echo json_encode(['error' => $e->getMessage()]); +} diff --git a/api/search.php b/api/search.php index c966953..b47ee0f 100644 --- a/api/search.php +++ b/api/search.php @@ -15,13 +15,17 @@ try { $db = db(); // Search Projects - $stmt = $db->prepare("SELECT id, name, code FROM projects WHERE name LIKE ? OR code LIKE ? LIMIT 5"); + $stmt = $db->prepare("SELECT id, name, code, is_archived FROM projects WHERE name LIKE ? OR code LIKE ? LIMIT 5"); $stmt->execute(['%' . $q . '%', '%' . $q . '%']); foreach ($stmt->fetchAll() as $row) { + $label = $row['name'] . ' (' . $row['code'] . ')'; + if ($row['is_archived']) { + $label .= ' [ARCHIVED]'; + } $results[] = [ 'type' => 'Project', 'id' => $row['id'], - 'label' => $row['name'] . ' (' . $row['code'] . ')', + 'label' => $label, 'url' => 'project_detail.php?id=' . $row['id'] ]; } diff --git a/employee_detail.php b/employee_detail.php index c4fa2a1..1d2401a 100644 --- a/employee_detail.php +++ b/employee_detail.php @@ -27,15 +27,61 @@ $stmt = $db->prepare(" JOIN labour_types lt ON l.labour_type_id = lt.id WHERE l.employee_id = ? ORDER BY l.entry_date DESC - LIMIT 20 + LIMIT 10 "); $stmt->execute([$id]); -$entries = $stmt->fetchAll(); +$labourEntries = $stmt->fetchAll(); // Fetch summary stats -$stats = $db->prepare("SELECT SUM(hours) as total_hours, COUNT(*) as entry_count FROM labour_entries WHERE employee_id = ?"); -$stats->execute([$id]); +$stats = $db->prepare(" + SELECT + (SELECT SUM(hours) FROM labour_entries WHERE employee_id = ?) as total_hours, + (SELECT COUNT(DISTINCT project_id) FROM labour_entries WHERE employee_id = ?) as project_count, + (SELECT COUNT(*) FROM attachments a JOIN labour_entries le ON a.entity_id = le.id WHERE a.entity_type = 'labour_entry' AND le.employee_id = ?) as file_count +"); +$stats->execute([$id, $id, $id]); $stats = $stats->fetch(); + +// Fetch recent expenses (linked via attachments uploaded by this employee name) +$expenseStmt = $db->prepare(" + SELECT ex.*, et.name as expense_type, s.name as supplier_name, p.name as project_name + FROM expenses ex + JOIN expense_types et ON ex.expense_type_id = et.id + LEFT JOIN suppliers s ON ex.supplier_id = s.id + JOIN projects p ON ex.project_id = p.id + JOIN attachments a ON a.entity_id = ex.id AND a.entity_type = 'expense' + WHERE a.uploaded_by = ? + ORDER BY ex.entry_date DESC + LIMIT 10 +"); +$expenseStmt->execute([$employee['name']]); +$expenseEntries = $expenseStmt->fetchAll(); + +// Fetch recent files (uploaded by this employee or linked to their labour) +$fileStmt = $db->prepare(" + SELECT a.*, + COALESCE(le.entry_date, ex.entry_date) as entry_date, + COALESCE(p1.name, p2.name) as project_name + FROM attachments a + LEFT JOIN labour_entries le ON a.entity_id = le.id AND a.entity_type = 'labour_entry' + LEFT JOIN projects p1 ON le.project_id = p1.id + LEFT JOIN expenses ex ON a.entity_id = ex.id AND a.entity_type = 'expense' + LEFT JOIN projects p2 ON ex.project_id = p2.id + WHERE a.uploaded_by = ? OR (a.entity_type = 'labour_entry' AND le.employee_id = ?) + ORDER BY a.created_at DESC + LIMIT 10 +"); +$fileStmt->execute([$employee['name'], $id]); +$recentFiles = $fileStmt->fetchAll(); + +function formatBytes($bytes, $precision = 2) { + $units = ['B', 'KB', 'MB', 'GB', 'TB']; + $bytes = max($bytes, 0); + $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); + $pow = min($pow, count($units) - 1); + $bytes /= pow(1024, $pow); + return round($bytes, $precision) . ' ' . $units[$pow]; +} ?>
= htmlspecialchars($employee['position'] ?? 'Staff') ?>
+= htmlspecialchars($employee['position'] ?? 'Staff') ?> • Joined = $employee['start_date'] ? date('M j, Y', strtotime($employee['start_date'])) : 'N/A' ?>
| Date | +Project | +Type | +Hours | +
|---|---|---|---|
| No entries found. | |||
| = date('M j, Y', strtotime($le['entry_date'])) ?> | += htmlspecialchars($le['project_name']) ?> | += htmlspecialchars($le['labour_type']) ?> | += number_format($le['hours'], 1) ?> | +
| Date | +||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Date | Project | Type | -Hours | -Notes | +Amount | |||
|---|---|---|---|---|---|---|---|---|
| No entries found for this employee. | -||||||||
| No expenses captured by this employee. | ||||||||
| = date('Y-m-d', strtotime($e['entry_date'])) ?> | -= htmlspecialchars($e['project_name']) ?> | -= htmlspecialchars($e['labour_type']) ?> | -= number_format($e['hours'], 1) ?> | -= htmlspecialchars(substr($e['notes'], 0, 50)) ?>= strlen($e['notes']) > 50 ? '...' : '' ?> | += date('M j, Y', strtotime($ee['entry_date'])) ?> | += htmlspecialchars($ee['project_name']) ?> | += htmlspecialchars($ee['expense_type']) ?> | +$= number_format($ee['amount'], 2) ?> | +
| Filename | +Linked To | +Project | +Size | +Date | +Action | +
|---|---|---|---|---|---|
| No files found. | |||||
|
+
+
+ = htmlspecialchars($f['file_name']) ?>
+
+ |
+ + = ucfirst($f['entity_type']) ?> + = $f['entry_date'] ?> + | += htmlspecialchars($f['project_name'] ?? 'N/A') ?> | += formatBytes((int)$f['file_size']) ?> | += date('M j, Y', strtotime($f['created_at'])) ?> | ++ View + |
| = htmlspecialchars($e['first_name'] . ' ' . $e['last_name']) ?> | ++ + = htmlspecialchars($e['first_name'] . ' ' . $e['last_name']) ?> + + | = htmlspecialchars($e['position']) ?> | $= number_format((float)($e['current_wage'] ?? 0), 2) ?>/h | = $e['is_limited'] ? 'Limited' : 'Regular' ?> | - + View + |