OpenMAIC-React / src /components /scene-renderers /pbl /issueboard-panel.tsx
muthuk1's picture
Convert OpenMAIC from Next.js to React (Vite)
f56a29b verified
import type { PBLIssueboard, PBLIssue } from '@/lib/pbl/types';
import { useI18n } from '@/lib/hooks/use-i18n';
interface IssueboardPanelProps {
readonly issueboard: PBLIssueboard;
}
export function IssueboardPanel({ issueboard }: IssueboardPanelProps) {
const { t } = useI18n();
const sortedIssues = [...issueboard.issues].sort((a, b) => a.index - b.index);
const doneCount = sortedIssues.filter((i) => i.is_done).length;
const totalCount = sortedIssues.length;
const progressPercent = totalCount > 0 ? Math.round((doneCount / totalCount) * 100) : 0;
return (
<div className="flex flex-col h-full">
{/* Header */}
<div className="px-4 py-3 border-b">
<h2 className="font-semibold text-sm">{t('pbl.issueboard.title')}</h2>
<div className="mt-2 flex items-center gap-2">
<div className="flex-1 h-2 bg-muted rounded-full overflow-hidden">
<div
className="h-full bg-primary rounded-full transition-all duration-500"
style={{ width: `${progressPercent}%` }}
/>
</div>
<span className="text-xs text-muted-foreground whitespace-nowrap">
{doneCount}/{totalCount}
</span>
</div>
</div>
{/* Issue List */}
<div className="flex-1 overflow-y-auto p-3 space-y-2">
{sortedIssues.map((issue) => (
<IssueCard key={issue.id} issue={issue} />
))}
{sortedIssues.length === 0 && (
<p className="text-sm text-muted-foreground text-center py-4">
{t('pbl.issueboard.noIssues')}
</p>
)}
</div>
</div>
);
}
function IssueCard({ issue }: { issue: PBLIssue }) {
const { t } = useI18n();
const statusColor = issue.is_done
? 'border-green-500/50 bg-green-50 dark:bg-green-950/20'
: issue.is_active
? 'border-primary bg-primary/5'
: 'border-muted';
const statusLabel = issue.is_done
? t('pbl.issueboard.statusDone')
: issue.is_active
? t('pbl.issueboard.statusActive')
: t('pbl.issueboard.statusPending');
const statusBadgeColor = issue.is_done
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
: issue.is_active
? 'bg-primary/10 text-primary'
: 'bg-muted text-muted-foreground';
return (
<div className={`border rounded-lg p-3 ${statusColor} transition-colors`}>
<div className="flex items-start justify-between gap-2">
<h3 className="font-medium text-sm leading-tight">{issue.title}</h3>
<span
className={`text-[10px] font-medium px-1.5 py-0.5 rounded-full whitespace-nowrap ${statusBadgeColor}`}
>
{statusLabel}
</span>
</div>
<p className="text-xs text-muted-foreground mt-1 line-clamp-2">{issue.description}</p>
<div className="text-[10px] text-muted-foreground mt-1.5">{issue.person_in_charge}</div>
</div>
);
}