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))}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The previous format method displayed a combination of date and time, the new method requests only hours and minutes. Maybe it is fine though as we dont usually have multi-day trips?

</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
7 changes: 5 additions & 2 deletions app/components/mydevices/dt/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from '~/components/ui/dropdown-menu'
import { type Device } from '~/db/schema'
import { DeviceIdCell } from './device-id-cell'
import { useHydrated } from '~/hooks/use-hydrated'

export type SenseBox = {
id: string
Expand All @@ -32,8 +33,10 @@ 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
const hydrated = useHydrated()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As getColumns is used (conditionally) in the ProfilePage component this line would violate the rules of hooks in react. This was not detected by our linting rules due to the responsible rule being turned off in oxlintrc.json at the moment (my bad 😅). As the hydration state is already present in the ProfilePage component anyway, what about passing it down to getColumns like:

export function getColumns(
  translation: UseTranslationResponse<'data-table', any>,
  opts?: { isOwner?: boolean; hydrated?: boolean },
): ColumnDef<SenseBox>[] {
  const hydrated = opts?.hydrated ?? false
  // ...
}```  


return [
{
accessorKey: 'name',
Expand Down Expand Up @@ -90,7 +93,7 @@ export function getColumns(
},
cell: ({ row }) => {
const date = new Date(row.getValue('createdAt'))
return <div>{date.toLocaleDateString()}</div>
return <div>{hydrated && 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, 9),
})
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const dateTimeFormat = new Intl.DateTimeFormat(i18n.language, {
year: 'numeric',
month: 'long',
day: 'numeric',
})
Comment thread
scheidtdav marked this conversation as resolved.

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 = 31 * ONE_DAY_IN_S
const ONE_YEAR_IN_S = 365 * ONE_DAY_IN_S
const ONE_QUARTER_IN_S = ONE_YEAR_IN_S / 4

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')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I dont know how this is handled in general, but quarter sounds a bit weird to me, what about having only month and year level instead?

return r.format(-Math.round(diffInSeconds / ONE_YEAR_IN_S), 'year')
}
7 changes: 6 additions & 1 deletion app/routes/admin.devices._index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
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'
import { useHydrated } from '~/hooks/use-hydrated'

export async function loader({}: Route.LoaderArgs) {

Check warning on line 7 in app/routes/admin.devices._index.tsx

View workflow job for this annotation

GitHub Actions / ⬣ Lint

eslint(no-empty-pattern)

Empty object binding pattern
const devices = await getDevices('json')
return { devices }
}
Expand All @@ -11,6 +13,8 @@
loaderData,
}: Route.ComponentProps) {
const { devices } = loaderData
const { i18n } = useTranslation()
const hydrated = useHydrated()

return (
<div className="flex w-full flex-col">
Expand Down Expand Up @@ -43,7 +47,8 @@
</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()}
{hydrated &&
new Date(device.createdAt).toLocaleString(i18n.language)}
</td>
<td className="border-r-2 border-black p-2">
<Link
Expand Down
11 changes: 9 additions & 2 deletions app/routes/admin.users._index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
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'
import { useHydrated } from '~/hooks/use-hydrated'

export async function loader({}: Route.LoaderArgs) {
const users = await getUsers()

return { users }
}

export default function AdminUsersIndexRoute({
loaderData,
}: Route.ComponentProps) {
const { users } = loaderData
const { i18n } = useTranslation()
const hydrated = useHydrated()

return (
<div className="flex w-full flex-col">
Expand Down Expand Up @@ -45,10 +50,12 @@ 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()}
{hydrated &&
new Date(user.createdAt).toLocaleString(i18n.language)}
</td>
<td className="border-r-2 border-black p-2">
{new Date(user.updatedAt).toLocaleString()}
{hydrated &&
new Date(user.updatedAt).toLocaleString(i18n.language)}
</td>
{/* <td className="border-r-2 border-black p-2">
{user.devicesCount}
Expand Down
9 changes: 7 additions & 2 deletions app/routes/device.$deviceId.edit.logs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from '~/db/models/log-entry.server'
import { type LogEntry } from '~/db/schema/log-entry'
import { getUserId } from '~/services/session-service.server'
import { useHydrated } from '~/hooks/use-hydrated'

export async function loader({ request, params }: Route.LoaderArgs) {
const userId = await getUserId(request)
Expand Down Expand Up @@ -94,8 +95,9 @@ 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 hydrated = useHydrated()

const submit = useSubmit()

Expand Down Expand Up @@ -169,7 +171,10 @@ export default function Logs() {
<TableRow key={logEntry.id}>
<TableCell>{logEntry.content}</TableCell>
<TableCell>
{new Date(logEntry.createdAt).toLocaleString()}
{hydrated &&
new Date(logEntry.createdAt).toLocaleString(
i18n.language,
)}
</TableCell>
<TableCell>
<Form method="post">
Expand Down
16 changes: 11 additions & 5 deletions app/routes/device.$deviceId.edit.transfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
getDeviceTransfer,
createDeviceTransfer,
} from '~/services/transfer-service.server'
import { useHydrated } from '~/hooks/use-hydrated'

type LoaderData = {
deviceId: string
Expand Down Expand Up @@ -144,7 +145,8 @@ 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 hydrated = useHydrated()

const [copied, setCopied] = useState(false)

Expand Down Expand Up @@ -293,10 +295,14 @@ export default function EditDeviceTransfer() {
<p className="mt-3 text-sm">
{t('valid_until')}{' '}
<b>
{new Date(transferExpiresAt).toLocaleString(t('locale'), {
dateStyle: 'medium',
timeStyle: 'short',
})}
{hydrated &&
new Date(transferExpiresAt).toLocaleString(
i18n.language,
{
dateStyle: 'medium',
timeStyle: 'short',
},
)}
</b>
</p>
) : null}
Expand Down
11 changes: 7 additions & 4 deletions app/routes/profile.$username.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { getInitials } from '~/lib/strings'
import { getUserId } from '~/services/session-service.server'
import { claimDevice } from '~/services/transfer-service.server'
import { userNameFromURl } from '~/services/user-service.server'
import { useHydrated } from '~/hooks/use-hydrated'

type ActionData = {
success: boolean
Expand Down Expand Up @@ -127,8 +128,9 @@ export default function ProfilePage() {
deviceSchemas,
} = useLoaderData<typeof loader>()

const { t } = useTranslation('profile')
const { t, i18n } = useTranslation('profile')
const columnsTranslation = useTranslation('data-table')
const hydrated = useHydrated()
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const isOwner = !!profile?.userId && requestingUserId === profile.userId

Expand Down Expand Up @@ -158,9 +160,10 @@ export default function ProfilePage() {
</h4>
<p className="text-muted-foreground text-sm">
{t('user_since')}{' '}
{new Date(profile?.user?.createdAt || '').toLocaleDateString(
t('locale'),
)}
{hydrated &&
new Date(profile?.user?.createdAt || '').toLocaleDateString(
i18n.language,
)}
</p>
</div>
</div>
Expand Down
Loading