diff --git a/.husky/pre-commit b/.husky/pre-commit index 36af2198..2312dc58 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - npx lint-staged diff --git a/package.json b/package.json index d52a7777..db28ef61 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "@react-spring/web": "^10.0.3", "@svgr/cli": "^8.1.0", "@tanstack/react-query": "^5.90.2", - "@umami/react-zen": "^0.189.0", + "@umami/react-zen": "^0.195.0", "@umami/redis-client": "^0.29.0", "bcryptjs": "^3.0.2", "chalk": "^5.6.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3ad08bc..5b3d9103 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,8 +45,8 @@ importers: specifier: ^5.90.2 version: 5.90.2(react@19.1.1) '@umami/react-zen': - specifier: ^0.189.0 - version: 0.189.0(@babel/core@7.28.3)(@types/react@19.1.16)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.1.3)(use-sync-external-store@1.6.0(react@19.1.1)) + specifier: ^0.195.0 + version: 0.195.0(@babel/core@7.28.3)(@types/react@19.1.16)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.1.3)(use-sync-external-store@1.6.0(react@19.1.1)) '@umami/redis-client': specifier: ^0.29.0 version: 0.29.0 @@ -2756,8 +2756,8 @@ packages: resolution: {integrity: sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@umami/react-zen@0.189.0': - resolution: {integrity: sha512-E5t5HvMrGfuilrnF6LJV+jeooC4qXpwUC4VGhnTPV24B1vdMC2W9ByzZreNaomgZy8XOVAk1wZf8QX1elloUjA==} + '@umami/react-zen@0.195.0': + resolution: {integrity: sha512-DI/o0AOwq6wfWEx+PgXFQ8xV0NJFP9xY1qd0+cv2Bme9Bho0U5+vxyFPkHawJyC7bfMDi3BgC7JHgTqeCqE99A==} '@umami/redis-client@0.29.0': resolution: {integrity: sha512-Jaqh++jskqDB7ny75pfC02OvKp1JTS4asGDsFrRL3qy8sxL3PAl9+/mybCJe4/6vWrXDJKqpgkSfUDJq2bFjyw==} @@ -6350,8 +6350,8 @@ packages: peerDependencies: react: '>=16.13.1' - react-hook-form@7.64.0: - resolution: {integrity: sha512-fnN+vvTiMLnRqKNTVhDysdrUay0kUUAymQnFIznmgDvapjveUWOOPqMNzPg+A+0yf9DuE2h6xzBjN1s+Qx8wcg==} + react-hook-form@7.65.0: + resolution: {integrity: sha512-xtOzDz063WcXvGWaHgLNrNzlsdFgtUWcb32E6WFaGTd7kPZG3EeDusjdZfUsPwKCKVXy1ZlntifaHZ4l8pAsmw==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 @@ -10387,7 +10387,7 @@ snapshots: '@typescript-eslint/types': 8.45.0 eslint-visitor-keys: 4.2.1 - '@umami/react-zen@0.189.0(@babel/core@7.28.3)(@types/react@19.1.16)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.1.3)(use-sync-external-store@1.6.0(react@19.1.1))': + '@umami/react-zen@0.195.0(@babel/core@7.28.3)(@types/react@19.1.16)(babel-plugin-react-compiler@19.1.0-rc.2)(immer@10.1.3)(use-sync-external-store@1.6.0(react@19.1.1))': dependencies: '@fontsource/jetbrains-mono': 5.2.8 '@internationalized/date': 3.10.0 @@ -10401,7 +10401,7 @@ snapshots: react: 19.1.1 react-aria-components: 1.13.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) react-dom: 19.1.1(react@19.1.1) - react-hook-form: 7.64.0(react@19.1.1) + react-hook-form: 7.65.0(react@19.1.1) react-icons: 5.5.0(react@19.1.1) thenby: 1.3.4 zustand: 5.0.8(@types/react@19.1.16)(immer@10.1.3)(react@19.1.1)(use-sync-external-store@1.6.0(react@19.1.1)) @@ -14534,7 +14534,7 @@ snapshots: '@babel/runtime': 7.28.3 react: 19.1.1 - react-hook-form@7.64.0(react@19.1.1): + react-hook-form@7.65.0(react@19.1.1): dependencies: react: 19.1.1 diff --git a/src/app/(main)/App.tsx b/src/app/(main)/App.tsx index 52c9257a..b32b2785 100644 --- a/src/app/(main)/App.tsx +++ b/src/app/(main)/App.tsx @@ -28,9 +28,9 @@ export function App({ children }) { } return ( - - - + + + diff --git a/src/app/(main)/links/LinksTable.tsx b/src/app/(main)/links/LinksTable.tsx index b8471829..5df15b78 100644 --- a/src/app/(main)/links/LinksTable.tsx +++ b/src/app/(main)/links/LinksTable.tsx @@ -1,23 +1,18 @@ import Link from 'next/link'; -import { DataTable, DataColumn, Row } from '@umami/react-zen'; +import { DataTable, DataColumn, Row, DataTableProps } from '@umami/react-zen'; import { useMessages, useNavigation, useSlug } from '@/components/hooks'; -import { Empty } from '@/components/common/Empty'; import { DateDistance } from '@/components/common/DateDistance'; import { ExternalLink } from '@/components/common/ExternalLink'; import { LinkEditButton } from './LinkEditButton'; import { LinkDeleteButton } from './LinkDeleteButton'; -export function LinksTable({ data = [] }) { +export function LinksTable(props: DataTableProps) { const { formatMessage, labels } = useMessages(); const { websiteId, renderUrl } = useNavigation(); const { getSlugUrl } = useSlug('link'); - if (data.length === 0) { - return ; - } - return ( - + {({ id, name }: any) => { return {name}; diff --git a/src/app/(main)/pixels/PixelsTable.tsx b/src/app/(main)/pixels/PixelsTable.tsx index dcb5307c..4edbb1cf 100644 --- a/src/app/(main)/pixels/PixelsTable.tsx +++ b/src/app/(main)/pixels/PixelsTable.tsx @@ -1,23 +1,18 @@ import Link from 'next/link'; -import { DataTable, DataColumn, Row } from '@umami/react-zen'; +import { DataTable, DataColumn, Row, DataTableProps } from '@umami/react-zen'; import { useMessages, useNavigation, useSlug } from '@/components/hooks'; -import { Empty } from '@/components/common/Empty'; import { DateDistance } from '@/components/common/DateDistance'; import { PixelEditButton } from './PixelEditButton'; import { PixelDeleteButton } from './PixelDeleteButton'; import { ExternalLink } from '@/components/common/ExternalLink'; -export function PixelsTable({ data = [] }) { +export function PixelsTable(props: DataTableProps) { const { formatMessage, labels } = useMessages(); const { renderUrl } = useNavigation(); const { getSlugUrl } = useSlug('pixel'); - if (data.length === 0) { - return ; - } - return ( - + {({ id, name }: any) => { return {name}; diff --git a/src/app/(main)/teams/TeamsTable.tsx b/src/app/(main)/teams/TeamsTable.tsx index 5a22bcda..b5f11233 100644 --- a/src/app/(main)/teams/TeamsTable.tsx +++ b/src/app/(main)/teams/TeamsTable.tsx @@ -1,19 +1,17 @@ -import { DataColumn, DataTable } from '@umami/react-zen'; +import { DataColumn, DataTable, DataTableProps } from '@umami/react-zen'; import { useMessages } from '@/components/hooks'; import { ROLES } from '@/lib/constants'; import { ReactNode } from 'react'; -export function TeamsTable({ - data = [], - renderLink, -}: { - data: any[]; +export interface TeamsTableProps extends DataTableProps { renderLink?: (row: any) => ReactNode; -}) { +} + +export function TeamsTable({ renderLink, ...props }: TeamsTableProps) { const { formatMessage, labels } = useMessages(); return ( - + {renderLink} diff --git a/src/app/(main)/websites/WebsitesTable.tsx b/src/app/(main)/websites/WebsitesTable.tsx index d6d74390..c2f7e0d4 100644 --- a/src/app/(main)/websites/WebsitesTable.tsx +++ b/src/app/(main)/websites/WebsitesTable.tsx @@ -1,30 +1,22 @@ import { ReactNode } from 'react'; -import { Icon, DataTable, DataColumn } from '@umami/react-zen'; +import { Icon, DataTable, DataColumn, DataTableProps } from '@umami/react-zen'; import { LinkButton } from '@/components/common/LinkButton'; import { useMessages, useNavigation } from '@/components/hooks'; import { SquarePen } from '@/components/icons'; -import { Empty } from '@/components/common/Empty'; -export function WebsitesTable({ - data = [], - showActions, - renderLink, -}: { - data: Record[]; +export interface WebsitesTableProps extends DataTableProps { showActions?: boolean; allowEdit?: boolean; allowView?: boolean; renderLink?: (row: any) => ReactNode; -}) { +} + +export function WebsitesTable({ showActions, renderLink, ...props }: WebsitesTableProps) { const { formatMessage, labels } = useMessages(); const { renderUrl } = useNavigation(); - if (data.length === 0) { - return ; - } - return ( - + {renderLink} diff --git a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx index 7cefdd27..e9e3e6a0 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsTable.tsx @@ -1,6 +1,5 @@ -import { DataTable, DataColumn, Icon, Row, Text } from '@umami/react-zen'; +import { DataTable, DataColumn, Row, Text, DataTableProps, IconLabel } from '@umami/react-zen'; import { useFormat, useMessages, useNavigation } from '@/components/hooks'; -import { Empty } from '@/components/common/Empty'; import { Avatar } from '@/components/common/Avatar'; import Link from 'next/link'; import { Eye } from '@/components/icons'; @@ -8,35 +7,44 @@ import { Lightning } from '@/components/svg'; import { DateDistance } from '@/components/common/DateDistance'; import { TypeIcon } from '@/components/common/TypeIcon'; -export function EventsTable({ data = [] }) { +export function EventsTable(props: DataTableProps) { const { formatMessage, labels } = useMessages(); const { updateParams } = useNavigation(); const { formatValue } = useFormat(); - if (data.length === 0) { - return ; - } - return ( - + {(row: any) => { return ( - - - - - {row.eventName ? : } - - {formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)} - - + + + : } + label={formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)} + /> + + {row.eventName || row.urlPath} ); }} + + {(row: any) => { + return ( + + + + ); + }} + {(row: any) => ( diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx index 4c459bdc..53b35b21 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsDataTable.tsx @@ -8,7 +8,7 @@ export function SessionsDataTable({ websiteId }: { websiteId?: string; teamId?: return ( {({ data }) => { - return ; + return ; }} ); diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index 391c57f4..ab4977ea 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -1,17 +1,17 @@ import Link from 'next/link'; -import { DataColumn, DataTable } from '@umami/react-zen'; +import { DataColumn, DataTable, DataTableProps } from '@umami/react-zen'; import { useFormat, useMessages, useNavigation } from '@/components/hooks'; import { Avatar } from '@/components/common/Avatar'; import { TypeIcon } from '@/components/common/TypeIcon'; import { DateDistance } from '@/components/common/DateDistance'; -export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean }) { +export function SessionsTable(props: DataTableProps) { const { formatMessage, labels } = useMessages(); const { formatValue } = useFormat(); const { updateParams } = useNavigation(); return ( - + {(row: any) => ( diff --git a/src/components/common/DataGrid.tsx b/src/components/common/DataGrid.tsx index d95ee515..18cb763c 100644 --- a/src/components/common/DataGrid.tsx +++ b/src/components/common/DataGrid.tsx @@ -1,5 +1,12 @@ -import { ReactNode, useState, useCallback } from 'react'; -import { SearchField, Row, Column } from '@umami/react-zen'; +import { + ReactNode, + useState, + useCallback, + ReactElement, + cloneElement, + isValidElement, +} from 'react'; +import { SearchField, Row, Column, useBreakpoint } from '@umami/react-zen'; import { UseQueryResult } from '@tanstack/react-query'; import { useMessages, useNavigation } from '@/components/hooks'; import { Pager } from '@/components/common/Pager'; @@ -35,6 +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 handleSearch = (value: string) => { if (value !== search) { @@ -50,6 +59,8 @@ export function DataGrid({ [search], ); + const child = data ? (typeof children === 'function' ? children(data) : children) : null; + return ( {allowSearch && ( @@ -73,7 +84,11 @@ export function DataGrid({ > {data && ( <> - {typeof children === 'function' ? children(data) : children} + + {isValidElement(child) + ? cloneElement(child as ReactElement, { displayMode }) + : child} + {showPager && ( {children} diff --git a/src/components/input/NavButton.tsx b/src/components/input/NavButton.tsx index 257cd685..8be13696 100644 --- a/src/components/input/NavButton.tsx +++ b/src/components/input/NavButton.tsx @@ -61,6 +61,7 @@ export function NavButton({ showText = true }: TeamsButtonProps) { borderRadius shadow="1" maxHeight="40px" + role="button" style={{ cursor: 'pointer', textWrap: 'nowrap', outline: 'none' }} >