KENANGA APPS 01
This commit is contained in:
parent
3ffbcb7ced
commit
f44439d5ee
File diff suppressed because one or more lines are too long
@ -1,39 +1,15 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const chatForm = document.getElementById('chat-form');
|
||||
const chatInput = document.getElementById('chat-input');
|
||||
const chatMessages = document.getElementById('chat-messages');
|
||||
document.querySelectorAll('.toast').forEach((toastEl) => {
|
||||
if (window.bootstrap) {
|
||||
const toast = new bootstrap.Toast(toastEl, { delay: 4200 });
|
||||
toast.show();
|
||||
}
|
||||
});
|
||||
|
||||
const appendMessage = (text, sender) => {
|
||||
const msgDiv = document.createElement('div');
|
||||
msgDiv.classList.add('message', sender);
|
||||
msgDiv.textContent = text;
|
||||
chatMessages.appendChild(msgDiv);
|
||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
};
|
||||
|
||||
chatForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const message = chatInput.value.trim();
|
||||
if (!message) return;
|
||||
|
||||
appendMessage(message, 'visitor');
|
||||
chatInput.value = '';
|
||||
|
||||
try {
|
||||
const response = await fetch('api/chat.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ message })
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
// Artificial delay for realism
|
||||
setTimeout(() => {
|
||||
appendMessage(data.reply, 'bot');
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
appendMessage("Sorry, something went wrong. Please try again.", 'bot');
|
||||
}
|
||||
const moneyInputs = document.querySelectorAll('.money-input');
|
||||
moneyInputs.forEach((input) => {
|
||||
input.addEventListener('input', () => {
|
||||
if (Number(input.value) < 0) input.value = '0';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
460
index.php
460
index.php
@ -1,150 +1,332 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
@ini_set('display_errors', '1');
|
||||
@error_reporting(E_ALL);
|
||||
@date_default_timezone_set('UTC');
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
@date_default_timezone_set('Asia/Jakarta');
|
||||
|
||||
$phpVersion = PHP_VERSION;
|
||||
$now = date('Y-m-d H:i:s');
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Sistem manajemen percetakan Kenanga Kreasindo untuk order, SPK, produksi, invoice, dan tracking customer.';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
$projectName = $_SERVER['PROJECT_NAME'] ?? 'Kenanga Kreasindo Printing';
|
||||
|
||||
function e(string $value): string { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); }
|
||||
function rupiah(float $value): string { return 'Rp ' . number_format($value, 0, ',', '.'); }
|
||||
function nextNumber(PDO $pdo, string $prefix, string $column): string {
|
||||
$stmt = $pdo->prepare("SELECT COUNT(*) AS total FROM print_orders WHERE $column LIKE :prefix");
|
||||
$stmt->execute([':prefix' => $prefix . '%']);
|
||||
$count = (int)($stmt->fetch()['total'] ?? 0) + 1;
|
||||
return $prefix . str_pad((string)$count, 5, '0', STR_PAD_LEFT);
|
||||
}
|
||||
function statusBadge(string $status): string {
|
||||
return match ($status) {
|
||||
'Order diterima' => 'secondary',
|
||||
'SPK dibuat' => 'info',
|
||||
'Produksi berjalan' => 'primary',
|
||||
'QC / Finishing' => 'warning',
|
||||
'Barang selesai' => 'success',
|
||||
'Invoice / Tagihan' => 'dark',
|
||||
default => 'secondary',
|
||||
};
|
||||
}
|
||||
function ensureSchema(PDO $pdo): void {
|
||||
$pdo->exec("CREATE TABLE IF NOT EXISTS print_orders (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
order_no VARCHAR(40) NOT NULL UNIQUE,
|
||||
spk_no VARCHAR(40) NOT NULL UNIQUE,
|
||||
invoice_no VARCHAR(40) NOT NULL UNIQUE,
|
||||
customer_name VARCHAR(140) NOT NULL,
|
||||
customer_phone VARCHAR(40) NOT NULL,
|
||||
customer_email VARCHAR(160) NULL,
|
||||
project_name VARCHAR(180) NOT NULL,
|
||||
category VARCHAR(100) NOT NULL,
|
||||
quantity INT UNSIGNED NOT NULL DEFAULT 1,
|
||||
size_info VARCHAR(120) NULL,
|
||||
material VARCHAR(160) NULL,
|
||||
finishing VARCHAR(180) NULL,
|
||||
deadline DATE NULL,
|
||||
operator_name VARCHAR(120) NULL,
|
||||
cs_name VARCHAR(120) NULL,
|
||||
status VARCHAR(40) NOT NULL DEFAULT 'Order diterima',
|
||||
progress_note TEXT NULL,
|
||||
unit_price DECIMAL(14,2) NOT NULL DEFAULT 0,
|
||||
discount DECIMAL(14,2) NOT NULL DEFAULT 0,
|
||||
paid_amount DECIMAL(14,2) NOT NULL DEFAULT 0,
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_phone (customer_phone),
|
||||
INDEX idx_status (status)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
|
||||
}
|
||||
|
||||
$pdo = db();
|
||||
ensureSchema($pdo);
|
||||
$errors = [];
|
||||
$notice = '';
|
||||
$createdId = 0;
|
||||
$statuses = ['Order diterima', 'SPK dibuat', 'Produksi berjalan', 'QC / Finishing', 'Barang selesai', 'Invoice / Tagihan'];
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
if ($action === 'create_order') {
|
||||
$customerName = trim((string)($_POST['customer_name'] ?? ''));
|
||||
$customerPhone = preg_replace('/[^0-9+]/', '', (string)($_POST['customer_phone'] ?? ''));
|
||||
$customerEmail = trim((string)($_POST['customer_email'] ?? ''));
|
||||
$project = trim((string)($_POST['project_name'] ?? ''));
|
||||
$category = trim((string)($_POST['category'] ?? ''));
|
||||
$qty = max(1, (int)($_POST['quantity'] ?? 1));
|
||||
$deadline = trim((string)($_POST['deadline'] ?? '')) ?: null;
|
||||
$unitPrice = max(0, (float)($_POST['unit_price'] ?? 0));
|
||||
$discount = max(0, (float)($_POST['discount'] ?? 0));
|
||||
$paid = max(0, (float)($_POST['paid_amount'] ?? 0));
|
||||
|
||||
foreach ([
|
||||
'Nama customer' => $customerName,
|
||||
'Nomor telepon' => $customerPhone,
|
||||
'Nama pekerjaan' => $project,
|
||||
'Kategori' => $category,
|
||||
] as $label => $value) {
|
||||
if ($value === '') { $errors[] = $label . ' wajib diisi.'; }
|
||||
}
|
||||
if ($customerEmail !== '' && !filter_var($customerEmail, FILTER_VALIDATE_EMAIL)) { $errors[] = 'Format email customer tidak valid.'; }
|
||||
|
||||
if (!$errors) {
|
||||
$year = date('y');
|
||||
$month = date('m');
|
||||
$orderNo = nextNumber($pdo, 'ORD-' . date('Y') . '-', 'order_no');
|
||||
$invoiceNo = nextNumber($pdo, 'VI-' . $year . '-', 'invoice_no');
|
||||
$spkPrefix = str_pad((string)(((int)$pdo->query('SELECT COUNT(*) FROM print_orders')->fetchColumn()) + 1), 2, '0', STR_PAD_LEFT) . '-SPK-' . $month . '-' . date('Y');
|
||||
$spkNo = $spkPrefix;
|
||||
|
||||
$stmt = $pdo->prepare('INSERT INTO print_orders (order_no, spk_no, invoice_no, customer_name, customer_phone, customer_email, project_name, category, quantity, size_info, material, finishing, deadline, operator_name, cs_name, status, progress_note, unit_price, discount, paid_amount) VALUES (:order_no, :spk_no, :invoice_no, :customer_name, :customer_phone, :customer_email, :project_name, :category, :quantity, :size_info, :material, :finishing, :deadline, :operator_name, :cs_name, :status, :progress_note, :unit_price, :discount, :paid_amount)');
|
||||
$stmt->execute([
|
||||
':order_no' => $orderNo,
|
||||
':spk_no' => $spkNo,
|
||||
':invoice_no' => $invoiceNo,
|
||||
':customer_name' => $customerName,
|
||||
':customer_phone' => $customerPhone,
|
||||
':customer_email' => $customerEmail ?: null,
|
||||
':project_name' => $project,
|
||||
':category' => $category,
|
||||
':quantity' => $qty,
|
||||
':size_info' => trim((string)($_POST['size_info'] ?? '')) ?: null,
|
||||
':material' => trim((string)($_POST['material'] ?? '')) ?: null,
|
||||
':finishing' => trim((string)($_POST['finishing'] ?? '')) ?: null,
|
||||
':deadline' => $deadline,
|
||||
':operator_name' => trim((string)($_POST['operator_name'] ?? '')) ?: null,
|
||||
':cs_name' => trim((string)($_POST['cs_name'] ?? '')) ?: null,
|
||||
':status' => 'SPK dibuat',
|
||||
':progress_note' => trim((string)($_POST['progress_note'] ?? 'Order diterima dan SPK siap dicetak.')),
|
||||
':unit_price' => $unitPrice,
|
||||
':discount' => $discount,
|
||||
':paid_amount' => $paid,
|
||||
]);
|
||||
$createdId = (int)$pdo->lastInsertId();
|
||||
$notice = 'Order berhasil dibuat. SPK dan Invoice sudah otomatis tersedia.';
|
||||
}
|
||||
} elseif ($action === 'update_status') {
|
||||
$id = (int)($_POST['id'] ?? 0);
|
||||
$status = (string)($_POST['status'] ?? '');
|
||||
$note = trim((string)($_POST['progress_note'] ?? ''));
|
||||
if ($id > 0 && in_array($status, $statuses, true)) {
|
||||
$stmt = $pdo->prepare('UPDATE print_orders SET status = :status, progress_note = :note WHERE id = :id');
|
||||
$stmt->execute([':status' => $status, ':note' => $note, ':id' => $id]);
|
||||
$notice = 'Status produksi berhasil diperbarui.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$trackResult = null;
|
||||
if (isset($_GET['track_order'], $_GET['track_phone'])) {
|
||||
$trackOrder = trim((string)$_GET['track_order']);
|
||||
$trackPhone = preg_replace('/[^0-9+]/', '', (string)$_GET['track_phone']);
|
||||
$stmt = $pdo->prepare('SELECT * FROM print_orders WHERE order_no = :order_no AND customer_phone = :phone LIMIT 1');
|
||||
$stmt->execute([':order_no' => $trackOrder, ':phone' => $trackPhone]);
|
||||
$trackResult = $stmt->fetch() ?: false;
|
||||
}
|
||||
|
||||
$orders = $pdo->query('SELECT * FROM print_orders ORDER BY created_at DESC LIMIT 25')->fetchAll();
|
||||
$totalOrders = (int)$pdo->query('SELECT COUNT(*) FROM print_orders')->fetchColumn();
|
||||
$activeOrders = (int)$pdo->query("SELECT COUNT(*) FROM print_orders WHERE status NOT IN ('Barang selesai','Invoice / Tagihan')")->fetchColumn();
|
||||
$finishedOrders = (int)$pdo->query("SELECT COUNT(*) FROM print_orders WHERE status = 'Barang selesai'")->fetchColumn();
|
||||
$finance = $pdo->query('SELECT COALESCE(SUM(quantity * unit_price - discount),0) AS billed, COALESCE(SUM(paid_amount),0) AS paid FROM print_orders')->fetch();
|
||||
$receivable = max(0, (float)$finance['billed'] - (float)$finance['paid']);
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="id">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>New Style</title>
|
||||
<?php
|
||||
// Read project preview data from environment
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
<?php if ($projectDescription): ?>
|
||||
<!-- Meta description -->
|
||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||
<!-- Open Graph meta tags -->
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<!-- Twitter meta tags -->
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<!-- Open Graph image -->
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<!-- Twitter image -->
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-color-start: #6a11cb;
|
||||
--bg-color-end: #2575fc;
|
||||
--text-color: #ffffff;
|
||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||
color: var(--text-color);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 100vh;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
body::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
||||
animation: bg-pan 20s linear infinite;
|
||||
z-index: -1;
|
||||
}
|
||||
@keyframes bg-pan {
|
||||
0% { background-position: 0% 0%; }
|
||||
100% { background-position: 100% 100%; }
|
||||
}
|
||||
main {
|
||||
padding: 2rem;
|
||||
}
|
||||
.card {
|
||||
background: var(--card-bg-color);
|
||||
border: 1px solid var(--card-border-color);
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.loader {
|
||||
margin: 1.25rem auto 1.25rem;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
||||
border-top-color: #fff;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
.hint {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px; height: 1px;
|
||||
padding: 0; margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap; border: 0;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
margin: 0 0 1rem;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
p {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
code {
|
||||
background: rgba(0,0,0,0.2);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
}
|
||||
footer {
|
||||
position: absolute;
|
||||
bottom: 1rem;
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
</style>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><?= e($projectName) ?> — Order, SPK, Produksi & Invoice</title>
|
||||
<meta name="description" content="<?= e($projectDescription) ?>">
|
||||
<meta property="og:title" content="<?= e($projectName) ?>">
|
||||
<meta property="og:description" content="<?= e($projectDescription) ?>">
|
||||
<meta property="twitter:title" content="<?= e($projectName) ?>">
|
||||
<meta property="twitter:description" content="<?= e($projectDescription) ?>">
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<meta property="og:image" content="<?= e($projectImageUrl) ?>">
|
||||
<meta property="twitter:image" content="<?= e($projectImageUrl) ?>">
|
||||
<?php endif; ?>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?= time() ?>">
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="card">
|
||||
<h1>Analyzing your requirements and generating your website…</h1>
|
||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||
<span class="sr-only">Loading…</span>
|
||||
<nav class="navbar navbar-expand-lg kk-navbar sticky-top">
|
||||
<div class="container-fluid px-3 px-lg-4">
|
||||
<a class="navbar-brand d-flex align-items-center gap-2" href="#top" aria-label="Kenanga Kreasindo Home">
|
||||
<span class="brand-mark">K</span><span>Kenanga Kreasindo</span>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu" aria-controls="navMenu" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
|
||||
<div class="collapse navbar-collapse" id="navMenu">
|
||||
<div class="navbar-nav ms-auto gap-lg-2">
|
||||
<a class="nav-link" href="#order">Order</a>
|
||||
<a class="nav-link" href="#produksi">Produksi</a>
|
||||
<a class="nav-link" href="#tracking">Tracking Customer</a>
|
||||
<a class="nav-link" href="#laporan">Keuangan</a>
|
||||
</div>
|
||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main id="top" class="container-fluid px-3 px-lg-4 py-4">
|
||||
<?php if ($notice || $errors): ?>
|
||||
<div class="toast-container position-fixed top-0 end-0 p-3">
|
||||
<div class="toast show" role="status" aria-live="polite" aria-atomic="true">
|
||||
<div class="toast-header"><strong class="me-auto"><?= $errors ? 'Validasi belum lengkap' : 'Berhasil' ?></strong><button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button></div>
|
||||
<div class="toast-body"><?= $errors ? e(implode(' ', $errors)) : e($notice) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="hero panel mb-4" aria-labelledby="heroTitle">
|
||||
<div class="row g-4 align-items-center">
|
||||
<div class="col-lg-7">
|
||||
<p class="eyebrow mb-2">Sistem Percetakan</p>
|
||||
<h1 id="heroTitle">Order masuk, SPK, produksi, invoice, dan tracking customer dalam satu alur.</h1>
|
||||
<p class="hero-copy mb-4">MVP awal untuk Percetakan Kenanga Kreasindo: admin membuat order, sistem menerbitkan SPK dan invoice, produksi memperbarui status, customer mengecek progres memakai nomor order + telepon.</p>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a href="#order" class="btn btn-dark btn-lg">Buat Order & SPK</a>
|
||||
<a href="#tracking" class="btn btn-outline-dark btn-lg">Cek Status Customer</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<div class="company-card">
|
||||
<div class="small text-muted">Percetakan</div>
|
||||
<h2 class="h4 mb-3">Kenanga Kreasindo</h2>
|
||||
<div class="meta-row"><span>Alamat</span><strong>Perum BCL Jln Kenanga Raya</strong></div>
|
||||
<div class="meta-row"><span>Email</span><strong>kenangakreasindo@gmail.com</strong></div>
|
||||
<div class="meta-row"><span>Runtime</span><strong>PHP <?= e(PHP_VERSION) ?></strong></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="laporan" class="row g-3 mb-4" aria-label="Ringkasan laporan">
|
||||
<div class="col-6 col-lg-3"><div class="stat-card"><span>Total Order</span><strong><?= $totalOrders ?></strong></div></div>
|
||||
<div class="col-6 col-lg-3"><div class="stat-card"><span>Produksi Aktif</span><strong><?= $activeOrders ?></strong></div></div>
|
||||
<div class="col-6 col-lg-3"><div class="stat-card"><span>Barang Selesai</span><strong><?= $finishedOrders ?></strong></div></div>
|
||||
<div class="col-6 col-lg-3"><div class="stat-card"><span>Piutang</span><strong><?= e(rupiah($receivable)) ?></strong></div></div>
|
||||
</section>
|
||||
|
||||
<div class="row g-4">
|
||||
<section id="order" class="col-xl-5" aria-labelledby="orderTitle">
|
||||
<div class="panel h-100">
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">Penerimaan Order</p>
|
||||
<h2 id="orderTitle">Buat order + SPK</h2>
|
||||
</div>
|
||||
<form method="post" class="kk-form" novalidate>
|
||||
<input type="hidden" name="action" value="create_order">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6"><label class="form-label">Nama Customer *</label><input class="form-control" name="customer_name" required></div>
|
||||
<div class="col-md-6"><label class="form-label">Telepon *</label><input class="form-control" name="customer_phone" required placeholder="08..."></div>
|
||||
<div class="col-12"><label class="form-label">Email</label><input class="form-control" name="customer_email" type="email" placeholder="opsional"></div>
|
||||
<div class="col-md-8"><label class="form-label">Nama Pekerjaan *</label><input class="form-control" name="project_name" required placeholder="Contoh: Banner Grand Opening"></div>
|
||||
<div class="col-md-4"><label class="form-label">Kategori *</label><select class="form-select" name="category" required><option value="Digital Printing">Digital Printing</option><option value="Offset">Offset</option><option value="Merchandise">Merchandise</option><option value="Apparel">Apparel</option><option value="Lainnya">Lainnya</option></select></div>
|
||||
<div class="col-md-4"><label class="form-label">Qty *</label><input class="form-control" name="quantity" type="number" min="1" value="1" required></div>
|
||||
<div class="col-md-4"><label class="form-label">Ukuran</label><input class="form-control" name="size_info" placeholder="A3 / 3x1 m"></div>
|
||||
<div class="col-md-4"><label class="form-label">Deadline</label><input class="form-control" name="deadline" type="date"></div>
|
||||
<div class="col-md-6"><label class="form-label">Bahan</label><input class="form-control" name="material" placeholder="Flexi 280 / Art carton"></div>
|
||||
<div class="col-md-6"><label class="form-label">Finishing</label><input class="form-control" name="finishing" placeholder="Laminasi, potong, mata ayam"></div>
|
||||
<div class="col-md-6"><label class="form-label">CS</label><input class="form-control" name="cs_name" placeholder="Nama CS"></div>
|
||||
<div class="col-md-6"><label class="form-label">Operator</label><input class="form-control" name="operator_name" placeholder="Nama operator"></div>
|
||||
<div class="col-md-4"><label class="form-label">Harga / Unit</label><input class="form-control money-input" name="unit_price" type="number" min="0" value="0"></div>
|
||||
<div class="col-md-4"><label class="form-label">Diskon</label><input class="form-control money-input" name="discount" type="number" min="0" value="0"></div>
|
||||
<div class="col-md-4"><label class="form-label">Terbayar</label><input class="form-control money-input" name="paid_amount" type="number" min="0" value="0"></div>
|
||||
<div class="col-12"><label class="form-label">Detail Pesanan / Catatan SPK</label><textarea class="form-control" name="progress_note" rows="3" placeholder="Instruksi file, warna, referensi, catatan operator..."></textarea></div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center mt-4">
|
||||
<small class="text-muted">* wajib diisi. Nomor order, SPK, dan invoice otomatis.</small>
|
||||
<button class="btn btn-dark" type="submit">Simpan Order</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="produksi" class="col-xl-7" aria-labelledby="productionTitle">
|
||||
<div class="panel h-100">
|
||||
<div class="section-heading d-flex justify-content-between align-items-start gap-3">
|
||||
<div><p class="eyebrow">Produksi & Monitoring</p><h2 id="productionTitle">Daftar order terbaru</h2></div>
|
||||
<?php if ($createdId): ?><a class="btn btn-outline-dark btn-sm" href="order_detail.php?id=<?= $createdId ?>">Lihat order baru</a><?php endif; ?>
|
||||
</div>
|
||||
<?php if (!$orders): ?>
|
||||
<div class="empty-state"><strong>Belum ada order.</strong><span>Buat order pertama untuk menghasilkan SPK, tracking, dan invoice.</span></div>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table align-middle kk-table">
|
||||
<thead><tr><th>Order</th><th>Customer</th><th>Pekerjaan</th><th>Status</th><th>Tagihan</th><th>Aksi</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($orders as $order): $amount = ((float)$order['quantity'] * (float)$order['unit_price']) - (float)$order['discount']; ?>
|
||||
<tr>
|
||||
<td><strong><?= e($order['order_no']) ?></strong><div class="small text-muted"><?= e(date('d M Y', strtotime($order['created_at']))) ?></div></td>
|
||||
<td><?= e($order['customer_name']) ?><div class="small text-muted"><?= e($order['customer_phone']) ?></div></td>
|
||||
<td><?= e($order['project_name']) ?><div class="small text-muted"><?= e($order['category']) ?> · <?= (int)$order['quantity'] ?> pcs</div></td>
|
||||
<td><span class="badge text-bg-<?= e(statusBadge($order['status'])) ?>"><?= e($order['status']) ?></span></td>
|
||||
<td><?= e(rupiah(max(0, $amount))) ?><div class="small text-muted">Bayar <?= e(rupiah((float)$order['paid_amount'])) ?></div></td>
|
||||
<td><div class="btn-group btn-group-sm"><a class="btn btn-outline-dark" href="order_detail.php?id=<?= (int)$order['id'] ?>">Detail</a><a class="btn btn-outline-dark" href="spk.php?id=<?= (int)$order['id'] ?>">SPK</a><a class="btn btn-outline-dark" href="invoice.php?id=<?= (int)$order['id'] ?>">Invoice</a></div></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section id="tracking" class="panel mt-4" aria-labelledby="trackingTitle">
|
||||
<div class="row g-4 align-items-start">
|
||||
<div class="col-lg-5">
|
||||
<div class="section-heading"><p class="eyebrow">Customer Portal</p><h2 id="trackingTitle">Cek progres order</h2></div>
|
||||
<p class="text-muted">Customer tidak perlu login. Masukkan nomor order dan telepon yang tercatat di order.</p>
|
||||
<form method="get" class="row g-2">
|
||||
<div class="col-md-6"><label class="form-label">Nomor Order</label><input class="form-control" name="track_order" value="<?= e((string)($_GET['track_order'] ?? '')) ?>" placeholder="ORD-2026-00001" required></div>
|
||||
<div class="col-md-6"><label class="form-label">Telepon</label><input class="form-control" name="track_phone" value="<?= e((string)($_GET['track_phone'] ?? '')) ?>" placeholder="08..." required></div>
|
||||
<div class="col-12"><button class="btn btn-dark" type="submit">Cek Status</button></div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<?php if ($trackResult === null): ?>
|
||||
<div class="empty-state"><strong>Hasil tracking muncul di sini.</strong><span>Gunakan nomor order dari admin/Kasir.</span></div>
|
||||
<?php elseif ($trackResult === false): ?>
|
||||
<div class="alert alert-warning mb-0">Order tidak ditemukan. Pastikan nomor order dan telepon sudah benar.</div>
|
||||
<?php else: ?>
|
||||
<div class="tracking-card">
|
||||
<div class="d-flex justify-content-between gap-3 flex-wrap"><div><span class="small text-muted">Order</span><h3><?= e($trackResult['order_no']) ?></h3></div><span class="badge text-bg-<?= e(statusBadge($trackResult['status'])) ?> align-self-start"><?= e($trackResult['status']) ?></span></div>
|
||||
<p class="mb-2"><strong><?= e($trackResult['project_name']) ?></strong> — <?= e($trackResult['category']) ?>, <?= (int)$trackResult['quantity'] ?> pcs</p>
|
||||
<p class="text-muted mb-3"><?= nl2br(e((string)$trackResult['progress_note'])) ?></p>
|
||||
<ol class="progress-steps">
|
||||
<?php $current = array_search($trackResult['status'], $statuses, true); foreach ($statuses as $i => $s): ?>
|
||||
<li class="<?= $i <= (int)$current ? 'done' : '' ?>"><span><?= e($s) ?></span></li>
|
||||
<?php endforeach; ?>
|
||||
</ol>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="container-fluid px-3 px-lg-4 pb-4">
|
||||
<div class="footer-line">© <?= date('Y') ?> Kenanga Kreasindo · Perum BCL Jln Kenanga Raya · kenangakreasindo@gmail.com</div>
|
||||
</footer>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=<?= time() ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
15
invoice.php
Normal file
15
invoice.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
@date_default_timezone_set('Asia/Jakarta');
|
||||
function e(string $value): string { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); }
|
||||
function rupiah(float $value): string { return 'Rp ' . number_format($value, 0, ',', '.'); }
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare('SELECT * FROM print_orders WHERE id = :id LIMIT 1');
|
||||
$stmt->execute([':id' => max(0, (int)($_GET['id'] ?? 0))]);
|
||||
$order = $stmt->fetch();
|
||||
if (!$order) { http_response_code(404); echo 'Invoice tidak ditemukan.'; exit; }
|
||||
$subtotal = (float)$order['quantity'] * (float)$order['unit_price'];
|
||||
$total = max(0, $subtotal - (float)$order['discount']);
|
||||
$remaining = max(0, $total - (float)$order['paid_amount']);
|
||||
?>
|
||||
<!doctype html><html lang="id"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Invoice <?= e($order['invoice_no']) ?> — Kenanga Kreasindo</title><meta name="description" content="Invoice Kenanga Kreasindo."><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"><link rel="stylesheet" href="assets/css/custom.css?v=<?= time() ?>"></head><body class="print-body"><main class="print-sheet"><div class="print-actions"><a class="btn btn-outline-dark btn-sm" href="order_detail.php?id=<?= (int)$order['id'] ?>">← Detail</a><button class="btn btn-dark btn-sm" onclick="window.print()">Print / Save PDF</button></div><section class="doc-card"><header class="doc-header"><div><h1>Kenanga Kreasindo</h1><p>Perum BCL Jln Kenanga Raya · kenangakreasindo@gmail.com</p></div><div class="doc-number"><span>Invoice No</span><strong><?= e($order['invoice_no']) ?></strong><small><?= e(date('d M Y', strtotime($order['created_at']))) ?></small></div></header><div class="row g-3 mb-4"><div class="col-6"><div class="doc-box"><span>Company / Customer</span><strong><?= e($order['customer_name']) ?></strong><small><?= e($order['customer_phone']) ?></small></div></div><div class="col-6"><div class="doc-box"><span>Project</span><strong><?= e($order['project_name']) ?></strong><small>Order: <?= e($order['order_no']) ?></small></div></div></div><table class="table invoice-table"><thead><tr><th>Item</th><th>Category</th><th class="text-end">Qty</th><th class="text-end">Disc</th><th class="text-end">Price/Unit</th><th class="text-end">Amount</th></tr></thead><tbody><tr><td><?= e($order['project_name']) ?></td><td><?= e($order['category']) ?></td><td class="text-end"><?= (int)$order['quantity'] ?></td><td class="text-end"><?= e(rupiah((float)$order['discount'])) ?></td><td class="text-end"><?= e(rupiah((float)$order['unit_price'])) ?></td><td class="text-end"><?= e(rupiah($total)) ?></td></tr></tbody></table><div class="row justify-content-end"><div class="col-md-5"><div class="total-box"><div><span>Subtotal</span><strong><?= e(rupiah($subtotal)) ?></strong></div><div><span>Disc</span><strong><?= e(rupiah((float)$order['discount'])) ?></strong></div><div><span>Total</span><strong><?= e(rupiah($total)) ?></strong></div><div><span>Paid</span><strong><?= e(rupiah((float)$order['paid_amount'])) ?></strong></div><div class="remaining"><span>Remaining payment</span><strong><?= e(rupiah($remaining)) ?></strong></div></div></div></div><div class="payment-box"><strong>Informasi Pembayaran</strong><p>Transfer ke rekening resmi Kenanga Kreasindo. Nomor rekening dapat diisi pada iterasi berikutnya sesuai rekening aktif.</p></div><div class="signature-row"><div>Received</div><div>Kenanga Kreasindo</div><div>Customer</div></div></section></main></body></html>
|
||||
20
order_detail.php
Normal file
20
order_detail.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
@date_default_timezone_set('Asia/Jakarta');
|
||||
function e(string $value): string { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); }
|
||||
function rupiah(float $value): string { return 'Rp ' . number_format($value, 0, ',', '.'); }
|
||||
function badge(string $status): string { return match ($status) { 'SPK dibuat' => 'info', 'Produksi berjalan' => 'primary', 'QC / Finishing' => 'warning', 'Barang selesai' => 'success', 'Invoice / Tagihan' => 'dark', default => 'secondary' }; }
|
||||
$pdo = db();
|
||||
$id = max(0, (int)($_GET['id'] ?? 0));
|
||||
$stmt = $pdo->prepare('SELECT * FROM print_orders WHERE id = :id LIMIT 1');
|
||||
$stmt->execute([':id' => $id]);
|
||||
$order = $stmt->fetch();
|
||||
if (!$order) { http_response_code(404); echo 'Order tidak ditemukan.'; exit; }
|
||||
$statuses = ['Order diterima', 'SPK dibuat', 'Produksi berjalan', 'QC / Finishing', 'Barang selesai', 'Invoice / Tagihan'];
|
||||
$amount = ((float)$order['quantity'] * (float)$order['unit_price']) - (float)$order['discount'];
|
||||
$remaining = max(0, $amount - (float)$order['paid_amount']);
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Detail order percetakan Kenanga Kreasindo.';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
<!doctype html><html lang="id"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Detail <?= e($order['order_no']) ?> — Kenanga Kreasindo</title><meta name="description" content="<?= e($projectDescription) ?>"><?php if ($projectImageUrl): ?><meta property="og:image" content="<?= e($projectImageUrl) ?>"><meta property="twitter:image" content="<?= e($projectImageUrl) ?>"><?php endif; ?><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"><link rel="stylesheet" href="assets/css/custom.css?v=<?= time() ?>"></head>
|
||||
<body><main class="container py-4"><a class="btn btn-outline-dark btn-sm mb-3" href="index.php#produksi">← Kembali</a><div class="panel"><div class="d-flex justify-content-between flex-wrap gap-3"><div><p class="eyebrow">Detail Order</p><h1><?= e($order['order_no']) ?></h1><p class="text-muted mb-0"><?= e($order['customer_name']) ?> · <?= e($order['customer_phone']) ?></p></div><span class="badge text-bg-<?= e(badge($order['status'])) ?> align-self-start"><?= e($order['status']) ?></span></div><hr><div class="row g-4"><div class="col-lg-7"><h2 class="h5">Pekerjaan</h2><dl class="detail-grid"><dt>Project</dt><dd><?= e($order['project_name']) ?></dd><dt>Kategori</dt><dd><?= e($order['category']) ?></dd><dt>Qty</dt><dd><?= (int)$order['quantity'] ?></dd><dt>Ukuran</dt><dd><?= e((string)($order['size_info'] ?: '-')) ?></dd><dt>Bahan</dt><dd><?= e((string)($order['material'] ?: '-')) ?></dd><dt>Finishing</dt><dd><?= e((string)($order['finishing'] ?: '-')) ?></dd><dt>Deadline</dt><dd><?= e($order['deadline'] ? date('d M Y', strtotime($order['deadline'])) : '-') ?></dd></dl></div><div class="col-lg-5"><h2 class="h5">Keuangan</h2><dl class="detail-grid"><dt>Invoice</dt><dd><?= e($order['invoice_no']) ?></dd><dt>Total</dt><dd><?= e(rupiah(max(0, $amount))) ?></dd><dt>Terbayar</dt><dd><?= e(rupiah((float)$order['paid_amount'])) ?></dd><dt>Sisa Tagihan</dt><dd><strong><?= e(rupiah($remaining)) ?></strong></dd></dl></div></div><hr><form method="post" action="index.php#produksi" class="row g-3"><input type="hidden" name="action" value="update_status"><input type="hidden" name="id" value="<?= (int)$order['id'] ?>"><div class="col-md-4"><label class="form-label">Update Status</label><select class="form-select" name="status"><?php foreach ($statuses as $status): ?><option value="<?= e($status) ?>" <?= $status === $order['status'] ? 'selected' : '' ?>><?= e($status) ?></option><?php endforeach; ?></select></div><div class="col-md-8"><label class="form-label">Catatan Progress</label><textarea class="form-control" rows="3" name="progress_note"><?= e((string)$order['progress_note']) ?></textarea></div><div class="col-12 d-flex gap-2 flex-wrap"><button class="btn btn-dark" type="submit">Simpan Status</button><a class="btn btn-outline-dark" href="spk.php?id=<?= (int)$order['id'] ?>">Cetak SPK</a><a class="btn btn-outline-dark" href="invoice.php?id=<?= (int)$order['id'] ?>">Cetak Invoice</a></div></form></div></main><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script></body></html>
|
||||
11
spk.php
Normal file
11
spk.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
@date_default_timezone_set('Asia/Jakarta');
|
||||
function e(string $value): string { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); }
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare('SELECT * FROM print_orders WHERE id = :id LIMIT 1');
|
||||
$stmt->execute([':id' => max(0, (int)($_GET['id'] ?? 0))]);
|
||||
$order = $stmt->fetch();
|
||||
if (!$order) { http_response_code(404); echo 'SPK tidak ditemukan.'; exit; }
|
||||
?>
|
||||
<!doctype html><html lang="id"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>SPK <?= e($order['spk_no']) ?> — Kenanga Kreasindo</title><meta name="description" content="Surat Perintah Kerja Kenanga Kreasindo."><link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"><link rel="stylesheet" href="assets/css/custom.css?v=<?= time() ?>"></head><body class="print-body"><main class="print-sheet"><div class="print-actions"><a class="btn btn-outline-dark btn-sm" href="order_detail.php?id=<?= (int)$order['id'] ?>">← Detail</a><button class="btn btn-dark btn-sm" onclick="window.print()">Print / Save PDF</button></div><section class="doc-card"><header class="doc-header"><div><h1>Kenanga Kreasindo</h1><p>Perum BCL Jln Kenanga Raya · kenangakreasindo@gmail.com</p></div><div class="doc-number"><span>No SPK</span><strong><?= e($order['spk_no']) ?></strong></div></header><div class="doc-title">Surat Perintah Kerja</div><div class="row g-3 mb-3"><div class="col-6"><div class="doc-box"><span>PIC / Customer</span><strong><?= e($order['customer_name']) ?></strong><small><?= e($order['customer_phone']) ?></small></div></div><div class="col-6"><div class="doc-box"><span>Deadline / Pengambilan</span><strong><?= e($order['deadline'] ? date('d M Y', strtotime($order['deadline'])) : '-') ?></strong><small>CS: <?= e((string)($order['cs_name'] ?: '-')) ?></small></div></div></div><table class="table doc-table"><tbody><tr><th>Pekerjaan</th><td><?= e($order['project_name']) ?></td></tr><tr><th>Kategori / Qty</th><td><?= e($order['category']) ?> · <?= (int)$order['quantity'] ?> pcs</td></tr><tr><th>Bahan</th><td><?= e((string)($order['material'] ?: '-')) ?></td></tr><tr><th>Ukuran</th><td><?= e((string)($order['size_info'] ?: '-')) ?></td></tr><tr><th>Finishing</th><td><?= e((string)($order['finishing'] ?: '-')) ?></td></tr><tr><th>Detail Pesanan</th><td><?= nl2br(e((string)$order['progress_note'])) ?></td></tr><tr><th>Operator</th><td><?= e((string)($order['operator_name'] ?: '-')) ?></td></tr></tbody></table><h2 class="h6 mt-4">QC Checklist</h2><div class="qc-grid"><div><strong>Printing</strong><label><input type="checkbox"> Bahan sesuai</label><label><input type="checkbox"> Ukuran sesuai</label><label><input type="checkbox"> Hasil cetak sesuai</label></div><div><strong>Finishing</strong><label><input type="checkbox"> Potong / finishing sesuai</label><label><input type="checkbox"> Jumlah sesuai</label><label><input type="checkbox"> Packing selesai</label></div></div><div class="signature-row"><div>Operator</div><div>Checker QC</div><div>Customer</div></div></section></main></body></html>
|
||||
Loading…
x
Reference in New Issue
Block a user