diff --git a/app.py b/app.py index e726fbc..08fbf76 100644 --- a/app.py +++ b/app.py @@ -560,92 +560,6 @@ def get_cutoff_time_options(*selected_cutoff_times): return sorted(option_values) -def ensure_db_writable(db_path): - db_path = Path(db_path) - if not db_path.exists(): - abort(404, description=f'Database file not found: {db_path}') - if not os.access(db_path, os.W_OK): - abort(403, description=f'Cannot save jumlah karung because database is not writable: {db_path}') - if not os.access(db_path.parent, os.W_OK): - abort(403, description=f'Cannot save jumlah karung because database directory is not writable: {db_path.parent}') - -def update_db_data(db_path, table_name, row_id, counter_value): - """Update one database row by primary key.""" - ensure_db_writable(db_path) - - try: - with sqlite3.connect(db_path) as conn: - cursor = conn.cursor() - cursor.execute( - f""" - UPDATE {table_name} - SET counter_value = ? - WHERE id = ? - """, - (counter_value, row_id) - ) - updated_count = cursor.rowcount - except sqlite3.OperationalError as error: - abort(403, description=f'Cannot save jumlah karung to database {db_path}: {error}') - - if updated_count == 0: - abort(404, description='No database row was found for the requested edit.') - -def update_db_rows(db_path, table_name, row_values): - ensure_db_writable(db_path) - - try: - with sqlite3.connect(db_path) as conn: - cursor = conn.cursor() - for row_id, counter_value in row_values.items(): - cursor.execute( - f""" - UPDATE {table_name} - SET counter_value = ? - WHERE id = ? - """, - (counter_value, row_id) - ) - - if cursor.rowcount == 0: - abort(404, description='No database row was found for one of the requested edits.') - except sqlite3.OperationalError as error: - abort(403, description=f'Cannot save jumlah karung to database {db_path}: {error}') - -def get_counter_value_from_form(counter_value_raw): - if not counter_value_raw: - abort(400, description='Jumlah karung is required to edit database data.') - - try: - counter_value = int(counter_value_raw) - except ValueError: - abort(400, description='Jumlah karung must be a whole number.') - - if counter_value < 0: - abort(400, description='Jumlah karung cannot be negative.') - - return counter_value - -def get_bulk_counter_values_from_form(): - row_values = {} - - for field_name, field_value in request.form.items(): - if not field_name.startswith('counter_value_'): - continue - - row_id_raw = field_name.removeprefix('counter_value_') - try: - row_id = int(row_id_raw) - except ValueError: - abort(400, description='Invalid database row id in edit form.') - - row_values[row_id] = get_counter_value_from_form(field_value.strip()) - - if not row_values: - abort(400, description='No jumlah karung values were submitted.') - - return row_values - def get_json_data(json_path): """Fetch current data from JSON file""" try: @@ -982,35 +896,6 @@ def get_k1_combined(): response.headers['X-Request-ID'] = payload['meta']['requestId'] return response -@app.route('/database///edit', methods=['POST']) -def edit_db_row(db_name, row_id): - db_config = DB_CONFIGS.get(db_name) - if not db_config: - abort(404, description='Unknown database. Use tuang or masuk.') - - counter_value = get_counter_value_from_form(request.form.get('counter_value', '').strip()) - - update_db_data( - db_config['path'], - db_config['table'], - row_id, - counter_value - ) - return redirect(url_for('index')) - -@app.route('/database//edit', methods=['POST']) -def edit_db_rows(db_name): - db_config = DB_CONFIGS.get(db_name) - if not db_config: - abort(404, description='Unknown database. Use tuang or masuk.') - - update_db_rows( - db_config['path'], - db_config['table'], - get_bulk_counter_values_from_form() - ) - return redirect(url_for('index')) - @app.route('/settings', methods=['POST']) def update_dashboard_settings(): settings = get_settings_from_form() diff --git a/templates/index.html b/templates/index.html index dcabafd..80c04a8 100644 --- a/templates/index.html +++ b/templates/index.html @@ -429,8 +429,7 @@ .theme-select, .selected-cycle-select, - .settings-input, - .edit-input { + .settings-input { width: 100%; color: var(--text); border: 1px solid var(--border); @@ -449,8 +448,7 @@ .theme-select:focus, .selected-cycle-select:focus, - .settings-input:focus, - .edit-input:focus { + .settings-input:focus { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-soft); } @@ -494,8 +492,7 @@ } .selected-cycle-button, - .settings-button, - .edit-button { + .settings-button { display: inline-flex; align-items: center; justify-content: center; @@ -510,8 +507,7 @@ } html[data-theme="futuristic"] .selected-cycle-button, - html[data-theme="futuristic"] .settings-button, - html[data-theme="futuristic"] .edit-button { + html[data-theme="futuristic"] .settings-button { color: #031016; border: 1px solid rgba(143, 251, 255, 0.65); border-radius: 6px; @@ -521,8 +517,7 @@ } .selected-cycle-button:hover, - .settings-button:hover, - .edit-button:hover { + .settings-button:hover { transform: translateY(-1px); box-shadow: 0 12px 26px var(--accent-soft); } @@ -817,37 +812,6 @@ font-size: 20px; } - .action-buttons { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: flex-end; - gap: 8px; - } - - .save-button { - background: linear-gradient(135deg, var(--balance), #86efac); - } - - .save-button:disabled { - cursor: not-allowed; - opacity: 0.45; - transform: none; - box-shadow: none; - } - - .edit-status { - width: 100%; - color: var(--muted); - font-size: 12px; - font-weight: 800; - text-align: right; - } - - .edit-status.has-changes { - color: var(--warning); - } - .table-scroll { overflow-x: auto; border: 1px solid var(--border); @@ -908,24 +872,6 @@ background: rgba(56, 189, 248, 0.12); } - .edit-input { - min-width: 96px; - padding: 10px 12px; - border-radius: 12px; - font-weight: 800; - } - - .edit-input:disabled { - color: var(--muted); - cursor: not-allowed; - opacity: 0.72; - } - - .edit-input.is-changed { - border-color: var(--warning); - background: rgba(245, 158, 11, 0.14); - } - .settings-form { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)) auto; @@ -1006,8 +952,7 @@ html[data-theme="classic"] .theme-select, html[data-theme="classic"] .selected-cycle-select, - html[data-theme="classic"] .settings-input, - html[data-theme="classic"] .edit-input { + html[data-theme="classic"] .settings-input { color: #333333; border: 1px solid #cccccc; border-radius: 5px; @@ -1263,8 +1208,7 @@ gap: 15px; } - html[data-theme="classic"] .settings-button, - html[data-theme="classic"] .edit-button { + html[data-theme="classic"] .settings-button { min-height: auto; padding: 11px 16px; border-radius: 5px; @@ -1272,8 +1216,7 @@ font-weight: 600; } - html[data-theme="classic"] .settings-button:hover, - html[data-theme="classic"] .edit-button:hover { + html[data-theme="classic"] .settings-button:hover { background: #1f2d3a; } @@ -1334,19 +1277,10 @@ align-items: stretch; } - .action-buttons { - justify-content: stretch; - } - - .edit-button, .selected-cycle-button, .settings-button { width: 100%; } - - .edit-status { - text-align: left; - } } @@ -1357,7 +1291,7 @@

Karung telemetry

Karung Counter Dashboard

- Monitor realtime JSON counters, active cycle totals, balance movement, and editable database history from one responsive control surface. + Monitor realtime JSON counters, active cycle totals, balance movement, and database history from one responsive control surface.

@@ -1510,41 +1444,30 @@

Karung Tuang

- {% if tuang_db_data %} -
- - - -
- {% endif %}
{% if tuang_db_data %} -
-
- - - - - - - - - - {% for item in tuang_db_data %} - - - - - - {% endfor %} - -
Camera NameTanggalJumlah Karung
- TUANG {{ item.camera_name }} - {{ item.display_date }} - -
-
-
+
+ + + + + + + + + + {% for item in tuang_db_data %} + + + + + + {% endfor %} + +
Camera NameTanggalJumlah Karung
+ TUANG {{ item.camera_name }} + {{ item.display_date }}{{ item.counter_value }}
+
{% else %}

Tidak ada data historis karung tuang

{% endif %} @@ -1553,41 +1476,30 @@

Karung Masuk

- {% if masuk_db_data %} -
- - - -
- {% endif %}
{% if masuk_db_data %} -
-
- - - - - - - - - - {% for item in masuk_db_data %} - - - - - - {% endfor %} - -
Camera NameTanggalJumlah Karung
- MASUK {{ item.camera_name }} - {{ item.date }} - -
-
-
+
+ + + + + + + + + + {% for item in masuk_db_data %} + + + + + + {% endfor %} + +
Camera NameTanggalJumlah Karung
+ MASUK {{ item.camera_name }} + {{ item.date }}{{ item.counter_value }}
+
{% else %}

Tidak ada data historis karung masuk

{% endif %} @@ -1688,43 +1600,6 @@ }); } - function updateTableEditorStatus(tableKey) { - const counterInputs = document.querySelectorAll(`.${tableKey}-counter-input`); - const statusElement = document.getElementById(`${tableKey}-edit-status`); - - if (!statusElement) { - return; - } - - const changedCount = Array.from(counterInputs).filter((counterInput) => { - const isChanged = counterInput.value !== counterInput.dataset.originalValue; - counterInput.classList.toggle('is-changed', isChanged); - return isChanged; - }).length; - - statusElement.classList.toggle('has-changes', changedCount > 0); - statusElement.textContent = changedCount > 0 - ? `${changedCount} perubahan belum disimpan` - : 'Mode edit aktif'; - } - - function enableTableEditor(tableKey) { - const counterInputs = document.querySelectorAll(`.${tableKey}-counter-input`); - const saveButton = document.getElementById(`${tableKey}-save`); - - counterInputs.forEach((counterInput) => { - counterInput.disabled = false; - counterInput.oninput = () => updateTableEditorStatus(tableKey); - }); - saveButton.disabled = false; - updateTableEditorStatus(tableKey); - - if (counterInputs.length > 0) { - counterInputs[0].focus(); - counterInputs[0].select(); - } - } - function escapeHtml(value) { return String(value) .replace(/&/g, '&')