diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index c4b1c82..0138c0d 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -11,10 +11,10 @@ class DashboardController extends Controller public function index(Request $request, ActivityService $activityService) { $user = $request->user(); - $unreadDynamics = $activityService->getUnreadDynamicsGrouped($user); + $unreadEntities = $activityService->getUnreadEntitiesGrouped($user); return Inertia::render('Dashboard', [ - 'unreadDynamics' => $unreadDynamics, + 'unreadEntities' => $unreadEntities, ]); } } diff --git a/app/Http/Controllers/LedgerController.php b/app/Http/Controllers/LedgerController.php index 3d5ebc6..3d32330 100644 --- a/app/Http/Controllers/LedgerController.php +++ b/app/Http/Controllers/LedgerController.php @@ -85,9 +85,7 @@ class LedgerController extends Controller return Inertia::render('Ledgers/Show', [ 'dynamic' => new DynamicResource($dynamic), 'ledger' => new LedgerResource($ledger), - 'mutations' => MutationResource::collection($ledger->mutations), - 'participants' => UserResource::collection($dynamic->participants), - 'messages' => MessageResource::collection($dynamic->chat->messages()->with(['user', 'media'])->latest()->paginate(\App\Models\Message::PAGINATION_COUNT)), + 'messages' => MessageResource::collection($dynamic->getOrCreateChat()->messages()->with(['user', 'media'])->latest()->paginate(\App\Models\Message::PAGINATION_COUNT)), 'can' => [ 'update' => $request->user()->can('update', $ledger), 'close' => $request->user()->can('close', $ledger), @@ -99,7 +97,7 @@ class LedgerController extends Controller { $this->authorize('view', $ledger); - return MessageResource::collection($dynamic->chat->messages()->with(['user', 'media'])->latest()->paginate(\App\Models\Message::PAGINATION_COUNT)); + return MessageResource::collection($dynamic->getOrCreateChat()->messages()->with(['user', 'media'])->latest()->paginate(\App\Models\Message::PAGINATION_COUNT)); } /** diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index f991367..be3be92 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -50,7 +50,7 @@ class HandleInertiaRequests extends Middleware $service = app(ActivityService::class); - return count($service->getUnreadDynamicsGrouped($request->user())); + return count($service->getUnreadEntitiesGrouped($request->user())); }, ]; } diff --git a/app/Http/Resources/ChatResource.php b/app/Http/Resources/ChatResource.php new file mode 100644 index 0000000..ecaa8d6 --- /dev/null +++ b/app/Http/Resources/ChatResource.php @@ -0,0 +1,24 @@ + + */ + public function toArray(Request $request): array + { + $data = parent::toArray($request); + + if ($this->whenLoaded('messages')) { + $data['messages'] = MessageResource::collection($this->messages); + } + + return $data; + } +} diff --git a/app/Http/Resources/DynamicResource.php b/app/Http/Resources/DynamicResource.php index f37e533..1692407 100644 --- a/app/Http/Resources/DynamicResource.php +++ b/app/Http/Resources/DynamicResource.php @@ -17,9 +17,12 @@ class DynamicResource extends BaseResource if ($this->ledgers) { $result['ledgers'] = LedgerResource::collection($this->ledgers); } - if ($this->participants) { + if ($this->whenLoaded('participants')) { $result['participants'] = ParticipantResource::collection($this->participants); } + if ($this->whenLoaded('chat')) { + $result['chat'] = new ChatResource($this->chat); + } return $result; } } diff --git a/app/Http/Resources/LedgerResource.php b/app/Http/Resources/LedgerResource.php index 5a912b6..2f19071 100644 --- a/app/Http/Resources/LedgerResource.php +++ b/app/Http/Resources/LedgerResource.php @@ -13,6 +13,10 @@ class LedgerResource extends BaseResource */ public function toArray(Request $request): array { - return parent::toArray($request); + $data = parent::toArray($request); + + $data['mutations'] = MutationResource::collection($this->whenLoaded('mutations')); + + return $data; } } diff --git a/app/Models/Dynamic.php b/app/Models/Dynamic.php index 32ae1f3..b9b15b4 100644 --- a/app/Models/Dynamic.php +++ b/app/Models/Dynamic.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Support\Str; +use App\Models\Chat; class Dynamic extends Model { @@ -64,4 +65,13 @@ class Dynamic extends Model public function getUrlAttribute(): string { return route('dynamics.show', $this); } + + public function getOrCreateChat(): Chat + { + if ($this->chat) { + return $this->chat; + } + + return $this->chat()->create([]); + } } diff --git a/app/Services/ActivityService.php b/app/Services/ActivityService.php index 2ce189d..95e220c 100644 --- a/app/Services/ActivityService.php +++ b/app/Services/ActivityService.php @@ -45,8 +45,9 @@ class ActivityService return $cursor ? $cursor->read_at : Carbon::parse('1970-01-01'); } - public function createMessage($chat, $user, $content, $subject = null) + public function createMessage($dynamic, $user, $content, $subject = null) { + $chat = $dynamic->getOrCreateChat(); $message = $chat->messages()->create([ 'user_id' => $user ? $user->id : null, 'content' => $content, @@ -92,6 +93,11 @@ class ActivityService */ public function getActivitiesForDynamic(Dynamic $dynamic): array { + $chat = $dynamic->getOrCreateChat(); + if (!$chat) { + return []; + } + $participants = $dynamic->participants()->withPivot('display_name')->get(); $participantsMap = $participants->reduce(function ($acc, $p) { $acc[$p->id] = $p->pivot->display_name ?? $p->name; @@ -99,7 +105,7 @@ class ActivityService return $acc; }, []); - $messages = Message::where('chat_id', $dynamic->chat->id) + $messages = Message::where('chat_id', $chat->id) ->with(['user', 'subject']) ->latest() ->get(); @@ -122,10 +128,10 @@ class ActivityService /** * Get unread activities grouped by active entities (Dynamics, Ledgers) for the given user. */ - public function getUnreadDynamicsGrouped(User $user): array + public function getUnreadEntitiesGrouped(User $user): array { $groupedDynamics = []; - $participatingDynamics = $user->dynamics()->with('ledgers')->get(); + $participatingDynamics = $user->dynamics()->with(['chat', 'ledgers'])->get(); foreach ($participatingDynamics as $dynamic) { $readAt = $this->getCursorReadAt($user, $dynamic); diff --git a/resources/js/pages/Dashboard.vue b/resources/js/pages/Dashboard.vue index fbdb728..bf37bb7 100644 --- a/resources/js/pages/Dashboard.vue +++ b/resources/js/pages/Dashboard.vue @@ -14,7 +14,7 @@ defineOptions({ }); defineProps<{ - unreadDynamics: Array<{ + unreadEntities: Array<{ id: number; name: string; url: string; @@ -50,10 +50,10 @@ function formatTime(isoString: string): string {

Recent Activity

-
+
diff --git a/tests/Feature/DashboardTest.php b/tests/Feature/DashboardTest.php index c63aae1..fa2cc0d 100644 --- a/tests/Feature/DashboardTest.php +++ b/tests/Feature/DashboardTest.php @@ -18,7 +18,7 @@ test('authenticated users can visit the dashboard', function () { $response = $this->get(route('dashboard')); $response->assertOk(); - $response->assertInertia(fn ($page) => $page->component('Dashboard')->has('unreadDynamics')); + $response->assertInertia(fn ($page) => $page->component('Dashboard')->has('unreadEntities')); }); test('visiting dynamic updates the read cursor', function () { @@ -100,12 +100,12 @@ test('dashboard groups and filters unread entities correctly based on cursor', f // Verify unread grouping structure $response->assertInertia(fn ($page) => $page ->component('Dashboard') - ->where('unreadDynamics.0.name', 'Testing Dynamic') - ->where('unreadDynamics.0.unread_count', 1) - ->has('unreadDynamics.0.context_activities', 1) // Should have old message as context - ->where('unreadDynamics.0.context_activities.0.content', 'Old message context') - ->has('unreadDynamics.0.new_activities', 1) // Should have unread message - ->where('unreadDynamics.0.new_activities.0.content', 'New unread message alert') + ->where('unreadEntities.0.name', 'Testing Dynamic') + ->where('unreadEntities.0.unread_count', 1) + ->has('unreadEntities.0.context_activities', 1) // Should have old message as context + ->where('unreadEntities.0.context_activities.0.content', 'Old message context') + ->has('unreadEntities.0.new_activities', 1) // Should have unread message + ->where('unreadEntities.0.new_activities.0.content', 'New unread message alert') ); // Now visit the Dynamic, which clears the unread count @@ -116,7 +116,7 @@ test('dashboard groups and filters unread entities correctly based on cursor', f $response2->assertOk(); $response2->assertInertia(fn ($page) => $page ->component('Dashboard') - ->has('unreadDynamics', 0) + ->has('unreadEntities', 0) ); Carbon::setTestNow(); // Reset test time