'use client' /** * Slack Message Card — unified visual for slack-messages markers. * * PURE PRESENTATION (no internal nav). The card receives the structured * `` prop bundle via `anchorProps` (composed by the caller from a * runtime hook) and renders the rest from the `SlackMessageItem` shape * directly. */ import React from 'react' import { SlackIcon } from '../../icons/slack-icon' import { ExternalLink, Hash } from 'lucide-react' import { COMPACT_CARD_ICON_SLOT, COMPACT_CARD_META_ROW, COMPACT_CARD_META_ROW_BOX, COMPACT_CARD_OUTER, COMPACT_CARD_OUTER_STATIC, COMPACT_CARD_ROW_FILLER, COMPACT_CARD_SKELETON_IMAGE_SLOT, COMPACT_CARD_SKELETON_OUTER, COMPACT_CARD_SUMMARY, COMPACT_CARD_TEXT_COL, COMPACT_CARD_TITLE, COMPACT_CARD_TITLE_ROW, safeHref, } from '../utils/compact-card-classes' import { formatDateUTC as formatDate } from '../../../utils/format' import type { SlackMessageItem } from '../types/entities/slack-message' export interface SlackMessageCardAnchorProps { href: string target?: '_blank' rel?: 'noopener noreferrer' onClick?: (e: React.MouseEvent) => void } export interface SlackMessageCardProps { item: SlackMessageItem variant?: 'row' | 'compact' className?: string anchorProps?: SlackMessageCardAnchorProps } function parseChannelFromUrl(url: string | null | undefined): string | null { if (!url) return null try { const u = new URL(url) if (!u.host.endsWith('.slack.com')) return null const parts = u.pathname.split('/').filter(Boolean) const archivesIdx = parts.indexOf('archives') if (archivesIdx !== -1 && parts.length > archivesIdx + 1) return parts[archivesIdx + 1] } catch { /* malformed URL */ } return null } export function SlackMessageCard({ item, variant = 'compact', className, anchorProps }: SlackMessageCardProps) { const channel = item.channel ?? parseChannelFromUrl(item.url) ?? null const dateText = formatDate(item.dateUpdated, { fallback: '', timezone: 'local' }) if (variant === 'row') { return (
{item.title} {item.preview ? ( {item.preview} ) : null} {channel ? ( {channel} ) : null} {dateText ? ( {dateText} ) : null}
) } const metaParts: React.ReactNode[] = [] if (channel) { metaParts.push( {channel} , ) } if (dateText) metaParts.push({dateText}) const href = safeHref(item.url) const body = ( <> {item.title} {item.preview || COMPACT_CARD_ROW_FILLER} {metaParts.length > 0 ? metaParts.map((part, i) => ( {i > 0 ? · : null} {part} )) : {COMPACT_CARD_ROW_FILLER}} {href ? ( ) : null} ) if (anchorProps) { return (
{body} ) } return href ? ( {body} ) : ( {body} ) } export function SlackMessageCardSkeleton({ variant = 'compact', className }: { variant?: 'row' | 'compact'; className?: string }) { if (variant === 'row') { return (
) } return ( ) }