feat(kanban): deadlines en cards (context menu, badges, calendario, history)
- migration 009 + columna deadline TEXT en cards - backend: CardPatch.HasDeadline, eventos deadline_set/deadline_cleared - KanbanCard: menu derecho con DatePicker, badge countdown con colores por ratio (azul>=50%, amarillo<50%, rojo<10%, red.9 overdue) - App.tsx: filtro "Con deadline", handleSetCardDeadline optimista, jump-to-card + highlight - CalendarView: popover por dia con seq_num + titulo, click navega a card en tablero - HistoryModal: render eventos deadline_set/deadline_cleared - .gitignore: *.log Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,41 +1,2 @@
|
||||
// Escala unidades segun magnitud: m | h Xm | D Xh | S XD | M XS.
|
||||
// <1 minuto cae como "0m" para mantener la unidad mas pequena coherente.
|
||||
const MIN = 60_000;
|
||||
const HOUR = 60 * MIN;
|
||||
const DAY = 24 * HOUR;
|
||||
const WEEK = 7 * DAY;
|
||||
const MONTH = 30 * DAY;
|
||||
|
||||
export function formatDuration(ms: number): string {
|
||||
if (!Number.isFinite(ms) || ms < 0) return "0m";
|
||||
if (ms < HOUR) return `${Math.floor(ms / MIN)}m`;
|
||||
if (ms < DAY) {
|
||||
const h = Math.floor(ms / HOUR);
|
||||
const m = Math.floor((ms % HOUR) / MIN);
|
||||
return m === 0 ? `${h}h` : `${h}h ${m}m`;
|
||||
}
|
||||
if (ms < WEEK) {
|
||||
const d = Math.floor(ms / DAY);
|
||||
const h = Math.floor((ms % DAY) / HOUR);
|
||||
return h === 0 ? `${d}D` : `${d}D ${h}h`;
|
||||
}
|
||||
if (ms < MONTH) {
|
||||
const w = Math.floor(ms / WEEK);
|
||||
const d = Math.floor((ms % WEEK) / DAY);
|
||||
return d === 0 ? `${w}S` : `${w}S ${d}D`;
|
||||
}
|
||||
const m = Math.floor(ms / MONTH);
|
||||
const w = Math.floor((ms % MONTH) / WEEK);
|
||||
return w === 0 ? `${m}M` : `${m}M ${w}S`;
|
||||
}
|
||||
|
||||
export function formatDateTimeShort(iso: string): string {
|
||||
const d = new Date(iso);
|
||||
if (Number.isNaN(d.getTime())) return "";
|
||||
const dd = String(d.getDate()).padStart(2, "0");
|
||||
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const yy = String(d.getFullYear()).slice(-2);
|
||||
const hh = String(d.getHours()).padStart(2, "0");
|
||||
const mi = String(d.getMinutes()).padStart(2, "0");
|
||||
return `${dd}/${mm}/${yy} ${hh}:${mi}`;
|
||||
}
|
||||
export { formatDuration } from "@fn_library/core/format_duration";
|
||||
export { formatDateTimeShort } from "@fn_library/core/format_datetime_short";
|
||||
|
||||
Reference in New Issue
Block a user