Fixes for mobile.
Some checks are pending
Create docker images (cloud) / Build, push, and deploy (push) Waiting to run
Node.js CI / build (postgresql, 18.18, 10) (push) Waiting to run

This commit is contained in:
Mike Cao 2025-10-18 01:12:45 -07:00
parent bcafa12349
commit 9a5604f236
11 changed files with 38 additions and 58 deletions

View file

@ -78,7 +78,7 @@
"@react-spring/web": "^10.0.3",
"@svgr/cli": "^8.1.0",
"@tanstack/react-query": "^5.90.5",
"@umami/react-zen": "^0.200.0",
"@umami/react-zen": "^0.203.0",
"@umami/redis-client": "^0.29.0",
"bcryptjs": "^3.0.2",
"chalk": "^5.6.2",

10
pnpm-lock.yaml generated
View file

@ -45,8 +45,8 @@ importers:
specifier: ^5.90.5
version: 5.90.5(react@19.2.0)
'@umami/react-zen':
specifier: ^0.200.0
version: 0.200.0(@babel/core@7.28.3)(@types/react@19.2.2)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.1.3)(use-sync-external-store@1.6.0(react@19.2.0))
specifier: ^0.203.0
version: 0.203.0(@babel/core@7.28.3)(@types/react@19.2.2)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.1.3)(use-sync-external-store@1.6.0(react@19.2.0))
'@umami/redis-client':
specifier: ^0.29.0
version: 0.29.0
@ -2921,8 +2921,8 @@ packages:
resolution: {integrity: sha512-ptkmIf2iDkNUjdeu2bQqhFPV1m6qTnFFjg7PPDjxKWaMaP0Z6I9l30Jr3g5QqbZGdw8YdYvLp+XnqnWWZOg/NA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@umami/react-zen@0.200.0':
resolution: {integrity: sha512-En5H5XpssItNPPSxb+xwGyMUalmqeoWCzMInGi/MJau/kkcJ2V0mSrgZ8RVRryl8cBg8fpktK7JaCTNULweUFA==}
'@umami/react-zen@0.203.0':
resolution: {integrity: sha512-lgGUapA0zDbLu63GINaEPndIsT8ry85vE316AWU/EEH3qYDBNscetcBfZFr+DTD/c5eLKy9OxmMwIpbs7k+/UA==}
'@umami/redis-client@0.29.0':
resolution: {integrity: sha512-Jaqh++jskqDB7ny75pfC02OvKp1JTS4asGDsFrRL3qy8sxL3PAl9+/mybCJe4/6vWrXDJKqpgkSfUDJq2bFjyw==}
@ -10670,7 +10670,7 @@ snapshots:
'@typescript-eslint/types': 8.46.1
eslint-visitor-keys: 4.2.1
'@umami/react-zen@0.200.0(@babel/core@7.28.3)(@types/react@19.2.2)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.1.3)(use-sync-external-store@1.6.0(react@19.2.0))':
'@umami/react-zen@0.203.0(@babel/core@7.28.3)(@types/react@19.2.2)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.1.3)(use-sync-external-store@1.6.0(react@19.2.0))':
dependencies:
'@fontsource/jetbrains-mono': 5.2.8
'@internationalized/date': 3.10.0

View file

@ -33,7 +33,7 @@ export function MobileNav() {
];
return (
<Grid columns="auto 1fr" flexGrow={1}>
<Grid columns="auto 1fr" flexGrow={1} backgroundColor="3" borderRadius>
<MobileMenuButton>
{({ close }) => {
return (

View file

@ -1,6 +1,6 @@
import { Dialog, Modal, useBreakpoint } from '@umami/react-zen';
import { Dialog, Modal } from '@umami/react-zen';
import { WebsiteExpandedView } from '@/app/(main)/websites/[websiteId]/WebsiteExpandedView';
import { useNavigation } from '@/components/hooks';
import { useNavigation, useMobile } from '@/components/hooks';
export function ExpandedViewModal({
websiteId,
@ -14,8 +14,7 @@ export function ExpandedViewModal({
query: { view },
updateParams,
} = useNavigation();
const breakpoint = useBreakpoint();
const isMobile = ['xs', 'sm', 'md'].includes(breakpoint);
const { isMobile } = useMobile();
const handleClose = (close: () => void) => {
router.push(updateParams({ view: undefined }));

View file

@ -6,9 +6,9 @@ import {
cloneElement,
isValidElement,
} from 'react';
import { SearchField, Row, Column, useBreakpoint } from '@umami/react-zen';
import { SearchField, Row, Column } from '@umami/react-zen';
import { UseQueryResult } from '@tanstack/react-query';
import { useMessages, useNavigation } from '@/components/hooks';
import { useMessages, useMobile, useNavigation } from '@/components/hooks';
import { Pager } from '@/components/common/Pager';
import { LoadingPanel } from '@/components/common/LoadingPanel';
import { PageResult } from '@/lib/types';
@ -42,8 +42,8 @@ export function DataGrid({
const { router, updateParams, query: queryParams } = useNavigation();
const [search, setSearch] = useState(queryParams?.search || data?.search || '');
const showPager = allowPaging && data && data.count > data.pageSize;
const breakpoint = useBreakpoint();
const displayMode = ['xs', 'sm', 'md', 'lg'].includes(breakpoint) ? 'cards' : undefined;
const { isMobile } = useMobile();
const displayMode = isMobile ? 'cards' : undefined;
const handleSearch = (value: string) => {
if (value !== search) {

View file

@ -1,16 +1,8 @@
import { useState, Key, Fragment } from 'react';
import {
Modal,
Select,
ListItem,
ListSeparator,
Dialog,
SelectProps,
useBreakpoint,
} from '@umami/react-zen';
import { Modal, Select, ListItem, ListSeparator, Dialog, SelectProps } from '@umami/react-zen';
import { endOfYear } from 'date-fns';
import { DatePickerForm } from '@/components/metrics/DatePickerForm';
import { useMessages } from '@/components/hooks';
import { useMessages, useMobile } from '@/components/hooks';
import { DateDisplay } from '@/components/common/DateDisplay';
import { parseDateRange } from '@/lib/date';
@ -33,8 +25,7 @@ export function DateFilter({
const { formatMessage, labels } = useMessages();
const [showPicker, setShowPicker] = useState(false);
const { startDate, endDate } = parseDateRange(value) || {};
const breakpoint = useBreakpoint();
const isMobile = ['xs', 'sm', 'md'].includes(breakpoint);
const { isMobile } = useMobile();
const options = [
{ label: formatMessage(labels.today), value: '0day' },
@ -119,7 +110,8 @@ export function DateFilter({
placeholder={formatMessage(labels.selectDate)}
onChange={handleChange}
renderValue={renderValue}
popoverProps={{ placement, isNonModal: isMobile }}
popoverProps={{ placement }}
isFullscreen={isMobile}
>
{options.map(({ label, value, divider }: any) => {
return (

View file

@ -25,7 +25,7 @@ export function DialogButton({
icon,
label,
title,
width = '800px',
width,
height,
minWidth,
minHeight,

View file

@ -1,7 +1,7 @@
import { useFilters, useMessages, useNavigation } from '@/components/hooks';
import { useFilters, useMessages, useMobile, useNavigation } from '@/components/hooks';
import { FieldFilters } from '@/components/input/FieldFilters';
import { SegmentFilters } from '@/components/input/SegmentFilters';
import { Button, Column, Row, Tab, TabList, TabPanel, Tabs, useBreakpoint } from '@umami/react-zen';
import { Button, Column, Row, Tab, TabList, TabPanel, Tabs } from '@umami/react-zen';
import { useState } from 'react';
export interface FilterEditFormProps {
@ -20,8 +20,7 @@ export function FilterEditForm({ websiteId, onChange, onClose }: FilterEditFormP
const [currentFilters, setCurrentFilters] = useState(filters);
const [currentSegment, setCurrentSegment] = useState(segment);
const [currentCohort, setCurrentCohort] = useState(cohort);
const breakpoint = useBreakpoint();
const isMobile = ['xs', 'sm', 'md'].includes(breakpoint);
const { isMobile } = useMobile();
const excludeFilters = pathname.includes('/pixels') || pathname.includes('/links');
const handleReset = () => {

View file

@ -12,9 +12,14 @@ import {
Column,
Pressable,
IconLabel,
useBreakpoint,
} from '@umami/react-zen';
import { useConfig, useLoginQuery, useMessages, useNavigation } from '@/components/hooks';
import {
useConfig,
useLoginQuery,
useMessages,
useMobile,
useNavigation,
} from '@/components/hooks';
import {
BookText,
ChevronRight,
@ -40,11 +45,10 @@ export function NavButton({ showText = true }: TeamsButtonProps) {
const { cloudMode } = useConfig();
const { formatMessage, labels } = useMessages();
const { teamId } = useNavigation();
const breakpoint = useBreakpoint();
const { isMobile } = useMobile();
const team = user?.teams?.find(({ id }) => id === teamId);
const selectedKeys = new Set([teamId || 'user']);
const label = teamId ? team?.name : user.username;
const isMobile = ['xs', 'sm', 'md'].includes(breakpoint);
const getUrl = (url: string) => {
return cloudMode ? `${process.env.cloudUrl}${url}` : url;

View file

@ -1,22 +1,18 @@
import { Button, Icon, DialogTrigger, Dialog, Text, Modal, useBreakpoint } from '@umami/react-zen';
import { ListFilter } from '@/components/icons';
import { FilterEditForm } from '@/components/input/FilterEditForm';
import { DialogButton } from '@/components/input/DialogButton';
import { useMessages, useNavigation } from '@/components/hooks';
import { filtersArrayToObject } from '@/lib/params';
export function WebsiteFilterButton({
websiteId,
showText = true,
}: {
websiteId: string;
position?: 'bottom' | 'top' | 'left' | 'right';
alignment?: 'end' | 'center' | 'start';
showText?: boolean;
}) {
const { formatMessage, labels } = useMessages();
const { updateParams, router } = useNavigation();
const breakpoint = useBreakpoint();
const isMobile = ['xs', 'sm', 'md'].includes(breakpoint);
const handleChange = ({ filters, segment, cohort }: any) => {
const params = filtersArrayToObject(filters);
@ -27,20 +23,10 @@ export function WebsiteFilterButton({
};
return (
<DialogTrigger>
<Button variant="outline">
<Icon>
<ListFilter />
</Icon>
{showText && <Text>{formatMessage(labels.filter)}</Text>}
</Button>
<Modal placement={isMobile ? 'fullscreen' : 'center'}>
<Dialog title={formatMessage(labels.filters)}>
{({ close }) => {
return <FilterEditForm websiteId={websiteId} onChange={handleChange} onClose={close} />;
}}
</Dialog>
</Modal>
</DialogTrigger>
<DialogButton icon={<ListFilter />} label={formatMessage(labels.filter)} variant="outline">
{({ close }) => {
return <FilterEditForm websiteId={websiteId} onChange={handleChange} onClose={close} />;
}}
</DialogButton>
);
}

View file

@ -47,7 +47,7 @@ export function DatePickerForm({
<Calendar value={date} minValue={minDate} maxValue={maxDate} onChange={setDate} />
)}
{selected.includes(FILTER_RANGE) && (
<Row gap>
<Row gap wrap="wrap" style={{ margin: '0 auto' }}>
<Calendar
value={startDate}
minValue={minDate}