Notifications change
This commit is contained in:
parent
67ebe4b225
commit
3a2eed7dda
|
|
@ -3,6 +3,8 @@
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\Activity;
|
use App\Models\Activity;
|
||||||
|
use App\Models\ClientCase;
|
||||||
|
use App\Models\Contract;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
|
@ -18,6 +20,11 @@ public function unread(Request $request)
|
||||||
$today = now()->toDateString();
|
$today = now()->toDateString();
|
||||||
$perPage = max(1, min(100, (int) $request->integer('perPage', 15)));
|
$perPage = max(1, min(100, (int) $request->integer('perPage', 15)));
|
||||||
$search = trim((string) $request->input('search', ''));
|
$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()
|
$query = Activity::query()
|
||||||
->select(['id', 'due_date', 'amount', 'contract_id', 'client_case_id', 'created_at'])
|
->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)
|
->where('anr.user_id', $user->id)
|
||||||
->whereColumn('anr.due_date', 'activities.due_date');
|
->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
|
// allow simple search by contract reference or person name
|
||||||
->when($search !== '', function ($q) use ($search) {
|
->when($search !== '', function ($q) use ($search) {
|
||||||
$s = mb_strtolower($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'])
|
$q->select(['contracts.id', 'contracts.uuid', 'contracts.reference', 'contracts.client_case_id'])
|
||||||
->with([
|
->with([
|
||||||
'clientCase' => function ($qq) {
|
'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) {
|
'account' => function ($qq) {
|
||||||
$qq->select(['accounts.id', 'accounts.contract_id', 'accounts.balance_amount', 'accounts.initial_amount']);
|
$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) {
|
'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([
|
->with([
|
||||||
'person' => function ($qq) {
|
'person' => function ($qq) {
|
||||||
$qq->select(['person.id', 'person.full_name']);
|
$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
|
// Use a custom page parameter name to match the frontend DataTableServer
|
||||||
$activities = $query->paginate($perPage, ['*'], 'unread-page')->withQueryString();
|
$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', [
|
return Inertia::render('Notifications/Unread', [
|
||||||
'activities' => $activities,
|
'activities' => $activities,
|
||||||
'today' => $today,
|
'today' => $today,
|
||||||
|
'clients' => $clients,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,20 @@ public function share(Request $request): array
|
||||||
'contract' => function ($q) {
|
'contract' => function ($q) {
|
||||||
$q->select(['contracts.id', 'contracts.uuid', 'contracts.reference', 'contracts.client_case_id'])
|
$q->select(['contracts.id', 'contracts.uuid', 'contracts.reference', 'contracts.client_case_id'])
|
||||||
->with([
|
->with([
|
||||||
|
// Include client (via case) so the UI can render client.person.full_name
|
||||||
'clientCase' => function ($qq) {
|
'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) {
|
'account' => function ($qq) {
|
||||||
$qq->select(['accounts.id', 'accounts.contract_id', 'accounts.balance_amount', 'accounts.initial_amount']);
|
$qq->select(['accounts.id', 'accounts.contract_id', 'accounts.balance_amount', 'accounts.initial_amount']);
|
||||||
|
|
@ -95,14 +107,54 @@ public function share(Request $request): array
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
'clientCase' => function ($q) {
|
'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([
|
->with([
|
||||||
'person' => function ($qq) {
|
'person' => function ($qq) {
|
||||||
$qq->select(['person.id', 'person.full_name']);
|
$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 [
|
return [
|
||||||
'dueToday' => [
|
'dueToday' => [
|
||||||
|
|
|
||||||
|
|
@ -95,9 +95,15 @@ async function markRead(item) {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="px-3 py-2 text-xs text-gray-400 border-b sticky top-0 bg-white z-10 flex items-center justify-between">
|
<div
|
||||||
|
class="px-3 py-2 text-xs text-gray-400 border-b sticky top-0 bg-white z-10 flex items-center justify-between"
|
||||||
|
>
|
||||||
<span>Zapadejo danes</span>
|
<span>Zapadejo danes</span>
|
||||||
<Link :href="route('notifications.unread')" class="text-indigo-600 hover:text-indigo-700">Vsa obvestila</Link>
|
<Link
|
||||||
|
:href="route('notifications.unread')"
|
||||||
|
class="text-indigo-600 hover:text-indigo-700"
|
||||||
|
>Vsa obvestila</Link
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<!-- Scrollable content area with max height -->
|
<!-- Scrollable content area with max height -->
|
||||||
<div class="max-h-80 overflow-auto">
|
<div class="max-h-80 overflow-auto">
|
||||||
|
|
@ -141,6 +147,19 @@ async function markRead(item) {
|
||||||
<span v-else>{{ item.client_case?.person?.full_name || "—" }}</span>
|
<span v-else>{{ item.client_case?.person?.full_name || "—" }}</span>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Partner / Client full name (use contract.client when available; fallback to case.client) -->
|
||||||
|
<div
|
||||||
|
class="text-xs text-gray-500 truncate"
|
||||||
|
v-if="item.contract?.client?.person?.full_name"
|
||||||
|
>
|
||||||
|
Partner: {{ item.contract.client.person.full_name }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-xs text-gray-500 truncate"
|
||||||
|
v-else-if="item.client_case?.client?.person?.full_name"
|
||||||
|
>
|
||||||
|
Partner: {{ item.client_case.client.person.full_name }}
|
||||||
|
</div>
|
||||||
<div class="text-gray-600 truncate" v-if="item.contract">
|
<div class="text-gray-600 truncate" v-if="item.contract">
|
||||||
{{ fmtEUR(item.contract?.account?.balance_amount) }}
|
{{ fmtEUR(item.contract?.account?.balance_amount) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,13 @@ import AppLayout from "@/Layouts/AppLayout.vue";
|
||||||
import SectionTitle from "@/Components/SectionTitle.vue";
|
import SectionTitle from "@/Components/SectionTitle.vue";
|
||||||
import DataTableServer from "@/Components/DataTable/DataTableServer.vue";
|
import DataTableServer from "@/Components/DataTable/DataTableServer.vue";
|
||||||
import { Link, router } from "@inertiajs/vue3";
|
import { Link, router } from "@inertiajs/vue3";
|
||||||
|
import { ref, computed, watch } from "vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
activities: { type: Object, required: true },
|
activities: { type: Object, required: true },
|
||||||
today: { type: String, required: true },
|
today: { type: String, required: true },
|
||||||
|
// Optional: full list of clients with unread items to populate filter dropdown
|
||||||
|
clients: { type: Array, default: () => [] },
|
||||||
});
|
});
|
||||||
|
|
||||||
function fmtDate(d) {
|
function fmtDate(d) {
|
||||||
|
|
@ -30,6 +33,39 @@ function fmtEUR(value) {
|
||||||
return formatted.replace("\u00A0", " ");
|
return formatted.replace("\u00A0", " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Client filter (like Segments/Show.vue) ---
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const initialClient = urlParams.get("client") || urlParams.get("client_id") || "";
|
||||||
|
const selectedClient = ref(initialClient);
|
||||||
|
|
||||||
|
const clientOptions = computed(() => {
|
||||||
|
// Prefer server-provided clients list; fallback to deriving from rows
|
||||||
|
const list = Array.isArray(props.clients) && props.clients.length
|
||||||
|
? props.clients
|
||||||
|
: (Array.isArray(props.activities?.data) ? props.activities.data : [])
|
||||||
|
.map((row) => {
|
||||||
|
const cc = row.contract?.client_case || row.client_case;
|
||||||
|
return cc ? { value: cc.uuid, label: cc.person?.full_name || "(neznana stranka)" } : null;
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.reduce((acc, cur) => {
|
||||||
|
if (!acc.find((x) => x.value === cur.value)) acc.push(cur);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
return list.sort((a, b) => (a.label || "").localeCompare(b.label || ""));
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(selectedClient, (val) => {
|
||||||
|
const query = {};
|
||||||
|
if (val) query.client = val;
|
||||||
|
router.get(route("notifications.unread"), query, {
|
||||||
|
preserveState: true,
|
||||||
|
preserveScroll: true,
|
||||||
|
only: ["activities"],
|
||||||
|
replace: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
async function markRead(id) {
|
async function markRead(id) {
|
||||||
try {
|
try {
|
||||||
await window.axios.post(route("notifications.activity.read"), { activity_id: id });
|
await window.axios.post(route("notifications.activity.read"), { activity_id: id });
|
||||||
|
|
@ -52,9 +88,36 @@ async function markRead(id) {
|
||||||
</SectionTitle>
|
</SectionTitle>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Filters -->
|
||||||
|
<div class="mb-4 flex items-center gap-3">
|
||||||
|
<div class="flex-1 max-w-sm">
|
||||||
|
<label class="block text-sm font-medium text-gray-700 mb-1">Partner</label>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<select
|
||||||
|
v-model="selectedClient"
|
||||||
|
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm"
|
||||||
|
>
|
||||||
|
<option value="">Vsi partnerji</option>
|
||||||
|
<option v-for="opt in clientOptions" :key="opt.value || opt.label" :value="opt.value">
|
||||||
|
{{ opt.label }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<button
|
||||||
|
v-if="selectedClient"
|
||||||
|
type="button"
|
||||||
|
class="text-sm text-gray-600 hover:text-gray-900"
|
||||||
|
@click="selectedClient = ''"
|
||||||
|
>
|
||||||
|
Počisti
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<DataTableServer
|
<DataTableServer
|
||||||
:columns="[
|
:columns="[
|
||||||
{ key: 'what', label: 'Zadeva', sortable: false },
|
{ key: 'what', label: 'Zadeva', sortable: false },
|
||||||
|
{ key: 'partner', label: 'Partner', sortable: false },
|
||||||
{
|
{
|
||||||
key: 'balance',
|
key: 'balance',
|
||||||
label: 'Stanje',
|
label: 'Stanje',
|
||||||
|
|
@ -74,6 +137,7 @@ async function markRead(id) {
|
||||||
route-name="notifications.unread"
|
route-name="notifications.unread"
|
||||||
page-param-name="unread-page"
|
page-param-name="unread-page"
|
||||||
:only-props="['activities']"
|
:only-props="['activities']"
|
||||||
|
:query="{ client: selectedClient || undefined }"
|
||||||
>
|
>
|
||||||
<template #cell-what="{ row }">
|
<template #cell-what="{ row }">
|
||||||
<div class="font-medium text-gray-800 truncate">
|
<div class="font-medium text-gray-800 truncate">
|
||||||
|
|
@ -107,6 +171,15 @@ async function markRead(id) {
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template #cell-partner="{ row }">
|
||||||
|
<div class="truncate">
|
||||||
|
{{
|
||||||
|
(row.contract?.client_case?.person?.full_name) ||
|
||||||
|
(row.client_case?.person?.full_name) ||
|
||||||
|
'—'
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<template #cell-balance="{ row }">
|
<template #cell-balance="{ row }">
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<span v-if="row.contract">{{
|
<span v-if="row.contract">{{
|
||||||
|
|
|
||||||
84
tests/Feature/NotificationsUnreadFilterTest.php
Normal file
84
tests/Feature/NotificationsUnreadFilterTest.php
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature;
|
||||||
|
|
||||||
|
use App\Models\Activity;
|
||||||
|
use App\Models\ClientCase;
|
||||||
|
use App\Models\Contract;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Inertia\Testing\AssertableInertia as Assert;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class NotificationsUnreadFilterTest extends TestCase
|
||||||
|
{
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
public function test_unread_lists_clients_and_filters_by_client_case(): void
|
||||||
|
{
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$this->actingAs($user);
|
||||||
|
|
||||||
|
// Create two client cases (each with a contract)
|
||||||
|
$caseA = ClientCase::factory()->create();
|
||||||
|
$contractA = Contract::factory()->create(['client_case_id' => $caseA->id]);
|
||||||
|
|
||||||
|
$caseB = ClientCase::factory()->create();
|
||||||
|
$contractB = Contract::factory()->create(['client_case_id' => $caseB->id]);
|
||||||
|
|
||||||
|
// Create required related models for activities
|
||||||
|
$action = \App\Models\Action::factory()->create();
|
||||||
|
$decision = \App\Models\Decision::factory()->create();
|
||||||
|
|
||||||
|
// Create activities due today: one tied to contract (A), one tied directly to client case (B)
|
||||||
|
Activity::query()->create([
|
||||||
|
'due_date' => now()->toDateString(),
|
||||||
|
'amount' => 100,
|
||||||
|
'action_id' => $action->id,
|
||||||
|
'decision_id' => $decision->id,
|
||||||
|
'client_case_id' => $caseA->id,
|
||||||
|
'contract_id' => $contractA->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Activity::query()->create([
|
||||||
|
'due_date' => now()->toDateString(),
|
||||||
|
'amount' => 200,
|
||||||
|
'action_id' => $action->id,
|
||||||
|
'decision_id' => $decision->id,
|
||||||
|
'client_case_id' => $caseB->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Request unread without filter
|
||||||
|
$resp = $this->get(route('notifications.unread'));
|
||||||
|
$resp->assertStatus(200);
|
||||||
|
|
||||||
|
$resp->assertInertia(function (Assert $page) use ($caseA, $caseB) {
|
||||||
|
$props = $page->toArray()['props'];
|
||||||
|
// Activities: both should be present
|
||||||
|
$this->assertEquals(2, (int) ($props['activities']['total'] ?? 0));
|
||||||
|
|
||||||
|
// Clients list should include both case UUIDs
|
||||||
|
$clientValues = collect($props['clients'] ?? [])->pluck('value')->all();
|
||||||
|
$this->assertContains($caseA->uuid, $clientValues);
|
||||||
|
$this->assertContains($caseB->uuid, $clientValues);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply filter for case A
|
||||||
|
$resp2 = $this->get(route('notifications.unread', ['client' => $caseA->uuid]));
|
||||||
|
$resp2->assertStatus(200);
|
||||||
|
|
||||||
|
$resp2->assertInertia(function (Assert $page) use ($caseA) {
|
||||||
|
$props = $page->toArray()['props'];
|
||||||
|
// Only one activity should be returned for case A
|
||||||
|
$this->assertEquals(1, (int) ($props['activities']['total'] ?? 0));
|
||||||
|
$data = $props['activities']['data'] ?? [];
|
||||||
|
$this->assertCount(1, $data);
|
||||||
|
|
||||||
|
// Assert the activity relates to the requested client case (either directly or via contract)
|
||||||
|
$row = $data[0] ?? [];
|
||||||
|
$contractCaseUuid = data_get($row, 'contract.client_case.uuid');
|
||||||
|
$directCaseUuid = data_get($row, 'client_case.uuid');
|
||||||
|
$this->assertTrue($contractCaseUuid === $caseA->uuid || $directCaseUuid === $caseA->uuid);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user