Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e88f58f
feat: scroll safe layout
jona159 Aug 13, 2026
f333934
feat: width on small screen
jona159 Aug 13, 2026
20e1634
feat: width on small screen
jona159 Aug 13, 2026
e805356
feat: stack items
jona159 Aug 13, 2026
7562f0d
feat: padding, scroll tabs
jona159 Aug 13, 2026
7799295
feat: precautions for cms content
jona159 Aug 13, 2026
e8bb8fd
feat: split sidebar and content until lg, reduce mobile padding
jona159 Aug 13, 2026
98113bd
feat: padding, shrinking
jona159 Aug 13, 2026
d0f6808
feat: improve grid
jona159 Aug 13, 2026
ee8cace
feat: shrink safe
jona159 Aug 13, 2026
e134eb7
feat: stack and truncate
jona159 Aug 13, 2026
9f06cec
feat: stack, padding
jona159 Aug 13, 2026
4bea2a1
feat: stack api selector
jona159 Aug 13, 2026
dfe7dc6
feat: viewport portal to escape map stacking context for overlays nes…
jona159 Aug 13, 2026
45d1c9b
feat: responsive ui components
jona159 Aug 13, 2026
9e16ade
feat: mobile header items
jona159 Aug 13, 2026
5d9e86a
feat: replace grid
jona159 Aug 13, 2026
45d52ec
feat: styles
jona159 Aug 13, 2026
ae1472f
fix: wrap tooltip provider
jona159 Aug 13, 2026
983c734
feat: replace dependency
jona159 Aug 21, 2026
020283b
Merge branch 'dev' into feat/responsive
jona159 Aug 21, 2026
8268912
feat: show entry logs in dialog on all screens to remove mantine depe…
jona159 Aug 21, 2026
58ec6c7
chore: rm mantine
jona159 Aug 21, 2026
7451e58
fix: use https url
jona159 Aug 24, 2026
5ed2a92
fix: revert styling for edge cases#
jona159 Aug 24, 2026
5b38902
fix: accessibility
jona159 Aug 24, 2026
9237d6c
fix: button variant
jona159 Aug 24, 2026
5cc9b6d
fix: wrong id, aria labels
jona159 Aug 24, 2026
1923a77
fix: wrap long localized labels on mobile
jona159 Aug 24, 2026
231b0c3
fix: rm unnecessary custom data attribute
jona159 Aug 24, 2026
a318fc7
fix: let parent route own overlay positioning, remove viewport portal
jona159 Aug 24, 2026
29b674d
fix: return ordered entry logs
jona159 Aug 24, 2026
6dbef81
fix: regenerate lock file
jona159 Aug 24, 2026
63e019d
fix: rm ring opacity as unsupported in tw v4
jona159 Aug 24, 2026
bca51a8
feat: implement use media query and reserve globe view for desktop la…
jona159 Aug 24, 2026
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
12 changes: 6 additions & 6 deletions app/components/device-detail/device-detail-box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,12 @@ export default function DeviceDetailBox() {
>
<div
ref={nodeRef}
className="absolute top-14 right-4 bottom-6 left-4 z-40 flex flex-row px-4 py-2 md:top-auto md:bottom-7.5 md:left-2.5 md:max-h-[calc(100vh-8rem)] md:w-1/3 md:p-0"
className="absolute top-30 right-4 bottom-6 left-4 z-40 flex flex-row px-4 py-2 lg:top-auto lg:bottom-7.5 lg:left-2.5 lg:max-h-[calc(100dvh-8rem)] lg:w-[min(28rem,calc(100vw-2rem))] lg:p-0"
>
<div
id="deviceDetailBox"
className={
'relative float-left flex h-full max-h-[calc(100vh-4rem)] w-auto flex-col gap-4 rounded-xl bg-white px-4 py-2 text-sm font-medium text-zinc-800 shadow-lg ring-1 shadow-zinc-800/5 ring-zinc-900/5 md:max-h-[calc(100vh-8rem)] dark:bg-zinc-800 dark:text-zinc-200 dark:opacity-95 dark:ring-white dark:backdrop-blur-xs'
'relative float-left flex h-full max-h-[calc(100dvh-4rem)] w-full min-w-0 flex-col gap-4 rounded-xl bg-white px-4 py-2 text-sm font-medium text-zinc-800 shadow-lg ring-1 shadow-zinc-800/5 ring-zinc-900/5 lg:max-h-[calc(100dvh-8rem)] dark:bg-zinc-800 dark:text-zinc-200 dark:opacity-95 dark:ring-white dark:backdrop-blur-xs'
}
>
{navigation.state === 'loading' && (
Expand All @@ -230,7 +230,7 @@ export default function DeviceDetailBox() {
: 'h-4 w-4 rounded-full bg-red-500'
}
></div>
<div className="flex flex-1 text-center text-xl text-zinc-600 dark:dark:text-zinc-100">
<div className="min-w-0 flex-1 truncate text-center text-xl text-zinc-600 dark:dark:text-zinc-100">
{data.device.name}
</div>
<AlertDialog>
Expand Down Expand Up @@ -313,7 +313,7 @@ export default function DeviceDetailBox() {
/>
</div>
<div className="no-scrollbar relative flex-1 overflow-y-scroll">
<div className="space-y-4 sm:flex sm:space-y-0 sm:space-x-4">
<div className="space-y-4 md:flex md:space-y-0 md:space-x-4">
<div className="md:w-1/2">
{data.deviceImageUrl ? (
<img
Expand All @@ -327,7 +327,7 @@ export default function DeviceDetailBox() {
</div>
)}
</div>
<div className="space-y-2 sm:w-1/2">
<div className="space-y-2 md:w-1/2">
<InfoItem
icon={LandPlot}
title={t('exposure')}
Expand Down Expand Up @@ -493,7 +493,7 @@ export default function DeviceDetailBox() {
</AlertDescription>
</Alert>
)}
<div className="grid gap-4 md:grid-cols-2 2xl:grid-cols-4">
<div className="grid gap-4 md:grid-cols-2">
{sensors &&
sensors.map(
(sensor: SensorWithLatestMeasurement) => {
Expand Down
125 changes: 38 additions & 87 deletions app/components/device-detail/entry-logs.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useMediaQuery } from '@mantine/hooks'
import { Activity, Clock, ExternalLink } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
Expand All @@ -11,16 +10,6 @@ import {
DialogTitle,
DialogTrigger,
} from '../ui/dialog'
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from '../ui/drawer'
import {
Tooltip,
TooltipContent,
Expand All @@ -37,95 +26,57 @@ export default function EntryLogs({
entryLogs: LogEntry[]
}) {
const [open, setOpen] = useState(false)
const isDesktop = useMediaQuery('(min-width: 768px)')
const { t, i18n } = useTranslation('device-detail-box')
const latestEntry = entryLogs[0]

if (isDesktop) {
return (
<div className="flex flex-col">
<p className="pb-4 font-bold">{t('logs')}</p>
<div className="flex items-center">
<div className="flex w-full items-start space-x-4">
<div className="border-muted-foreground text-muted-foreground flex h-10 w-10 shrink-0 items-center justify-center rounded-full border-4">
<Activity className="h-5 w-5" />
</div>
<div className="grow">
<p className="mb-2 text-sm font-medium">
{entryLogs[entryLogs.length - 1].content}
</p>
<div className="text-muted-foreground flex items-center text-xs">
<Clock className="mr-1 h-3 w-3" />
{new Date(entryLogs[0].createdAt).toLocaleString(i18n.language)}
</div>
</div>
</div>
<div className="shrink">
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="ghost">
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
{' '}
<ExternalLink className="ml-2 h-5 w-5" />
</TooltipTrigger>
<TooltipContent className="z-auto overflow-visible">
<p>{t('show_all_logs')}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-2/3">
<DialogHeader>
<DialogTitle>{t('device_logs')}</DialogTitle>
<DialogDescription>{t('logs_owner_hint')}</DialogDescription>
</DialogHeader>
<LogList entryLogs={entryLogs} locale={i18n.language} />
</DialogContent>
</Dialog>
</div>
</div>
</div>
)
}
if (!latestEntry) return null

return (
<div className="flex flex-col">
<p className="pb-4 font-bold">{t('logs')}</p>
<div className="flex items-center">
<div className="flex w-full items-start space-x-4">
<div className="bg-primary flex h-10 w-10 shrink-0 items-center justify-center rounded-full">
<Activity className="text-primary-foreground h-5 w-5" />
<div className="flex min-w-0 items-center">
<div className="flex min-w-0 flex-1 items-start space-x-4">
<div className="border-muted-foreground text-muted-foreground flex h-10 w-10 shrink-0 items-center justify-center rounded-full border-4">
<Activity className="h-5 w-5" />
</div>
<div className="grow">
<p className="mb-2 text-sm font-medium">{entryLogs[0].content}</p>
<div className="min-w-0 grow">
<p className="mb-2 text-sm font-medium wrap-break-word">
{latestEntry.content}
</p>
<div className="text-muted-foreground flex items-center text-xs">
<Clock className="mr-1 h-3 w-3" />
{new Date(entryLogs[0].createdAt).toLocaleString(i18n.language)}
{new Date(latestEntry.createdAt).toLocaleString(i18n.language)}
</div>
</div>
</div>
<div className="shrink"></div>
<Drawer open={open} onOpenChange={setOpen}>
<DrawerTrigger asChild>
<Button variant="ghost">
<ExternalLink className="ml-2 h-5 w-5" />
</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader className="text-left">
<DrawerTitle>{t('device_logs')}</DrawerTitle>
<DrawerDescription>{t('logs_owner_hint')}</DrawerDescription>
</DrawerHeader>
<Dialog open={open} onOpenChange={setOpen}>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<DialogTrigger asChild>
<Button
variant="ghost"
size="icon"
className="shrink-0"
aria-label={t('show_all_logs')}
>
<ExternalLink className="h-5 w-5" />
</Button>
</DialogTrigger>
</TooltipTrigger>
<TooltipContent>
<p>{t('show_all_logs')}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<DialogContent className="sm:max-w-2/3">
<DialogHeader>
<DialogTitle>{t('device_logs')}</DialogTitle>
<DialogDescription>{t('logs_owner_hint')}</DialogDescription>
</DialogHeader>
<LogList entryLogs={entryLogs} locale={i18n.language} />
<DrawerFooter className="pt-2">
<DrawerClose asChild>
<Button variant="outline">{t('close')}</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
</DialogContent>
</Dialog>
</div>
</div>
)
Expand Down
24 changes: 15 additions & 9 deletions app/components/device/new/custom-device-config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,13 +186,19 @@ export function CustomDeviceConfig() {
return (
<div className="space-y-4 p-2">
<Tabs defaultValue="schema" className="space-y-4">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="schema">{t('device_schema_tab')}</TabsTrigger>
<TabsTrigger value="manual">{t('manual_sensors_tab')}</TabsTrigger>
<TabsTrigger value="selected">
{t('selected_sensors_tab', { count: sensors.length })}
</TabsTrigger>
</TabsList>
<div className="overflow-x-auto pb-1">
<TabsList className="h-auto w-full min-w-max justify-evenly">
<TabsTrigger value="schema" className="shrink-0">
{t('device_schema_tab')}
</TabsTrigger>
<TabsTrigger value="manual" className="shrink-0">
{t('manual_sensors_tab')}
</TabsTrigger>
<TabsTrigger value="selected" className="shrink-0">
{t('selected_sensors_tab', { count: sensors.length })}
</TabsTrigger>
</TabsList>
</div>

<TabsContent value="schema" className="space-y-4">
<div className="border-border bg-muted/20 rounded-lg border border-dashed p-4">
Expand Down Expand Up @@ -281,11 +287,11 @@ export function CustomDeviceConfig() {
{t('device_schema_example_download')}
</a>
</div>
<div className="flex items-center gap-2">
<div className="flex flex-col gap-2 sm:flex-row sm:items-center">
<Input
type="file"
accept="application/json,.json"
className="max-w-64"
className="w-full max-w-none sm:max-w-64"
onChange={(event) => {
const file = event.target.files?.[0]
if (file) void importDeviceSchema(file)
Expand Down
4 changes: 2 additions & 2 deletions app/components/device/new/general-info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export function GeneralInfoStep() {
</div>
</div>
<div className="space-y-2">
<div className="flex items-center space-x-4">
<div className="flex flex-col gap-4 sm:flex-row sm:items-center">
<div className="flex items-center space-x-2">
<Checkbox
id="isTemporary"
Expand Down Expand Up @@ -195,7 +195,7 @@ export function GeneralInfoStep() {
</TooltipProvider>
</div>
{temporaryExpirationDate && (
<div className="flex grow items-center space-x-2">
<div className="flex min-w-0 grow flex-col gap-2 sm:flex-row sm:items-center">
<Label
htmlFor="temporaryExpirationDate"
className="text-sm font-medium whitespace-nowrap"
Expand Down
6 changes: 3 additions & 3 deletions app/components/device/new/location-info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ export function LocationStep() {
</BaseMap>
</div>

<div className="bg-background flex w-full items-center justify-around p-4">
<div>
<div className="bg-background flex w-full flex-col gap-4 p-4 sm:flex-row sm:items-start">
<div className="min-w-0 flex-1">
<Label htmlFor="latitude">{t('latitude')}</Label>
<Input
id="latitude"
Expand All @@ -158,7 +158,7 @@ export function LocationStep() {
) : null}
</div>

<div>
<div className="min-w-0 flex-1">
<Label htmlFor="longitude">{t('longitude')}</Label>
<Input
id="longitude"
Expand Down
12 changes: 6 additions & 6 deletions app/components/device/new/new-device-stepper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,12 @@ export default function NewDeviceStepper() {
<FormProvider {...form}>
<Form
onSubmit={form.handleSubmit(onSubmit, onError)}
className="bg-card flex h-full w-1/2 flex-col justify-between space-y-6 rounded-lg border p-6"
className="bg-card flex h-full min-h-0 w-full max-w-5xl flex-col space-y-6 rounded-lg border p-4 sm:p-6"
>
<div className="space-y-4">
<div className="shrink-0 space-y-4">
{/* Breadcrumb Navigation */}
<Breadcrumb>
<BreadcrumbList>
<Breadcrumb className="w-full overflow-x-auto pb-2">
<BreadcrumbList className="w-max flex-nowrap">
{Stepper.steps.map((step, index) => {
return (
<div className="flex gap-2" key={index}>
Expand Down Expand Up @@ -246,7 +246,7 @@ export default function NewDeviceStepper() {
</div>

{/* Form Content */}
<div className="h-full overflow-auto">
<div className="min-h-0 flex-1 overflow-auto">
{stepper.match({
advanced: () => <AdvancedStep integrations={integrations} />,
'general-info': () => <GeneralInfoStep />,
Expand All @@ -258,7 +258,7 @@ export default function NewDeviceStepper() {
</div>

{/* Navigation Buttons */}
<div className="mt-4 flex justify-between">
<div className="mt-4 flex shrink-0 justify-between">
<Button
type="button"
variant="secondary"
Expand Down
16 changes: 8 additions & 8 deletions app/components/header/download.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ export default function Download({

return (
<Dialog open={open} onOpenChange={handleDialogOpenChange}>
<DialogContent className="max-h-screen overflow-y-auto sm:max-w-xl">
<DialogContent className="p-4 sm:max-w-xl sm:p-6">
<DialogHeader>
<DialogTitle>{t('downloadOptions')}</DialogTitle>
<DialogDescription>{t('downloadDescription')}</DialogDescription>
Expand All @@ -326,7 +326,7 @@ export default function Download({
))}

<div className="grid gap-3">
<div className="flex items-center justify-between">
<div className="flex flex-wrap items-center justify-between gap-2">
<Label htmlFor="devices">{t('devices')}</Label>

<span className="text-sm font-medium text-blue-600">
Expand Down Expand Up @@ -426,7 +426,7 @@ export default function Download({
))}
</fieldset>

<div className="flex h-16 items-center justify-center">
<div className="flex min-h-16 items-center justify-center text-center">
{isBusy ? (
<PulsingDownloadAnimation />
) : showReadyAnimation ? (
Expand All @@ -435,10 +435,10 @@ export default function Download({
</div>

{errorMessage && (
<div className="flex items-center gap-3 rounded-md border border-red-300 bg-red-100 p-3 text-red-700">
<div className="flex min-w-0 items-center gap-3 rounded-md border border-red-300 bg-red-100 p-3 text-red-700">
<AlertTriangle className="h-8 w-8 shrink-0 animate-pulse text-red-500" />

<p className="text-sm">
<p className="min-w-0 text-sm wrap-break-word">
{t('error')}{' '}
{actionData && 'link' in actionData && actionData.link ? (
<>
Expand Down Expand Up @@ -467,10 +467,10 @@ export default function Download({
)}

<DialogFooter>
<div className="flex w-full items-center justify-center gap-4">
<div className="flex w-full flex-col items-stretch justify-center gap-2 sm:flex-row sm:items-center sm:gap-4">
<Button
type="submit"
className="text-dark bg-blue-100 transition-colors hover:bg-blue-200"
className="text-dark w-full bg-blue-100 transition-colors hover:bg-blue-200 sm:w-auto"
disabled={
isBusy || deviceIds.length === 0 || !hasSelectedFields
}
Expand All @@ -485,7 +485,7 @@ export default function Download({
<a
href={actionData.href}
download={actionData.download}
className={`text-dark flex items-center rounded px-4 py-2 transition-colors ${
className={`text-dark flex min-h-10 w-full items-center justify-center rounded px-4 py-2 text-center transition-colors sm:w-auto ${
downloadStarted
? 'animate-pulse bg-blue-300'
: 'bg-green-100 hover:bg-green-400'
Expand Down
Loading
Loading