feat(ui): consolidate shared UI patterns into reusable components
- Token cleanup: replace raw hex colors (#c084fc, #60a5fa, #6ee7b7, #fdba74) in BoardCard.vue with nexus-tokens.css CSS variables (--clr-iris, --clr-bao, --clr-agent, --clr-review) - New composable: useFormatDate (formatDate, relativeTime, toDateInputValue, minutesSince, hoursSince) — extracts duplicated date helpers from TaskBoardView and BoardCard - New composable: useConfirm — reusable confirmation dialog logic (open/close, error/success state, Escape binding, body scroll lock) - New component: StatusPill — unified state pill for backlog/progress/ review/blocked/done, replacing inline .detail-state-pill classes - New component: SkeletonLoader — loading placeholder with shimmer animation (card/text/circle variants) - TaskBoardView: imports StatusPill & useFormatDate, removes 35+ lines of duplicated helpers - ui/index.ts: exports new StatusPill & SkeletonLoader - Build verified: pnpm build green (vue-tsc --noEmit + vite build pass)
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* useFormatDate — Einheitliche Datums-/Zeitformatierung für Nexus V2
|
||||
*
|
||||
* Kapselt die mehrfach wiederholten formatDate-, relativeTime- und
|
||||
* toDateInputValue-Helper, die bisher in TaskBoardView, BoardCard,
|
||||
* mit anderen Views dupliziert waren.
|
||||
*
|
||||
* Alle Ausgaben orientieren sich an de-DE Locale.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Datum als lesbaren String (de-DE, kurzes/medium-DateStyle).
|
||||
*/
|
||||
export function formatDate(date?: string | null, withTime = false): string {
|
||||
if (!date) return '—'
|
||||
return new Date(date).toLocaleString('de-DE', withTime
|
||||
? { dateStyle: 'medium', timeStyle: 'short' }
|
||||
: { dateStyle: 'medium' })
|
||||
}
|
||||
|
||||
/**
|
||||
* ISO-Datumsstring in YYYY-MM-DD für <input type="date">.
|
||||
*/
|
||||
export function toDateInputValue(date?: string | null): string {
|
||||
if (!date) return ''
|
||||
return new Date(date).toISOString().slice(0, 10)
|
||||
}
|
||||
|
||||
/**
|
||||
* Relative Zeit ("gerade eben", "vor 5 min", "vor 3 h", "vor 2 d").
|
||||
* Optionaler Fallback, wenn date fehlt.
|
||||
*/
|
||||
export function relativeTime(date?: string | null, fallback = 'keine Aktivität'): string {
|
||||
if (!date) return fallback
|
||||
const diffMs = Date.now() - new Date(date).getTime()
|
||||
const mins = Math.max(0, Math.round(diffMs / 60000))
|
||||
if (mins < 1) return 'gerade eben'
|
||||
if (mins < 60) return `vor ${mins} min`
|
||||
const hours = Math.round(mins / 60)
|
||||
if (hours < 24) return `vor ${hours} h`
|
||||
const days = Math.round(hours / 24)
|
||||
return `vor ${days} d`
|
||||
}
|
||||
|
||||
/**
|
||||
* Minuten seit einem Datum (oder Infinity falls kein Datum).
|
||||
*/
|
||||
export function minutesSince(dateStr?: string | null): number {
|
||||
if (!dateStr) return Infinity
|
||||
return (Date.now() - new Date(dateStr).getTime()) / 60000
|
||||
}
|
||||
|
||||
/**
|
||||
* Stunden seit einem Datum (gerundet).
|
||||
*/
|
||||
export function hoursSince(dateStr: string): number {
|
||||
const now = Date.now()
|
||||
const then = new Date(dateStr).getTime()
|
||||
return Math.round((now - then) / 3600000)
|
||||
}
|
||||
Reference in New Issue
Block a user