Notifications change

This commit is contained in:
Simon Pocrnjič
2025-10-23 00:10:38 +02:00
parent 67ebe4b225
commit 3a2eed7dda
5 changed files with 312 additions and 6 deletions
@@ -3,6 +3,8 @@
namespace App\Http\Controllers;
use App\Models\Activity;
use App\Models\ClientCase;
use App\Models\Contract;
use Illuminate\Http\Request;
use Inertia\Inertia;
@@ -18,6 +20,11 @@ public function unread(Request $request)
$today = now()->toDateString();
$perPage = max(1, min(100, (int) $request->integer('perPage', 15)));
$search = trim((string) $request->input('search', ''));
$clientUuid = trim((string) $request->input('client', ''));
$clientCaseId = null;
if ($clientUuid !== '') {
$clientCaseId = ClientCase::query()->where('uuid', $clientUuid)->value('id');
}
$query = Activity::query()
->select(['id', 'due_date', 'amount', 'contract_id', 'client_case_id', 'created_at'])
@@ -29,6 +36,16 @@ public function unread(Request $request)
->where('anr.user_id', $user->id)
->whereColumn('anr.due_date', 'activities.due_date');
})
->when($clientCaseId, function ($q) use ($clientCaseId) {
// Match activities for the client case directly OR via contracts belonging to the case
$q->where(function ($qq) use ($clientCaseId) {
$qq->where('activities.client_case_id', $clientCaseId)
->orWhereIn('activities.contract_id', Contract::query()
->select('id')
->where('client_case_id', $clientCaseId)
);
});
})
// allow simple search by contract reference or person name
->when($search !== '', function ($q) use ($search) {
$s = mb_strtolower($search);
@@ -45,7 +62,17 @@ public function unread(Request $request)
$q->select(['contracts.id', 'contracts.uuid', 'contracts.reference', 'contracts.client_case_id'])
->with([
'clientCase' => function ($qq) {
$qq->select(['client_cases.id', 'client_cases.uuid']);
$qq->select(['client_cases.id', 'client_cases.uuid', 'client_cases.client_id'])
->with([
'client' => function ($qqq) {
$qqq->select(['clients.id', 'clients.person_id'])
->with([
'person' => function ($qqqq) {
$qqqq->select(['person.id', 'person.full_name']);
},
]);
},
]);
},
'account' => function ($qq) {
$qq->select(['accounts.id', 'accounts.contract_id', 'accounts.balance_amount', 'accounts.initial_amount']);
@@ -53,11 +80,19 @@ public function unread(Request $request)
]);
},
'clientCase' => function ($q) {
$q->select(['client_cases.id', 'client_cases.uuid', 'client_cases.person_id'])
$q->select(['client_cases.id', 'client_cases.uuid', 'client_cases.person_id', 'client_cases.client_id'])
->with([
'person' => function ($qq) {
$qq->select(['person.id', 'person.full_name']);
},
'client' => function ($qq) {
$qq->select(['clients.id', 'clients.person_id'])
->with([
'person' => function ($qqq) {
$qqq->select(['person.id', 'person.full_name']);
},
]);
},
]);
},
])
@@ -67,9 +102,52 @@ public function unread(Request $request)
// Use a custom page parameter name to match the frontend DataTableServer
$activities = $query->paginate($perPage, ['*'], 'unread-page')->withQueryString();
// Build a distinct clients list for the filter (client_case UUID + person.full_name)
// Collect client_case_ids from both direct activities and via contracts
$baseForClients = Activity::query()
->select(['contract_id', 'client_case_id'])
->whereNotNull('due_date')
->whereDate('due_date', '<=', $today)
->whereNotExists(function ($q) use ($user) {
$q->from('activity_notification_reads as anr')
->whereColumn('anr.activity_id', 'activities.id')
->where('anr.user_id', $user->id)
->whereColumn('anr.due_date', 'activities.due_date');
})
->when($clientCaseId, function ($q) use ($clientCaseId) {
$q->where(function ($qq) use ($clientCaseId) {
$qq->where('activities.client_case_id', $clientCaseId)
->orWhereIn('activities.contract_id', Contract::query()->select('id')->where('client_case_id', $clientCaseId));
});
})
->get();
$contractIds = $baseForClients->pluck('contract_id')->filter()->unique()->values();
$directCaseIds = $baseForClients->pluck('client_case_id')->filter()->unique()->values();
$mapContractToCase = $contractIds->isNotEmpty()
? Contract::query()->whereIn('id', $contractIds)->pluck('client_case_id', 'id')
: collect();
$caseIds = $directCaseIds
->merge($contractIds->map(fn ($cid) => $mapContractToCase->get($cid)))
->filter()
->unique()
->values();
$clients = ClientCase::query()
->whereIn('id', $caseIds)
->with(['person:id,full_name'])
->get(['id', 'uuid', 'person_id'])
->map(fn ($cc) => [
'value' => $cc->uuid,
'label' => optional($cc->person)->full_name ?: '(neznana stranka)',
])
->sortBy('label', SORT_NATURAL | SORT_FLAG_CASE)
->values();
return Inertia::render('Notifications/Unread', [
'activities' => $activities,
'today' => $today,
'clients' => $clients,
]);
}
}
+54 -2
View File
@@ -86,8 +86,20 @@ public function share(Request $request): array
'contract' => function ($q) {
$q->select(['contracts.id', 'contracts.uuid', 'contracts.reference', 'contracts.client_case_id'])
->with([
// Include client (via case) so the UI can render client.person.full_name
'clientCase' => function ($qq) {
$qq->select(['client_cases.id', 'client_cases.uuid']);
// Include person_id to ensure nested person loads correctly and to avoid null clientCase due to narrow selects
$qq->select(['client_cases.id', 'client_cases.uuid', 'client_cases.client_id', 'client_cases.person_id'])
->with([
'client' => function ($qqq) {
$qqq->select(['clients.id', 'clients.person_id'])
->with([
'person' => function ($qqqq) {
$qqqq->select(['person.id', 'person.full_name']);
},
]);
},
]);
},
'account' => function ($qq) {
$qq->select(['accounts.id', 'accounts.contract_id', 'accounts.balance_amount', 'accounts.initial_amount']);
@@ -95,15 +107,55 @@ public function share(Request $request): array
]);
},
'clientCase' => function ($q) {
$q->select(['client_cases.id', 'client_cases.uuid', 'client_cases.person_id'])
$q->select(['client_cases.id', 'client_cases.uuid', 'client_cases.person_id', 'client_cases.client_id'])
->with([
'person' => function ($qq) {
$qq->select(['person.id', 'person.full_name']);
},
'client' => function ($qq) {
$qq->select(['clients.id', 'clients.person_id'])
->with([
'person' => function ($qqq) {
$qqq->select(['person.id', 'person.full_name']);
},
]);
},
]);
},
]);
// For convenience on the frontend, mirror client onto the contract so it can be accessed as contract.client.person
// 1) Build a map of contract_id -> client_id using a lightweight join
$contractIds = $activities->pluck('contract_id')->filter()->unique()->values();
if ($contractIds->isNotEmpty()) {
$mapContractToClient = \App\Models\Contract::query()
->whereIn('contracts.id', $contractIds)
->join('client_cases', 'client_cases.id', '=', 'contracts.client_case_id')
->pluck('client_cases.client_id', 'contracts.id');
// 2) Load all needed clients with their person
$clientIds = $mapContractToClient->filter()->unique()->values();
$clientsById = $clientIds->isNotEmpty()
? \App\Models\Client::query()
->whereIn('clients.id', $clientIds)
->with(['person:id,full_name'])
->get(['clients.id', 'clients.person_id'])
->keyBy('id')
: collect();
// 3) Attach client relation on each contract instance
foreach ($activities as $act) {
$contract = $act->getRelation('contract');
if (! $contract) {
continue;
}
$cid = $mapContractToClient->get($contract->id);
if ($cid && $clientsById->has($cid)) {
$contract->setRelation('client', $clientsById->get($cid));
}
}
}
return [
'dueToday' => [
'count' => $activities->count(),