From 6e4da9eadf3fb98c6ca331c9042de51e76a370ee Mon Sep 17 00:00:00 2001
From: Tajim
Date: Thu, 27 Aug 2026 12:05:32 +0600
Subject: [PATCH 1/3] feat(auth): redirect user to public profile upon login
---
app/Http/Controllers/Admin/AuthController.php | 6 +++++-
tests/Feature/AuthenticationTest.php | 3 ++-
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/app/Http/Controllers/Admin/AuthController.php b/app/Http/Controllers/Admin/AuthController.php
index 4bfdee2..ce3230a 100644
--- a/app/Http/Controllers/Admin/AuthController.php
+++ b/app/Http/Controllers/Admin/AuthController.php
@@ -81,7 +81,11 @@ public function handleGoogleCallback(Request $request)
Auth::login($user, remember: true);
$request->session()->regenerate();
- $redirect = redirect()->intended(route('profile.edit'));
+ $defaultUrl = $user->username
+ ? route('user.profile', $user->username)
+ : route('profile.edit');
+
+ $redirect = redirect()->intended($defaultUrl);
if ($isNewUser) {
$redirect->with('success', 'Account not found. New account created.');
diff --git a/tests/Feature/AuthenticationTest.php b/tests/Feature/AuthenticationTest.php
index 78aa597..374ccc0 100644
--- a/tests/Feature/AuthenticationTest.php
+++ b/tests/Feature/AuthenticationTest.php
@@ -27,7 +27,8 @@
$response = $this->get(route('auth.google.callback'));
- $response->assertRedirect(route('profile.edit'));
+ $user = User::where('email', 'newuser@example.com')->first();
+ $response->assertRedirect(route('user.profile', $user->username));
$response->assertSessionHas('success', 'Account not found. New account created.');
$this->assertAuthenticated();
$this->assertDatabaseHas('users', [
From 91a8317812e6261eee40ecff4dd7a4637b8719ae Mon Sep 17 00:00:00 2001
From: Tajim
Date: Thu, 27 Aug 2026 12:05:35 +0600
Subject: [PATCH 2/3] feat(user): add is_verified attribute and reusable
VerifiedBadge component
---
app/Http/Controllers/BlogController.php | 4 ++--
app/Http/Controllers/ResourceController.php | 2 +-
app/Models/User.php | 8 +++++++
resources/js/components/UserListItem.vue | 14 ++++++------
resources/js/components/VerifiedBadge.vue | 24 +++++++++++++++++++++
resources/js/pages/Blog/Show.vue | 20 ++++++-----------
resources/js/pages/Profile.vue | 4 ++++
7 files changed, 52 insertions(+), 24 deletions(-)
create mode 100644 resources/js/components/VerifiedBadge.vue
diff --git a/app/Http/Controllers/BlogController.php b/app/Http/Controllers/BlogController.php
index 88e3e51..9ad2f44 100644
--- a/app/Http/Controllers/BlogController.php
+++ b/app/Http/Controllers/BlogController.php
@@ -62,7 +62,7 @@ public function show(Blog $blog)
$reactionsCount = $blog->reactions()->count();
$reactors = $blog->reactions()
- ->with(['user:id,name,username,image_path,institution', 'user.roles:id,name'])
+ ->with(['user:id,name,username,image_path,institution'])
->latest('id')
->limit(50)
->get()
@@ -71,7 +71,7 @@ public function show(Blog $blog)
->values();
$comments = $blog->comments()
- ->with(['user:id,name,username,image_path,institution', 'user.roles:id,name'])
+ ->with(['user:id,name,username,image_path,institution'])
->latest()
->get();
diff --git a/app/Http/Controllers/ResourceController.php b/app/Http/Controllers/ResourceController.php
index 656bc35..bf4cf79 100644
--- a/app/Http/Controllers/ResourceController.php
+++ b/app/Http/Controllers/ResourceController.php
@@ -38,7 +38,7 @@ public function show($id)
$completionsCount = ResourceCompletion::where('resource_id', $id)->count();
$completers = ResourceCompletion::where('resource_id', $id)
- ->with(['user:id,name,username,image_path,institution', 'user.roles:id,name'])
+ ->with(['user:id,name,username,image_path,institution'])
->latest()
->take(10)
->get()
diff --git a/app/Models/User.php b/app/Models/User.php
index bda86bd..c86e494 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -64,8 +64,16 @@ protected static function booted(): void
protected $appends = [
'image_url',
+ 'is_verified',
];
+ public function getIsVerifiedAttribute(): bool
+ {
+ return $this->relationLoaded('roles')
+ ? $this->roles->isNotEmpty()
+ : $this->roles()->exists();
+ }
+
public function getImageUrlAttribute()
{
if (! $this->image_path) {
diff --git a/resources/js/components/UserListItem.vue b/resources/js/components/UserListItem.vue
index 0954ac8..937d50b 100644
--- a/resources/js/components/UserListItem.vue
+++ b/resources/js/components/UserListItem.vue
@@ -1,6 +1,6 @@
+
+
+
+
diff --git a/resources/js/pages/Blog/Show.vue b/resources/js/pages/Blog/Show.vue
index 3c91ae1..955e65b 100644
--- a/resources/js/pages/Blog/Show.vue
+++ b/resources/js/pages/Blog/Show.vue
@@ -14,10 +14,10 @@ import {
LogIn,
X,
Loader2,
- BadgeCheck,
} from 'lucide-vue-next';
import { computed, ref, watch } from 'vue';
import UserListItem from '@/components/UserListItem.vue';
+import VerifiedBadge from '@/components/VerifiedBadge.vue';
const props = defineProps({
blog: {
@@ -638,13 +638,8 @@ const goBack = () => {
{{
comment.user?.name || 'Anonymous'
}}
-
{
class="mt-1 inline-flex items-center gap-1.5 text-lg font-semibold text-slate-900 underline transition dark:text-gray-100"
>
{{ blog.user?.name }}
-
diff --git a/resources/js/pages/Profile.vue b/resources/js/pages/Profile.vue
index fe1696d..21ae4af 100644
--- a/resources/js/pages/Profile.vue
+++ b/resources/js/pages/Profile.vue
@@ -21,6 +21,10 @@ const page = usePage();
const user = computed(() => props.user || page.props.auth?.user);
const hasNoRole = computed(() => {
+ if (typeof user.value?.is_verified === 'boolean') {
+ return !user.value.is_verified;
+ }
+
const roles = user.value?.roles;
return !roles || roles.length === 0;
From c57d5b9219108f832d8c8f5933dd81249e545512 Mon Sep 17 00:00:00 2001
From: Tajim
Date: Thu, 27 Aug 2026 12:05:38 +0600
Subject: [PATCH 3/3] feat(profile): enhance public profile stats, activities
sorting, and badge display
---
.../Controllers/UserProfileController.php | 67 ++-
resources/js/pages/User/Show.vue | 434 +++++++++---------
resources/js/pages/platform/JoinTeam.vue | 8 +-
tests/Feature/UserProfileTest.php | 43 ++
4 files changed, 302 insertions(+), 250 deletions(-)
diff --git a/app/Http/Controllers/UserProfileController.php b/app/Http/Controllers/UserProfileController.php
index 4a9d859..a4e94cf 100644
--- a/app/Http/Controllers/UserProfileController.php
+++ b/app/Http/Controllers/UserProfileController.php
@@ -21,8 +21,6 @@ public function show(string $username)
$user = User::where('username', $username)
->firstOrFail();
- $user->load('roles:id,name');
-
// Study Completions
$completedResourcesCount = $user->completedResources()->count();
$recentCompletions = $user->completedResources()
@@ -40,7 +38,7 @@ public function show(string $username)
->get();
$blogsCount = $user->blogs()->where('is_published', true)->count();
$totalBlogViews = (int) $user->blogs()->where('is_published', true)->sum('views');
- $totalBlogLikes = BlogReaction::whereIn('blog_id', $user->blogs()->where('is_published', true)->select('id'))->count();
+ $upvotesCount = NodeVote::where('user_id', $user->id)->where('type', 'up')->count();
$sharedResourcesCount = Resource::where('user_id', $user->id)->count();
// Appreciations (Received & Given)
@@ -52,23 +50,45 @@ public function show(string $username)
$appreciators = $user->appreciators()
->select(['users.id', 'users.name', 'users.username', 'users.image_path', 'users.institution', 'users.title'])
- ->with('roles:id,name')
- ->latest('user_appreciations.id')
- ->take(50)
+ ->inRandomOrder()
+ ->take(30)
->get();
$appreciating = $user->appreciatingUsers()
->select(['users.id', 'users.name', 'users.username', 'users.image_path', 'users.institution', 'users.title'])
- ->with('roles:id,name')
- ->latest('user_appreciations.id')
- ->take(50)
+ ->inRandomOrder()
+ ->take(30)
->get();
- // Recent Community Activities (Uploads, completed topics, comments made, reactions given, upvoted folders, appreciations given)
+ // Recent Community Activities (Folders created, uploads, completed topics, comments made, reactions given, upvoted folders, appreciations given)
+ $recentFolders = Node::where('user_id', $user->id)
+ ->with([
+ 'subject:id,name,slug',
+ 'parent:id,name,slug',
+ 'parent.parent:id,name,slug',
+ ])
+ ->latest('id')
+ ->take(3)
+ ->get()
+ ->map(function ($node) {
+ $url = $this->buildNodeUrl($node);
+
+ return [
+ 'type' => 'folder',
+ 'title' => $node->name,
+ 'subtitle' => $node->subject?->name.($node->parent ? ' · '.$node->parent->name : ''),
+ 'url' => $url,
+ 'created_at' => $node->created_at?->diffForHumans(),
+ 'timestamp' => $node->created_at?->timestamp ?? 0,
+ ];
+ })
+ ->filter()
+ ->values();
+
$recentUploads = Resource::where('user_id', $user->id)
->with(['node:id,name,subject_id', 'node.subject:id,name'])
->latest()
- ->take(4)
+ ->take(3)
->get()
->map(fn ($item) => [
'type' => 'upload',
@@ -77,12 +97,13 @@ public function show(string $username)
'resource_type' => $item->resource_type,
'url' => "/resources/{$item->id}",
'created_at' => $item->created_at?->diffForHumans(),
+ 'timestamp' => $item->created_at?->timestamp ?? 0,
]);
$recentCompleted = ResourceCompletion::where('user_id', $user->id)
->with(['resource:id,title,node_id', 'resource.node:id,name,subject_id', 'resource.node.subject:id,name'])
->latest()
- ->take(4)
+ ->take(3)
->get()
->map(fn ($item) => [
'type' => 'completion',
@@ -90,26 +111,28 @@ public function show(string $username)
'subtitle' => $item->resource?->node?->subject?->name.' · '.$item->resource?->node?->name,
'url' => $item->resource ? "/resources/{$item->resource->id}" : null,
'created_at' => $item->created_at?->diffForHumans(),
+ 'timestamp' => $item->created_at?->timestamp ?? 0,
])
->filter(fn ($item) => $item['title'] !== null);
$recentReactions = BlogReaction::where('user_id', $user->id)
->with('blog:id,title,slug')
->latest()
- ->take(4)
+ ->take(3)
->get()
->map(fn ($item) => [
'type' => 'reaction',
'title' => $item->blog?->title,
'url' => $item->blog ? "/blogs/{$item->blog->slug}" : null,
'created_at' => $item->created_at?->diffForHumans(),
+ 'timestamp' => $item->created_at?->timestamp ?? 0,
])
->filter(fn ($item) => $item['title'] !== null);
$recentComments = BlogComment::where('user_id', $user->id)
->with('blog:id,title,slug')
->latest()
- ->take(4)
+ ->take(3)
->get()
->map(fn ($item) => [
'type' => 'comment',
@@ -117,6 +140,7 @@ public function show(string $username)
'content' => $item->content,
'url' => $item->blog ? "/blogs/{$item->blog->slug}" : null,
'created_at' => $item->created_at?->diffForHumans(),
+ 'timestamp' => $item->created_at?->timestamp ?? 0,
])
->filter(fn ($item) => $item['title'] !== null);
@@ -128,7 +152,7 @@ public function show(string $username)
'node.parent.parent:id,name,slug',
])
->latest('id')
- ->take(4)
+ ->take(3)
->get()
->map(function ($item) {
$node = $item->node;
@@ -144,6 +168,7 @@ public function show(string $username)
'subtitle' => $node->subject?->name.($node->parent ? ' · '.$node->parent->name : ''),
'url' => $url,
'created_at' => $item->created_at?->diffForHumans(),
+ 'timestamp' => $item->created_at?->timestamp ?? 0,
];
})
->filter()
@@ -152,7 +177,7 @@ public function show(string $username)
$recentAppreciations = UserAppreciation::where('appreciator_id', $user->id)
->with('user:id,name,username')
->latest('id')
- ->take(4)
+ ->take(3)
->get()
->map(fn ($item) => [
'type' => 'appreciation',
@@ -160,6 +185,7 @@ public function show(string $username)
'username' => $item->user?->username,
'url' => $item->user ? "/u/{$item->user->username}" : null,
'created_at' => $item->created_at?->diffForHumans(),
+ 'timestamp' => $item->created_at?->timestamp ?? 0,
])
->filter(fn ($item) => $item['title'] !== null)
->values();
@@ -169,7 +195,6 @@ public function show(string $username)
->whereNotNull('username')
->whereHas('roles')
->select(['id', 'name', 'username', 'title', 'institution', 'image_path', 'about'])
- ->with('roles:id,name')
->inRandomOrder()
->take(2)
->get();
@@ -180,7 +205,6 @@ public function show(string $username)
$randomUsers = User::whereNotIn('id', $excludedIds)
->whereNotNull('username')
->select(['id', 'name', 'username', 'title', 'institution', 'image_path', 'about'])
- ->with('roles:id,name')
->inRandomOrder()
->take($remainingNeeded)
->get();
@@ -200,13 +224,13 @@ public function show(string $username)
'instagram' => $user->instagram,
'github' => $user->github,
'created_at' => $user->created_at?->format('M Y') ?? '2026',
- 'roles' => $user->roles->pluck('name'),
- 'is_staff' => $user->roles->isNotEmpty(),
+ 'is_verified' => $user->is_verified,
+ 'is_staff' => $user->is_verified,
],
'stats' => [
'completedResourcesCount' => $completedResourcesCount,
'blogsCount' => $blogsCount,
- 'totalBlogLikes' => (int) $totalBlogLikes,
+ 'upvotesCount' => $upvotesCount,
'totalBlogViews' => (int) $totalBlogViews,
'sharedResourcesCount' => $sharedResourcesCount,
],
@@ -218,6 +242,7 @@ public function show(string $username)
'recentCompletions' => $recentCompletions,
'blogs' => $publishedBlogs,
'recentActivities' => [
+ 'folders' => $recentFolders->values(),
'uploads' => $recentUploads->values(),
'completions' => $recentCompleted->values(),
'reactions' => $recentReactions->values(),
diff --git a/resources/js/pages/User/Show.vue b/resources/js/pages/User/Show.vue
index 7560753..a66ca85 100644
--- a/resources/js/pages/User/Show.vue
+++ b/resources/js/pages/User/Show.vue
@@ -13,6 +13,7 @@ import {
Eye,
Facebook,
FileText,
+ Folder,
Github,
GraduationCap,
Heart,
@@ -25,6 +26,7 @@ import {
} from 'lucide-vue-next';
import { computed, ref, watch } from 'vue';
import UserListItem from '@/components/UserListItem.vue';
+import VerifiedBadge from '@/components/VerifiedBadge.vue';
const props = defineProps<{
profileUser: {
@@ -39,13 +41,14 @@ const props = defineProps<{
instagram: string | null;
github: string | null;
created_at: string;
- roles: string[];
- is_staff: boolean;
+ is_verified?: boolean;
+ is_staff?: boolean;
+ roles?: string[];
};
stats: {
completedResourcesCount: number;
blogsCount: number;
- totalBlogLikes: number;
+ upvotesCount: number;
totalBlogViews: number;
sharedResourcesCount: number;
};
@@ -58,7 +61,7 @@ const props = defineProps<{
username: string;
image_path?: string | null;
institution?: string | null;
- roles?: Array<{ id: number; name: string }>;
+ is_verified?: boolean;
}>;
appreciating?: Array<{
id: number;
@@ -66,7 +69,7 @@ const props = defineProps<{
username: string;
image_path?: string | null;
institution?: string | null;
- roles?: Array<{ id: number; name: string }>;
+ is_verified?: boolean;
}>;
recentCompletions: Array<{
id: number;
@@ -93,6 +96,14 @@ const props = defineProps<{
created_at: string;
}>;
recentActivities: {
+ folders?: Array<{
+ type: string;
+ title: string;
+ subtitle?: string;
+ url: string | null;
+ created_at: string;
+ timestamp?: number;
+ }>;
uploads: Array<{
type: string;
title: string;
@@ -100,6 +111,7 @@ const props = defineProps<{
resource_type?: string;
url: string | null;
created_at: string;
+ timestamp?: number;
}>;
completions: Array<{
type: string;
@@ -107,12 +119,14 @@ const props = defineProps<{
subtitle?: string;
url: string | null;
created_at: string;
+ timestamp?: number;
}>;
reactions: Array<{
type: string;
title: string;
url: string | null;
created_at: string;
+ timestamp?: number;
}>;
comments: Array<{
type: string;
@@ -120,6 +134,7 @@ const props = defineProps<{
content?: string;
url: string | null;
created_at: string;
+ timestamp?: number;
}>;
upvotes?: Array<{
type: string;
@@ -127,6 +142,7 @@ const props = defineProps<{
subtitle?: string;
url: string | null;
created_at: string;
+ timestamp?: number;
}>;
appreciations?: Array<{
type: string;
@@ -134,6 +150,7 @@ const props = defineProps<{
username?: string;
url: string | null;
created_at: string;
+ timestamp?: number;
}>;
};
suggestedUsers?: Array<{
@@ -145,7 +162,7 @@ const props = defineProps<{
image_url: string | null;
image_path: string | null;
about: string | null;
- roles?: Array<{ id: number; name: string }>;
+ is_verified?: boolean;
}>;
}>();
@@ -163,6 +180,34 @@ const activeTab = ref<'completed' | 'blogs' | 'activity'>(
: 'completed',
);
+interface ActivityItem {
+ type: string;
+ title?: string | null;
+ subtitle?: string | null;
+ content?: string | null;
+ username?: string | null;
+ resource_type?: string | null;
+ url: string | null;
+ created_at: string;
+ timestamp?: number;
+}
+
+const sortedActivities = computed(() => {
+ const list: ActivityItem[] = [
+ ...((props.recentActivities.folders || []) as ActivityItem[]),
+ ...((props.recentActivities.uploads || []) as ActivityItem[]),
+ ...((props.recentActivities.completions || []) as ActivityItem[]),
+ ...((props.recentActivities.reactions || []) as ActivityItem[]),
+ ...((props.recentActivities.comments || []) as ActivityItem[]),
+ ...((props.recentActivities.upvotes || []) as ActivityItem[]),
+ ...((props.recentActivities.appreciations || []) as ActivityItem[]),
+ ];
+
+ return list.sort((a, b) => (b.timestamp ?? 0) - (a.timestamp ?? 0));
+});
+
+const totalActivitiesCount = computed(() => sortedActivities.value.length);
+
const localIsAppreciated = ref(props.isAppreciated);
const localAppreciationsCount = ref(props.appreciationsCount);
const showAppreciatorsModal = ref(false);
@@ -219,45 +264,6 @@ const handleAppreciate = () => {
},
);
};
-
-const getRoleBadge = (roles: string[]) => {
- if (roles.includes('admin')) {
- return {
- label: 'Admin',
- class: 'bg-rose-50 text-rose-700 border-rose-200/80 dark:bg-rose-950/40 dark:text-rose-300 dark:border-rose-800/60',
- };
- }
-
- if (roles.includes('editor')) {
- return {
- label: 'Editor',
- class: 'bg-purple-50 text-purple-700 border-purple-200/80 dark:bg-purple-950/40 dark:text-purple-300 dark:border-purple-800/60',
- };
- }
-
- if (roles.includes('manager')) {
- return {
- label: 'Staff',
- class: 'bg-amber-50 text-amber-700 border-amber-200/80 dark:bg-amber-950/40 dark:text-amber-300 dark:border-amber-800/60',
- };
- }
-
- return {
- label: 'Student',
- class: 'bg-blue-50 text-blue-700 border-blue-200/80 dark:bg-blue-950/40 dark:text-blue-300 dark:border-blue-800/60',
- };
-};
-
-const roleInfo = computed(() => getRoleBadge(props.profileUser.roles));
-
-const totalActivitiesCount = computed(
- () =>
- (props.recentActivities.uploads?.length || 0) +
- (props.recentActivities.reactions?.length || 0) +
- (props.recentActivities.comments?.length || 0) +
- (props.recentActivities.upvotes?.length || 0) +
- (props.recentActivities.appreciations?.length || 0),
-);
@@ -360,13 +366,13 @@ const totalActivitiesCount = computed(
{{ profileUser.name }}
-
-
- {{ roleInfo.label }}
-
+
@@ -608,26 +614,26 @@ const totalActivitiesCount = computed(
-
+
- {{ stats.totalBlogLikes }}
+ {{ stats.upvotesCount }}
- Likes Gained
+ Upvotes Given
@@ -874,96 +880,125 @@ const totalActivitiesCount = computed(
-
-
-
-
-
-
-
Uploaded
-
-
+
+
+
+
+
+
+
+
- {{ item.title }}
-
-
+
+
+
+
- ({{ item.subtitle }})
-
-
-
-
- {{ item.created_at }}
-
-
+
+
-
-
-
-
-
-
-
-
Liked
-
-
+
- {{ item.title }}
-
-
-
-
- {{ item.created_at }}
-
-
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
Commented on
+ >
+ Created folder
+
+ Uploaded
+
+ Completed topic
+
+ Liked
+
+ Commented on
+
+ Upvoted folder
+
+ Appreciated
+
+
{{ item.title }}
+
+ {{ item.title }}
+
+
+
+ ({{ item.subtitle }})
+
+
+ (@{{ item.username }})
+
+
{{ item.created_at }}
+
+
"{{ item.content }}"
-
-
-
-
-
-
- Upvoted folder
-
-
- {{ item.title }}
-
-
- ({{ item.subtitle }})
-
-
-
-
- {{ item.created_at }}
-
-
-
-
-
-
-
-
-
-
- Appreciated
-
-
- {{ item.title }}
-
-
- (@{{ item.username }})
-
-
-
-
- {{ item.created_at }}
-
-
@@ -1125,18 +1100,7 @@ const totalActivitiesCount = computed(
>
{{ person.name }}
-
-
-
+
+
+
+ and
+ {{ localAppreciationsCount - appreciators.length }}
+ more...
+
+
+
+ and
+ {{ appreciatingCount - appreciating.length }}
+ more...
+
- About Us Recognition
+ About Us Recognition & Verified Badge
আমাদের অনুমোদিত প্রমোটর, কিউরেটর, মডারেটর,
- রাইটার এবং ডেভেলপারদের অবদানকে সম্মান জানাতে
- আমাদের অফিশিয়াল
+ রাইটার এবং ডেভেলপারদের প্রোফাইলে একটি অফিশিয়াল
+ Verified Contributor Badge
+ যুক্ত হবে এবং তাদের অবদানকে সম্মান জানাতে আমাদের
+ অফিশিয়াল
assertOk();
$response->assertInertia(fn ($page) => $page
->component('User/Show')
+ ->where('stats.upvotesCount', 1)
->has('recentActivities.upvotes', 1)
->where('recentActivities.upvotes.0.title', 'Differentiation Master Notes')
->where('recentActivities.upvotes.0.subtitle', 'Higher Math · Calculus')
->where('recentActivities.upvotes.0.url', '/higher-math/calculus/diff-notes')
);
});
+
+test('user model computes is_verified attribute based on role presence', function () {
+ $regularUser = User::factory()->create();
+ expect($regularUser->is_verified)->toBeFalse();
+
+ $role = Role::firstOrCreate(['name' => 'editor', 'guard_name' => 'web']);
+ $staffUser = User::factory()->create();
+ $staffUser->assignRole($role);
+
+ expect($staffUser->is_verified)->toBeTrue();
+ expect($staffUser->toArray())->toHaveKey('is_verified', true);
+});
+
+test('public profile returns created folders in recent activities', function () {
+ $user = User::factory()->create(['username' => 'folder_creator']);
+
+ $subject = Subject::create([
+ 'name' => 'Physics',
+ 'slug' => 'physics',
+ 'course' => 'hsc',
+ 'tailwind_format' => 'bg-blue-500',
+ 'icon' => 'atom',
+ ]);
+
+ Node::create([
+ 'user_id' => $user->id,
+ 'subject_id' => $subject->id,
+ 'name' => 'Thermodynamics Chapter',
+ 'slug' => 'thermodynamics-chapter',
+ ]);
+
+ $response = $this->get('/u/folder_creator');
+
+ $response->assertOk();
+ $response->assertInertia(fn ($page) => $page
+ ->component('User/Show')
+ ->has('recentActivities.folders', 1)
+ ->where('recentActivities.folders.0.title', 'Thermodynamics Chapter')
+ ->where('recentActivities.folders.0.subtitle', 'Physics')
+ ->where('recentActivities.folders.0.url', '/physics/thermodynamics-chapter')
+ );
+});