remove edit

This commit is contained in:
Alberto-Audrix committed 2026-08-28 13:54:35 +07:00
1 parent fc617b9cf0
commit c608580f16
2 files changed
+53 -293

No files matched your search

-115
View File
@@ -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/<db_name>/<int:row_id>/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/<db_name>/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()
+53 -178
View File
@@ -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;
}
}
</style>
</head>
@@ -1357,7 +1291,7 @@
<p class="eyebrow">Karung telemetry</p>
<h1>Karung Counter Dashboard</h1>
<p class="hero-copy">
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.
</p>
</div>
<div class="theme-card">
@@ -1510,41 +1444,30 @@
<div class="table-card">
<div class="table-header">
<h3 class="table-title">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>
<span id="tuang-edit-status" class="edit-status"></span>
</div>
{% endif %}
</div>
{% if tuang_db_data %}
<form id="tuang-edit-form" action="{{ url_for('edit_db_rows', db_name='tuang') }}" method="post">
<div class="table-scroll">
<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="camera-cell"><span class="badge badge-tuang">TUANG</span> {{ item.camera_name }}</span>
</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 }}" data-original-value="{{ item.counter_value }}" min="0" step="1" required disabled>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</form>
<div class="table-scroll">
<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="camera-cell"><span class="badge badge-tuang">TUANG</span> {{ item.camera_name }}</span>
</td>
<td>{{ item.display_date }}</td>
<td>{{ item.counter_value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="no-data">Tidak ada data historis karung tuang</p>
{% endif %}
@@ -1553,41 +1476,30 @@
<div class="table-card">
<div class="table-header">
<h3 class="table-title">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>
<span id="masuk-edit-status" class="edit-status"></span>
</div>
{% endif %}
</div>
{% if masuk_db_data %}
<form id="masuk-edit-form" action="{{ url_for('edit_db_rows', db_name='masuk') }}" method="post">
<div class="table-scroll">
<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="camera-cell"><span class="badge badge-masuk">MASUK</span> {{ item.camera_name }}</span>
</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 }}" data-original-value="{{ item.counter_value }}" min="0" step="1" required disabled>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</form>
<div class="table-scroll">
<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="camera-cell"><span class="badge badge-masuk">MASUK</span> {{ item.camera_name }}</span>
</td>
<td>{{ item.date }}</td>
<td>{{ item.counter_value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="no-data">Tidak ada data historis karung masuk</p>
{% 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, '&amp;')