update to filtering

This commit is contained in:
Flatlogic Bot 2026-02-15 15:54:39 +00:00
parent 353a0f1f3b
commit 037f8c27be
5 changed files with 350 additions and 66 deletions

57
api/export_expenses.php Normal file
View File

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../db/config.php';
$tenant_id = 1;
$filter_project = (int)($_GET['project_id'] ?? 0);
$filter_supplier = (int)($_GET['supplier_id'] ?? 0);
$filter_start = $_GET['start_date'] ?? '';
$filter_end = $_GET['end_date'] ?? '';
$where = ["e.tenant_id = ?"];
$params = [$tenant_id];
if ($filter_project) {
$where[] = "e.project_id = ?";
$params[] = $filter_project;
}
if ($filter_supplier) {
$where[] = "e.supplier_id = ?";
$params[] = $filter_supplier;
}
if ($filter_start) {
$where[] = "e.entry_date >= ?";
$params[] = $filter_start;
}
if ($filter_end) {
$where[] = "e.entry_date <= ?";
$params[] = $filter_end;
}
$where_clause = implode(" AND ", $where);
$stmt = db()->prepare("
SELECT e.entry_date, s.name as supplier_name, p.name as project_name, e.amount, e.allocation_percent, et.name as expense_type, e.notes
FROM expenses e
JOIN projects p ON e.project_id = p.id
JOIN suppliers s ON e.supplier_id = s.id
LEFT JOIN expense_types et ON e.expense_type_id = et.id
WHERE $where_clause
ORDER BY e.entry_date DESC
");
$stmt->execute($params);
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
$filename_suffix = date('Y-m-d_H-i-s');
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=expenses_export_' . $filename_suffix . '.csv');
$output = fopen('php://output', 'w');
fputcsv($output, ['Date', 'Supplier', 'Project', 'Amount', 'Allocation %', 'Expense Type', 'Notes']);
foreach ($data as $row) {
fputcsv($output, $row);
}
fclose($output);
exit;

68
api/export_labour.php Normal file
View File

@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../db/config.php';
$tenant_id = 1;
$filter_project = (int)($_GET['project_id'] ?? 0);
$filter_employee = (int)($_GET['employee_id'] ?? 0);
$filter_type = (int)($_GET['labour_type_id'] ?? 0);
$filter_evidence = (int)($_GET['evidence_type_id'] ?? 0);
$filter_start = $_GET['start_date'] ?? '';
$filter_end = $_GET['end_date'] ?? '';
$where = ["le.tenant_id = ?"];
$params = [$tenant_id];
if ($filter_project) {
$where[] = "le.project_id = ?";
$params[] = $filter_project;
}
if ($filter_employee) {
$where[] = "le.employee_id = ?";
$params[] = $filter_employee;
}
if ($filter_type) {
$where[] = "le.labour_type_id = ?";
$params[] = $filter_type;
}
if ($filter_evidence) {
$where[] = "le.evidence_type_id = ?";
$params[] = $filter_evidence;
}
if ($filter_start) {
$where[] = "le.entry_date >= ?";
$params[] = $filter_start;
}
if ($filter_end) {
$where[] = "le.entry_date <= ?";
$params[] = $filter_end;
}
$where_clause = implode(" AND ", $where);
$stmt = db()->prepare("
SELECT le.entry_date, e.name as employee_name, p.name as project_name, le.hours, lt.name as labour_type, et.name as evidence_type, le.notes
FROM labour_entries le
JOIN projects p ON le.project_id = p.id
JOIN employees e ON le.employee_id = e.id
LEFT JOIN labour_types lt ON le.labour_type_id = lt.id
LEFT JOIN evidence_types et ON le.evidence_type_id = et.id
WHERE $where_clause
ORDER BY le.entry_date DESC
");
$stmt->execute($params);
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
$filename_suffix = date('Y-m-d_H-i-s');
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=labour_export_' . $filename_suffix . '.csv');
$output = fopen('php://output', 'w');
fputcsv($output, ['Date', 'Employee', 'Project', 'Hours', 'Labour Type', 'Evidence Type', 'Notes']);
foreach ($data as $row) {
fputcsv($output, $row);
}
fclose($output);
exit;

View File

@ -48,16 +48,43 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_expense'])) {
}
// Fetch Data
$filter_project = (int)($_GET['project_id'] ?? 0);
$filter_supplier = (int)($_GET['supplier_id'] ?? 0);
$filter_start = $_GET['start_date'] ?? '';
$filter_end = $_GET['end_date'] ?? '';
$where = ["e.tenant_id = ?"];
$params = [$tenant_id];
if ($filter_project) {
$where[] = "e.project_id = ?";
$params[] = $filter_project;
}
if ($filter_supplier) {
$where[] = "e.supplier_id = ?";
$params[] = $filter_supplier;
}
if ($filter_start) {
$where[] = "e.entry_date >= ?";
$params[] = $filter_start;
}
if ($filter_end) {
$where[] = "e.entry_date <= ?";
$params[] = $filter_end;
}
$where_clause = implode(" AND ", $where);
$expenseEntries = db()->prepare("
SELECT e.*, p.name as project_name, s.name as supplier_name, et.name as expense_type
FROM expenses e
JOIN projects p ON e.project_id = p.id
JOIN suppliers s ON e.supplier_id = s.id
LEFT JOIN expense_types et ON e.expense_type_id = et.id
WHERE e.tenant_id = ?
WHERE $where_clause
ORDER BY e.entry_date DESC, e.created_at DESC
");
$expenseEntries->execute([$tenant_id]);
$expenseEntries->execute($params);
$expenseList = $expenseEntries->fetchAll();
$projects = db()->prepare("SELECT id, name FROM projects WHERE tenant_id = ? AND is_archived = 0 ORDER BY name");
@ -79,7 +106,52 @@ include __DIR__ . '/includes/header.php';
<div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="fw-bold mb-0">Expenses</h2>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addExpenseModal">+ Add Expense</button>
<div>
<a href="api/export_expenses.php?<?= http_build_query($_GET) ?>" class="btn btn-outline-success me-2"><i class="bi bi-file-earmark-excel me-1"></i> Export to Excel</a>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addExpenseModal">+ Add Expense</button>
</div>
</div>
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<form method="GET" class="row g-2 align-items-end">
<div class="col-md-3">
<label class="form-label small fw-bold">Project</label>
<select name="project_id" class="form-select form-select-sm">
<option value="">All Projects</option>
<?php
$allProjects = db()->prepare("SELECT id, name FROM projects WHERE tenant_id = ? ORDER BY name");
$allProjects->execute([$tenant_id]);
foreach ($allProjects->fetchAll() as $p): ?>
<option value="<?= $p['id'] ?>" <?= $filter_project == $p['id'] ? 'selected' : '' ?>><?= htmlspecialchars($p['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3">
<label class="form-label small fw-bold">Supplier</label>
<select name="supplier_id" class="form-select form-select-sm">
<option value="">All Suppliers</option>
<?php foreach ($supplierList as $s): ?>
<option value="<?= $s['id'] ?>" <?= $filter_supplier == $s['id'] ? 'selected' : '' ?>><?= htmlspecialchars($s['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2">
<label class="form-label small fw-bold">From</label>
<input type="date" name="start_date" class="form-control form-control-sm" value="<?= htmlspecialchars($filter_start) ?>">
</div>
<div class="col-md-2">
<label class="form-label small fw-bold">To</label>
<input type="date" name="end_date" class="form-control form-control-sm" value="<?= htmlspecialchars($filter_end) ?>">
</div>
<div class="col-md-2">
<div class="d-flex gap-2">
<button type="submit" class="btn btn-sm btn-primary w-100">Filter</button>
<a href="expenses.php" class="btn btn-sm btn-outline-secondary w-100">Reset</a>
</div>
</div>
</form>
</div>
</div>
<?php if (isset($_GET['success'])): ?>

View File

@ -82,6 +82,43 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_labour'])) {
}
// Fetch Data
$filter_project = (int)($_GET['project_id'] ?? 0);
$filter_employee = (int)($_GET['employee_id'] ?? 0);
$filter_type = (int)($_GET['labour_type_id'] ?? 0);
$filter_evidence = (int)($_GET['evidence_type_id'] ?? 0);
$filter_start = $_GET['start_date'] ?? '';
$filter_end = $_GET['end_date'] ?? '';
$where = ["le.tenant_id = ?"];
$params = [$tenant_id];
if ($filter_project) {
$where[] = "le.project_id = ?";
$params[] = $filter_project;
}
if ($filter_employee) {
$where[] = "le.employee_id = ?";
$params[] = $filter_employee;
}
if ($filter_type) {
$where[] = "le.labour_type_id = ?";
$params[] = $filter_type;
}
if ($filter_evidence) {
$where[] = "le.evidence_type_id = ?";
$params[] = $filter_evidence;
}
if ($filter_start) {
$where[] = "le.entry_date >= ?";
$params[] = $filter_start;
}
if ($filter_end) {
$where[] = "le.entry_date <= ?";
$params[] = $filter_end;
}
$where_clause = implode(" AND ", $where);
$labourEntries = db()->prepare("
SELECT le.*, p.name as project_name, e.name as employee_name, lt.name as labour_type, et.name as evidence_type
FROM labour_entries le
@ -89,10 +126,10 @@ $labourEntries = db()->prepare("
JOIN employees e ON le.employee_id = e.id
LEFT JOIN labour_types lt ON le.labour_type_id = lt.id
LEFT JOIN evidence_types et ON le.evidence_type_id = et.id
WHERE le.tenant_id = ?
WHERE $where_clause
ORDER BY le.entry_date DESC, le.created_at DESC
");
$labourEntries->execute([$tenant_id]);
$labourEntries->execute($params);
$labourList = $labourEntries->fetchAll();
$projects = db()->prepare("SELECT id, name FROM projects WHERE tenant_id = ? AND is_archived = 0 ORDER BY name");
@ -141,11 +178,72 @@ include __DIR__ . '/includes/header.php';
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="fw-bold mb-0">Labour Tracking</h2>
<div>
<a href="api/export_labour.php?<?= http_build_query($_GET) ?>" class="btn btn-outline-success me-2"><i class="bi bi-file-earmark-excel me-1"></i> Export to Excel</a>
<button class="btn btn-outline-primary me-2" data-bs-toggle="modal" data-bs-target="#bulkLabourModal"><i class="bi bi-calendar3 me-1"></i> Bulk Add</button>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addLabourModal">+ Add Labour Entry</button>
</div>
</div>
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<form method="GET" class="row g-2 align-items-end">
<div class="col-md-2">
<label class="form-label small fw-bold">Project</label>
<select name="project_id" class="form-select form-select-sm">
<option value="">All Projects</option>
<?php
$allProjects = db()->prepare("SELECT id, name FROM projects WHERE tenant_id = ? ORDER BY name");
$allProjects->execute([$tenant_id]);
foreach ($allProjects->fetchAll() as $p): ?>
<option value="<?= $p['id'] ?>" <?= $filter_project == $p['id'] ? 'selected' : '' ?>><?= htmlspecialchars($p['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2">
<label class="form-label small fw-bold">Employee</label>
<select name="employee_id" class="form-select form-select-sm">
<option value="">All Employees</option>
<?php foreach ($employeeList as $e): ?>
<option value="<?= $e['id'] ?>" <?= $filter_employee == $e['id'] ? 'selected' : '' ?>><?= htmlspecialchars($e['first_name'] . ' ' . $e['last_name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2">
<label class="form-label small fw-bold">Labour Type</label>
<select name="labour_type_id" class="form-select form-select-sm">
<option value="">All Types</option>
<?php foreach ($labourTypeList as $lt): ?>
<option value="<?= $lt['id'] ?>" <?= $filter_type == $lt['id'] ? 'selected' : '' ?>><?= htmlspecialchars($lt['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2">
<label class="form-label small fw-bold">Evidence</label>
<select name="evidence_type_id" class="form-select form-select-sm">
<option value="">All Evidence</option>
<?php foreach ($evidenceTypeList as $et): ?>
<option value="<?= $et['id'] ?>" <?= $filter_evidence == $et['id'] ? 'selected' : '' ?>><?= htmlspecialchars($et['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-1">
<label class="form-label small fw-bold">From</label>
<input type="date" name="start_date" class="form-control form-control-sm" value="<?= htmlspecialchars($filter_start) ?>">
</div>
<div class="col-md-1">
<label class="form-label small fw-bold">To</label>
<input type="date" name="end_date" class="form-control form-control-sm" value="<?= htmlspecialchars($filter_end) ?>">
</div>
<div class="col-md-2">
<div class="d-flex gap-1">
<button type="submit" class="btn btn-sm btn-primary flex-grow-1">Filter</button>
<a href="labour.php" class="btn btn-sm btn-outline-secondary">Reset</a>
</div>
</div>
</form>
</div>
</div>
<?php if (isset($_GET['success'])): ?>
<div class="alert alert-success alert-dismissible fade show border-0 shadow-sm mb-4" role="alert">
Labour entry successfully saved.

View File

@ -107,7 +107,6 @@ include __DIR__ . '/includes/header.php';
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="fw-bold mb-0">Projects</h2>
<div class="d-flex gap-2">
<button class="btn btn-outline-secondary" onclick="toggleFilters()"><i class="bi bi-funnel"></i> Filter</button>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addProjectModal">+ New Project</button>
</div>
</div>
@ -131,6 +130,56 @@ include __DIR__ . '/includes/header.php';
</div>
<?php endif; ?>
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<form method="GET" class="row g-3 align-items-end">
<div class="col-md-3">
<label class="form-label small fw-bold">Search</label>
<div class="input-group input-group-sm">
<span class="input-group-text bg-white"><i class="bi bi-search"></i></span>
<input type="text" name="search" class="form-control" placeholder="Project name or code..." value="<?= htmlspecialchars($search) ?>">
</div>
</div>
<div class="col-md-2">
<label class="form-label small fw-bold">Status</label>
<select name="status" class="form-select form-select-sm">
<option value="">All Statuses</option>
<option value="active" <?= $status_filter === 'active' ? 'selected' : '' ?>>Active</option>
<option value="on_hold" <?= $status_filter === 'on_hold' ? 'selected' : '' ?>>On Hold</option>
<option value="completed" <?= $status_filter === 'completed' ? 'selected' : '' ?>>Completed</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label small fw-bold">Start Date</label>
<div class="d-flex gap-2">
<select name="date_preset" class="form-select form-select-sm" onchange="handleDatePreset(this.value)" style="width: 140px;">
<option value="">Any Time</option>
<option value="this_month" <?= $date_preset === 'this_month' ? 'selected' : '' ?>>This Month</option>
<option value="this_year" <?= $date_preset === 'this_year' ? 'selected' : '' ?>>This Year</option>
<option value="custom" <?= $date_preset === 'custom' ? 'selected' : '' ?>>Custom...</option>
</select>
<div id="customDateRange" class="d-flex gap-1 <?= $date_preset === 'custom' ? '' : 'd-none' ?>">
<input type="date" name="start_from" class="form-control form-control-sm" value="<?= htmlspecialchars($start_from) ?>" placeholder="From">
<input type="date" name="start_to" class="form-control form-control-sm" value="<?= htmlspecialchars($start_to) ?>" placeholder="To">
</div>
</div>
</div>
<div class="col-md-2">
<div class="form-check form-switch mb-1">
<input class="form-check-input" type="checkbox" name="include_archived" id="includeArchived" value="1" <?= $include_archived ? 'checked' : '' ?>>
<label class="form-check-label small fw-bold" for="includeArchived">Archived</label>
</div>
</div>
<div class="col-md-2 text-end">
<div class="d-flex gap-2">
<button type="submit" class="btn btn-sm btn-primary flex-grow-1">Filter</button>
<a href="projects.php" class="btn btn-sm btn-outline-secondary">Reset</a>
</div>
</div>
</form>
</div>
</div>
<div class="row position-relative">
<div class="col-12">
<div class="card border-0 shadow-sm">
@ -198,62 +247,6 @@ include __DIR__ . '/includes/header.php';
</div>
</div>
</div>
<!-- Filter Sidebar (Absolute or slide-in) -->
<div class="filter-sidebar shadow-sm" id="projectFilterSidebar" style="display:none; position: absolute; top: 0; right: 0; z-index: 1050; width: 300px;">
<div class="card border-0">
<div class="card-header bg-light d-flex justify-content-between align-items-center">
<span class="fw-bold">FILTERS</span>
<button type="button" class="btn-close" onclick="toggleFilters()"></button>
</div>
<div class="card-body">
<form method="GET">
<div class="mb-3">
<label class="form-label small fw-bold">Search</label>
<input type="text" name="search" class="form-control form-control-sm" value="<?= htmlspecialchars($search) ?>">
</div>
<div class="mb-3">
<label class="form-label small fw-bold">Status</label>
<select name="status" class="form-select form-select-sm">
<option value="">All Statuses</option>
<option value="active" <?= $status_filter === 'active' ? 'selected' : '' ?>>Active</option>
<option value="on_hold" <?= $status_filter === 'on_hold' ? 'selected' : '' ?>>On Hold</option>
<option value="completed" <?= $status_filter === 'completed' ? 'selected' : '' ?>>Completed</option>
</select>
</div>
<div class="mb-3">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="include_archived" id="includeArchived" value="1" <?= $include_archived ? 'checked' : '' ?>>
<label class="form-check-label small" for="includeArchived">Include Archived</label>
</div>
</div>
<div class="mb-3">
<label class="form-label small fw-bold">Start Date</label>
<select name="date_preset" class="form-select form-select-sm" onchange="handleDatePreset(this.value)">
<option value="">Any Time</option>
<option value="this_month" <?= $date_preset === 'this_month' ? 'selected' : '' ?>>This Month</option>
<option value="this_year" <?= $date_preset === 'this_year' ? 'selected' : '' ?>>This Year</option>
<option value="custom" <?= $date_preset === 'custom' ? 'selected' : '' ?>>Custom Range...</option>
</select>
</div>
<div id="customDateRange" class="<?= $date_preset === 'custom' ? '' : 'd-none' ?>">
<div class="mb-2">
<label class="form-label extra-small text-muted">From</label>
<input type="date" name="start_from" class="form-control form-control-sm" value="<?= htmlspecialchars($start_from) ?>">
</div>
<div class="mb-3">
<label class="form-label extra-small text-muted">To</label>
<input type="date" name="start_to" class="form-control form-control-sm" value="<?= htmlspecialchars($start_to) ?>">
</div>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-sm btn-primary">Apply Filters</button>
<a href="projects.php" class="btn btn-sm btn-link text-decoration-none">Clear</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@ -311,10 +304,6 @@ include __DIR__ . '/includes/header.php';
</div>
<script>
function toggleFilters() {
const sidebar = document.getElementById('projectFilterSidebar');
sidebar.style.display = sidebar.style.display === 'none' ? 'block' : 'none';
}
function handleDatePreset(val) {
const custom = document.getElementById('customDateRange');
if (val === 'custom') {