Phase 0.3: migrate date formatting to date-fns
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
||||
type EntryPriority,
|
||||
type EntrySubtype,
|
||||
} from '@/api/calendar';
|
||||
import { formatDateTimeInput } from '@/utils/date';
|
||||
|
||||
export interface AppointmentModalProps {
|
||||
open: boolean;
|
||||
@@ -41,9 +42,7 @@ export interface AppointmentModalProps {
|
||||
|
||||
function toDateTimeLocalValue(d: Date | null | undefined): string {
|
||||
if (!d) return '';
|
||||
// datetime-local needs YYYY-MM-DDTHH:mm in local time
|
||||
const pad = (n: number) => String(n).padStart(2, '0');
|
||||
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||
return formatDateTimeInput(d);
|
||||
}
|
||||
|
||||
function fromDateTimeLocalValue(v: string): Date | null {
|
||||
|
||||
@@ -21,21 +21,11 @@ import {
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Input } from '@/components/ui/Input';
|
||||
import { Calendar as CalendarIcon, Pencil, Trash2, X } from 'lucide-react';
|
||||
import { formatDateTime } from '@/utils/date';
|
||||
|
||||
function formatDate(dateStr: string | null | undefined): string {
|
||||
if (!dateStr) return '-';
|
||||
try {
|
||||
const d = new Date(dateStr);
|
||||
return d.toLocaleDateString(undefined, {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
} catch {
|
||||
return dateStr;
|
||||
}
|
||||
return formatDateTime(dateStr) || '-';
|
||||
}
|
||||
|
||||
function priorityClass(p: string): string {
|
||||
|
||||
@@ -7,6 +7,7 @@ import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import clsx from 'clsx';
|
||||
import type { CalendarEntry } from '@/api/calendar';
|
||||
import { formatDateShort } from '@/utils/date';
|
||||
|
||||
export type KanbanStatus = 'open' | 'in_progress' | 'done' | 'cancelled';
|
||||
|
||||
@@ -38,8 +39,7 @@ function priorityBadgeClass(p: string): string {
|
||||
|
||||
function fmtDue(entry: CalendarEntry): string | null {
|
||||
if (!entry.due_date) return null;
|
||||
const d = new Date(entry.due_date);
|
||||
return d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' });
|
||||
return formatDateShort(entry.due_date) || null;
|
||||
}
|
||||
|
||||
export function KanbanBoard({ board, loading, onMove, onSelect }: KanbanBoardProps) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
type CalendarEntry,
|
||||
type Subtask,
|
||||
} from '@/api/calendar';
|
||||
import { formatDateShort } from '@/utils/date';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Input } from '@/components/ui/Input';
|
||||
|
||||
@@ -129,7 +130,7 @@ export function TaskDetailPanel({ entry, onClose, onChanged }: TaskDetailPanelPr
|
||||
</span>
|
||||
{entry.due_date && (
|
||||
<span className="px-2 py-0.5 rounded bg-primary-50 text-primary-700">
|
||||
{new Date(entry.due_date).toLocaleDateString('de-DE')}
|
||||
{formatDateShort(entry.due_date)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -9,15 +9,11 @@ import { useTranslation } from 'react-i18next';
|
||||
import type { DmsFile } from '@/api/dms';
|
||||
import { getFileIcon, formatFileSize } from './FileExplorer';
|
||||
import { Download, Eye, FileText, Share2, Trash2, X } from 'lucide-react';
|
||||
import { formatDateTime } from '@/utils/date';
|
||||
|
||||
function formatDate(dateStr: string | null | undefined): string {
|
||||
if (!dateStr) return '-';
|
||||
try {
|
||||
const d = new Date(dateStr);
|
||||
return d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
|
||||
} catch {
|
||||
return dateStr;
|
||||
}
|
||||
return formatDateTime(dateStr) || '-';
|
||||
}
|
||||
|
||||
function getFileSize(file: DmsFile): number {
|
||||
|
||||
@@ -38,6 +38,8 @@ export function formatFileSize(bytes: number): string {
|
||||
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
||||
}
|
||||
import { formatDateShort } from '@/utils/date';
|
||||
|
||||
|
||||
function getFileSize(file: DmsFile): number {
|
||||
return file.size_bytes ?? file.size ?? 0;
|
||||
@@ -45,12 +47,7 @@ function getFileSize(file: DmsFile): number {
|
||||
|
||||
function formatDate(dateStr: string | null | undefined): string {
|
||||
if (!dateStr) return '';
|
||||
try {
|
||||
const d = new Date(dateStr);
|
||||
return d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });
|
||||
} catch {
|
||||
return dateStr;
|
||||
}
|
||||
return formatDateShort(dateStr) || '';
|
||||
}
|
||||
|
||||
export type ViewMode = 'list' | 'table' | 'icons-sm' | 'icons-md' | 'icons-lg';
|
||||
|
||||
@@ -8,6 +8,7 @@ import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Modal } from '@/components/ui/Modal';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { formatDateShort } from '@/utils/date';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { getFilePreviewUrl, type DmsFile } from '@/api/dms';
|
||||
import { FileText } from 'lucide-react';
|
||||
@@ -56,7 +57,7 @@ export function FilePreviewModal({ open, file, onClose }: FilePreviewModalProps)
|
||||
<span className="text-sm text-secondary-500">{formatFileSize(file.size_bytes ?? file.size ?? 0)}</span>
|
||||
{file.created_at && (
|
||||
<span className="text-sm text-secondary-500">
|
||||
{t('dms.fileModified')}: {new Date(file.created_at).toLocaleDateString()}
|
||||
{t('dms.fileModified')}: {formatDateShort(file.created_at)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Modal } from '@/components/ui/Modal';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Input } from '@/components/ui/Input';
|
||||
import { formatDateShort } from '@/utils/date';
|
||||
import { Select } from '@/components/ui/Select';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { EmptyState } from '@/components/ui/EmptyState';
|
||||
@@ -259,7 +260,7 @@ export function ShareDialog({ open, file, onClose, onShared }: ShareDialogProps)
|
||||
)}
|
||||
{link.expires_at && (
|
||||
<Badge variant="info">
|
||||
{t('permissions.expiresAt')}: {new Date(link.expires_at).toLocaleDateString()}
|
||||
{t('permissions.expiresAt')}: {formatDateShort(link.expires_at)}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { formatTime } from '@/utils/date';
|
||||
import { ResizablePanel } from '@/components/ui/ResizablePanel';
|
||||
import { useUIStore } from '@/store/uiStore';
|
||||
import { useCommStore } from '@/store/commStore';
|
||||
@@ -241,7 +242,7 @@ function MessageFeed({
|
||||
)}
|
||||
{msg.created_at && (
|
||||
<span className="text-[10px] opacity-50 mt-0.5 block">
|
||||
{new Date(msg.created_at).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}
|
||||
{formatTime(msg.created_at)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@ import { decodeMimeHeader } from '@/api/mail';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { EmptyState } from '@/components/ui/EmptyState';
|
||||
import { FileText, Loader2 } from 'lucide-react';
|
||||
import { formatDateTime } from '@/utils/date';
|
||||
|
||||
export interface MailDetailProps {
|
||||
mail: Mail | null; loading: boolean;
|
||||
@@ -26,14 +27,7 @@ export interface MailDetailProps {
|
||||
}
|
||||
|
||||
function formatFullDate(dateStr: string): string {
|
||||
const date = new Date(dateStr);
|
||||
return date.toLocaleString([], {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
});
|
||||
return formatDateTime(dateStr) || dateStr;
|
||||
}
|
||||
|
||||
function formatBytes(bytes: number): string {
|
||||
|
||||
@@ -11,6 +11,7 @@ import { decodeMimeHeader } from '@/api/mail';
|
||||
import { EmptyState } from '@/components/ui/EmptyState';
|
||||
import { Pagination } from '@/components/ui/Pagination';
|
||||
import { ChevronUp, Loader2, Paperclip, Star } from 'lucide-react';
|
||||
import { formatSmartDate } from '@/utils/date';
|
||||
|
||||
export interface MailListProps {
|
||||
mails: Mail[];
|
||||
@@ -30,12 +31,7 @@ export interface MailListProps {
|
||||
}
|
||||
|
||||
function formatDate(dateStr: string): string {
|
||||
const date = new Date(dateStr);
|
||||
const now = new Date();
|
||||
if (date.toDateString() === now.toDateString()) {
|
||||
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||
}
|
||||
return date.toLocaleDateString([], { month: 'short', day: 'numeric' });
|
||||
return formatSmartDate(dateStr) || dateStr;
|
||||
}
|
||||
|
||||
export function MailList({
|
||||
|
||||
Reference in New Issue
Block a user