diff --git a/app/Controllers/Api/EntryApiController.php b/app/Controllers/Api/EntryApiController.php index d00e418..124e78d 100755 --- a/app/Controllers/Api/EntryApiController.php +++ b/app/Controllers/Api/EntryApiController.php @@ -195,11 +195,18 @@ class EntryApiController extends BaseController $date = $input['date']; $results = $input['results']; + $deletedIds = $input['deletedIds'] ?? []; $savedIds = []; // Start transaction $this->resultModel->db->transBegin(); + // Handle deletions first + foreach ($deletedIds as $resultId) { + $this->resultModel->delete((int) $resultId); + } + + // Save/update results foreach ($results as $r) { if (!isset($r['controlId']) || !isset($r['testId']) || !isset($r['value'])) { continue; @@ -228,7 +235,7 @@ class EntryApiController extends BaseController return $this->respond([ 'status' => 'success', - 'message' => 'Saved ' . count($savedIds) . ' results', + 'message' => 'Saved ' . count($savedIds) . ' results' . (count($deletedIds) > 0 ? ', deleted ' . count($deletedIds) : ''), 'data' => ['savedIds' => $savedIds] ], 200); } catch (\Exception $e) { diff --git a/app/Views/entry/daily.php b/app/Views/entry/daily.php index 160f203..ab0cb3d 100755 --- a/app/Views/entry/daily.php +++ b/app/Views/entry/daily.php @@ -100,8 +100,9 @@ Test Mean ± 2SD - Result + Result Comment + Action @@ -123,18 +124,30 @@ step="0.01" :placeholder="'...'" class="input input-bordered input-sm w-full font-mono" - :class="getInputClass(test, $el.value)" + :class="getInputClass(test, $el.value) + ' ' + (deletedResults.includes(test.testId) ? 'input-disabled' : '')" + :disabled="deletedResults.includes(test.testId)" @input.debounce.300ms="updateResult(test.testId, $el.value)" - :value="test.existingResult ? test.existingResult.resValue : ''"> + :value="getResultValue(test.testId)"> + + + @@ -163,6 +176,7 @@ document.addEventListener('alpine:init', () => { saving: false, resultsData: {}, commentsData: {}, + deletedResults: [], deptId: null, departments: null, @@ -236,9 +250,10 @@ document.addEventListener('alpine:init', () => { const json = await response.json(); this.tests = json.data || []; - // Initialize resultsData and commentsData with existing values + // Initialize resultsData, commentsData, and deletedResults this.resultsData = {}; this.commentsData = {}; + this.deletedResults = []; for (const test of this.tests) { if (test.existingResult && test.existingResult.resValue !== null) { this.resultsData[test.testId] = test.existingResult.resValue; @@ -263,7 +278,17 @@ document.addEventListener('alpine:init', () => { this.saving = true; try { const results = []; + const deletedIds = []; + for (const test of this.tests) { + // Check if this test is marked for deletion + if (this.deletedResults.includes(test.testId)) { + if (test.existingResult && test.existingResult.resultId) { + deletedIds.push(test.existingResult.resultId); + } + continue; + } + const value = this.resultsData[test.testId]; if (value !== undefined && value !== '') { results.push({ @@ -279,15 +304,20 @@ document.addEventListener('alpine:init', () => { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ date: this.date, - results: results + results: results, + deletedIds: deletedIds }) }); const json = await response.json(); if (json.status === 'success') { - // Save comments using the returned result IDs + // Save comments for non-deleted tests const savedIds = json.data.savedIds || []; for (const item of savedIds) { + // Skip if this test is marked for deletion + if (this.deletedResults.includes(item.testId)) { + continue; + } const comment = this.commentsData[item.testId]; if (comment) { await this.saveComment(item.testId, this.date, comment); @@ -299,6 +329,7 @@ document.addEventListener('alpine:init', () => { // Refresh data this.resultsData = {}; this.commentsData = {}; + this.deletedResults = []; await this.fetchTests(); } else { this.$dispatch('notify', { type: 'error', message: json.message || 'Failed to save' }); @@ -329,12 +360,51 @@ document.addEventListener('alpine:init', () => { } }, + getResultValue(testId) { + // If marked for deletion, show empty + if (this.deletedResults.includes(testId)) { + return ''; + } + // Return from resultsData if changed + if (testId in this.resultsData) { + return this.resultsData[testId]; + } + // Return existing result from database + const test = this.tests.find(t => t.testId === testId); + if (test && test.existingResult && test.existingResult.resValue !== null) { + return test.existingResult.resValue; + } + return ''; + }, + updateResult(testId, value) { if (value === '') { delete this.resultsData[testId]; } else { this.resultsData[testId] = value; } + // Remove from deletedResults if user enters a value + if (value !== '' && this.deletedResults.includes(testId)) { + this.deletedResults = this.deletedResults.filter(id => id !== testId); + } + }, + + toggleDelete(testId) { + const test = this.tests.find(t => t.testId === testId); + const hasExistingResult = test && test.existingResult && test.existingResult.resValue !== null; + const hasChanges = testId in this.resultsData || testId in this.commentsData; + + if (this.deletedResults.includes(testId)) { + // Restore - remove from deleted list + this.deletedResults = this.deletedResults.filter(id => id !== testId); + } else { + // Mark for deletion + if (hasExistingResult || hasChanges) { + this.deletedResults.push(testId); + // Clear any pending changes for this test + delete this.resultsData[testId]; + } + } }, updateComment(testId, value) { @@ -384,7 +454,11 @@ document.addEventListener('alpine:init', () => { }, get canSave() { - return this.selectedControl && (Object.keys(this.resultsData).length > 0 || Object.keys(this.commentsData).length > 0) && !this.saving; + return this.selectedControl && ( + Object.keys(this.resultsData).length > 0 || + Object.keys(this.commentsData).length > 0 || + this.deletedResults.length > 0 + ) && !this.saving; }, setToday() {