Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions app/components/device-detail/device-detail-box.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import clsx from 'clsx'
import { formatDistanceToNow } from 'date-fns'
import { de, enUS } from 'date-fns/locale'
import {
ChevronUp,
Minus,
Expand Down Expand Up @@ -81,6 +79,7 @@ import { useGlobalCompareMode } from './useGlobalCompareMode'
import { type SensorWithLatestMeasurement } from '~/db/schema'
import { getArchiveLink } from '~/lib/archive-link'
import { type loader } from '~/routes/explore.$deviceId'
import { dateDiffToNowInWords } from '~/lib/date'

export interface MeasurementProps {
sensorId: string
Expand All @@ -96,7 +95,6 @@ export default function DeviceDetailBox() {
const matches = useMatches()
const { toast } = useToast()
const { t, i18n } = useTranslation('device-detail-box')
const dateLocale = i18n.language.startsWith('de') ? de : enUS
const dateFormatter = new Intl.DateTimeFormat(i18n.resolvedLanguage, {
year: 'numeric',
month: 'long',
Expand Down Expand Up @@ -569,15 +567,12 @@ export default function DeviceDetailBox() {
></div>
<p className="text-muted-foreground text-xs">
{sensor.lastMeasurement
? formatDistanceToNow(
? dateDiffToNowInWords(
i18n.language,
new Date(
sensor.lastMeasurement
.createdAt,
),
{
addSuffix: true,
locale: dateLocale,
},
)
: t('no_recent_data')}
</p>
Expand Down Expand Up @@ -656,15 +651,12 @@ export default function DeviceDetailBox() {
></div>
<p className="text-muted-foreground text-xs">
{sensor.lastMeasurement
? formatDistanceToNow(
? dateDiffToNowInWords(
i18n.language,
new Date(
sensor.lastMeasurement
.createdAt,
),
{
addSuffix: true,
locale: dateLocale,
},
)
: t('no_recent_data')}
</p>
Expand Down
1 change: 0 additions & 1 deletion app/components/device-detail/graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
type ChartOptions,
} from 'chart.js'
import 'chartjs-adapter-date-fns'
// import { de, enGB } from "date-fns/locale";
import { Download, RefreshCcw, X } from 'lucide-react'
import {
useMemo,
Expand Down
12 changes: 9 additions & 3 deletions app/components/map/layers/mobile/mobile-overview-layer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import bbox from '@turf/bbox'
import { point, featureCollection } from '@turf/helpers'
import { format } from 'date-fns'
import { type FeatureCollection, type Point } from 'geojson'
import { CalendarClock } from 'lucide-react'
import { useState, useEffect, useMemo, useCallback } from 'react'
Expand All @@ -10,6 +9,7 @@ import {
type LocationPoint,
categorizeIntoTrips,
} from '~/lib/mobile-box-helper'
import { useTranslation } from 'react-i18next'

const FIT_PADDING = 100

Expand Down Expand Up @@ -121,6 +121,12 @@ export default function MobileOverviewLayer({
}: {
locations: LocationPoint[]
}) {
const { i18n } = useTranslation()
const dateTimeFormat = new Intl.DateTimeFormat(i18n.language, {
hour: 'numeric',
minute: '2-digit',
})

// Generate trips and assign colors once
const trips = useMemo(() => categorizeIntoTrips(locations, 50), [locations])

Expand Down Expand Up @@ -451,7 +457,7 @@ export default function MobileOverviewLayer({
)}
<div>
<p className="text-primary text-sm font-bold">
{format(new Date(popupInfo.startTime), 'Pp')}
{dateTimeFormat.format(new Date(popupInfo.startTime))}
</p>
</div>
{popupInfo.isCluster &&
Expand All @@ -461,7 +467,7 @@ export default function MobileOverviewLayer({
To
</span>
<p className="text-primary text-sm font-bold">
{format(new Date(popupInfo.endTime), 'Pp')}
{dateTimeFormat.format(new Date(popupInfo.endTime))}
</p>
</div>
)}
Expand Down
4 changes: 2 additions & 2 deletions app/components/mydevices/dt/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function getColumns(
useTranslation: UseTranslationResponse<'data-table', any>,
opts?: { isOwner?: boolean },
): ColumnDef<SenseBox>[] {
const { t } = useTranslation
const { t, i18n } = useTranslation
const isOwner = opts?.isOwner ?? false
return [
{
Expand Down Expand Up @@ -90,7 +90,7 @@ export function getColumns(
},
cell: ({ row }) => {
const date = new Date(row.getValue('createdAt'))
return <div>{date.toLocaleDateString()}</div>
return <div>{date.toLocaleDateString(i18n.language)}</div>
},
},
{
Expand Down
17 changes: 12 additions & 5 deletions app/components/ui/range-picker.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client'

import * as React from 'react'
import { addDays, format } from 'date-fns'
import { CalendarIcon } from 'lucide-react'
import { type DateRange } from '@daypicker/react'

Expand All @@ -13,11 +12,19 @@ import {
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover'
import { useTranslation } from 'react-i18next'

export function DatePickerWithRange() {
const { i18n } = useTranslation()
const [date, setDate] = React.useState<DateRange | undefined>({
from: new Date(new Date().getFullYear(), 0, 20),
to: addDays(new Date(new Date().getFullYear(), 0, 20), 20),
to: new Date(new Date().getFullYear(), 1, 10),
})
Comment on lines 17 to +22

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- current file ---'
cat -n app/components/ui/range-picker.tsx | sed -n '1,80p'

printf '%s\n' '--- recent file history ---'
git log --oneline -5 -- app/components/ui/range-picker.tsx

printf '%s\n' '--- parent version of the relevant file ---'
parent="$(git rev-parse HEAD^)"
git show "$parent:app/components/ui/range-picker.tsx" | cat -n | sed -n '1,80p'

printf '%s\n' '--- relevant diff ---'
git diff --unified=20 HEAD^ HEAD -- app/components/ui/range-picker.tsx

Repository: openSenseMap/frontend

Length of output: 4674


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- commits that changed addDays usage ---'
git log --all --oneline -S'addDays' -- app/components/ui/range-picker.tsx

printf '%s\n' '--- historical range-picker versions ---'
for commit in 4dd61e5 d493050 4205b56 HEAD; do
	printf '\n[%s]\n' "$commit"
	git show "$commit:app/components/ui/range-picker.tsx" 2>/dev/null \
		| cat -n | sed -n '1,35p' || true
done

printf '%s\n' '--- addDays references in repository ---'
rg -n 'addDays|new Date\\(.*0, 20|new Date\\(.*1, 10' --glob '!node_modules' .

Repository: openSenseMap/frontend

Length of output: 5460


Preserve the original 20-day range offset.

addDays(January 20, 20) returns February 9. Set to to February 9 to preserve the previous default range.


const dateTimeFormat = new Intl.DateTimeFormat(i18n.language, {
year: 'numeric',
month: 'long',
day: 'numeric',
})
Comment on lines +24 to 28

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

files=(
  "app/components/ui/range-picker.tsx"
  "app/components/map/layers/mobile/mobile-overview-layer.tsx"
  "app/components/mydevices/dt/columns.tsx"
  "app/routes/admin.devices._index.tsx"
  "app/routes/admin.users._index.tsx"
  "app/routes/device.\$deviceId.edit.logs.tsx"
  "app/routes/device.\$deviceId.edit.transfer.tsx"
  "app/routes/profile.\$username.tsx"
)

for file in "${files[@]}"; do
  echo "===== $file ====="
  wc -l "$file"
  rg -n -C 4 'Intl\.DateTimeFormat|toLocale(Date|String)|dateTimeFormat|useTranslation|createdAt|updatedAt|startTime|endTime|transferExpiresAt' "$file" || true
done

echo "===== SSR/client boundaries and timezone configuration ====="
rg -n -S 'clientOnly|ClientOnly|Hydrat|timeZone|timezone|TZ=|process\.env\.TZ|defaultTimeZone|DateTimeFormat' app package.json vite.config.* remix.config.* i18n* 2>/dev/null || true

echo "===== route/component usage ====="
rg -n -S 'RangePicker|MobileOverviewLayer|admin\.devices|admin\.users|edit\.logs|edit\.transfer|profile\.' app | head -300

Repository: openSenseMap/frontend

Length of output: 18711


🏁 Script executed:

#!/bin/bash
set -eu

echo "===== relevant source context ====="
sed -n '1,75p' app/components/ui/range-picker.tsx
sed -n '105,145p;430,480p' app/components/map/layers/mobile/mobile-overview-layer.tsx
sed -n '1,110p' app/components/mydevices/dt/columns.tsx
sed -n '1,75p' app/routes/admin.devices._index.tsx
sed -n '1,65p' app/routes/admin.users._index.tsx
sed -n '85,110p;160,182p' 'app/routes/device.$deviceId.edit.logs.tsx'
sed -n '135,160p;286,304p' 'app/routes/device.$deviceId.edit.transfer.tsx'
sed -n '120,170p;190,215p' 'app/routes/profile.$username.tsx'

echo "===== framework and hydration setup ====="
sed -n '1,90p' app/entry.server.tsx 2>/dev/null || true
sed -n '1,80p' app/entry.client.tsx
sed -n '1,55p' app/components/client-only.tsx
rg -n -S 'renderTo|hydrate|SSR|ssr|ClientOnly|DatePickerWithRange|MobileOverviewLayer|getColumns|DataTable' app --glob '*.{ts,tsx}' | head -250

echo "===== date/time configuration ====="
rg -n -S 'timeZone|timezone|TZ=|process\.env\.TZ|resolvedLanguage|supportedLngs|fallbackLng|i18n' . --glob '!node_modules' --glob '!build' --glob '!dist' | head -300

echo "===== standalone Intl default-time-zone probe ====="
TZ=UTC node - <<'JS'
const d = new Date('2024-01-15T23:30:00Z')
console.log('UTC:', new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeStyle: 'short' }).format(d))
JS
TZ=America/Los_Angeles node - <<'JS'
const d = new Date('2024-01-15T23:30:00Z')
console.log('Los_Angeles:', new Intl.DateTimeFormat('en-US', { dateStyle: 'medium', timeStyle: 'short' }).format(d))
JS
TZ=UTC node - <<'JS'
const d = new Date('2024-01-15T23:30:00Z')
console.log('explicit UTC:', new Intl.DateTimeFormat('en-US', {
  dateStyle: 'medium', timeStyle: 'short', timeZone: 'UTC'
}).format(d))
JS
TZ=America/Los_Angeles node - <<'JS'
const d = new Date('2024-01-15T23:30:00Z')
console.log('explicit UTC:', new Intl.DateTimeFormat('en-US', {
  dateStyle: 'medium', timeStyle: 'short', timeZone: 'UTC'
}).format(d))
JS

Repository: openSenseMap/frontend

Length of output: 42073


🏁 Script executed:

#!/bin/bash
set -eu

echo "===== DataTable render path ====="
sed -n '1,180p' app/components/mydevices/dt/data-table.tsx
sed -n '1,145p' 'app/routes/explore.$deviceId.tsx'

echo "===== range-picker and mobile state/output conditions ====="
rg -n -C 5 'DatePickerWithRange|popupInfo|setPopupInfo|MobileOverviewLayer|ClientOnly|useHydrated' app --glob '*.{ts,tsx}'

echo "===== date-only versus instant formatting probe ====="
node - <<'JS'
const instant = new Date('2024-01-15T23:30:00Z')
const dateOnly = new Date(2024, 0, 20)

for (const tz of ['UTC', 'America/Los_Angeles']) {
  process.env.TZ = tz
  console.log(tz, {
    instantDate: instant.toLocaleDateString('en-US'),
    instantDateTime: instant.toLocaleString('en-US'),
    dateOnlyDate: dateOnly.toLocaleDateString('en-US'),
    dateOnlyDateTime: dateOnly.toLocaleString('en-US'),
  })
}
JS

Repository: openSenseMap/frontend

Length of output: 27119


Use a deterministic time zone for SSR-rendered timestamps.

These formatters omit timeZone. Different server and browser time zones can produce different strings for the same timestamp and cause hydration mismatches. Add an explicit timeZone, or defer formatting until the client.

Apply this to:

  • app/components/mydevices/dt/columns.tsx:93
  • app/routes/admin.devices._index.tsx:48
  • app/routes/admin.users._index.tsx:50-53
  • app/routes/device.$deviceId.edit.logs.tsx:172-174
  • app/routes/device.$deviceId.edit.transfer.tsx:296-299
  • app/routes/profile.$username.tsx:161-163

The range picker uses local date-only values. The mobile popup is populated by client-side map events, so neither confirms this hydration issue.

📍 Affects 8 files
  • app/components/ui/range-picker.tsx#L24-L28 (this comment)
  • app/components/ui/range-picker.tsx#L44-L48
  • app/components/map/layers/mobile/mobile-overview-layer.tsx#L124-L128
  • app/components/map/layers/mobile/mobile-overview-layer.tsx#L460-L470
  • app/components/mydevices/dt/columns.tsx#L35-L35
  • app/components/mydevices/dt/columns.tsx#L93-L93
  • app/routes/admin.devices._index.tsx#L15-L15
  • app/routes/admin.devices._index.tsx#L48-L48
  • app/routes/admin.users._index.tsx#L15-L15
  • app/routes/admin.users._index.tsx#L50-L53
  • app/routes/device.$deviceId.edit.logs.tsx#L97-L97
  • app/routes/device.$deviceId.edit.logs.tsx#L172-L174
  • app/routes/device.$deviceId.edit.transfer.tsx#L147-L147
  • app/routes/device.$deviceId.edit.transfer.tsx#L296-L299
  • app/routes/profile.$username.tsx#L130-L130
  • app/routes/profile.$username.tsx#L162-L163

Source: Linters/SAST tools


return (
Expand All @@ -34,11 +41,11 @@ export function DatePickerWithRange() {
{date?.from ? (
date.to ? (
<>
{format(date.from, 'LLL dd, y')} -{' '}
{format(date.to, 'LLL dd, y')}
{dateTimeFormat.format(date.from)} -{' '}
{dateTimeFormat.format(date.to)}
</>
) : (
format(date.from, 'LLL dd, y')
dateTimeFormat.format(date.from)
)
) : (
<span>Pick a date</span>
Expand Down
30 changes: 30 additions & 0 deletions app/lib/date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const ONE_MINUTE_IN_S = 60
const ONE_HOUR_IN_S = 60 * ONE_MINUTE_IN_S
const ONE_DAY_IN_S = 24 * ONE_HOUR_IN_S
const ONE_WEEK_IN_S = 7 * ONE_DAY_IN_S
const ONE_MONTH_IN_S = 4 * ONE_WEEK_IN_S
const ONE_QUARTER_IN_S = 3 * ONE_MONTH_IN_S
const ONE_YEAR_IN_S = 12 * ONE_MONTH_IN_S
Comment on lines +1 to +7

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- candidate files ---'
fd -t f -i 'date' . | head -80

printf '%s\n' '--- date symbols and references ---'
rg -n --glob '!node_modules' 'ONE_(MONTH|YEAR|QUARTER|WEEK|DAY|HOUR|MINUTE)_IN_S|app/lib/date|format.*date|relative' app tests . 2>/dev/null | head -240

printf '%s\n' '--- date.ts outline ---'
if command -v ast-grep >/dev/null 2>&1; then
  ast-grep outline app/lib/date.ts
else
  wc -l app/lib/date.ts
fi

printf '%s\n' '--- app/lib/date.ts ---'
cat -n app/lib/date.ts

Repository: openSenseMap/frontend

Length of output: 30854


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- exact call sites ---'
rg -n --glob '!node_modules' 'dateDiffToNowInWords' .

printf '%s\n' '--- test/config files ---'
git ls-files | rg '(^|/)(package\.json|.*date.*spec|.*date.*test|vitest|jest|README|CHANGELOG|CONTRIBUTING)' | head -160

printf '%s\n' '--- app/lib/date.ts history summary ---'
git diff --stat -- app/lib/date.ts
git diff -- app/lib/date.ts | head -220

printf '%s\n' '--- behavioral boundary calculation ---'
node - <<'JS'
const MINUTE = 60;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
const WEEK = 7 * DAY;
const MONTH = 4 * WEEK;
const QUARTER = 3 * MONTH;
const YEAR = 12 * MONTH;

function bucket(ageDays) {
  const diff = Math.round(ageDays * DAY);
  const abs = Math.abs(diff);
  if (abs < MINUTE) return ['second', -diff];
  if (abs < HOUR) return ['minute', -Math.round(diff / MINUTE)];
  if (abs < DAY) return ['hour', -Math.round(diff / HOUR)];
  if (abs < WEEK) return ['day', -Math.round(diff / DAY)];
  if (abs < MONTH) return ['week', -Math.round(diff / WEEK)];
  if (abs < QUARTER) return ['month', -Math.round(diff / MONTH)];
  if (abs < YEAR) return ['quarter', -Math.round(diff / QUARTER)];
  return ['year', -Math.round(diff / YEAR)];
}
for (const days of [27, 27.99, 28, 29, 56, 83, 84, 335, 336, 350, 365, 366]) {
  console.log(`${days} days -> ${bucket(days)[1]} ${bucket(days)[0]}`);
}
for (const days of [350, 365, 366]) {
  const d = new Date(Date.UTC(2025, 0, 1));
  d.setUTCDate(d.getUTCDate() - days);
  const elapsed = (Date.UTC(2025, 0, 1) - d.getTime()) / 86400000;
  console.log(`calendar probe: ${days} requested -> ${d.toISOString()} -> ${elapsed} elapsed days`);
}
JS

Repository: openSenseMap/frontend

Length of output: 1248


Use calendar-aware differences for month and year output.

ONE_YEAR_IN_S is 336 days, so a 350-day-old timestamp is formatted as 1 year ago. Month and quarter output also uses fixed 28-day blocks. If fixed durations are intentional, document and test this policy.


export const dateDiffToNowInWords = (locale: string, date: Date) => {
const r = new Intl.RelativeTimeFormat(locale)
const now = new Date()
const diffInSeconds = Math.round((now.getTime() - date.getTime()) / 1000)
const absDiffInSeconds = Math.abs(diffInSeconds)

if (absDiffInSeconds < ONE_MINUTE_IN_S)
return r.format(-diffInSeconds, 'second')
if (absDiffInSeconds < ONE_HOUR_IN_S)
return r.format(-Math.round(diffInSeconds / ONE_MINUTE_IN_S), 'minute')
if (absDiffInSeconds < ONE_DAY_IN_S)
return r.format(-Math.round(diffInSeconds / ONE_HOUR_IN_S), 'hour')
if (absDiffInSeconds < ONE_WEEK_IN_S)
return r.format(-Math.round(diffInSeconds / ONE_DAY_IN_S), 'day')
if (absDiffInSeconds < ONE_MONTH_IN_S)
return r.format(-Math.round(diffInSeconds / ONE_WEEK_IN_S), 'week')
if (absDiffInSeconds < ONE_QUARTER_IN_S)
return r.format(-Math.round(diffInSeconds / ONE_MONTH_IN_S), 'month')
if (absDiffInSeconds < ONE_YEAR_IN_S)
return r.format(-Math.round(diffInSeconds / ONE_QUARTER_IN_S), 'quarter')
return r.format(-Math.round(diffInSeconds / ONE_YEAR_IN_S), 'year')
}
4 changes: 3 additions & 1 deletion app/routes/admin.devices._index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Link } from 'react-router'
import { type Route } from './+types/admin.devices._index'
import { getDevices } from '~/db/models/device.server'
import { useTranslation } from 'react-i18next'

export async function loader({}: Route.LoaderArgs) {
const devices = await getDevices('json')
Expand All @@ -11,6 +12,7 @@ export default function AdminDevicesIndexRoute({
loaderData,
}: Route.ComponentProps) {
const { devices } = loaderData
const { i18n } = useTranslation()

return (
<div className="flex w-full flex-col">
Expand Down Expand Up @@ -43,7 +45,7 @@ export default function AdminDevicesIndexRoute({
</td>
<td className="border-r-2 border-black p-2">{device.status}</td>
<td className="border-r-2 border-black p-2">
{new Date(device.createdAt).toLocaleString()}
{new Date(device.createdAt).toLocaleString(i18n.language)}
</td>
<td className="border-r-2 border-black p-2">
<Link
Expand Down
6 changes: 4 additions & 2 deletions app/routes/admin.users._index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Link } from 'react-router'
import { type Route } from './+types/admin.users._index'
import { getUsers } from '~/db/models/user.server'
import { useTranslation } from 'react-i18next'

export async function loader({}: Route.LoaderArgs) {
const users = await getUsers()
Expand All @@ -11,6 +12,7 @@ export default function AdminUsersIndexRoute({
loaderData,
}: Route.ComponentProps) {
const { users } = loaderData
const { i18n } = useTranslation()

return (
<div className="flex w-full flex-col">
Expand Down Expand Up @@ -45,10 +47,10 @@ export default function AdminUsersIndexRoute({
</td>
<td className="border-r-2 border-black p-2">{user.role}</td>
<td className="border-r-2 border-black p-2">
{new Date(user.createdAt).toLocaleString()}
{new Date(user.createdAt).toLocaleString(i18n.language)}
</td>
<td className="border-r-2 border-black p-2">
{new Date(user.updatedAt).toLocaleString()}
{new Date(user.updatedAt).toLocaleString(i18n.language)}
</td>
{/* <td className="border-r-2 border-black p-2">
{user.devicesCount}
Expand Down
6 changes: 4 additions & 2 deletions app/routes/device.$deviceId.edit.logs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export default function Logs() {
const { logEntries } = useLoaderData<typeof loader>()
const actionData = useActionData<typeof action>()
const { toast } = useToast()
const { t } = useTranslation('edit-device-logs')
const { t, i18n } = useTranslation('edit-device-logs')
const [newLogContent, setNewLogContent] = useState('')

const submit = useSubmit()
Expand Down Expand Up @@ -169,7 +169,9 @@ export default function Logs() {
<TableRow key={logEntry.id}>
<TableCell>{logEntry.content}</TableCell>
<TableCell>
{new Date(logEntry.createdAt).toLocaleString()}
{new Date(logEntry.createdAt).toLocaleString(
i18n.language,
)}
</TableCell>
<TableCell>
<Form method="post">
Expand Down
4 changes: 2 additions & 2 deletions app/routes/device.$deviceId.edit.transfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export default function EditDeviceTransfer() {
const { deviceName, existingTransfer } = useLoaderData<typeof loader>()
const actionData = useActionData<typeof action>()
const navigation = useNavigation()
const { t } = useTranslation('device-transfer')
const { t, i18n } = useTranslation('device-transfer')

const [copied, setCopied] = useState(false)

Expand Down Expand Up @@ -293,7 +293,7 @@ export default function EditDeviceTransfer() {
<p className="mt-3 text-sm">
{t('valid_until')}{' '}
<b>
{new Date(transferExpiresAt).toLocaleString(t('locale'), {
{new Date(transferExpiresAt).toLocaleString(i18n.language, {
dateStyle: 'medium',
timeStyle: 'short',
})}
Expand Down
4 changes: 2 additions & 2 deletions app/routes/profile.$username.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export default function ProfilePage() {
deviceSchemas,
} = useLoaderData<typeof loader>()

const { t } = useTranslation('profile')
const { t, i18n } = useTranslation('profile')
const columnsTranslation = useTranslation('data-table')

const isOwner = !!profile?.userId && requestingUserId === profile.userId
Expand Down Expand Up @@ -159,7 +159,7 @@ export default function ProfilePage() {
<p className="text-muted-foreground text-sm">
{t('user_since')}{' '}
{new Date(profile?.user?.createdAt || '').toLocaleDateString(
t('locale'),
i18n.language,
)}
</p>
</div>
Expand Down
Loading