refactor: use date times with hooks everywhere

This commit is contained in:
Nicolas Meienberger 2026-04-02 22:00:26 +02:00 committed by Nico
parent 8b796cfec2
commit 1b780ed127
6 changed files with 35 additions and 27 deletions

View file

@ -1,6 +1,11 @@
import { afterEach, describe, expect, test, vi } from "vitest";
import {
DEFAULT_TIME_FORMAT,
import { DEFAULT_TIME_FORMAT, inferDateTimePreferences, rawFormatters } from "../datetime";
afterEach(() => {
vi.restoreAllMocks();
});
const {
formatDate,
formatDateTime,
formatDateWithMonth,
@ -8,12 +13,7 @@ import {
formatShortDateTime,
formatTime,
formatTimeAgo,
inferDateTimePreferences,
} from "../datetime";
afterEach(() => {
vi.restoreAllMocks();
});
} = rawFormatters;
const sampleDate = new Date("2026-01-10T14:30:00.000Z");

View file

@ -149,7 +149,7 @@ export function inferDateTimePreferences(locale?: string) {
}
// 1/10/2026, 2:30 PM
export function formatDateTime(date: DateInput, options: DateFormatOptions = {}): string {
function formatDateTime(date: DateInput, options: DateFormatOptions = {}): string {
return formatValidDate(
date,
(validDate) => `${formatConfiguredDate(validDate, options, true)}, ${formatConfiguredTime(validDate, options)}`,
@ -157,22 +157,22 @@ export function formatDateTime(date: DateInput, options: DateFormatOptions = {})
}
// Jan 10, 2026
export function formatDateWithMonth(date: DateInput, options: DateFormatOptions = {}): string {
function formatDateWithMonth(date: DateInput, options: DateFormatOptions = {}): string {
return formatValidDate(date, (validDate) => formatConfiguredDateWithMonth(validDate, options));
}
// 1/10/2026
export function formatDate(date: DateInput, options: DateFormatOptions = {}): string {
function formatDate(date: DateInput, options: DateFormatOptions = {}): string {
return formatValidDate(date, (validDate) => formatConfiguredDate(validDate, options, true));
}
// 1/10
export function formatShortDate(date: DateInput, options: DateFormatOptions = {}): string {
function formatShortDate(date: DateInput, options: DateFormatOptions = {}): string {
return formatValidDate(date, (validDate) => formatConfiguredDate(validDate, options, false));
}
// 1/10, 2:30 PM
export function formatShortDateTime(date: DateInput, options: DateFormatOptions = {}): string {
function formatShortDateTime(date: DateInput, options: DateFormatOptions = {}): string {
return formatValidDate(
date,
(validDate) => `${formatConfiguredDate(validDate, options, false)}, ${formatConfiguredTime(validDate, options)}`,
@ -180,12 +180,12 @@ export function formatShortDateTime(date: DateInput, options: DateFormatOptions
}
// 2:30 PM
export function formatTime(date: DateInput, options: DateFormatOptions = {}): string {
function formatTime(date: DateInput, options: DateFormatOptions = {}): string {
return formatValidDate(date, (validDate) => formatConfiguredTime(validDate, options));
}
// 5 minutes ago
export function formatTimeAgo(date: DateInput, now = Date.now()): string {
function formatTimeAgo(date: DateInput, now = Date.now()): string {
return formatValidDate(date, (validDate) => {
if (Math.abs(now - validDate.getTime()) < 120_000) {
return "just now";
@ -222,3 +222,13 @@ export function useTimeFormat() {
[locale, timeZone, currentNow, dateFormat, timeFormat],
);
}
export const rawFormatters = {
formatDateTime,
formatDateWithMonth,
formatDate,
formatShortDate,
formatShortDateTime,
formatTime,
formatTimeAgo,
};

View file

@ -31,13 +31,13 @@ import {
} from "~/client/components/ui/dropdown-menu";
import { Separator } from "~/client/components/ui/separator";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/client/components/ui/tabs";
import { formatDateTime, formatTimeAgo } from "~/client/lib/datetime";
import { parseError } from "~/client/lib/errors";
import { cn } from "~/client/lib/utils";
import type { BackupSchedule, Snapshot } from "~/client/lib/types";
import type { GetRepositoryStatsResponse } from "~/client/api-client/types.gen";
import { RepositoryInfoTabContent } from "../tabs/info";
import { RepositorySnapshotsTabContent } from "../tabs/snapshots";
import { useTimeFormat } from "~/client/lib/datetime";
export default function RepositoryDetailsPage({
repositoryId,
@ -50,6 +50,7 @@ export default function RepositoryDetailsPage({
initialBackupSchedules?: BackupSchedule[];
initialStats?: GetRepositoryStatsResponse;
}) {
const { formatDateTime, formatTimeAgo } = useTimeFormat();
const navigate = useNavigate();
const { tab } = useSearch({ from: "/(dashboard)/repositories/$repositoryId/" });
const activeTab = tab || "info";

View file

@ -2,11 +2,11 @@ import { Archive, Clock, FolderOpen, HardDrive, Lock, Settings, Shield } from "l
import { Card, CardContent, CardTitle } from "~/client/components/ui/card";
import type { Repository } from "~/client/lib/types";
import type { GetRepositoryStatsResponse } from "~/client/api-client/types.gen";
import { formatDateTime, formatTimeAgo } from "~/client/lib/datetime";
import type { RepositoryConfig } from "@zerobyte/core/restic";
import { DoctorReport } from "../components/doctor-report";
import { CompressionStatsChart } from "../components/compression-stats-chart";
import { cn } from "~/client/lib/utils";
import { useTimeFormat } from "~/client/lib/datetime";
type Props = {
repository: Repository;
@ -33,6 +33,7 @@ function ConfigRow({ icon, label, value, mono, valueClassName }: ConfigRowProps)
export const RepositoryInfoTabContent = ({ repository, initialStats }: Props) => {
const effectiveLocalPath = getEffectiveLocalPath(repository);
const { formatDateTime, formatTimeAgo } = useTimeFormat();
const config = repository.config as RepositoryConfig;
const hasLocalPath = Boolean(effectiveLocalPath);

View file

@ -24,9 +24,9 @@ import { authClient } from "~/client/lib/auth-client";
import {
DATE_FORMATS,
type DateFormatPreference,
formatDateTime,
TIME_FORMATS,
type TimeFormatPreference,
useTimeFormat,
} from "~/client/lib/datetime";
import { logger } from "~/client/lib/logger";
import { type AppContext } from "~/context";
@ -50,7 +50,7 @@ export function SettingsPage({ appContext, initialMembers, initialSsoSettings, i
const [downloadDialogOpen, setDownloadDialogOpen] = useState(false);
const [downloadPassword, setDownloadPassword] = useState("");
const [isChangingPassword, setIsChangingPassword] = useState(false);
const { locale, dateFormat, timeFormat } = useRootLoaderData();
const { dateFormat, timeFormat } = useRootLoaderData();
const { tab } = useSearch({ from: "/(dashboard)/settings/" });
const activeTab = tab || "account";
@ -58,12 +58,7 @@ export function SettingsPage({ appContext, initialMembers, initialSsoSettings, i
const navigate = useNavigate();
const { activeMember, activeOrganization } = useOrganizationContext();
const isOrgAdmin = activeMember?.role === "owner" || activeMember?.role === "admin";
const dateTimePreview = formatDateTime("2026-01-10T14:30:00.000Z", {
locale,
timeZone: "UTC",
dateFormat,
timeFormat,
});
const { formatDateTime } = useTimeFormat();
const handleLogout = async () => {
await authClient.signOut({
@ -275,7 +270,7 @@ export function SettingsPage({ appContext, initialMembers, initialSsoSettings, i
</Select>
</div>
</div>
<p className="text-sm text-muted-foreground">Preview: {dateTimePreview}</p>
<p className="text-sm text-muted-foreground">Preview: {formatDateTime(new Date())}</p>
</div>
</CardContent>

View file

@ -35,16 +35,17 @@ import { Separator } from "~/client/components/ui/separator";
import { Switch } from "~/client/components/ui/switch";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/client/components/ui/tabs";
import { ManagedBadge } from "~/client/components/managed-badge";
import { formatDateTime, formatTimeAgo } from "~/client/lib/datetime";
import { parseError } from "~/client/lib/errors";
import { cn } from "~/client/lib/utils";
import { VolumeInfoTabContent } from "../tabs/info";
import { FilesTabContent } from "../tabs/files";
import { useTimeFormat } from "~/client/lib/datetime";
export function VolumeDetails({ volumeId }: { volumeId: string }) {
const navigate = useNavigate();
const searchParams = useSearch({ from: "/(dashboard)/volumes/$volumeId/" });
const activeTab = searchParams.tab || "info";
const { formatDateTime, formatTimeAgo } = useTimeFormat();
const { data } = useSuspenseQuery({
...getVolumeOptions({ path: { shortId: volumeId } }),