import { useFlag } from 'common' import { AlertTriangle, ChevronRight, Inbox } from 'lucide-react' import { Badge, Button, cn } from 'ui' import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader' import type { AdvisorItem } from './AdvisorPanel.types' import { advisorCategoryIcons, formatItemDate, getAdvisorItemSecondaryText, getAdvisorPanelItemDisplayTitle, severityBadgeVariants, severityColorClasses, severityLabels, } from './AdvisorPanel.utils' import { EmptyAdvisor } from './EmptyAdvisor' import type { Notification } from '@/data/notifications/notifications-v2-query' import type { AdvisorCategory, AdvisorSeverity } from '@/state/advisor-state' const NoProjectNotice = () => { const isHealthAdvisorEnabled = useFlag('healthAdvisor') return (

Project required

{isHealthAdvisorEnabled ? 'Select a project to view its security, performance and health advisories' : 'Select a project to view its security and performance advisories'}

) } interface AdvisorPanelBodyProps { isLoading: boolean isError: boolean filteredItems: AdvisorItem[] categoryFilters: AdvisorCategory[] severityFilters: AdvisorSeverity[] onItemClick: (item: AdvisorItem) => void onClearFilters: () => void onShowHiddenItems: () => void hiddenItemsCount: number hasAnyFilters: boolean hasProjectRef?: boolean projectNameByRef?: ReadonlyMap } export const AdvisorPanelBody = ({ isLoading, isError, filteredItems, categoryFilters, severityFilters, onItemClick, onClearFilters, onShowHiddenItems, hiddenItemsCount, hasAnyFilters, hasProjectRef = true, projectNameByRef, }: AdvisorPanelBodyProps) => { // Notifications are the only items that exist without a project, so the notice replaces // the list whenever the user has narrowed to categories that need one. const needsProject = categoryFilters.length > 0 && categoryFilters.every((category) => category !== 'messages') if (!hasProjectRef && needsProject) { return } if (isLoading) { return (
) } if (isError) { return (

Error loading advisories

Please try again later.

) } if (filteredItems.length === 0) { return ( ) } return ( <>
{filteredItems.map((item) => { const CategoryIcon = advisorCategoryIcons[item.category] const severityClass = severityColorClasses[item.severity] const isNotification = item.source === 'notification' const notification = isNotification ? (item.original as Notification) : null const isUnread = notification?.status === 'new' const primaryText = getAdvisorPanelItemDisplayTitle(item) const secondaryText = getAdvisorItemSecondaryText(item, projectNameByRef) const metadataText = secondaryText ?? (item.createdAt ? formatItemDate(item.createdAt) : undefined) // Date strings (e.g. "a few seconds ago") come from formatItemDate and // need sentence-case capitalisation; entity strings (lint / signal) don't. const metadataCapitalize = secondaryText === undefined && item.createdAt !== undefined return (
) })}
{severityFilters.length > 0 && hiddenItemsCount > 0 && (
)} ) }