Files

534 lines
31 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<div class="page-header">
<div>
<div style="display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;">
<h1 class="page-title"><?= e($session['title']) ?></h1>
<span class="badge badge-<?= match($session['status']) {
'open' => 'completed',
'closed' => 'warning',
'arrived' => 'processing',
default => 'neutral'
} ?>" style="font-size: 0.85rem; padding: 0.35rem 0.75rem;">
<?= strtoupper(e($session['status'])) ?>
</span>
</div>
<p class="page-subtitle">
🏢 Canteen: <strong><?= e($session['canteen_name']) ?></strong> &bull;
👤 Hosted by: <strong><?= e($session['creator_name']) ?></strong> (@<?= e($session['creator_username']) ?>) &bull;
📅 Date: <strong><?= date('F j, Y', strtotime($session['session_date'])) ?></strong>
</p>
</div>
<!-- Status Controls for Session Creator -->
<?php if ($isCreator): ?>
<div style="display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap;">
<form method="POST" action="/sessions/<?= e($session['id']) ?>/status" style="display: inline-flex; gap: 0.5rem;">
<?= \App\Core\View::csrfField() ?>
<select name="status" class="form-select" onchange="this.form.submit()" style="font-size: 0.85rem; padding: 0.4rem 0.75rem;">
<option value="open" <?= $session['status'] === 'open' ? 'selected' : '' ?>>🟢 Status: Open (Taking Orders)</option>
<option value="closed" <?= $session['status'] === 'closed' ? 'selected' : '' ?>>🟡 Status: Closed (Sent to Canteen)</option>
<option value="arrived" <?= $session['status'] === 'arrived' ? 'selected' : '' ?>>🔵 Status: Food Arrived (Billing)</option>
<option value="completed" <?= $session['status'] === 'completed' ? 'selected' : '' ?>>⚪ Status: Completed / Settled</option>
</select>
</form>
</div>
<?php endif; ?>
</div>
<?php if (!empty($session['notes'])): ?>
<div class="alert alert-info" style="background-color: #eff6ff; border: 1px solid #bfdbfe; color: #1e40af; margin-bottom: 1.5rem;">
<span>📢 <strong>Host Note:</strong> <?= e($session['notes']) ?></span>
</div>
<?php endif; ?>
<!-- Quick Stats Bar -->
<div class="grid-stats" style="margin-bottom: 1.5rem;">
<div class="stat-card">
<div>
<div class="stat-label">Coworkers Ordered</div>
<div class="stat-value"><?= count($session['orders']) ?></div>
</div>
<div class="stat-icon" style="background: #eef2ff; color: #4f46e5;">👥</div>
</div>
<div class="stat-card">
<div>
<div class="stat-label">Total Food Items</div>
<div class="stat-value"><?= array_sum(array_column($session['recap'], 'total_quantity')) ?></div>
</div>
<div class="stat-icon" style="background: #ecfdf5; color: #10b981;">🍱</div>
</div>
<div class="stat-card">
<div>
<div class="stat-label">Total Session Bill</div>
<div class="stat-value"><?= money(array_sum(array_column($session['orders'], 'total_bill'))) ?></div>
</div>
<div class="stat-icon" style="background: #fffbeb; color: #f59e0b;">💵</div>
</div>
</div>
<!-- Main 2-Column Hub Layout -->
<div style="display: grid; grid-template-columns: 1.2fr 1fr; gap: 1.5rem; align-items: start;">
<!-- Left Column: Self-Ordering Widget & Live Coworker Orders Feed -->
<div>
<!-- Interactive Self-Order Form (Alpine.js) -->
<?php if ($session['status'] === 'open'): ?>
<div class="card" x-data="canteenOrderForm()" style="border: 2px solid var(--primary);">
<div class="card-header" style="background: var(--primary-light); display: flex; justify-content: space-between; align-items: center;">
<div>
<h2 class="card-title" style="color: var(--primary);">
<?= $myOrder ? '✏️ Update Your Lunch Order' : ' Place Your Lunch Order' ?>
</h2>
<p style="font-size: 0.8rem; color: var(--text-muted); margin-top: 0.15rem;">
Ordering as: <strong><?= e($currentUser['name']) ?></strong> (@<?= e($currentUser['username']) ?>)
</p>
</div>
<?php if ($myOrder): ?>
<span class="badge badge-completed">Already Ordered</span>
<?php endif; ?>
</div>
<div class="card-body">
<form method="POST" action="/employee-orders" @submit="validateSubmit($event)">
<?= \App\Core\View::csrfField() ?>
<input type="hidden" name="session_id" value="<?= e($session['id']) ?>">
<!-- Multi-Select Menu Selection -->
<div class="form-group">
<label class="form-label" style="display: flex; justify-content: space-between; align-items: center;">
<span>Click items to Multi-Select:</span>
<span style="font-size: 0.75rem; font-weight: normal; color: var(--text-muted);">
<span x-text="selectedItems.length">0</span> item(s) selected
</span>
</label>
<?php if (empty($session['menus'])): ?>
<div style="padding: 1.5rem; text-align: center; background: #f8fafc; border: 1px dashed var(--border-color);">
<p style="color: var(--text-muted); font-size: 0.9rem;">No menu listed yet for today.</p>
</div>
<?php else: ?>
<div class="menu-grid" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 0.6rem;">
<?php foreach ($session['menus'] as $menu): ?>
<div class="menu-select-card"
:class="{ 'is-selected': isSelected(<?= e($menu['id']) ?>) }"
@click="toggleItem(<?= e($menu['id']) ?>, '<?= addslashes(e($menu['name'])) ?>')">
<div style="display: flex; align-items: flex-start; gap: 0.5rem;">
<input type="checkbox"
:checked="isSelected(<?= e($menu['id']) ?>)"
style="margin-top: 0.2rem; pointer-events: none;">
<div style="flex: 1;">
<div style="font-weight: 600; font-size: 0.875rem; line-height: 1.3;">
<?= e($menu['name']) ?>
</div>
<?php if ((float)$menu['final_price'] > 0): ?>
<div style="font-size: 0.775rem; color: var(--success); font-weight: 600; margin-top: 0.2rem;">
<?= money($menu['final_price']) ?>
</div>
<?php else: ?>
<div style="font-size: 0.725rem; color: var(--text-muted); margin-top: 0.2rem;">
(Price settled on arrival)
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<!-- Selected Items Customizer (Quantity & Notes) -->
<template x-if="selectedItems.length > 0">
<div style="margin-top: 1.25rem; background: #f8fafc; border: 1px solid var(--border-color); padding: 1rem;">
<div style="font-weight: 600; font-size: 0.85rem; margin-bottom: 0.75rem; color: var(--text-main);">
📝 Customize Quantities &amp; Requests:
</div>
<div style="display: flex; flex-direction: column; gap: 0.75rem;">
<template x-for="(item, idx) in selectedItems" :key="idx">
<div style="display: flex; align-items: center; gap: 0.75rem; background: white; padding: 0.6rem 0.75rem; border: 1px solid var(--border-color); flex-wrap: wrap;">
<input type="hidden" :name="`items[${idx}][session_menu_id]`" :value="item.id">
<input type="hidden" :name="`items[${idx}][item_name]`" :value="item.name">
<div style="flex: 2; min-width: 140px; font-weight: 600; font-size: 0.85rem;" x-text="item.name"></div>
<div style="display: flex; align-items: center; gap: 0.35rem;">
<button type="button" class="btn btn-secondary btn-sm" style="padding: 0.15rem 0.5rem; height: 28px;" @click="item.quantity = Math.max(1, item.quantity - 1)">-</button>
<input type="number" min="1" :name="`items[${idx}][quantity]`" class="form-control" style="width: 50px; text-align: center; padding: 0.2rem;" x-model.number="item.quantity">
<button type="button" class="btn btn-secondary btn-sm" style="padding: 0.15rem 0.5rem; height: 28px;" @click="item.quantity++">+</button>
</div>
<div style="flex: 3; min-width: 160px;">
<input type="text" :name="`items[${idx}][notes]`" class="form-control" placeholder="Notes (e.g. no spicy, extra egg)" x-model="item.notes" style="font-size: 0.825rem; padding: 0.3rem 0.5rem;">
</div>
<button type="button" @click="removeItem(idx)" style="background: none; border: none; color: var(--danger); font-size: 1.25rem; cursor: pointer; padding: 0 0.25rem;">&times;</button>
</div>
</template>
</div>
</div>
</template>
<!-- General Order Note -->
<div class="form-group" style="margin-top: 1rem;">
<label class="form-label">General Note (Optional)</label>
<input type="text" name="notes" class="form-control" placeholder="e.g. Please put in plastic bag" value="<?= e($myOrder['notes'] ?? '') ?>">
</div>
<button type="submit" class="btn btn-primary" style="width: 100%; padding: 0.75rem; margin-top: 0.5rem; font-size: 1rem;">
🚀 <?= $myOrder ? 'Update My Order' : 'Submit My Lunch Order' ?>
</button>
</form>
</div>
</div>
<?php else: ?>
<div class="card" style="background: #f8fafc; border: 1px dashed var(--border-color);">
<div class="card-body" style="text-align: center; padding: 2rem;">
<div style="font-size: 2rem; margin-bottom: 0.5rem;">🔒</div>
<h3 style="font-size: 1.1rem; font-weight: 600;">Orders Closed for this Session</h3>
<p style="color: var(--text-muted); font-size: 0.875rem; margin-top: 0.25rem;">
This order session is no longer taking new submissions. Please contact the host (<strong><?= e($session['creator_name']) ?></strong>) directly.
</p>
</div>
</div>
<?php endif; ?>
<!-- Live Coworker Orders Feed -->
<div class="card" style="margin-top: 1.5rem;">
<div class="card-header">
<div>
<h2 class="card-title">📋 Orders in this Session (<?= count($session['orders']) ?> people)</h2>
<p style="font-size: 0.8rem; color: var(--text-muted);">Real-time feed of all team members who joined</p>
</div>
</div>
<?php if (empty($session['orders'])): ?>
<div class="card-body" style="text-align: center; padding: 3rem; color: var(--text-muted);">
No orders placed yet. Be the first to order above!
</div>
<?php else: ?>
<div style="display: flex; flex-direction: column;">
<?php foreach ($session['orders'] as $order): ?>
<div style="padding: 1.25rem 1.5rem; border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; flex-wrap: wrap; <?= (int)$order['user_id'] === (int)$currentUser['id'] ? 'background-color: #faf5ff;' : '' ?>">
<div style="flex: 1; min-width: 250px;">
<div style="display: flex; align-items: center; gap: 0.5rem;">
<strong style="font-size: 1rem; color: var(--text-main);">
<?= e($order['user_name']) ?>
<?= (int)$order['user_id'] === (int)$currentUser['id'] ? ' <span style="font-size: 0.75rem; color: var(--primary); font-weight: normal;">(You)</span>' : '' ?>
</strong>
<?php if (!empty($order['mattermost_username'])): ?>
<span style="font-size: 0.775rem; color: var(--text-muted); background: #f1f5f9; padding: 0.15rem 0.4rem; border: 1px solid var(--border-color);">@<?= e($order['mattermost_username']) ?></span>
<?php endif; ?>
<span style="font-size: 0.75rem; color: var(--text-muted); margin-left: auto;"><?= date('H:i', strtotime($order['created_at'])) ?></span>
</div>
<ul style="list-style: none; margin-top: 0.5rem; display: flex; flex-direction: column; gap: 0.35rem;">
<?php foreach ($order['items'] as $item): ?>
<li style="font-size: 0.9rem; display: flex; align-items: center; gap: 0.5rem;">
<span style="font-weight: 700; color: var(--primary); background: var(--primary-light); padding: 0.1rem 0.45rem; border: 1px solid var(--border-color); font-size: 0.8rem;"><?= e($item['quantity']) ?>x</span>
<span><?= e($item['item_name']) ?></span>
<?php if (!empty($item['notes'])): ?>
<em style="color: var(--text-muted); font-size: 0.8rem;">(<?= e($item['notes']) ?>)</em>
<?php endif; ?>
<?php if ((float)$item['total_price'] > 0): ?>
<span style="margin-left: auto; font-weight: 600; font-size: 0.85rem;"><?= money($item['total_price']) ?></span>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
<?php if (!empty($order['notes'])): ?>
<div style="margin-top: 0.5rem; font-size: 0.8rem; color: var(--text-muted); background: #f8fafc; padding: 0.35rem 0.6rem; border: 1px solid var(--border-color);">
💬 Note: <?= e($order['notes']) ?>
</div>
<?php endif; ?>
</div>
<!-- Personal Bill & Payment Status -->
<div style="text-align: right; min-width: 140px;">
<div style="font-size: 1.15rem; font-weight: 700; color: var(--text-main);">
<?= (float)$order['total_bill'] > 0 ? money($order['total_bill']) : '<span style="font-size: 0.8rem; color: var(--text-muted); font-weight: normal;">Pending Price</span>' ?>
</div>
<div style="margin-top: 0.35rem;">
<?php if ($isCreator): ?>
<form method="POST" action="/employee-orders/<?= e($order['id']) ?>/toggle-paid" style="display: inline;">
<?= \App\Core\View::csrfField() ?>
<input type="hidden" name="session_id" value="<?= e($session['id']) ?>">
<button type="submit" class="badge badge-<?= $order['status'] === 'paid' ? 'completed' : 'pending' ?>" style="cursor: pointer; border: none;">
<?= $order['status'] === 'paid' ? '✓ PAID' : '⌛ UNPAID' ?>
</button>
</form>
<?php else: ?>
<span class="badge badge-<?= $order['status'] === 'paid' ? 'completed' : 'pending' ?>">
<?= $order['status'] === 'paid' ? '✓ PAID' : '⌛ UNPAID' ?>
</span>
<?php endif; ?>
<?php if ($isCreator || (int)$order['user_id'] === (int)$currentUser['id']): ?>
<form method="POST" action="/employee-orders/<?= e($order['id']) ?>/delete" style="display: inline; margin-left: 0.35rem;" onsubmit="return confirm('Cancel this order?');">
<?= \App\Core\View::csrfField() ?>
<input type="hidden" name="session_id" value="<?= e($session['id']) ?>">
<button type="submit" style="background: none; border: none; color: var(--danger); font-size: 0.8rem; cursor: pointer;">✕</button>
</form>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
<!-- Right Column: Host Tools (Recap, Settlement, Menu Management) -->
<div>
<!-- 1. Canteen Recap (1-Click Copy for Canteen Placement) -->
<div class="card" x-data="canteenRecapHelper()">
<div class="card-header" style="background: #f0fdf4; border-bottom: 1px solid #bbf7d0;">
<div>
<h2 class="card-title" style="color: #166534;">📦 Canteen Order Summary</h2>
<p style="font-size: 0.8rem; color: #15803d;">Grouped quantities for ordering</p>
</div>
<button type="button" class="btn btn-sm btn-success" @click="copyCanteenText()">
📋 <span x-text="copyBtnText">Copy Canteen Text</span>
</button>
</div>
<div class="card-body">
<?php if (empty($session['recap'])): ?>
<p style="color: var(--text-muted); font-size: 0.875rem; text-align: center; padding: 1rem;">
No items ordered yet.
</p>
<?php else: ?>
<div style="display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1rem;">
<?php foreach ($session['recap'] as $recap): ?>
<div style="display: flex; justify-content: space-between; align-items: flex-start; padding: 0.4rem 0; border-bottom: 1px dashed var(--border-color);">
<div>
<span style="font-weight: 700; color: var(--primary); font-size: 0.95rem;"><?= e($recap['total_quantity']) ?>x</span>
<strong style="margin-left: 0.35rem; font-size: 0.9rem;"><?= e($recap['item_name']) ?></strong>
<?php if (!empty($recap['combined_notes'])): ?>
<div style="font-size: 0.775rem; color: var(--text-muted); margin-left: 1.5rem;">
Note: <?= e($recap['combined_notes']) ?>
</div>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<textarea x-ref="recapText" style="display: none;"><?= e(generateCanteenCopyText($session)) ?></textarea>
</div>
</div>
<!-- 2. End-of-Day Food Arrival & Bill Settlement (Available for Host) -->
<?php if ($isCreator): ?>
<div class="card" style="border: 2px solid #3b82f6;" x-data="mattermostRecapHelper()">
<div class="card-header" style="background: #eff6ff; border-bottom: 1px solid #bfdbfe;">
<div>
<h2 class="card-title" style="color: #1e40af;">💰 Settle Canteen Bill (Arrival)</h2>
<p style="font-size: 0.8rem; color: #1e3a8a;">Enter final receipt prices to compute everyone's bill</p>
</div>
</div>
<div class="card-body">
<form method="POST" action="/sessions/<?= e($session['id']) ?>/settle-prices">
<?= \App\Core\View::csrfField() ?>
<div style="display: flex; flex-direction: column; gap: 0.6rem; margin-bottom: 1rem;">
<?php foreach ($session['menus'] as $menu): ?>
<div style="display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; font-size: 0.875rem;">
<span style="flex: 1; font-weight: 500;"><?= e($menu['name']) ?>:</span>
<div style="display: flex; align-items: center; gap: 0.25rem;">
<span style="color: var(--text-muted); font-size: 0.8rem; font-weight: 600;">Rp</span>
<input type="number"
step="1"
min="0"
name="prices[<?= e($menu['id']) ?>]"
class="form-control"
style="width: 110px; padding: 0.25rem 0.5rem; text-align: right;"
value="<?= (float)$menu['final_price'] > 0 ? e($menu['final_price']) : '' ?>"
placeholder="0">
</div>
</div>
<?php endforeach; ?>
</div>
<button type="submit" class="btn btn-primary" style="width: 100%; font-size: 0.9rem;">
⚡ Save Prices &amp; Compute All Bills
</button>
</form>
<?php if (array_sum(array_column($session['orders'], 'total_bill')) > 0): ?>
<div style="margin-top: 1.25rem; border-top: 1px solid var(--border-color); padding-top: 1rem;">
<button type="button" class="btn btn-secondary" style="width: 100%; font-size: 0.85rem;" @click="copyMattermostText()">
📢 <span x-text="mmBtnText">Copy Mattermost Billing Breakdown</span>
</button>
<textarea x-ref="mattermostText" style="display: none;"><?= e(generateMattermostBillText($session)) ?></textarea>
</div>
<?php endif; ?>
</div>
</div>
<!-- 3. Host Menu Manager -->
<div class="card">
<div class="card-header">
<h2 class="card-title">📝 Menu Options Manager</h2>
</div>
<div class="card-body">
<form method="POST" action="/sessions/<?= e($session['id']) ?>/add-menu">
<?= \App\Core\View::csrfField() ?>
<div class="form-group">
<label class="form-label" style="font-size: 0.85rem;">Add More Items (Paste line by line):</label>
<textarea name="menu_text" class="form-textarea" rows="2" placeholder="e.g. Extra Kerupuk&#10;Es Kopi Susu" required></textarea>
</div>
<button type="submit" class="btn btn-secondary btn-sm" style="width: 100%;">+ Add to Menu</button>
</form>
<?php if (!empty($session['menus'])): ?>
<div style="margin-top: 1rem; border-top: 1px solid var(--border-color); padding-top: 0.75rem;">
<ul style="list-style: none; display: flex; flex-direction: column; gap: 0.35rem;">
<?php foreach ($session['menus'] as $m): ?>
<li style="display: flex; justify-content: space-between; align-items: center; font-size: 0.85rem; padding: 0.25rem 0;">
<span>&bull; <?= e($m['name']) ?></span>
<form method="POST" action="/sessions/<?= e($session['id']) ?>/delete-menu" style="display: inline;">
<?= \App\Core\View::csrfField() ?>
<input type="hidden" name="menu_id" value="<?= e($m['id']) ?>">
<button type="submit" style="background: none; border: none; color: var(--text-muted); cursor: pointer; font-size: 0.9rem;" title="Delete item">&times;</button>
</form>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
</div>
</div>
<!-- Alpine.js Multi-Select Form Logic -->
<script>
function canteenOrderForm() {
<?php
$initialItems = [];
if (!empty($myOrder['items'])) {
foreach ($myOrder['items'] as $item) {
$initialItems[] = [
'id' => $item['session_menu_id'] ? (int)$item['session_menu_id'] : null,
'name' => $item['item_name'],
'quantity' => (int)$item['quantity'],
'notes' => $item['notes'] ?? '',
];
}
}
?>
return {
selectedItems: <?= json_encode($initialItems) ?>,
isSelected(menuId) {
return this.selectedItems.some(i => i.id === menuId);
},
toggleItem(menuId, menuName) {
const idx = this.selectedItems.findIndex(i => i.id === menuId);
if (idx >= 0) {
this.selectedItems.splice(idx, 1);
} else {
this.selectedItems.push({
id: menuId,
name: menuName,
quantity: 1,
notes: ''
});
}
},
removeItem(index) {
this.selectedItems.splice(index, 1);
},
validateSubmit(e) {
if (this.selectedItems.length === 0) {
alert('Please click on at least one menu item to select it!');
e.preventDefault();
return;
}
}
};
}
function canteenRecapHelper() {
return {
copyBtnText: 'Copy Canteen Text',
copyCanteenText() {
const text = this.$refs.recapText.value;
navigator.clipboard.writeText(text).then(() => {
this.copyBtnText = '✓ Copied to Clipboard!';
setTimeout(() => this.copyBtnText = 'Copy Canteen Text', 2500);
});
}
};
}
function mattermostRecapHelper() {
return {
mmBtnText: 'Copy Mattermost Breakdown',
copyMattermostText() {
const text = this.$refs.mattermostText.value;
navigator.clipboard.writeText(text).then(() => {
this.mmBtnText = '✓ Mattermost Summary Copied!';
setTimeout(() => this.mmBtnText = 'Copy Mattermost Breakdown', 2500);
});
}
};
}
</script>
<?php
function generateCanteenCopyText(array $session): string {
$out = "🍱 *Order Makan Siang - " . $session['title'] . "*\n";
$out .= "Canteen: " . $session['canteen_name'] . " (" . $session['session_date'] . ")\n\n";
$out .= "Rekap Pesanan:\n";
foreach ($session['recap'] as $r) {
$out .= "- " . $r['total_quantity'] . "x " . $r['item_name'];
if (!empty($r['combined_notes'])) {
$out .= " (" . $r['combined_notes'] . ")";
}
$out .= "\n";
}
$totalQty = array_sum(array_column($session['recap'], 'total_quantity'));
$out .= "\nTotal: " . $totalQty . " item(s). Terima kasih!";
return $out;
}
function generateMattermostBillText(array $session): string {
$out = "📢 **Tagihan Makan Siang (" . $session['title'] . ")**\n\n";
$out .= "| Nama | Pesanan | Total Tagihan | Status |\n";
$out .= "|:---|:---|:---|:---|\n";
foreach ($session['orders'] as $o) {
$itemList = [];
foreach ($o['items'] as $i) {
$itemList[] = $i['quantity'] . "x " . $i['item_name'];
}
$tag = !empty($o['mattermost_username']) ? "@" . $o['mattermost_username'] : $o['user_name'];
$status = $o['status'] === 'paid' ? "✅ Lunas" : "⏳ Belum";
$out .= "| " . $tag . " | " . implode(', ', $itemList) . " | " . money($o['total_bill']) . " | " . $status . " |\n";
}
$totalBill = array_sum(array_column($session['orders'], 'total_bill'));
$out .= "\n**Total Keseluruhan:** " . money($totalBill) . "\n";
$out .= "Mohon transfer/bayar ke " . $session['creator_name'] . " ya. Terima kasih! 🙏";
return $out;
}
?>