Files
forge/client/src/components/Board/BoardCard/index.tsx
T
Dmytro Tkachenko 28d817ebe9 Init
2026-08-29 11:59:28 +03:00

57 lines
2.2 KiB
TypeScript

import { ExternalIcon } from '../../icons';
import { parseDate, timeAgo, initials } from '../../../utils/format.utils';
import type { Ticket } from '../../../types/ticket.types';
import styles from './BoardCard.module.scss';
function daysSince(v: string | null): number | null {
const d = parseDate(v);
if (!d) return null;
return Math.floor((Date.now() - d.getTime()) / 86_400_000);
}
interface BoardCardProps {
ticket: Ticket;
color: string;
brandColor?: string;
onSelect: (t: Ticket) => void;
}
export default function BoardCard({ ticket, color, brandColor, onSelect }: BoardCardProps) {
const lifetime = daysSince(ticket.openedAt);
const inState = daysSince(ticket.stateChangedAt);
const bm = [ticket.brand, ticket.market].filter(Boolean).join(' · ');
return (
<div className={styles.card} style={{ '--col': color } as React.CSSProperties} onClick={() => onSelect(ticket)}>
<div className={styles.top}>
{ticket.link ? (
<a href={ticket.link} target="_blank" rel="noopener noreferrer" className={styles.number}
onClick={e => e.stopPropagation()} title="Open in ServiceNow">
{ticket.number}<ExternalIcon size={11} />
</a>
) : (
<span className={styles.number}>{ticket.number}</span>
)}
{ticket.jira?.status && <span className={styles.jira}>{ticket.jira.status}</span>}
</div>
<div className={styles.title} title={ticket.shortDesc}>{ticket.shortDesc || '—'}</div>
{bm && <span className={styles.bm} style={brandColor ? { background: `${brandColor}1a`, color: brandColor, borderColor: `${brandColor}55` } : undefined}>{bm}</span>}
<div className={styles.foot}>
<span className={styles.assignee}>
<span className={styles.avatar}>{initials(ticket.assignedTo)}</span>
<span className={styles.aName}>{ticket.assignedTo ?? 'Unassigned'}</span>
</span>
{inState != null && <span className={styles.pill} title="days in current state">{inState}d</span>}
</div>
<div className={styles.meta}>
{lifetime != null && <span title="lifetime"> {lifetime}d</span>}
<span title="last activity"> {timeAgo(ticket.lastActivityAt)}</span>
</div>
</div>
);
}