579 lines
20 KiB
HTML
579 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Karung Counter Dashboard</title>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
body {
|
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
background-color: #f5f5f5;
|
|
padding: 20px;
|
|
}
|
|
h1 {
|
|
text-align: center;
|
|
color: #333;
|
|
margin-bottom: 30px;
|
|
}
|
|
.container {
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
}
|
|
.selected-cycle {
|
|
background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%);
|
|
color: white;
|
|
border-radius: 10px;
|
|
padding: 18px 20px;
|
|
margin-bottom: 30px;
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
}
|
|
.selected-cycle h2 {
|
|
margin-bottom: 12px;
|
|
font-size: 20px;
|
|
}
|
|
.selected-cycle-details {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
gap: 12px;
|
|
}
|
|
.selected-cycle-label {
|
|
display: block;
|
|
font-size: 12px;
|
|
opacity: 0.8;
|
|
margin-bottom: 4px;
|
|
}
|
|
.selected-cycle-value {
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
word-break: break-word;
|
|
}
|
|
.selected-cycle-form {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
.selected-cycle-select {
|
|
flex: 1;
|
|
min-width: 0;
|
|
padding: 8px;
|
|
border: none;
|
|
border-radius: 5px;
|
|
font-size: 16px;
|
|
}
|
|
.selected-cycle-button {
|
|
background-color: #27ae60;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 5px;
|
|
padding: 8px 12px;
|
|
cursor: pointer;
|
|
font-weight: 700;
|
|
}
|
|
.selected-cycle-button:hover {
|
|
background-color: #219653;
|
|
}
|
|
.section {
|
|
background: white;
|
|
border-radius: 10px;
|
|
padding: 20px;
|
|
margin-bottom: 30px;
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
|
}
|
|
.section h2 {
|
|
color: #2c3e50;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 3px solid #3498db;
|
|
}
|
|
.section h2.tuang {
|
|
border-bottom-color: #e74c3c;
|
|
}
|
|
.section h2.masuk {
|
|
border-bottom-color: #27ae60;
|
|
}
|
|
.historical-summary {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 12px;
|
|
cursor: pointer;
|
|
list-style: none;
|
|
}
|
|
.historical-summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
.historical-summary h2 {
|
|
margin-bottom: 0;
|
|
padding-bottom: 0;
|
|
border-bottom: none;
|
|
}
|
|
.historical-toggle-label {
|
|
background-color: #3498db;
|
|
color: white;
|
|
border-radius: 5px;
|
|
padding: 8px 12px;
|
|
font-weight: 700;
|
|
white-space: nowrap;
|
|
}
|
|
.historical-toggle-label::after {
|
|
content: "Expand";
|
|
}
|
|
.historical-section[open] .historical-toggle-label::after {
|
|
content: "Collapse";
|
|
}
|
|
.historical-content {
|
|
margin-top: 20px;
|
|
}
|
|
.cards {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 20px;
|
|
margin-bottom: 30px;
|
|
}
|
|
.settings-form {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
gap: 15px;
|
|
align-items: end;
|
|
}
|
|
.form-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
.form-group label {
|
|
color: #2c3e50;
|
|
font-weight: 600;
|
|
}
|
|
.settings-input {
|
|
padding: 10px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 5px;
|
|
font-size: 14px;
|
|
}
|
|
.settings-button {
|
|
background-color: #2c3e50;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 5px;
|
|
padding: 11px 16px;
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
}
|
|
.settings-button:hover {
|
|
background-color: #1f2d3a;
|
|
}
|
|
.settings-hint {
|
|
color: #7f8c8d;
|
|
margin-top: 12px;
|
|
font-size: 14px;
|
|
}
|
|
.card {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
color: white;
|
|
padding: 20px;
|
|
border-radius: 10px;
|
|
text-align: center;
|
|
}
|
|
.card.tuang {
|
|
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
|
}
|
|
.card.masuk {
|
|
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
|
|
}
|
|
.card.balance {
|
|
background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
|
|
}
|
|
.card h3 {
|
|
font-size: 14px;
|
|
margin-bottom: 10px;
|
|
opacity: 0.9;
|
|
}
|
|
.card .value {
|
|
font-size: 36px;
|
|
font-weight: bold;
|
|
}
|
|
.card .text-value {
|
|
font-size: 24px;
|
|
word-break: break-word;
|
|
}
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-top: 20px;
|
|
}
|
|
th, td {
|
|
padding: 12px 15px;
|
|
text-align: left;
|
|
border-bottom: 1px solid #ddd;
|
|
}
|
|
th {
|
|
background-color: #34495e;
|
|
color: white;
|
|
font-weight: 600;
|
|
}
|
|
tr:hover {
|
|
background-color: #f8f9fa;
|
|
}
|
|
.badge {
|
|
display: inline-block;
|
|
padding: 5px 10px;
|
|
border-radius: 5px;
|
|
font-size: 12px;
|
|
font-weight: bold;
|
|
}
|
|
.badge-tuang {
|
|
background-color: #ffeaa7;
|
|
color: #d63031;
|
|
}
|
|
.badge-masuk {
|
|
background-color: #a8e6cf;
|
|
color: #00b894;
|
|
}
|
|
.json-section {
|
|
background-color: #f8f9fa;
|
|
border-left: 4px solid #3498db;
|
|
padding: 15px;
|
|
margin-bottom: 20px;
|
|
border-radius: 5px;
|
|
}
|
|
.json-section h3 {
|
|
margin-bottom: 15px;
|
|
color: #2c3e50;
|
|
}
|
|
.camera-list {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 15px;
|
|
}
|
|
.camera-item {
|
|
background: white;
|
|
padding: 15px;
|
|
border-radius: 8px;
|
|
border: 2px solid #e0e0e0;
|
|
}
|
|
.camera-item .camera-name {
|
|
font-size: 12px;
|
|
color: #7f8c8d;
|
|
margin-bottom: 5px;
|
|
word-break: break-all;
|
|
}
|
|
.camera-item .karung-count {
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
color: #2c3e50;
|
|
}
|
|
.no-data {
|
|
text-align: center;
|
|
color: #7f8c8d;
|
|
padding: 30px;
|
|
font-style: italic;
|
|
}
|
|
.filter-note {
|
|
color: #7f8c8d;
|
|
margin-bottom: 15px;
|
|
}
|
|
.date-badge {
|
|
display: inline-block;
|
|
background-color: #3498db;
|
|
color: white;
|
|
padding: 5px 15px;
|
|
border-radius: 20px;
|
|
font-size: 14px;
|
|
margin-bottom: 15px;
|
|
}
|
|
.edit-input {
|
|
width: 100%;
|
|
min-width: 120px;
|
|
padding: 8px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 5px;
|
|
}
|
|
.edit-input:disabled {
|
|
background-color: #f3f4f6;
|
|
color: #555;
|
|
}
|
|
.edit-input.count {
|
|
min-width: 80px;
|
|
}
|
|
.action-buttons {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
.table-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 15px;
|
|
}
|
|
.edit-button {
|
|
background-color: #3498db;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 5px;
|
|
padding: 8px 12px;
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
}
|
|
.edit-button:hover {
|
|
background-color: #2980b9;
|
|
}
|
|
.save-button {
|
|
background-color: #27ae60;
|
|
}
|
|
.save-button:hover {
|
|
background-color: #219653;
|
|
}
|
|
.save-button:disabled {
|
|
background-color: #bdc3c7;
|
|
cursor: not-allowed;
|
|
}
|
|
.save-button:disabled:hover {
|
|
background-color: #bdc3c7;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>📊 Karung Counter Dashboard</h1>
|
|
|
|
<div class="selected-cycle">
|
|
<h2>Selected Cycle</h2>
|
|
<div class="selected-cycle-details">
|
|
<div>
|
|
<span class="selected-cycle-label">Cycle Name</span>
|
|
<form class="selected-cycle-form" action="{{ url_for('select_dashboard_cycle') }}" method="post">
|
|
<select class="selected-cycle-select" name="cycle_name" required>
|
|
{% for cycle in cycle_options %}
|
|
<option value="{{ cycle.cycle_name }}" {% if cycle.cycle_name == dashboard_settings.cycle_name %}selected{% endif %}>{{ cycle.cycle_name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<button class="selected-cycle-button" type="submit">Pilih</button>
|
|
</form>
|
|
</div>
|
|
<div>
|
|
<span class="selected-cycle-label">Range</span>
|
|
<span class="selected-cycle-value">{{ dashboard_settings.cycle_start or 'awal data' }} - {{ dashboard_settings.cycle_end or 'akhir data' }}</span>
|
|
</div>
|
|
<div>
|
|
<span class="selected-cycle-label">Saldo Awal</span>
|
|
<span class="selected-cycle-value">{{ dashboard_settings.saldo_awal }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Summary Cards -->
|
|
<div class="cards">
|
|
<div class="card balance">
|
|
<h3>Cycle Name</h3>
|
|
<div class="value text-value">{{ dashboard_settings.cycle_name }}</div>
|
|
</div>
|
|
<div class="card tuang">
|
|
<h3>Karung Tuang Hari Ini (JSON)</h3>
|
|
<div class="value">{{ tuang_json_total }}</div>
|
|
</div>
|
|
<div class="card masuk">
|
|
<h3>Karung Masuk Hari Ini (JSON)</h3>
|
|
<div class="value">{{ masuk_json_total }}</div>
|
|
</div>
|
|
<div class="card tuang">
|
|
<h3>Total Karung Tuang (Siklus)</h3>
|
|
<div class="value">{{ tuang_db_total }}</div>
|
|
</div>
|
|
<div class="card masuk">
|
|
<h3>Total Karung Masuk (Siklus)</h3>
|
|
<div class="value">{{ masuk_db_total }}</div>
|
|
</div>
|
|
<div class="card balance">
|
|
<h3>Saldo Awal</h3>
|
|
<div class="value">{{ dashboard_settings.saldo_awal }}</div>
|
|
</div>
|
|
<div class="card balance">
|
|
<h3>Saldo Akhir</h3>
|
|
<div class="value">{{ saldo_akhir }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Current Date Data from JSON -->
|
|
<div class="section">
|
|
<h2>📅 Data Hari Ini ({{ current_date }}) - Dari JSON</h2>
|
|
|
|
<h3>Karung Tuang</h3>
|
|
{% if tuang_json_data %}
|
|
<div class="camera-list">
|
|
{% for camera, data in tuang_json_data.items() %}
|
|
<div class="camera-item">
|
|
<div class="camera-name">{{ camera }}</div>
|
|
<div class="karung-count">{{ data.karung|default(0) }} karung</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p class="no-data">Tidak ada data karung tuang untuk hari ini</p>
|
|
{% endif %}
|
|
|
|
<h3 style="margin-top: 30px;">Karung Masuk</h3>
|
|
{% if masuk_json_data %}
|
|
<div class="camera-list">
|
|
{% for camera, data in masuk_json_data.items() %}
|
|
<div class="camera-item">
|
|
<div class="camera-name">{{ camera }}</div>
|
|
<div class="karung-count">{{ data.karung|default(0) }} karung</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<p class="no-data">Tidak ada data karung masuk untuk hari ini</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Historical Data from Database -->
|
|
<details class="section historical-section" open>
|
|
<summary class="historical-summary">
|
|
<h2>🗄️ Data Historis - Dari Database</h2>
|
|
<span class="historical-toggle-label"></span>
|
|
</summary>
|
|
<div class="historical-content">
|
|
<p class="filter-note">
|
|
Siklus aktif:
|
|
{{ dashboard_settings.cycle_name }}.
|
|
Rentang:
|
|
{{ dashboard_settings.cycle_start }}
|
|
sampai
|
|
{{ dashboard_settings.cycle_end }}
|
|
</p>
|
|
|
|
<div class="section">
|
|
<div class="table-header">
|
|
<h3 class="tuang">Karung Tuang</h3>
|
|
{% if tuang_db_data %}
|
|
<div class="action-buttons">
|
|
<button class="edit-button" type="button" onclick="enableTableEditor('tuang')">Edit</button>
|
|
<button id="tuang-save" class="edit-button save-button" form="tuang-edit-form" type="submit" disabled>Simpan</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% if tuang_db_data %}
|
|
<form id="tuang-edit-form" action="{{ url_for('edit_db_rows', db_name='tuang') }}" method="post">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Camera Name</th>
|
|
<th>Tanggal</th>
|
|
<th>Jumlah Karung</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in tuang_db_data %}
|
|
<tr>
|
|
<td>
|
|
<span class="badge badge-tuang">TUANG</span> {{ item.camera_name }}
|
|
</td>
|
|
<td>
|
|
{{ item.display_date }}
|
|
</td>
|
|
<td>
|
|
<input class="edit-input count tuang-counter-input" type="number" name="counter_value_{{ item.id }}" value="{{ item.counter_value }}" min="0" required disabled>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</form>
|
|
{% else %}
|
|
<p class="no-data">Tidak ada data historis karung tuang</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="section">
|
|
<div class="table-header">
|
|
<h3 class="masuk">Karung Masuk</h3>
|
|
{% if masuk_db_data %}
|
|
<div class="action-buttons">
|
|
<button class="edit-button" type="button" onclick="enableTableEditor('masuk')">Edit</button>
|
|
<button id="masuk-save" class="edit-button save-button" form="masuk-edit-form" type="submit" disabled>Simpan</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% if masuk_db_data %}
|
|
<form id="masuk-edit-form" action="{{ url_for('edit_db_rows', db_name='masuk') }}" method="post">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Camera Name</th>
|
|
<th>Tanggal</th>
|
|
<th>Jumlah Karung</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in masuk_db_data %}
|
|
<tr>
|
|
<td>
|
|
<span class="badge badge-masuk">MASUK</span> {{ item.camera_name }}
|
|
</td>
|
|
<td>
|
|
{{ item.date }}
|
|
</td>
|
|
<td>
|
|
<input class="edit-input count masuk-counter-input" type="number" name="counter_value_{{ item.id }}" value="{{ item.counter_value }}" min="0" required disabled>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</form>
|
|
{% else %}
|
|
<p class="no-data">Tidak ada data historis karung masuk</p>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</details>
|
|
|
|
<!-- Cycle Settings -->
|
|
<div class="section">
|
|
<h2>⚙️ Pengaturan Siklus</h2>
|
|
<form class="settings-form" action="{{ url_for('update_dashboard_settings') }}" method="post">
|
|
<div class="form-group">
|
|
<label for="cycle_name">Cycle Name</label>
|
|
<input id="cycle_name" class="settings-input" type="text" name="cycle_name" value="{{ dashboard_settings.cycle_name }}" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="cycle_start">Cycle Start</label>
|
|
<input id="cycle_start" class="settings-input" type="date" name="cycle_start" value="{{ cycle_start_picker_value }}" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="cycle_end">Cycle End</label>
|
|
<input id="cycle_end" class="settings-input" type="date" name="cycle_end" value="{{ cycle_end_picker_value }}" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="saldo_awal">Saldo Awal</label>
|
|
<input id="saldo_awal" class="settings-input" type="number" name="saldo_awal" value="{{ dashboard_settings.saldo_awal }}" min="0" step="1" required>
|
|
</div>
|
|
<button class="settings-button" type="submit">Simpan Pengaturan</button>
|
|
</form>
|
|
<p class="settings-hint">Data historis hanya menampilkan data dalam rentang siklus.</p>
|
|
</div>
|
|
</div>
|
|
<script>
|
|
function enableTableEditor(tableKey) {
|
|
const counterInputs = document.querySelectorAll(`.${tableKey}-counter-input`);
|
|
const saveButton = document.getElementById(`${tableKey}-save`);
|
|
|
|
counterInputs.forEach((counterInput) => {
|
|
counterInput.disabled = false;
|
|
});
|
|
saveButton.disabled = false;
|
|
|
|
if (counterInputs.length > 0) {
|
|
counterInputs[0].focus();
|
|
counterInputs[0].select();
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|