feat: add pending feedback count to user notifications and sidebar

This commit is contained in:
Yoga Pangestu 2026-08-31 14:48:02 +07:00
parent 86ea75ec91
commit 941de83949
5 changed files with 44 additions and 14 deletions

View File

@ -2,6 +2,7 @@
namespace App\Http\Middleware;
use App\Services\Admin\FeedbackService;
use App\Services\NotificationService;
use Illuminate\Http\Request;
use Inertia\Inertia;
@ -48,6 +49,9 @@ public function share(Request $request): array
'unreadNotificationsCount' => fn () => $request->user()
? app(NotificationService::class)->unreadCount($request->user())
: 0,
'pendingFeedbackCount' => fn () => $request->user()
? app(FeedbackService::class)->pendingCount($request->user())
: 0,
'notifications' => Inertia::merge(fn () => $request->user()
? app(NotificationService::class)->paginated($request->user())
: null)->append('data', 'id'),

View File

@ -2,6 +2,7 @@
namespace App\Services\Admin;
use App\Enums\FeedbackStatus;
use App\Models\Feedback;
use App\Models\User;
use App\Support\TextAlignAttributeSanitizer;
@ -51,6 +52,14 @@ public function paginated(User $user, int $perPage = 25, string $search = '', ?s
->paginate($perPage);
}
public function pendingCount(User $user): int
{
return Feedback::query()
->where('user_id', $user->id)
->where('status', '!=', FeedbackStatus::Resolved)
->count();
}
public function create(User $user, array $data): Feedback
{
return Feedback::create([

View File

@ -224,21 +224,27 @@ function buildNavMain({
];
}
const navSecondary: { title: string; url: string; icon: Icon }[] = [
function buildNavSecondary(
pendingFeedbackCount: number,
): { title: string; url: string; icon: Icon; badge?: number }[] {
return [
{
title: 'Kritik dan Saran',
url: feedbackRoute.url(),
icon: IconMessageDots,
badge: pendingFeedbackCount,
},
{
title: 'Bantuan',
url: '#',
icon: IconHelp,
},
];
];
}
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
const { name, auth } = usePage<{ auth: Auth }>().props;
const { name, auth, pendingFeedbackCount } = usePage<{ auth: Auth }>()
.props;
const roleNames = auth?.user?.roles?.map((role) => role.name) ?? [];
const isStaff = roleNames.some((role) => STAFF_ROLES.includes(role));
const isMahasiswa = !isStaff && roleNames.includes('mahasiswa');
@ -273,7 +279,10 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
</SidebarHeader>
<SidebarContent>
<NavMain items={navMain} />
<NavSecondary items={navSecondary} className="mt-auto" />
<NavSecondary
items={buildNavSecondary(pendingFeedbackCount ?? 0)}
className="mt-auto"
/>
</SidebarContent>
</Sidebar>
);

View File

@ -8,6 +8,7 @@ import {
SidebarGroup,
SidebarGroupContent,
SidebarMenu,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
} from '@/components/ui/sidebar';
@ -20,6 +21,7 @@ export function NavSecondary({
title: string;
url: string;
icon: Icon;
badge?: number;
}[];
} & React.ComponentPropsWithoutRef<typeof SidebarGroup>) {
return (
@ -34,6 +36,11 @@ export function NavSecondary({
<span>{item.title}</span>
</Link>
</SidebarMenuButton>
{!!item.badge && (
<SidebarMenuBadge>
{item.badge}
</SidebarMenuBadge>
)}
</SidebarMenuItem>
))}
</SidebarMenu>

View File

@ -14,6 +14,7 @@ declare module '@inertiajs/core' {
auth: Auth;
sidebarOpen: boolean;
unreadNotificationsCount: number;
pendingFeedbackCount: number;
[key: string]: unknown;
};
}