Files
chaos-api/web/default/src/features/dashboard/components/overview/announcements-panel.tsx
T
2026-04-30 19:53:02 +08:00

98 lines
3.4 KiB
TypeScript
Vendored

import { memo, useState } from 'react'
import { Megaphone } from 'lucide-react'
import { useTranslation } from 'react-i18next'
import { getAnnouncementColorClass } from '@/lib/colors'
import { formatDateTimeObject } from '@/lib/time'
import { cn } from '@/lib/utils'
import { ScrollArea } from '@/components/ui/scroll-area'
import { useAnnouncements } from '@/features/dashboard/hooks/use-status-data'
import { getPreviewText } from '@/features/dashboard/lib'
import type { AnnouncementItem } from '@/features/dashboard/types'
import { PanelWrapper } from '../ui/panel-wrapper'
import { AnnouncementDetailModal } from './announcement-detail-dialog'
const AnnouncementStatusDot = memo(function AnnouncementStatusDot(props: {
type?: string
}) {
return (
<span
className={cn(
'mt-1.5 inline-block size-2 shrink-0 rounded-full',
getAnnouncementColorClass(props.type)
)}
/>
)
})
export function AnnouncementsPanel() {
const { t } = useTranslation()
const { items: list, loading } = useAnnouncements()
const [selectedAnnouncement, setSelectedAnnouncement] =
useState<AnnouncementItem | null>(null)
const [isDialogOpen, setIsDialogOpen] = useState(false)
const handleAnnouncementClick = (item: AnnouncementItem) => {
setSelectedAnnouncement(item)
setIsDialogOpen(true)
}
return (
<PanelWrapper
title={
<span className='flex items-center gap-2'>
<Megaphone className='text-muted-foreground/60 size-4' />
{t('Announcements')}
</span>
}
loading={loading}
empty={!list.length}
emptyMessage={t('No announcements at this time')}
height='h-56 sm:h-64'
>
<ScrollArea className='h-56 sm:h-64'>
<div className='-mx-3 sm:-mx-5'>
{list.map((item: AnnouncementItem, idx: number) => {
const key = item.id ?? `announcement-${idx}`
return (
<button
key={key}
type='button'
onClick={() => handleAnnouncementClick(item)}
className={cn(
'group hover:bg-muted/40 w-full px-3 py-3 text-left transition-colors sm:px-5 sm:py-3.5',
idx < list.length - 1 && 'border-border/60 border-b'
)}
>
<div className='flex items-start gap-2.5'>
<AnnouncementStatusDot type={item.type} />
<div className='min-w-0 flex-1 space-y-1'>
<p className='line-clamp-1 text-sm font-medium'>
{getPreviewText(item.content)}
</p>
<div className='flex items-center justify-between'>
{item.publishDate && (
<time className='text-muted-foreground/60 text-xs'>
{formatDateTimeObject(new Date(item.publishDate))}
</time>
)}
<span className='text-muted-foreground/40 text-xs opacity-0 transition-opacity group-hover:opacity-100'>
{t('Click for details')}
</span>
</div>
</div>
</div>
</button>
)
})}
</div>
</ScrollArea>
<AnnouncementDetailModal
open={isDialogOpen}
onOpenChange={setIsDialogOpen}
announcement={selectedAnnouncement}
/>
</PanelWrapper>
)
}