refactor: use date times with hooks everywhere
This commit is contained in:
parent
8b796cfec2
commit
1b780ed127
6 changed files with 35 additions and 27 deletions
|
|
@ -1,6 +1,11 @@
|
||||||
import { afterEach, describe, expect, test, vi } from "vitest";
|
import { afterEach, describe, expect, test, vi } from "vitest";
|
||||||
import {
|
import { DEFAULT_TIME_FORMAT, inferDateTimePreferences, rawFormatters } from "../datetime";
|
||||||
DEFAULT_TIME_FORMAT,
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
formatDate,
|
formatDate,
|
||||||
formatDateTime,
|
formatDateTime,
|
||||||
formatDateWithMonth,
|
formatDateWithMonth,
|
||||||
|
|
@ -8,12 +13,7 @@ import {
|
||||||
formatShortDateTime,
|
formatShortDateTime,
|
||||||
formatTime,
|
formatTime,
|
||||||
formatTimeAgo,
|
formatTimeAgo,
|
||||||
inferDateTimePreferences,
|
} = rawFormatters;
|
||||||
} from "../datetime";
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
vi.restoreAllMocks();
|
|
||||||
});
|
|
||||||
|
|
||||||
const sampleDate = new Date("2026-01-10T14:30:00.000Z");
|
const sampleDate = new Date("2026-01-10T14:30:00.000Z");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ export function inferDateTimePreferences(locale?: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1/10/2026, 2:30 PM
|
// 1/10/2026, 2:30 PM
|
||||||
export function formatDateTime(date: DateInput, options: DateFormatOptions = {}): string {
|
function formatDateTime(date: DateInput, options: DateFormatOptions = {}): string {
|
||||||
return formatValidDate(
|
return formatValidDate(
|
||||||
date,
|
date,
|
||||||
(validDate) => `${formatConfiguredDate(validDate, options, true)}, ${formatConfiguredTime(validDate, options)}`,
|
(validDate) => `${formatConfiguredDate(validDate, options, true)}, ${formatConfiguredTime(validDate, options)}`,
|
||||||
|
|
@ -157,22 +157,22 @@ export function formatDateTime(date: DateInput, options: DateFormatOptions = {})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jan 10, 2026
|
// 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));
|
return formatValidDate(date, (validDate) => formatConfiguredDateWithMonth(validDate, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1/10/2026
|
// 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));
|
return formatValidDate(date, (validDate) => formatConfiguredDate(validDate, options, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1/10
|
// 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));
|
return formatValidDate(date, (validDate) => formatConfiguredDate(validDate, options, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1/10, 2:30 PM
|
// 1/10, 2:30 PM
|
||||||
export function formatShortDateTime(date: DateInput, options: DateFormatOptions = {}): string {
|
function formatShortDateTime(date: DateInput, options: DateFormatOptions = {}): string {
|
||||||
return formatValidDate(
|
return formatValidDate(
|
||||||
date,
|
date,
|
||||||
(validDate) => `${formatConfiguredDate(validDate, options, false)}, ${formatConfiguredTime(validDate, options)}`,
|
(validDate) => `${formatConfiguredDate(validDate, options, false)}, ${formatConfiguredTime(validDate, options)}`,
|
||||||
|
|
@ -180,12 +180,12 @@ export function formatShortDateTime(date: DateInput, options: DateFormatOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2:30 PM
|
// 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));
|
return formatValidDate(date, (validDate) => formatConfiguredTime(validDate, options));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5 minutes ago
|
// 5 minutes ago
|
||||||
export function formatTimeAgo(date: DateInput, now = Date.now()): string {
|
function formatTimeAgo(date: DateInput, now = Date.now()): string {
|
||||||
return formatValidDate(date, (validDate) => {
|
return formatValidDate(date, (validDate) => {
|
||||||
if (Math.abs(now - validDate.getTime()) < 120_000) {
|
if (Math.abs(now - validDate.getTime()) < 120_000) {
|
||||||
return "just now";
|
return "just now";
|
||||||
|
|
@ -222,3 +222,13 @@ export function useTimeFormat() {
|
||||||
[locale, timeZone, currentNow, dateFormat, timeFormat],
|
[locale, timeZone, currentNow, dateFormat, timeFormat],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const rawFormatters = {
|
||||||
|
formatDateTime,
|
||||||
|
formatDateWithMonth,
|
||||||
|
formatDate,
|
||||||
|
formatShortDate,
|
||||||
|
formatShortDateTime,
|
||||||
|
formatTime,
|
||||||
|
formatTimeAgo,
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,13 @@ import {
|
||||||
} from "~/client/components/ui/dropdown-menu";
|
} from "~/client/components/ui/dropdown-menu";
|
||||||
import { Separator } from "~/client/components/ui/separator";
|
import { Separator } from "~/client/components/ui/separator";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/client/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/client/components/ui/tabs";
|
||||||
import { formatDateTime, formatTimeAgo } from "~/client/lib/datetime";
|
|
||||||
import { parseError } from "~/client/lib/errors";
|
import { parseError } from "~/client/lib/errors";
|
||||||
import { cn } from "~/client/lib/utils";
|
import { cn } from "~/client/lib/utils";
|
||||||
import type { BackupSchedule, Snapshot } from "~/client/lib/types";
|
import type { BackupSchedule, Snapshot } from "~/client/lib/types";
|
||||||
import type { GetRepositoryStatsResponse } from "~/client/api-client/types.gen";
|
import type { GetRepositoryStatsResponse } from "~/client/api-client/types.gen";
|
||||||
import { RepositoryInfoTabContent } from "../tabs/info";
|
import { RepositoryInfoTabContent } from "../tabs/info";
|
||||||
import { RepositorySnapshotsTabContent } from "../tabs/snapshots";
|
import { RepositorySnapshotsTabContent } from "../tabs/snapshots";
|
||||||
|
import { useTimeFormat } from "~/client/lib/datetime";
|
||||||
|
|
||||||
export default function RepositoryDetailsPage({
|
export default function RepositoryDetailsPage({
|
||||||
repositoryId,
|
repositoryId,
|
||||||
|
|
@ -50,6 +50,7 @@ export default function RepositoryDetailsPage({
|
||||||
initialBackupSchedules?: BackupSchedule[];
|
initialBackupSchedules?: BackupSchedule[];
|
||||||
initialStats?: GetRepositoryStatsResponse;
|
initialStats?: GetRepositoryStatsResponse;
|
||||||
}) {
|
}) {
|
||||||
|
const { formatDateTime, formatTimeAgo } = useTimeFormat();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { tab } = useSearch({ from: "/(dashboard)/repositories/$repositoryId/" });
|
const { tab } = useSearch({ from: "/(dashboard)/repositories/$repositoryId/" });
|
||||||
const activeTab = tab || "info";
|
const activeTab = tab || "info";
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ import { Archive, Clock, FolderOpen, HardDrive, Lock, Settings, Shield } from "l
|
||||||
import { Card, CardContent, CardTitle } from "~/client/components/ui/card";
|
import { Card, CardContent, CardTitle } from "~/client/components/ui/card";
|
||||||
import type { Repository } from "~/client/lib/types";
|
import type { Repository } from "~/client/lib/types";
|
||||||
import type { GetRepositoryStatsResponse } from "~/client/api-client/types.gen";
|
import type { GetRepositoryStatsResponse } from "~/client/api-client/types.gen";
|
||||||
import { formatDateTime, formatTimeAgo } from "~/client/lib/datetime";
|
|
||||||
import type { RepositoryConfig } from "@zerobyte/core/restic";
|
import type { RepositoryConfig } from "@zerobyte/core/restic";
|
||||||
import { DoctorReport } from "../components/doctor-report";
|
import { DoctorReport } from "../components/doctor-report";
|
||||||
import { CompressionStatsChart } from "../components/compression-stats-chart";
|
import { CompressionStatsChart } from "../components/compression-stats-chart";
|
||||||
import { cn } from "~/client/lib/utils";
|
import { cn } from "~/client/lib/utils";
|
||||||
|
import { useTimeFormat } from "~/client/lib/datetime";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
repository: Repository;
|
repository: Repository;
|
||||||
|
|
@ -33,6 +33,7 @@ function ConfigRow({ icon, label, value, mono, valueClassName }: ConfigRowProps)
|
||||||
|
|
||||||
export const RepositoryInfoTabContent = ({ repository, initialStats }: Props) => {
|
export const RepositoryInfoTabContent = ({ repository, initialStats }: Props) => {
|
||||||
const effectiveLocalPath = getEffectiveLocalPath(repository);
|
const effectiveLocalPath = getEffectiveLocalPath(repository);
|
||||||
|
const { formatDateTime, formatTimeAgo } = useTimeFormat();
|
||||||
|
|
||||||
const config = repository.config as RepositoryConfig;
|
const config = repository.config as RepositoryConfig;
|
||||||
const hasLocalPath = Boolean(effectiveLocalPath);
|
const hasLocalPath = Boolean(effectiveLocalPath);
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ import { authClient } from "~/client/lib/auth-client";
|
||||||
import {
|
import {
|
||||||
DATE_FORMATS,
|
DATE_FORMATS,
|
||||||
type DateFormatPreference,
|
type DateFormatPreference,
|
||||||
formatDateTime,
|
|
||||||
TIME_FORMATS,
|
TIME_FORMATS,
|
||||||
type TimeFormatPreference,
|
type TimeFormatPreference,
|
||||||
|
useTimeFormat,
|
||||||
} from "~/client/lib/datetime";
|
} from "~/client/lib/datetime";
|
||||||
import { logger } from "~/client/lib/logger";
|
import { logger } from "~/client/lib/logger";
|
||||||
import { type AppContext } from "~/context";
|
import { type AppContext } from "~/context";
|
||||||
|
|
@ -50,7 +50,7 @@ export function SettingsPage({ appContext, initialMembers, initialSsoSettings, i
|
||||||
const [downloadDialogOpen, setDownloadDialogOpen] = useState(false);
|
const [downloadDialogOpen, setDownloadDialogOpen] = useState(false);
|
||||||
const [downloadPassword, setDownloadPassword] = useState("");
|
const [downloadPassword, setDownloadPassword] = useState("");
|
||||||
const [isChangingPassword, setIsChangingPassword] = useState(false);
|
const [isChangingPassword, setIsChangingPassword] = useState(false);
|
||||||
const { locale, dateFormat, timeFormat } = useRootLoaderData();
|
const { dateFormat, timeFormat } = useRootLoaderData();
|
||||||
|
|
||||||
const { tab } = useSearch({ from: "/(dashboard)/settings/" });
|
const { tab } = useSearch({ from: "/(dashboard)/settings/" });
|
||||||
const activeTab = tab || "account";
|
const activeTab = tab || "account";
|
||||||
|
|
@ -58,12 +58,7 @@ export function SettingsPage({ appContext, initialMembers, initialSsoSettings, i
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { activeMember, activeOrganization } = useOrganizationContext();
|
const { activeMember, activeOrganization } = useOrganizationContext();
|
||||||
const isOrgAdmin = activeMember?.role === "owner" || activeMember?.role === "admin";
|
const isOrgAdmin = activeMember?.role === "owner" || activeMember?.role === "admin";
|
||||||
const dateTimePreview = formatDateTime("2026-01-10T14:30:00.000Z", {
|
const { formatDateTime } = useTimeFormat();
|
||||||
locale,
|
|
||||||
timeZone: "UTC",
|
|
||||||
dateFormat,
|
|
||||||
timeFormat,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleLogout = async () => {
|
const handleLogout = async () => {
|
||||||
await authClient.signOut({
|
await authClient.signOut({
|
||||||
|
|
@ -275,7 +270,7 @@ export function SettingsPage({ appContext, initialMembers, initialSsoSettings, i
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,16 +35,17 @@ import { Separator } from "~/client/components/ui/separator";
|
||||||
import { Switch } from "~/client/components/ui/switch";
|
import { Switch } from "~/client/components/ui/switch";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/client/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/client/components/ui/tabs";
|
||||||
import { ManagedBadge } from "~/client/components/managed-badge";
|
import { ManagedBadge } from "~/client/components/managed-badge";
|
||||||
import { formatDateTime, formatTimeAgo } from "~/client/lib/datetime";
|
|
||||||
import { parseError } from "~/client/lib/errors";
|
import { parseError } from "~/client/lib/errors";
|
||||||
import { cn } from "~/client/lib/utils";
|
import { cn } from "~/client/lib/utils";
|
||||||
import { VolumeInfoTabContent } from "../tabs/info";
|
import { VolumeInfoTabContent } from "../tabs/info";
|
||||||
import { FilesTabContent } from "../tabs/files";
|
import { FilesTabContent } from "../tabs/files";
|
||||||
|
import { useTimeFormat } from "~/client/lib/datetime";
|
||||||
|
|
||||||
export function VolumeDetails({ volumeId }: { volumeId: string }) {
|
export function VolumeDetails({ volumeId }: { volumeId: string }) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const searchParams = useSearch({ from: "/(dashboard)/volumes/$volumeId/" });
|
const searchParams = useSearch({ from: "/(dashboard)/volumes/$volumeId/" });
|
||||||
const activeTab = searchParams.tab || "info";
|
const activeTab = searchParams.tab || "info";
|
||||||
|
const { formatDateTime, formatTimeAgo } = useTimeFormat();
|
||||||
|
|
||||||
const { data } = useSuspenseQuery({
|
const { data } = useSuspenseQuery({
|
||||||
...getVolumeOptions({ path: { shortId: volumeId } }),
|
...getVolumeOptions({ path: { shortId: volumeId } }),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue