259 lines
9.9 KiB
PHP
259 lines
9.9 KiB
PHP
<?php
|
|
session_start();
|
|
require 'db/config.php';
|
|
|
|
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
|
|
header('Location: index.php');
|
|
exit;
|
|
}
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>M-TRACK | Import BOM</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.5/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;
|
|
left: 0;
|
|
height: 100vh;
|
|
background: var(--primary);
|
|
color: white;
|
|
padding: 2rem 1rem;
|
|
overflow-y: auto;
|
|
}
|
|
.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);
|
|
}
|
|
.drop-zone {
|
|
border: 2px dashed #3b82f6;
|
|
border-radius: 10px;
|
|
padding: 40px;
|
|
text-align: center;
|
|
background: white;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
.drop-zone.dragover {
|
|
background: #eff6ff;
|
|
border-color: #2563eb;
|
|
}
|
|
.drop-zone i {
|
|
font-size: 3rem;
|
|
color: #3b82f6;
|
|
}
|
|
#file-input { display: none; }
|
|
.card { border: 1px solid var(--border); box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
</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">
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h1>Import BOM</h1>
|
|
<a href="jobs.php" class="btn btn-outline-secondary"><i class="bi bi-arrow-left"></i> Back to Jobs</a>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-8 col-lg-6">
|
|
<div class="card bg-white p-4">
|
|
<?php $existingJobId = $_GET["job_id"] ?? ""; ?>
|
|
<form id="bomImportForm">
|
|
<?php if ($existingJobId): ?>
|
|
<input type="hidden" id="jobId" value="<?= htmlspecialchars($existingJobId) ?>">
|
|
<div class="alert alert-info">
|
|
<strong><i class="bi bi-info-circle"></i> Importing into existing job (ID: <?= htmlspecialchars($existingJobId) ?>)</strong>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="mb-3">
|
|
<label class="form-label fw-bold">Job Name (Optional)</label>
|
|
<input type="text" id="jobName" class="form-control" placeholder="Leave blank to use filename">
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="mb-4">
|
|
<label class="form-label fw-bold">BOM Excel File (.xlsx)</label>
|
|
<div class="drop-zone" id="dropZone" onclick="document.getElementById('file-input').click()">
|
|
<i class="bi bi-cloud-arrow-up mb-3 d-block"></i>
|
|
<h5>Click to select or drag and drop here</h5>
|
|
<p class="text-muted mb-0">Only .xlsx files are supported</p>
|
|
<input type="file" id="file-input" accept=".xlsx" required>
|
|
</div>
|
|
<div id="file-name-display" class="mt-2 text-primary fw-bold text-center" style="display:none;"></div>
|
|
</div>
|
|
|
|
<div class="d-grid">
|
|
<button type="submit" class="btn btn-primary btn-lg" id="uploadBtn" disabled>
|
|
<i class="bi bi-upload"></i> Import BOM Now
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
<div id="uploadStatus" class="mt-4" style="display:none;">
|
|
<div class="alert alert-info d-flex align-items-center">
|
|
<div class="spinner-border spinner-border-sm me-3" role="status"></div>
|
|
<div>Parsing Excel file and extracting images natively... Please wait.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="uploadError" class="mt-4 alert alert-danger" style="display:none;"></div>
|
|
<div id="uploadSuccess" class="mt-4 alert alert-success" style="display:none;"></div>
|
|
</div>
|
|
|
|
<div class="card mt-4 p-3 bg-light text-muted small">
|
|
<h6 class="fw-bold mb-2"><i class="bi bi-info-circle text-info"></i> How it works</h6>
|
|
<ul class="mb-0 ps-3">
|
|
<li>The system parses the <strong>Item</strong> column (e.g. 1, 1.6, 1.6.1) to build the assembly tree perfectly matching the Replit behavior.</li>
|
|
<li>Extracts standard fields like Part Number, QTY, Thickness, Material, and Description.</li>
|
|
<li>Natively extracts embedded thumbnail images from the Excel archive and attaches them!</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const fileInput = document.getElementById('file-input');
|
|
const dropZone = document.getElementById('dropZone');
|
|
const fileNameDisplay = document.getElementById('file-name-display');
|
|
const uploadBtn = document.getElementById('uploadBtn');
|
|
const form = document.getElementById('bomImportForm');
|
|
const uploadStatus = document.getElementById('uploadStatus');
|
|
const uploadError = document.getElementById('uploadError');
|
|
const uploadSuccess = document.getElementById('uploadSuccess');
|
|
|
|
dropZone.addEventListener('dragover', (e) => {
|
|
e.preventDefault();
|
|
dropZone.classList.add('dragover');
|
|
});
|
|
dropZone.addEventListener('dragleave', () => {
|
|
dropZone.classList.remove('dragover');
|
|
});
|
|
dropZone.addEventListener('drop', (e) => {
|
|
e.preventDefault();
|
|
dropZone.classList.remove('dragover');
|
|
if (e.dataTransfer.files.length) {
|
|
fileInput.files = e.dataTransfer.files;
|
|
handleFileSelect();
|
|
}
|
|
});
|
|
|
|
fileInput.addEventListener('change', handleFileSelect);
|
|
|
|
function handleFileSelect() {
|
|
if (fileInput.files.length > 0) {
|
|
fileNameDisplay.textContent = 'Selected: ' + fileInput.files[0].name;
|
|
fileNameDisplay.style.display = 'block';
|
|
uploadBtn.disabled = false;
|
|
}
|
|
}
|
|
|
|
form.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
if (fileInput.files.length === 0) return;
|
|
|
|
uploadBtn.disabled = true;
|
|
uploadStatus.style.display = 'block';
|
|
uploadError.style.display = 'none';
|
|
uploadSuccess.style.display = 'none';
|
|
|
|
const formData = new FormData();
|
|
formData.append('bomFile', fileInput.files[0]);
|
|
|
|
const jobIdEl = document.getElementById('jobId');
|
|
if (jobIdEl) {
|
|
formData.append('jobId', jobIdEl.value);
|
|
}
|
|
|
|
const jobNameEl = document.getElementById('jobName');
|
|
if (jobNameEl && jobNameEl.value.trim()) {
|
|
formData.append('jobName', jobNameEl.value.trim());
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('api/import_bom.php', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (response.ok && result.success) {
|
|
uploadStatus.style.display = 'none';
|
|
uploadSuccess.textContent = result.message + ' Redirecting...';
|
|
uploadSuccess.style.display = 'block';
|
|
|
|
setTimeout(() => {
|
|
window.location.href = 'jobs.php?id=' + result.job_id;
|
|
}, 1500);
|
|
} else {
|
|
throw new Error(result.error || 'Upload failed');
|
|
}
|
|
} catch (err) {
|
|
uploadStatus.style.display = 'none';
|
|
uploadBtn.disabled = false;
|
|
uploadError.textContent = err.message;
|
|
uploadError.style.display = 'block';
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|