Files
EJClaw/apps/dashboard/src/dashboardHelpers.ts
Eyejoker c19665a134 Extract dashboard TaskPanel component (#63)
* review: harden readonly git checks and lint verification

* test: satisfy readonly reviewer sandbox typing

* test: fix readonly reviewer sandbox assertions

* test: remove impossible readonly sandbox guards

* test: relax readonly sandbox assertions

* test: cast readonly sandbox expectation shape

* test: cast readonly sandbox expectation through unknown

* Phase 0 — STEP_DONE/TASK_DONE split + owner-follow-up integration smoke

* Add STEP_DONE guards, verdict storage, and stale delivery suppression

* style: format paired stepdone telemetry files

* Route STEP_DONE through reviewer

* Add structured Discord attachments

* style: format structured attachment test

* Fix room thread bot output parity

* Remove duplicate work item attachment migration from owner branch

* Remove legacy dashboard rooms renderer

* Extract dashboard parsed body renderer

* Extract dashboard redaction helpers

* Extract dashboard RoomCardV2 component

* Include RoomCardV2 test in vitest suite

* Extract dashboard RoomBoardV2 component

* Extract dashboard EmptyState component

* Extract dashboard InboxPanel component

* Split dashboard InboxPanel card renderer

* Extract dashboard TaskPanel component
2026-04-28 15:35:02 +09:00

70 lines
2.0 KiB
TypeScript

import type { DashboardTask, DashboardTaskAction } from './api';
import { localeTags, type Locale, type Messages } from './i18n';
export function formatDate(
value: string | null | undefined,
locale: Locale,
): string {
if (!value) return '-';
const date = new Date(value);
if (Number.isNaN(date.getTime())) return value;
const now = Date.now();
const ageMs = now - date.getTime();
if (ageMs >= 0 && ageMs < 60_000) {
return locale === 'ko'
? '방금'
: locale === 'ja'
? 'たった今'
: locale === 'zh'
? '刚刚'
: 'just now';
}
if (ageMs >= 0 && ageMs < 3_600_000) {
const mins = Math.floor(ageMs / 60_000);
return locale === 'ko'
? `${mins}분 전`
: locale === 'ja'
? `${mins}分前`
: locale === 'zh'
? `${mins} 分钟前`
: `${mins}m ago`;
}
const sameDay = new Date().toDateString() === date.toDateString();
if (sameDay) {
return new Intl.DateTimeFormat(localeTags[locale], {
hour: '2-digit',
hour12: false,
minute: '2-digit',
}).format(date);
}
const time = new Intl.DateTimeFormat(localeTags[locale], {
hour: '2-digit',
hour12: false,
minute: '2-digit',
}).format(date);
if (locale === 'ko')
return `${date.getMonth() + 1}${date.getDate()}${time}`;
if (locale === 'ja')
return `${date.getMonth() + 1}${date.getDate()}${time}`;
if (locale === 'zh')
return `${date.getMonth() + 1}${date.getDate()}${time}`;
return new Intl.DateTimeFormat(localeTags[locale], {
day: 'numeric',
hour: '2-digit',
hour12: false,
minute: '2-digit',
month: 'short',
}).format(date);
}
export function taskActionsFor(task: DashboardTask): DashboardTaskAction[] {
if (task.status === 'active') return ['pause', 'cancel'];
if (task.status === 'paused') return ['resume', 'cancel'];
return [];
}
export function statusLabel(status: string, t: Messages): string {
if (status in t.status) return t.status[status as keyof Messages['status']];
return status;
}