From af74433e79328fd81ee4288533a1ef1f0396de07 Mon Sep 17 00:00:00 2001 From: sanja <52755494+sanjacornelius@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:53:38 -0700 Subject: [PATCH 1/3] Add cases retention logs API endpoint Introduce CasesRetentionController with a logs() action to expose case retention policy logs. The endpoint supports text filtering, validated ordering (whitelisted columns), order direction, and paginated results (per_page default 10); when no valid order_by is provided it falls back to created_at descending. The controller uses DB::raw to transform dotted column notation into JSON extraction for ordering and returns results as an ApiCollection. Also registers the GET api/1.0/cases-retention/logs route under the existing authenticated API middleware group. --- .../Api/CasesRetentionController.php | 52 +++++++++++++++++++ routes/api.php | 4 ++ 2 files changed, 56 insertions(+) create mode 100644 ProcessMaker/Http/Controllers/Api/CasesRetentionController.php diff --git a/ProcessMaker/Http/Controllers/Api/CasesRetentionController.php b/ProcessMaker/Http/Controllers/Api/CasesRetentionController.php new file mode 100644 index 0000000000..242e71ebc1 --- /dev/null +++ b/ProcessMaker/Http/Controllers/Api/CasesRetentionController.php @@ -0,0 +1,52 @@ +input('filter')) { + $filter = '%' . mb_strtolower($filter) . '%'; + $query->where('process_id', 'like', $filter); + } + + $orderBy = $request->input('order_by'); + if ($orderBy && in_array($orderBy, self::LOG_SORT_COLUMNS, true)) { + $orderBy = DB::raw(preg_replace('/\.(.+)/', "->>'\$.$1'", $orderBy, 1)); + + $orderDirection = strtolower((string) $request->input('order_direction', 'asc')); + if (!in_array($orderDirection, ['asc', 'desc'], true)) { + $orderDirection = 'asc'; + } + + $query->orderBy($orderBy, $orderDirection); + } else { + $query->orderByDesc('created_at'); + } + + $response = $query->paginate($request->input('per_page', 10)); + + return new ApiCollection($response); + } +} diff --git a/routes/api.php b/routes/api.php index 99eb8b31d7..8617b3de7e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -4,6 +4,7 @@ use ProcessMaker\Http\Controllers\Admin\TenantQueueController; use ProcessMaker\Http\Controllers\Api\BookmarkController; use ProcessMaker\Http\Controllers\Api\CaseController; +use ProcessMaker\Http\Controllers\Api\CasesRetentionController; use ProcessMaker\Http\Controllers\Api\ChangePasswordController; use ProcessMaker\Http\Controllers\Api\CommentController; use ProcessMaker\Http\Controllers\Api\CssOverrideController; @@ -450,5 +451,8 @@ // Slack Connector Validation Route::post('connector-slack/validate-token', [ProcessMaker\Packages\Connectors\Slack\Controllers\SlackController::class, 'validateToken'])->name('connector-slack.validate-token'); + + // Cases Retention + Route::get('cases-retention/logs', [CasesRetentionController::class, 'logs'])->name('cases-retention.logs'); }); Route::post('devlink/bundle-updated/{bundle}/{token}', [DevLinkController::class, 'bundleUpdated'])->name('devlink.bundle-updated'); From 2289a3a7920f7027648c695c109c81ae189be303 Mon Sep 17 00:00:00 2001 From: sanja <52755494+sanjacornelius@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:54:23 -0700 Subject: [PATCH 2/3] Cast case_ids as array and add CaseIdsTableCell Treat case_ids as a native array across backend and frontend. Add a $casts entry on CaseRetentionPolicyLog and stop json-encoding case_ids in EvaluateProcessRetentionJob and the factory; update the unit test to assert the array value. On the UI side, introduce a dedicated CaseIdsTableCell Vue component (with preview + popover for overflow) and wire it into CasesRetentionLogs, replacing fake data with an API fetch and adjusting sorting/preview behavior. --- .../Jobs/EvaluateProcessRetentionJob.php | 2 +- .../Models/CaseRetentionPolicyLog.php | 4 + .../Models/CaseRetentionPolicyLogFactory.php | 2 +- .../components/CaseIdsTableCell.vue | 172 ++++++++++++++++++ .../components/CasesRetentionLogs.vue | 100 +++------- .../Models/CaseRetentionPolicyLogTest.php | 2 +- 6 files changed, 208 insertions(+), 74 deletions(-) create mode 100644 resources/js/admin/cases-retention/components/CaseIdsTableCell.vue diff --git a/ProcessMaker/Jobs/EvaluateProcessRetentionJob.php b/ProcessMaker/Jobs/EvaluateProcessRetentionJob.php index a04cba2800..e5b14a267d 100644 --- a/ProcessMaker/Jobs/EvaluateProcessRetentionJob.php +++ b/ProcessMaker/Jobs/EvaluateProcessRetentionJob.php @@ -184,7 +184,7 @@ public function handle(): void CaseRetentionPolicyLog::create([ 'process_id' => $this->processId, - 'case_ids' => json_encode($caseIds), + 'case_ids' => $caseIds, 'deleted_count' => $chunkSize, 'total_time_taken' => $chunkTimeMs, 'deleted_at' => Carbon::now(), diff --git a/ProcessMaker/Models/CaseRetentionPolicyLog.php b/ProcessMaker/Models/CaseRetentionPolicyLog.php index 35da191f1e..a2d16c7364 100644 --- a/ProcessMaker/Models/CaseRetentionPolicyLog.php +++ b/ProcessMaker/Models/CaseRetentionPolicyLog.php @@ -25,4 +25,8 @@ class CaseRetentionPolicyLog extends ProcessMakerModel 'total_time_taken', 'deleted_at', ]; + + protected $casts = [ + 'case_ids' => 'array', + ]; } diff --git a/database/factories/ProcessMaker/Models/CaseRetentionPolicyLogFactory.php b/database/factories/ProcessMaker/Models/CaseRetentionPolicyLogFactory.php index e38c9a3fb8..9a82e8d17d 100644 --- a/database/factories/ProcessMaker/Models/CaseRetentionPolicyLogFactory.php +++ b/database/factories/ProcessMaker/Models/CaseRetentionPolicyLogFactory.php @@ -22,7 +22,7 @@ public function definition() 'process_id' => function () { return Process::factory()->create()->id; }, - 'case_ids' => json_encode(CaseNumber::factory()->count($this->faker->numberBetween(1, 1000))->create()->pluck('id')->toArray()), + 'case_ids' => CaseNumber::factory()->count($this->faker->numberBetween(1, 1000))->create()->pluck('id')->toArray(), 'deleted_count' => $this->faker->numberBetween(1, 1000), 'total_time_taken' => $this->faker->numberBetween(1, 1000000), 'deleted_at' => $this->faker->dateTimeBetween('-1 year', 'now'), diff --git a/resources/js/admin/cases-retention/components/CaseIdsTableCell.vue b/resources/js/admin/cases-retention/components/CaseIdsTableCell.vue new file mode 100644 index 0000000000..8710bc181e --- /dev/null +++ b/resources/js/admin/cases-retention/components/CaseIdsTableCell.vue @@ -0,0 +1,172 @@ + + + + + + + + diff --git a/resources/js/admin/cases-retention/components/CasesRetentionLogs.vue b/resources/js/admin/cases-retention/components/CasesRetentionLogs.vue index 933e99eef1..ea6f5ff671 100644 --- a/resources/js/admin/cases-retention/components/CasesRetentionLogs.vue +++ b/resources/js/admin/cases-retention/components/CasesRetentionLogs.vue @@ -34,7 +34,11 @@ slot="case_ids" slot-scope="props" > - {{ props.rowData.case_ids.join(', ') }} +