diff --git a/frontend/src/app/(dashboard)/organizations/page.tsx b/frontend/src/app/(dashboard)/organizations/page.tsx
index b3c9ddb..df889a0 100644
--- a/frontend/src/app/(dashboard)/organizations/page.tsx
+++ b/frontend/src/app/(dashboard)/organizations/page.tsx
@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react';
import { useToast } from '@/lib/hooks/useToast';
import { Check, Trash2, UserPlus, X } from 'lucide-react';
import { useAuth } from '@/lib/hooks/useAuth';
+import { notifyPendingConnectionsChanged } from '@/lib/hooks/usePendingConnectionsCount';
import { useOrganizationInviteLinkCopy } from '@/lib/hooks/useOrganizationInviteLinkCopy';
import {
organizationApi,
@@ -246,6 +247,7 @@ export default function OrganizationsPage() {
toast.showSuccess(
action === 'ACCEPT' ? 'Connection request accepted.' : 'Connection request declined.',
);
+ notifyPendingConnectionsChanged();
await loadList();
} catch (e) {
toast.showError(formatApiMessage(e));
diff --git a/frontend/src/components/ui/shared/Sidebar.tsx b/frontend/src/components/ui/shared/Sidebar.tsx
index 80bc7f8..7738326 100644
--- a/frontend/src/components/ui/shared/Sidebar.tsx
+++ b/frontend/src/components/ui/shared/Sidebar.tsx
@@ -13,6 +13,7 @@ import {
CreditCard,
} from 'lucide-react';
import { useAuth } from '@/lib/hooks/useAuth';
+import { usePendingConnectionsCount } from '@/lib/hooks/usePendingConnectionsCount';
import { canAccessAppointmentsSection, canViewTab } from '@/components/shared/permissions';
import {
counterpartOrganizationType,
@@ -32,6 +33,7 @@ const menu = [
function Sidebar() {
const pathname = usePathname();
const { currentOrganization } = useAuth();
+ const pendingConnectionsCount = usePendingConnectionsCount();
const counterpartLabel = currentOrganization?.type === 'LAB' ? 'Clinics' : 'Labs';
const organizationsTabIcon = organizationTypeIcon(
counterpartOrganizationType(currentOrganization?.type),
@@ -75,6 +77,8 @@ function Sidebar() {
{visibleMenu.map((item) => {
const Icon = item.icon;
const isActive = pathname === item.path;
+ const showPendingBadge =
+ item.path === '/organizations' && pendingConnectionsCount > 0;
return (
- {item.name}
+ {item.name}
+ {showPendingBadge && (
+
+ {pendingConnectionsCount}
+
+ )}
);
})}
diff --git a/frontend/src/lib/api/organization.ts b/frontend/src/lib/api/organization.ts
index 7c940ce..0e54cd8 100644
--- a/frontend/src/lib/api/organization.ts
+++ b/frontend/src/lib/api/organization.ts
@@ -46,6 +46,11 @@ export const organizationApi = {
return response.data;
},
+ pendingIncomingCount: async (): Promise<{ success: boolean; data: { count: number } }> => {
+ const response = await apiClient.get('/organizations/connections/pending-count');
+ return response.data;
+ },
+
listInvitations: async (): Promise<{
success: boolean;
data: { items: OrganizationInvitationHistoryItemDto[] };
diff --git a/frontend/src/lib/hooks/usePendingConnectionsCount.ts b/frontend/src/lib/hooks/usePendingConnectionsCount.ts
new file mode 100644
index 0000000..4be203b
--- /dev/null
+++ b/frontend/src/lib/hooks/usePendingConnectionsCount.ts
@@ -0,0 +1,48 @@
+'use client';
+
+import { useCallback, useEffect, useState } from 'react';
+import { usePathname } from 'next/navigation';
+import { canViewTab } from '@/components/shared/permissions';
+import { organizationApi } from '@/lib/api/organization';
+import { useAuth } from '@/lib/hooks/useAuth';
+
+/** Tell the sidebar badge to refetch after accept/decline on the organizations page. */
+export function notifyPendingConnectionsChanged() {
+ window.dispatchEvent(new Event('pending-connections-changed'));
+}
+
+/**
+ * Sidebar-only: fetches GET /organizations/connections/pending-count.
+ * Independent from the organizations tab list API.
+ */
+export function usePendingConnectionsCount(): number {
+ const pathname = usePathname();
+ const { currentOrganization } = useAuth();
+ const [count, setCount] = useState(0);
+
+ const fetchCount = useCallback(async () => {
+ if (!currentOrganization?.id || !canViewTab(currentOrganization, 'TAB_ORGANIZATIONS_READ')) {
+ setCount(0);
+ return;
+ }
+
+ try {
+ const res = await organizationApi.pendingIncomingCount();
+ setCount(res.data.count);
+ } catch {
+ setCount(0);
+ }
+ }, [currentOrganization]);
+
+ useEffect(() => {
+ void fetchCount();
+ }, [fetchCount, pathname]);
+
+ useEffect(() => {
+ const onChanged = () => void fetchCount();
+ window.addEventListener('pending-connections-changed', onChanged);
+ return () => window.removeEventListener('pending-connections-changed', onChanged);
+ }, [fetchCount]);
+
+ return count;
+}