From 3eef4f4d446ff41419f1da54689b28487b91c794 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 20:17:58 +0000 Subject: [PATCH 1/4] Initial plan From 0a020b5d72d1ae81dfb305e1bff77eb75acf0495 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 20:20:41 +0000 Subject: [PATCH 2/4] feat: use project slugs instead of IDs in notification links Co-authored-by: 0xdevcollins <90073781+0xdevcollins@users.noreply.github.com> --- components/notifications/NotificationDropdown.tsx | 14 ++++++++++++-- components/notifications/NotificationItem.tsx | 8 ++++++++ types/notifications.ts | 1 + 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/components/notifications/NotificationDropdown.tsx b/components/notifications/NotificationDropdown.tsx index 12032373..5a034f4a 100644 --- a/components/notifications/NotificationDropdown.tsx +++ b/components/notifications/NotificationDropdown.tsx @@ -109,11 +109,21 @@ export const NotificationDropdown = ({ notification.data.teamInvitationId && notification.data.projectId ) { - router.push(`/projects/${notification.data.projectId}`); + // Prefer slug over ID for project links + if (notification.data.projectSlug) { + router.push(`/projects/${notification.data.projectSlug}`); + } else { + router.push(`/projects/${notification.data.projectId}`); + } } // Project notifications else if (notification.data.projectId) { - router.push(`/projects/${notification.data.projectId}`); + // Prefer slug over ID for project links + if (notification.data.projectSlug) { + router.push(`/projects/${notification.data.projectSlug}`); + } else { + router.push(`/projects/${notification.data.projectId}`); + } } // Comment notifications else if (notification.data.commentId) { diff --git a/components/notifications/NotificationItem.tsx b/components/notifications/NotificationItem.tsx index 3fc8b549..bece6384 100644 --- a/components/notifications/NotificationItem.tsx +++ b/components/notifications/NotificationItem.tsx @@ -37,11 +37,19 @@ export const NotificationItem = ({ // Team invitation notifications (navigate to project if available) if (notification.data.teamInvitationId && notification.data.projectId) { + // Prefer slug over ID for project links + if (notification.data.projectSlug) { + return `/projects/${notification.data.projectSlug}`; + } return `/projects/${notification.data.projectId}`; } // Project notifications if (notification.data.projectId) { + // Prefer slug over ID for project links + if (notification.data.projectSlug) { + return `/projects/${notification.data.projectSlug}`; + } return `/projects/${notification.data.projectId}`; } diff --git a/types/notifications.ts b/types/notifications.ts index c33b71ec..80b37fb3 100644 --- a/types/notifications.ts +++ b/types/notifications.ts @@ -104,6 +104,7 @@ export interface Notification { // Project fields projectId?: string; projectName?: string; + projectSlug?: string; // Member fields memberEmail?: string; role?: string; From c9233e22e1ae0cf08ddc8bb4c33076531dd60400 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 20:21:30 +0000 Subject: [PATCH 3/4] refactor: extract getProjectUrl helper to reduce code duplication Co-authored-by: 0xdevcollins <90073781+0xdevcollins@users.noreply.github.com> --- .../notifications/NotificationDropdown.tsx | 22 +++++++++---------- components/notifications/NotificationItem.tsx | 20 ++++++++--------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/components/notifications/NotificationDropdown.tsx b/components/notifications/NotificationDropdown.tsx index 5a034f4a..73d6c352 100644 --- a/components/notifications/NotificationDropdown.tsx +++ b/components/notifications/NotificationDropdown.tsx @@ -77,6 +77,14 @@ export const NotificationDropdown = ({ }: NotificationDropdownProps) => { const router = useRouter(); + const getProjectUrl = (data: Notification['data']): string => { + // Prefer slug over ID for project links + if (data.projectSlug) { + return `/projects/${data.projectSlug}`; + } + return `/projects/${data.projectId}`; + }; + const handleNotificationClick = async (notification: Notification) => { if (onNotificationClick) { onNotificationClick(notification); @@ -109,21 +117,11 @@ export const NotificationDropdown = ({ notification.data.teamInvitationId && notification.data.projectId ) { - // Prefer slug over ID for project links - if (notification.data.projectSlug) { - router.push(`/projects/${notification.data.projectSlug}`); - } else { - router.push(`/projects/${notification.data.projectId}`); - } + router.push(getProjectUrl(notification.data)); } // Project notifications else if (notification.data.projectId) { - // Prefer slug over ID for project links - if (notification.data.projectSlug) { - router.push(`/projects/${notification.data.projectSlug}`); - } else { - router.push(`/projects/${notification.data.projectId}`); - } + router.push(getProjectUrl(notification.data)); } // Comment notifications else if (notification.data.commentId) { diff --git a/components/notifications/NotificationItem.tsx b/components/notifications/NotificationItem.tsx index bece6384..9e6a9046 100644 --- a/components/notifications/NotificationItem.tsx +++ b/components/notifications/NotificationItem.tsx @@ -21,6 +21,14 @@ export const NotificationItem = ({ }: NotificationItemProps) => { const Icon = getNotificationIcon(notification.type); + const getProjectUrl = (data: Notification['data']): string => { + // Prefer slug over ID for project links + if (data.projectSlug) { + return `/projects/${data.projectSlug}`; + } + return `/projects/${data.projectId}`; + }; + const getNotificationLink = (): string => { // Organization notifications if (notification.data.organizationId) { @@ -37,20 +45,12 @@ export const NotificationItem = ({ // Team invitation notifications (navigate to project if available) if (notification.data.teamInvitationId && notification.data.projectId) { - // Prefer slug over ID for project links - if (notification.data.projectSlug) { - return `/projects/${notification.data.projectSlug}`; - } - return `/projects/${notification.data.projectId}`; + return getProjectUrl(notification.data); } // Project notifications if (notification.data.projectId) { - // Prefer slug over ID for project links - if (notification.data.projectSlug) { - return `/projects/${notification.data.projectSlug}`; - } - return `/projects/${notification.data.projectId}`; + return getProjectUrl(notification.data); } // Comment notifications From c5a3a85b34a773f8f1abc2edc44d381cc68bc742 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 3 Feb 2026 20:22:32 +0000 Subject: [PATCH 4/4] refactor: extract project URL logic to shared utility and add guard for missing fields Co-authored-by: 0xdevcollins <90073781+0xdevcollins@users.noreply.github.com> --- .../notifications/NotificationDropdown.tsx | 19 +++++++------- components/notifications/NotificationItem.tsx | 13 +++------- lib/notifications.ts | 25 +++++++++++++++++++ 3 files changed, 37 insertions(+), 20 deletions(-) create mode 100644 lib/notifications.ts diff --git a/components/notifications/NotificationDropdown.tsx b/components/notifications/NotificationDropdown.tsx index 73d6c352..9a5b983e 100644 --- a/components/notifications/NotificationDropdown.tsx +++ b/components/notifications/NotificationDropdown.tsx @@ -12,6 +12,7 @@ import { Notification } from '@/types/notifications'; import { Skeleton } from '@/components/ui/skeleton'; import { markAsRead } from '@/lib/api/notifications'; import { toast } from 'sonner'; +import { getProjectUrlFromNotification } from '@/lib/notifications'; interface NotificationDropdownProps { notifications: Notification[]; @@ -77,14 +78,6 @@ export const NotificationDropdown = ({ }: NotificationDropdownProps) => { const router = useRouter(); - const getProjectUrl = (data: Notification['data']): string => { - // Prefer slug over ID for project links - if (data.projectSlug) { - return `/projects/${data.projectSlug}`; - } - return `/projects/${data.projectId}`; - }; - const handleNotificationClick = async (notification: Notification) => { if (onNotificationClick) { onNotificationClick(notification); @@ -117,11 +110,17 @@ export const NotificationDropdown = ({ notification.data.teamInvitationId && notification.data.projectId ) { - router.push(getProjectUrl(notification.data)); + const url = getProjectUrlFromNotification(notification.data); + if (url) { + router.push(url); + } } // Project notifications else if (notification.data.projectId) { - router.push(getProjectUrl(notification.data)); + const url = getProjectUrlFromNotification(notification.data); + if (url) { + router.push(url); + } } // Comment notifications else if (notification.data.commentId) { diff --git a/components/notifications/NotificationItem.tsx b/components/notifications/NotificationItem.tsx index 9e6a9046..9591bb6d 100644 --- a/components/notifications/NotificationItem.tsx +++ b/components/notifications/NotificationItem.tsx @@ -5,6 +5,7 @@ import Link from 'next/link'; import { Notification } from '@/types/notifications'; import { getNotificationIcon } from './NotificationIcon'; import { cn } from '@/lib/utils'; +import { getProjectUrlFromNotification } from '@/lib/notifications'; interface NotificationItemProps { notification: Notification; @@ -21,14 +22,6 @@ export const NotificationItem = ({ }: NotificationItemProps) => { const Icon = getNotificationIcon(notification.type); - const getProjectUrl = (data: Notification['data']): string => { - // Prefer slug over ID for project links - if (data.projectSlug) { - return `/projects/${data.projectSlug}`; - } - return `/projects/${data.projectId}`; - }; - const getNotificationLink = (): string => { // Organization notifications if (notification.data.organizationId) { @@ -45,12 +38,12 @@ export const NotificationItem = ({ // Team invitation notifications (navigate to project if available) if (notification.data.teamInvitationId && notification.data.projectId) { - return getProjectUrl(notification.data); + return getProjectUrlFromNotification(notification.data) || '#'; } // Project notifications if (notification.data.projectId) { - return getProjectUrl(notification.data); + return getProjectUrlFromNotification(notification.data) || '#'; } // Comment notifications diff --git a/lib/notifications.ts b/lib/notifications.ts new file mode 100644 index 00000000..eb298136 --- /dev/null +++ b/lib/notifications.ts @@ -0,0 +1,25 @@ +import { Notification } from '@/types/notifications'; + +/** + * Generates a project URL from notification data. + * Prefers slug over ID for SEO-friendly URLs. + * + * @param data - The notification data object + * @returns The project URL path, or empty string if neither slug nor ID is available + */ +export const getProjectUrlFromNotification = ( + data: Notification['data'] +): string => { + // Prefer slug over ID for project links + if (data.projectSlug) { + return `/projects/${data.projectSlug}`; + } + + // Fallback to ID if slug is not available + if (data.projectId) { + return `/projects/${data.projectId}`; + } + + // Return empty string if neither is available + return ''; +};