425 lines
18 KiB
PHP
425 lines
18 KiB
PHP
<?php
|
|
session_start();
|
|
require 'db/config.php';
|
|
$db = db();
|
|
|
|
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
|
|
header('Location: index.php');
|
|
exit;
|
|
}
|
|
|
|
$userName = $_SESSION['user_name'];
|
|
$userId = $_SESSION['user_id'];
|
|
|
|
// Handle Job Creation
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['create_job'])) {
|
|
$name = $_POST['name'];
|
|
$qty = $_POST['quantity'];
|
|
$due = $_POST['due_date'];
|
|
$sn = $_POST['serial_number'];
|
|
|
|
try {
|
|
$stmt = $db->prepare("INSERT INTO jobs (name, quantity, due_date, serial_number) VALUES (?, ?, ?, ?)");
|
|
$stmt->execute([$name, $qty, empty($due) ? null : $due, empty($sn) ? null : $sn]);
|
|
$jobId = $db->lastInsertId();
|
|
header("Location: jobs.php?id=$jobId");
|
|
exit;
|
|
} catch (Exception $e) {
|
|
$error = "Error: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
// Handle Component Addition
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_component'])) {
|
|
$jobId = $_POST['job_id'];
|
|
$compName = $_POST['comp_name'];
|
|
$stmt = $db->prepare("INSERT INTO components (job_id, name) VALUES (?, ?)");
|
|
$stmt->execute([$jobId, $compName]);
|
|
header("Location: jobs.php?id=$jobId");
|
|
exit;
|
|
}
|
|
|
|
// Handle Operation Addition
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_op'])) {
|
|
$jobId = $_POST['job_id'];
|
|
$compId = $_POST['comp_id'];
|
|
$opName = $_POST['op_name'];
|
|
$proc = $_POST['process_type'];
|
|
$prio = $_POST['priority'];
|
|
$stmt = $db->prepare("INSERT INTO operations (component_id, name, process_type, priority) VALUES (?, ?, ?, ?)");
|
|
$stmt->execute([$compId, $opName, $proc, $prio]);
|
|
header("Location: jobs.php?id=$jobId");
|
|
exit;
|
|
}
|
|
|
|
$jobs = $db->query("SELECT * FROM jobs ORDER BY created_at DESC")->fetchAll();
|
|
|
|
$currentJobId = $_GET['id'] ?? null;
|
|
$currentJob = null;
|
|
$components = [];
|
|
|
|
if ($currentJobId) {
|
|
$stmt = $db->prepare("SELECT * FROM jobs WHERE id = ?");
|
|
$stmt->execute([$currentJobId]);
|
|
$currentJob = $stmt->fetch();
|
|
|
|
if ($currentJob) {
|
|
$stmt = $db->prepare("SELECT * FROM components WHERE job_id = ?");
|
|
$stmt->execute([$currentJobId]);
|
|
$components = $stmt->fetchAll();
|
|
|
|
foreach ($components as &$comp) {
|
|
$stmt = $db->prepare("SELECT * FROM operations WHERE component_id = ?");
|
|
$stmt->execute([$comp['id']]);
|
|
$comp['ops'] = $stmt->fetchAll();
|
|
}
|
|
}
|
|
}
|
|
|
|
?>
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>M-TRACK | Job Management</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root {
|
|
--sidebar-width: 240px;
|
|
--bg: #f8fafc;
|
|
--primary: #1e293b;
|
|
--accent: #3b82f6;
|
|
--text: #334155;
|
|
--border: #e2e8f0;
|
|
}
|
|
body {
|
|
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
|
background-color: var(--bg);
|
|
color: var(--text);
|
|
}
|
|
.sidebar {
|
|
width: var(--sidebar-width);
|
|
position: fixed;
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
background-color: var(--primary);
|
|
color: white;
|
|
z-index: 1000;
|
|
padding: 1.5rem 1rem;
|
|
}
|
|
.main-content {
|
|
margin-left: var(--sidebar-width);
|
|
padding: 2.5rem;
|
|
}
|
|
.sidebar h2 {
|
|
font-size: 1.25rem;
|
|
font-weight: 700;
|
|
margin-bottom: 2rem;
|
|
padding: 0 0.5rem;
|
|
letter-spacing: -0.025em;
|
|
}
|
|
.nav-pills .nav-link {
|
|
color: #94a3b8;
|
|
font-weight: 500;
|
|
font-size: 0.875rem;
|
|
padding: 0.625rem 0.75rem;
|
|
margin-bottom: 0.25rem;
|
|
border-radius: 4px;
|
|
}
|
|
.nav-pills .nav-link:hover {
|
|
color: white;
|
|
background-color: rgba(255,255,255,0.05);
|
|
}
|
|
.nav-pills .nav-link.active {
|
|
color: white;
|
|
background-color: var(--accent);
|
|
}
|
|
.card {
|
|
background: white;
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.card-header {
|
|
background: white;
|
|
border-bottom: 1px solid var(--border);
|
|
padding: 1rem 1.25rem;
|
|
font-weight: 600;
|
|
font-size: 0.875rem;
|
|
color: var(--primary);
|
|
}
|
|
.job-item {
|
|
padding: 0.75rem 1rem;
|
|
border-bottom: 1px solid var(--border);
|
|
cursor: pointer;
|
|
}
|
|
.job-item:hover { background: #f1f5f9; }
|
|
.job-item.active { background: #eff6ff; border-left: 3px solid var(--accent); }
|
|
.component-box {
|
|
background: #f8fafc;
|
|
border: 1px solid var(--border);
|
|
padding: 1rem;
|
|
border-radius: 4px;
|
|
margin-bottom: 1rem;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="sidebar">
|
|
<h2>M-TRACK</h2>
|
|
<nav class="nav nav-pills flex-column">
|
|
<a class="nav-link" href="dashboard.php"><i class="bi bi-grid-fill me-2"></i> Dashboard</a>
|
|
<a class="nav-link active" href="jobs.php"><i class="bi bi-briefcase me-2"></i> Jobs</a>
|
|
<a class="nav-link" href="shop_floor.php"><i class="bi bi-kanban me-2"></i> Shop Floor</a>
|
|
<a class="nav-link" href="inventory.php"><i class="bi bi-boxes me-2"></i> Inventory</a>
|
|
<a class="nav-link" href="users.php"><i class="bi bi-people me-2"></i> Users</a>
|
|
<a class="nav-link" href="time_study.php"><i class="bi bi-clock-history me-2"></i> Time Study</a>
|
|
<a class="nav-link" href="scan.php"><i class="bi bi-upc-scan me-2"></i> Scan</a>
|
|
<hr class="my-4 border-secondary opacity-25">
|
|
<a class="nav-link text-danger" href="logout.php"><i class="bi bi-box-arrow-right me-2"></i> Logout</a>
|
|
</nav>
|
|
</div>
|
|
|
|
<div class="main-content">
|
|
<?php if (!empty($error)): ?>
|
|
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
|
<?= htmlspecialchars($error) ?>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
<?php endif; ?>
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1>Job Management</h1>
|
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#newJobModal">
|
|
<i class="bi bi-plus-lg me-1"></i> New Job
|
|
</button>
|
|
<a href="bom_import.php" class="btn btn-outline-success ms-2">
|
|
<i class="bi bi-file-earmark-excel me-1"></i> Import BOM
|
|
</a>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-header">Existing Jobs</div>
|
|
<div class="card-body p-0" style="max-height: 70vh; overflow-y: auto;">
|
|
<?php if (empty($jobs)): ?>
|
|
<div class="p-4 text-center text-muted">No jobs found.</div>
|
|
<?php else: ?>
|
|
<?php foreach ($jobs as $job): ?>
|
|
<div class="job-item <?= $currentJobId == $job['id'] ? 'active' : '' ?>" onclick="location.href='jobs.php?id=<?= $job['id'] ?>'">
|
|
<div class="fw-bold"><?= htmlspecialchars($job['name']) ?></div>
|
|
<div class="small text-muted">SN: <?= htmlspecialchars($job['serial_number'] ?: 'N/A') ?> • <?= $job['status'] ?></div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-8">
|
|
<?php if ($currentJob): ?>
|
|
<div class="card mb-4">
|
|
<div class="card-header d-flex justify-content-between">
|
|
Job Details: <?= htmlspecialchars($currentJob['name']) ?>
|
|
<span class="badge bg-light text-dark border"><?= strtoupper($currentJob['status']) ?></span>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row small mb-3">
|
|
<div class="col-md-3"><strong>Serial:</strong> <?= htmlspecialchars($currentJob['serial_number']) ?></div>
|
|
<div class="col-md-3"><strong>Quantity:</strong> <?= $currentJob['quantity'] ?></div>
|
|
<div class="col-md-3"><strong>Due Date:</strong> <?= $currentJob['due_date'] ?></div>
|
|
<div class="col-md-3 text-end"><button class="btn btn-sm btn-outline-danger">Delete Job</button></div>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
<h6 class="mb-0 fw-bold">Components & Operations</h6>
|
|
<div>
|
|
<a href="bom_import.php?job_id=<?= $currentJobId ?>" class="btn btn-sm btn-outline-success me-2">
|
|
<i class="bi bi-file-earmark-excel me-1"></i> Upload BOM
|
|
</a>
|
|
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#newCompModal">
|
|
<i class="bi bi-plus me-1"></i> Add Component
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<?php foreach ($components as $comp): ?>
|
|
<div class="component-box">
|
|
<div class="d-flex justify-content-between mb-3">
|
|
<div class="d-flex align-items-center">
|
|
<?php if (!empty($comp["thumbnail_data"])): ?>
|
|
<img src="<?= $comp["thumbnail_data"] ?>" style="height:32px; width:32px; object-fit:cover; border-radius:4px; margin-right:10px; border:1px solid #dee2e6;">
|
|
<?php else: ?>
|
|
<div style="height:32px; width:32px; border-radius:4px; margin-right:10px; background:#e2e8f0; display:flex; align-items:center; justify-content:center; border:1px solid #cbd5e1;"><i class="bi bi-box" style="color:#94a3b8;"></i></div>
|
|
<?php endif; ?>
|
|
<div>
|
|
<h6 class="mb-0 fw-bold text-primary"><?= htmlspecialchars($comp["name"]) ?></h6>
|
|
<?php if(!empty($comp["material"]) || !empty($comp["thickness"])): ?>
|
|
<small class="text-muted"><?= htmlspecialchars($comp["material"]) ?> <?= htmlspecialchars($comp["thickness"]) ?></small>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
<button class="btn btn-sm btn-link text-primary p-0" onclick="setCompId(<?= $comp['id'] ?>)" data-bs-toggle="modal" data-bs-target="#newOpModal">
|
|
<i class="bi bi-plus-circle me-1"></i> Add Op
|
|
</button>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-bordered bg-white mb-0">
|
|
<thead class="table-light">
|
|
<tr class="small">
|
|
<th>Op Name</th>
|
|
<th>Process</th>
|
|
<th>Prio</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="small">
|
|
<?php if (empty($comp['ops'])): ?>
|
|
<tr><td colspan="4" class="text-center text-muted">No operations defined.</td></tr>
|
|
<?php else: ?>
|
|
<?php foreach ($comp['ops'] as $op): ?>
|
|
<tr>
|
|
<td><?= htmlspecialchars($op['name']) ?></td>
|
|
<td><?= strtoupper($op['process_type']) ?></td>
|
|
<td><?= $op['priority'] ?></td>
|
|
<td>
|
|
<span class="badge bg-<?= $op['status'] === 'completed' ? 'success' : ($op['status'] === 'in_progress' ? 'info' : 'secondary') ?>">
|
|
<?= $op['status'] ?>
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="card py-5 text-center text-muted">
|
|
<i class="bi bi-arrow-left h1 mb-3"></i>
|
|
<h5>Select a job from the list or create a new one.</h5>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- New Job Modal -->
|
|
<div class="modal fade" id="newJobModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<form method="POST" class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">New Job</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Job Name</label>
|
|
<input type="text" name="name" class="form-control" required placeholder="e.g. Project X">
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Quantity</label>
|
|
<input type="number" name="quantity" class="form-control" value="1">
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label">Due Date</label>
|
|
<input type="date" name="due_date" class="form-control">
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Serial Number</label>
|
|
<input type="text" name="serial_number" class="form-control" placeholder="SN-XXXX">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<a href="bom_import.php" class="btn btn-outline-success me-auto">Import BOM Instead</a>
|
|
<button type="submit" name="create_job" class="btn btn-primary">Create Job</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- New Component Modal -->
|
|
<div class="modal fade" id="newCompModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<form method="POST" class="modal-content">
|
|
<input type="hidden" name="job_id" value="<?= $currentJobId ?>">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Add Component</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Component Name</label>
|
|
<input type="text" name="comp_name" class="form-control" required placeholder="e.g. Chassis, Motor Mount">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" name="add_component" class="btn btn-primary">Add Component</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- New Operation Modal -->
|
|
<div class="modal fade" id="newOpModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<form method="POST" class="modal-content">
|
|
<input type="hidden" name="job_id" value="<?= $currentJobId ?>">
|
|
<input type="hidden" name="comp_id" id="modal_comp_id">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Add Operation</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Operation Name</label>
|
|
<input type="text" name="op_name" class="form-control" required placeholder="e.g. Laser Cut, MIG Weld">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Process Type</label>
|
|
<select name="process_type" class="form-select">
|
|
<option value="cutting">Cutting</option>
|
|
<option value="welding">Welding</option>
|
|
<option value="bending">Bending</option>
|
|
<option value="assembly">Assembly</option>
|
|
<option value="inspection">Inspection</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Priority (higher first)</label>
|
|
<input type="number" name="priority" class="form-control" value="0">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" name="add_op" class="btn btn-primary">Add Operation</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
function setCompId(id) {
|
|
document.getElementById('modal_comp_id').value = id;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|