From d0e1912fafdf097f48081e4eca6876031f077834 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Thu, 30 Nov 2023 23:40:58 -0800 Subject: [PATCH 1/5] Updated CSP rules. --- next.config.js | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/next.config.js b/next.config.js index eaaf8fe7..c73790b8 100644 --- a/next.config.js +++ b/next.config.js @@ -9,38 +9,21 @@ const contentSecurityPolicy = [ `script-src 'self' 'unsafe-eval' 'unsafe-inline'`, `style-src 'self' 'unsafe-inline'`, `connect-src 'self' api.umami.is`, - `frame-src *`, + `frame-ancestors 'self' ${process.env.ALLOWED_FRAME_URLS || ''}`, ]; -const cspHeader = (values = []) => ({ - key: 'Content-Security-Policy', - value: values - .join(';') - .replace(/\s{2,}/g, ' ') - .trim(), -}); - const headers = [ { key: 'X-DNS-Prefetch-Control', value: 'on', }, { - key: 'X-Frame-Options', - value: 'SAMEORIGIN', + key: 'Content-Security-Policy', + value: contentSecurityPolicy + .join(';') + .replace(/\s{2,}/g, ' ') + .trim(), }, - cspHeader(contentSecurityPolicy), -]; - -const shareHeaders = [ - { - key: 'X-DNS-Prefetch-Control', - value: 'on', - }, - cspHeader([ - ...contentSecurityPolicy, - `frame-ancestors 'self' ${process.env.ALLOWED_FRAME_URLS || ''}`, - ]), ]; if (process.env.FORCE_SSL) { @@ -142,10 +125,6 @@ const config = { source: '/:path*', headers, }, - { - source: '/share/:path*', - headers: shareHeaders, - }, ]; }, async rewrites() { From e068ac0dd98eb5874c6bddf4842c2bcfc1714b5f Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 1 Dec 2023 16:02:50 -0800 Subject: [PATCH 2/5] Fixed insights report. --- package.json | 2 +- src/app/(main)/reports/insights/InsightsTable.js | 4 ++-- src/components/hooks/useFormat.ts | 10 +++++----- src/pages/api/reports/insights.ts | 2 +- src/queries/analytics/reports/getInsights.ts | 5 +++-- yarn.lock | 8 ++++---- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index ba85fb3e..addfebdf 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "npm-run-all": "^4.1.5", "prisma": "5.6.0", "react": "^18.2.0", - "react-basics": "^0.109.0", + "react-basics": "^0.110.0", "react-beautiful-dnd": "^13.1.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.4", diff --git a/src/app/(main)/reports/insights/InsightsTable.js b/src/app/(main)/reports/insights/InsightsTable.js index 05d38042..4194fee8 100644 --- a/src/app/(main)/reports/insights/InsightsTable.js +++ b/src/app/(main)/reports/insights/InsightsTable.js @@ -37,10 +37,10 @@ export function InsightsTable() { width="100px" alignment="end" > - {row => row.visitors.toLocaleString()} + {row => row?.visitors?.toLocaleString()} - {row => row.views.toLocaleString()} + {row => row?.views?.toLocaleString()} ); diff --git a/src/components/hooks/useFormat.ts b/src/components/hooks/useFormat.ts index c1160162..f804eb72 100644 --- a/src/components/hooks/useFormat.ts +++ b/src/components/hooks/useFormat.ts @@ -9,23 +9,23 @@ export function useFormat() { const { locale } = useLocale(); const countryNames = useCountryNames(locale); - const formatBrowser = (value: string) => { + const formatBrowser = (value: string): string => { return BROWSERS[value] || value; }; - const formatCountry = (value: string) => { + const formatCountry = (value: string): string => { return countryNames[value] || value; }; - const formatRegion = (value: string) => { + const formatRegion = (value: string): string => { return regions[value] ? regions[value] : value; }; - const formatDevice = (value: string) => { + const formatDevice = (value: string): string => { return formatMessage(labels[value] || labels.unknown); }; - const formatValue = (value: string, type: string) => { + const formatValue = (value: string, type: string): string => { switch (type) { case 'browser': return formatBrowser(value); diff --git a/src/pages/api/reports/insights.ts b/src/pages/api/reports/insights.ts index 4344422a..c70d218e 100644 --- a/src/pages/api/reports/insights.ts +++ b/src/pages/api/reports/insights.ts @@ -55,7 +55,7 @@ const schema = { }), }; -function convertFilters(filters) { +function convertFilters(filters: any[]) { return filters.reduce((obj, { name, ...value }) => { obj[name] = value; diff --git a/src/queries/analytics/reports/getInsights.ts b/src/queries/analytics/reports/getInsights.ts index a5b1b773..282ed755 100644 --- a/src/queries/analytics/reports/getInsights.ts +++ b/src/queries/analytics/reports/getInsights.ts @@ -86,8 +86,9 @@ async function clickhouseQuery( ).then(a => { return Object.values(a).map(a => { return { - x: a.x, - y: Number(a.y), + ...a, + views: Number(a.views), + visitors: Number(a.visitors), }; }); }); diff --git a/yarn.lock b/yarn.lock index 1e405135..6584c9ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7476,10 +7476,10 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-basics@^0.109.0: - version "0.109.0" - resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.109.0.tgz#9c1f41ebf6abbcf67f7dd11a16a7fb5e3aedd38d" - integrity sha512-n955CwqIeQ/sTMxxvbtYpWtBWS07Rg39zrNKeUYN/JoCIq0YbbZiZDALAhh1Out+qsIe62NoOFA7JtHzk1EkHQ== +react-basics@^0.110.0: + version "0.110.0" + resolved "https://registry.yarnpkg.com/react-basics/-/react-basics-0.110.0.tgz#7b7689edcb96b973528abc91b964345edc6e000a" + integrity sha512-3XQx5hR0zTuEAxDDHyaqS2GfXOowb0Lri8ay65kjGUd7RmvdpnIr62MsrAwo62rtTwtzRzsJXHJCR5CKQ/D3rQ== dependencies: "@react-spring/web" "^9.7.3" classnames "^2.3.1" From b578162cb6399b0ed461071246475aec83fed534 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 1 Dec 2023 20:27:59 -0800 Subject: [PATCH 3/5] Refactored useQuery functions. --- src/app/(main)/console/TestConsole.js | 5 +++- src/app/(main)/dashboard/Dashboard.js | 7 ++--- src/app/(main)/dashboard/DashboardEdit.js | 5 +++- src/app/(main)/reports/ReportsDataTable.tsx | 8 +++--- .../(main)/reports/[id]/FilterSelectForm.js | 10 +++---- src/app/(main)/reports/[id]/ReportDetails.js | 5 +++- .../reports/event-data/EventDataParameters.js | 10 +++---- .../(main)/settings/teams/TeamsDataTable.js | 11 +++++--- .../(main)/settings/teams/[id]/TeamMembers.js | 10 +++---- .../settings/teams/[id]/TeamSettings.js | 10 +++---- .../settings/teams/[id]/TeamWebsiteAddForm.js | 5 +++- .../settings/teams/[id]/TeamWebsites.js | 10 +++---- src/app/(main)/settings/users/UserWebsites.js | 8 +++--- .../(main)/settings/users/UsersDataTable.js | 7 +++-- .../settings/users/[id]/UserSettings.js | 10 +++---- .../settings/websites/WebsiteSettings.js | 10 +++++-- .../settings/websites/WebsitesDataTable.tsx | 10 +++---- src/app/(main)/websites/[id]/WebsiteChart.js | 8 +++--- .../(main)/websites/[id]/WebsiteMetricsBar.js | 8 +++--- .../[id]/event-data/EventDataMetricsBar.js | 8 +++--- .../[id]/event-data/WebsiteEventData.js | 10 +++---- .../(main)/websites/[id]/realtime/Realtime.js | 16 +++++------ .../websites/[id]/realtime/RealtimeHome.js | 5 +++- src/components/common/DataTable.tsx | 26 +++++++----------- src/components/common/FilterLink.tsx | 2 +- src/components/hooks/useFilterQuery.ts | 27 ++++++++++--------- src/components/hooks/useReports.ts | 8 +++--- src/components/hooks/useShareToken.ts | 11 +++++--- src/components/hooks/useWebsite.ts | 4 ++- src/components/input/WebsiteSelect.js | 5 +++- src/components/metrics/ActiveUsers.js | 14 +++++----- src/components/metrics/EventsChart.js | 25 +++++++++-------- src/components/metrics/MetricsTable.js | 12 +++++---- 33 files changed, 179 insertions(+), 151 deletions(-) diff --git a/src/app/(main)/console/TestConsole.js b/src/app/(main)/console/TestConsole.js index b88bfd77..7aae09b2 100644 --- a/src/app/(main)/console/TestConsole.js +++ b/src/app/(main)/console/TestConsole.js @@ -14,7 +14,10 @@ import styles from './TestConsole.module.css'; export function TestConsole({ websiteId }) { const { get, useQuery } = useApi(); - const { data, isLoading, error } = useQuery(['websites:me'], () => get('/me/websites')); + const { data, isLoading, error } = useQuery({ + queryKey: ['websites:me'], + queryFn: () => get('/me/websites'), + }); const { router } = useNavigation(); function handleChange(value) { diff --git a/src/app/(main)/dashboard/Dashboard.js b/src/app/(main)/dashboard/Dashboard.js index 5fb65f23..6f0659bb 100644 --- a/src/app/(main)/dashboard/Dashboard.js +++ b/src/app/(main)/dashboard/Dashboard.js @@ -20,9 +20,10 @@ export function Dashboard() { const { get, useQuery } = useApi(); const { page, handlePageChange } = useApiFilter(); const pageSize = 10; - const { data: result, isLoading } = useQuery(['websites', page, pageSize], () => - get('/websites', { includeTeams: 1, page, pageSize }), - ); + const { data: result, isLoading } = useQuery({ + queryKey: ['websites', page, pageSize], + queryFn: () => get('/websites', { includeTeams: 1, page, pageSize }), + }); const { data, count } = result || {}; const hasData = data && data?.length !== 0; diff --git a/src/app/(main)/dashboard/DashboardEdit.js b/src/app/(main)/dashboard/DashboardEdit.js index 3af33867..0a8734e8 100644 --- a/src/app/(main)/dashboard/DashboardEdit.js +++ b/src/app/(main)/dashboard/DashboardEdit.js @@ -17,7 +17,10 @@ export function DashboardEdit() { const { formatMessage, labels } = useMessages(); const [order, setOrder] = useState(websiteOrder || []); const { get, useQuery } = useApi(); - const { data: result } = useQuery(['websites'], () => get('/websites', { includeTeams: 1 })); + const { data: result } = useQuery({ + queryKey: ['websites'], + queryFn: () => get('/websites', { includeTeams: 1 }), + }); const { data: websites } = result || {}; const ordered = useMemo(() => { diff --git a/src/app/(main)/reports/ReportsDataTable.tsx b/src/app/(main)/reports/ReportsDataTable.tsx index 0ca853dc..eeef203b 100644 --- a/src/app/(main)/reports/ReportsDataTable.tsx +++ b/src/app/(main)/reports/ReportsDataTable.tsx @@ -8,9 +8,11 @@ import useCache from 'store/cache'; export default function ReportsDataTable({ websiteId }: { websiteId?: string }) { const { get } = useApi(); const modified = useCache(state => (state as any)?.reports); - const queryResult = useFilterQuery(['reports', { websiteId, modified }], params => - get(websiteId ? `/websites/${websiteId}/reports` : `/reports`, params), - ); + const queryResult = useFilterQuery({ + queryKey: ['reports', { websiteId, modified }], + queryFn: (params: any) => + get(websiteId ? `/websites/${websiteId}/reports` : `/reports`, params), + }); return ( diff --git a/src/app/(main)/reports/[id]/FilterSelectForm.js b/src/app/(main)/reports/[id]/FilterSelectForm.js index 9ad4cd93..8457b7ff 100644 --- a/src/app/(main)/reports/[id]/FilterSelectForm.js +++ b/src/app/(main)/reports/[id]/FilterSelectForm.js @@ -8,16 +8,16 @@ import { useApi } from 'components/hooks'; function useValues(websiteId, type) { const now = Date.now(); const { get, useQuery } = useApi(); - const { data, error, isLoading } = useQuery( - ['websites:values', websiteId, type], - () => + const { data, error, isLoading } = useQuery({ + queryKey: ['websites:values', websiteId, type], + queryFn: () => get(`/websites/${websiteId}/values`, { type, startAt: +subDays(now, 90), endAt: now, }), - { enabled: !!(websiteId && type) }, - ); + enabled: !!(websiteId && type), + }); return { data, error, isLoading }; } diff --git a/src/app/(main)/reports/[id]/ReportDetails.js b/src/app/(main)/reports/[id]/ReportDetails.js index 8605ffb3..df91719a 100644 --- a/src/app/(main)/reports/[id]/ReportDetails.js +++ b/src/app/(main)/reports/[id]/ReportDetails.js @@ -14,7 +14,10 @@ const reports = { export default function ReportDetails({ reportId }) { const { get, useQuery } = useApi(); - const { data: report } = useQuery(['reports', reportId], () => get(`/reports/${reportId}`)); + const { data: report } = useQuery({ + queryKey: ['reports', reportId], + queryFn: () => get(`/reports/${reportId}`), + }); if (!report) { return null; diff --git a/src/app/(main)/reports/event-data/EventDataParameters.js b/src/app/(main)/reports/event-data/EventDataParameters.js index 6b9a0344..ac90fc2a 100644 --- a/src/app/(main)/reports/event-data/EventDataParameters.js +++ b/src/app/(main)/reports/event-data/EventDataParameters.js @@ -12,16 +12,16 @@ import styles from './EventDataParameters.module.css'; function useFields(websiteId, startDate, endDate) { const { get, useQuery } = useApi(); - const { data, error, isLoading } = useQuery( - ['fields', websiteId, startDate, endDate], - () => + const { data, error, isLoading } = useQuery({ + queryKey: ['fields', websiteId, startDate, endDate], + queryFn: () => get('/reports/event-data', { websiteId, startAt: +startDate, endAt: +endDate, }), - { enabled: !!(websiteId && startDate && endDate) }, - ); + enabled: !!(websiteId && startDate && endDate), + }); return { data, error, isLoading }; } diff --git a/src/app/(main)/settings/teams/TeamsDataTable.js b/src/app/(main)/settings/teams/TeamsDataTable.js index 164838f9..a8c9e21d 100644 --- a/src/app/(main)/settings/teams/TeamsDataTable.js +++ b/src/app/(main)/settings/teams/TeamsDataTable.js @@ -8,10 +8,13 @@ import useCache from 'store/cache'; export function TeamsDataTable() { const { get } = useApi(); const modified = useCache(state => state?.teams); - const queryResult = useFilterQuery(['teams', { modified }], params => { - return get(`/teams`, { - ...params, - }); + const queryResult = useFilterQuery({ + queryKey: ['teams', { modified }], + queryFn: params => { + return get(`/teams`, { + ...params, + }); + }, }); return ( diff --git a/src/app/(main)/settings/teams/[id]/TeamMembers.js b/src/app/(main)/settings/teams/[id]/TeamMembers.js index fb31b6fa..1b0c0d18 100644 --- a/src/app/(main)/settings/teams/[id]/TeamMembers.js +++ b/src/app/(main)/settings/teams/[id]/TeamMembers.js @@ -7,15 +7,15 @@ import useCache from 'store/cache'; export function TeamMembers({ teamId, readOnly }) { const { get } = useApi(); const modified = useCache(state => state?.['team:members']); - const queryResult = useFilterQuery( - ['team:members', { teamId, modified }], - params => { + const queryResult = useFilterQuery({ + queryKey: ['team:members', { teamId, modified }], + queryFn: params => { return get(`/teams/${teamId}/users`, { ...params, }); }, - { enabled: !!teamId }, - ); + enabled: !!teamId, + }); return ( <> diff --git a/src/app/(main)/settings/teams/[id]/TeamSettings.js b/src/app/(main)/settings/teams/[id]/TeamSettings.js index 8ec0ad85..d7065f97 100644 --- a/src/app/(main)/settings/teams/[id]/TeamSettings.js +++ b/src/app/(main)/settings/teams/[id]/TeamSettings.js @@ -17,15 +17,15 @@ export function TeamSettings({ teamId }) { const [tab, setTab] = useState('details'); const { get, useQuery } = useApi(); const { showToast } = useToasts(); - const { data, isLoading } = useQuery( - ['team', teamId], - () => { + const { data, isLoading } = useQuery({ + queryKey: ['team', teamId], + queryFn: () => { if (teamId) { return get(`/teams/${teamId}`); } }, - { cacheTime: 0 }, - ); + cacheTime: 0, + }); const canEdit = data?.teamUser?.find( ({ userId, role }) => role === ROLES.teamOwner && userId === user.id, ); diff --git a/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js b/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js index c83ec3d0..004ae54f 100644 --- a/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js +++ b/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js @@ -12,7 +12,10 @@ export function TeamWebsiteAddForm({ teamId, onSave, onClose }) { const { formatMessage, labels } = useMessages(); const { get, post, useQuery, useMutation } = useApi(); const { mutate, error } = useMutation(data => post(`/teams/${teamId}/websites`, data)); - const { data: websites, isLoading } = useQuery(['websites'], () => get('/websites')); + const { data: websites, isLoading } = useQuery({ + queryKey: ['websites'], + queryFn: () => get('/websites'), + }); const [selected, setSelected] = useState([]); const hasData = websites && websites.data.length > 0; diff --git a/src/app/(main)/settings/teams/[id]/TeamWebsites.js b/src/app/(main)/settings/teams/[id]/TeamWebsites.js index 9e76ffab..af167b89 100644 --- a/src/app/(main)/settings/teams/[id]/TeamWebsites.js +++ b/src/app/(main)/settings/teams/[id]/TeamWebsites.js @@ -13,15 +13,15 @@ export function TeamWebsites({ teamId }) { const { user } = useUser(); const { get } = useApi(); const modified = useCache(state => state?.['team:websites']); - const queryResult = useFilterQuery( - ['team:websites', { teamId, modified }], - params => { + const queryResult = useFilterQuery({ + queryKey: ['team:websites', { teamId, modified }], + queryFn: params => { return get(`/teams/${teamId}/websites`, { ...params, }); }, - { enabled: !!user }, - ); + enabled: !!user, + }); const handleChange = () => { queryResult.refetch(); diff --git a/src/app/(main)/settings/users/UserWebsites.js b/src/app/(main)/settings/users/UserWebsites.js index 18b5f1a7..cd53c512 100644 --- a/src/app/(main)/settings/users/UserWebsites.js +++ b/src/app/(main)/settings/users/UserWebsites.js @@ -7,15 +7,15 @@ export function UserWebsites({ userId }) { const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } = useApiFilter(); const { get, useQuery } = useApi(); - const { data, isLoading, error } = useQuery( - ['user:websites', userId, filter, page, pageSize], - () => + const { data, isLoading, error } = useQuery({ + queryKey: ['user:websites', userId, filter, page, pageSize], + queryFn: () => get(`/users/${userId}/websites`, { filter, page, pageSize, }), - ); + }); const hasData = data && data.length !== 0; return ( diff --git a/src/app/(main)/settings/users/UsersDataTable.js b/src/app/(main)/settings/users/UsersDataTable.js index 154e37ad..91125309 100644 --- a/src/app/(main)/settings/users/UsersDataTable.js +++ b/src/app/(main)/settings/users/UsersDataTable.js @@ -9,10 +9,9 @@ import useCache from 'store/cache'; export function UsersDataTable() { const { get } = useApi(); const modified = useCache(state => state?.users); - const queryResult = useFilterQuery(['users', { modified }], params => { - return get(`/users`, { - ...params, - }); + const queryResult = useFilterQuery({ + queryKey: ['users', { modified }], + queryFn: params => get(`/users`, params), }); return ( diff --git a/src/app/(main)/settings/users/[id]/UserSettings.js b/src/app/(main)/settings/users/[id]/UserSettings.js index ea340ab7..f635bb05 100644 --- a/src/app/(main)/settings/users/[id]/UserSettings.js +++ b/src/app/(main)/settings/users/[id]/UserSettings.js @@ -14,15 +14,15 @@ export function UserSettings({ userId }) { const [tab, setTab] = useState('details'); const { get, useQuery } = useApi(); const { showToast } = useToasts(); - const { data, isLoading } = useQuery( - ['user', userId], - () => { + const { data, isLoading } = useQuery({ + queryKey: ['user', userId], + queryFn: () => { if (userId) { return get(`/users/${userId}`); } }, - { cacheTime: 0 }, - ); + cacheTime: 0, + }); const handleSave = data => { showToast({ message: formatMessage(messages.saved), variant: 'success' }); diff --git a/src/app/(main)/settings/websites/WebsiteSettings.js b/src/app/(main)/settings/websites/WebsiteSettings.js index 82b38048..79ba08fc 100644 --- a/src/app/(main)/settings/websites/WebsiteSettings.js +++ b/src/app/(main)/settings/websites/WebsiteSettings.js @@ -1,6 +1,6 @@ 'use client'; import { useEffect, useState } from 'react'; -import { Item, Tabs, useToasts, Button, Text, Icon, Icons } from 'react-basics'; +import { Item, Tabs, useToasts, Button, Text, Icon, Icons, Loading } from 'react-basics'; import { useRouter } from 'next/navigation'; import Link from 'next/link'; import PageHeader from 'components/layout/PageHeader'; @@ -16,7 +16,9 @@ export function WebsiteSettings({ websiteId, openExternal = false, analyticsUrl const { formatMessage, labels, messages } = useMessages(); const { get, useQuery } = useApi(); const { showToast } = useToasts(); - const { data } = useQuery(['website', websiteId], () => get(`/websites/${websiteId}`), { + const { data, isLoading } = useQuery({ + queryKey: ['website', websiteId], + queryFn: () => get(`/websites/${websiteId}`), enabled: !!websiteId, cacheTime: 0, }); @@ -46,6 +48,10 @@ export function WebsiteSettings({ websiteId, openExternal = false, analyticsUrl } }, [data]); + if (isLoading || !values) { + return ; + } + return ( <> diff --git a/src/app/(main)/settings/websites/WebsitesDataTable.tsx b/src/app/(main)/settings/websites/WebsitesDataTable.tsx index fc6dd0c0..4d55e047 100644 --- a/src/app/(main)/settings/websites/WebsitesDataTable.tsx +++ b/src/app/(main)/settings/websites/WebsitesDataTable.tsx @@ -21,17 +21,17 @@ function useWebsites(userId: string, { includeTeams, onlyTeams }) { const { get } = useApi(); const modified = useCache((state: any) => state?.websites); - return useFilterQuery( - ['websites', { includeTeams, onlyTeams, modified }], - (params: any) => { + return useFilterQuery({ + queryKey: ['websites', { includeTeams, onlyTeams, modified }], + queryFn: (params: any) => { return get(`/users/${userId}/websites`, { includeTeams, onlyTeams, ...params, }); }, - { enabled: !!userId }, - ); + enabled: !!userId, + }); } export function WebsitesDataTable({ diff --git a/src/app/(main)/websites/[id]/WebsiteChart.js b/src/app/(main)/websites/[id]/WebsiteChart.js index d05ff422..15a7525f 100644 --- a/src/app/(main)/websites/[id]/WebsiteChart.js +++ b/src/app/(main)/websites/[id]/WebsiteChart.js @@ -12,12 +12,12 @@ export function WebsiteChart({ websiteId }) { } = useNavigation(); const { get, useQuery } = useApi(); - const { data, isLoading } = useQuery( - [ + const { data, isLoading } = useQuery({ + queryKey: [ 'websites:pageviews', { websiteId, modified, url, referrer, os, browser, device, country, region, city, title }, ], - () => + queryFn: () => get(`/websites/${websiteId}/pageviews`, { startAt: +startDate, endAt: +endDate, @@ -33,7 +33,7 @@ export function WebsiteChart({ websiteId }) { city, title, }), - ); + }); const chartData = useMemo(() => { if (data) { diff --git a/src/app/(main)/websites/[id]/WebsiteMetricsBar.js b/src/app/(main)/websites/[id]/WebsiteMetricsBar.js index 0dd6a4e2..d840c7b6 100644 --- a/src/app/(main)/websites/[id]/WebsiteMetricsBar.js +++ b/src/app/(main)/websites/[id]/WebsiteMetricsBar.js @@ -17,12 +17,12 @@ export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) { query: { url, referrer, title, os, browser, device, country, region, city }, } = useNavigation(); - const { data, error, isLoading, isFetched } = useQuery( - [ + const { data, error, isLoading, isFetched } = useQuery({ + queryKey: [ 'websites:stats', { websiteId, modified, url, referrer, title, os, browser, device, country, region, city }, ], - () => + queryFn: () => get(`/websites/${websiteId}/stats`, { startAt: +startDate, endAt: +endDate, @@ -36,7 +36,7 @@ export function WebsiteMetricsBar({ websiteId, showFilter = true, sticky }) { region, city, }), - ); + }); const { pageviews, uniques, bounces, totaltime } = data || {}; const num = Math.min(data && uniques.value, data && bounces.value); diff --git a/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.js b/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.js index 5be19185..b02e166c 100644 --- a/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.js +++ b/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.js @@ -11,15 +11,15 @@ export function EventDataMetricsBar({ websiteId }) { const [dateRange] = useDateRange(websiteId); const { startDate, endDate, modified } = dateRange; - const { data, error, isLoading, isFetched } = useQuery( - ['event-data:stats', { websiteId, startDate, endDate, modified }], - () => + const { data, error, isLoading, isFetched } = useQuery({ + queryKey: ['event-data:stats', { websiteId, startDate, endDate, modified }], + queryFn: () => get(`/event-data/stats`, { websiteId, startAt: +startDate, endAt: +endDate, }), - ); + }); return (
diff --git a/src/app/(main)/websites/[id]/event-data/WebsiteEventData.js b/src/app/(main)/websites/[id]/event-data/WebsiteEventData.js index b5982e32..b67ee95e 100644 --- a/src/app/(main)/websites/[id]/event-data/WebsiteEventData.js +++ b/src/app/(main)/websites/[id]/event-data/WebsiteEventData.js @@ -10,17 +10,17 @@ function useData(websiteId, event) { const [dateRange] = useDateRange(websiteId); const { startDate, endDate } = dateRange; const { get, useQuery } = useApi(); - const { data, error, isLoading } = useQuery( - ['event-data:events', { websiteId, startDate, endDate, event }], - () => + const { data, error, isLoading } = useQuery({ + queryKey: ['event-data:events', { websiteId, startDate, endDate, event }], + queryFn: () => get('/event-data/events', { websiteId, startAt: +startDate, endAt: +endDate, event, }), - { enabled: !!(websiteId && startDate && endDate) }, - ); + enabled: !!(websiteId && startDate && endDate), + }); return { data, error, isLoading }; } diff --git a/src/app/(main)/websites/[id]/realtime/Realtime.js b/src/app/(main)/websites/[id]/realtime/Realtime.js index 737bcd1b..37df458c 100644 --- a/src/app/(main)/websites/[id]/realtime/Realtime.js +++ b/src/app/(main)/websites/[id]/realtime/Realtime.js @@ -28,15 +28,13 @@ export function Realtime({ websiteId }) { const [currentData, setCurrentData] = useState(); const { get, useQuery } = useApi(); const { data: website } = useWebsite(websiteId); - const { data, isLoading, error } = useQuery( - ['realtime', websiteId], - () => get(`/realtime/${websiteId}`, { startAt: currentData?.timestamp || 0 }), - { - enabled: !!(websiteId && website), - refetchInterval: REALTIME_INTERVAL, - cache: false, - }, - ); + const { data, isLoading, error } = useQuery({ + queryKey: ['realtime', websiteId], + queryFn: () => get(`/realtime/${websiteId}`, { startAt: currentData?.timestamp || 0 }), + enabled: !!(websiteId && website), + refetchInterval: REALTIME_INTERVAL, + cache: false, + }); useEffect(() => { if (data) { diff --git a/src/app/(main)/websites/[id]/realtime/RealtimeHome.js b/src/app/(main)/websites/[id]/realtime/RealtimeHome.js index dbaeb541..154ac707 100644 --- a/src/app/(main)/websites/[id]/realtime/RealtimeHome.js +++ b/src/app/(main)/websites/[id]/realtime/RealtimeHome.js @@ -10,7 +10,10 @@ export function RealtimeHome() { const { formatMessage, labels, messages } = useMessages(); const { get, useQuery } = useApi(); const router = useRouter(); - const { data, isLoading, error } = useQuery(['websites:me'], () => get('/me/websites')); + const { data, isLoading, error } = useQuery({ + queryKey: ['websites:me'], + queryFn: () => get('/me/websites'), + }); useEffect(() => { if (data?.length) { diff --git a/src/components/common/DataTable.tsx b/src/components/common/DataTable.tsx index a3c63c0a..621a44fe 100644 --- a/src/components/common/DataTable.tsx +++ b/src/components/common/DataTable.tsx @@ -1,29 +1,16 @@ -import { ReactNode, Dispatch, SetStateAction } from 'react'; +import { ReactNode } from 'react'; import classNames from 'classnames'; import { Banner, Loading, SearchField } from 'react-basics'; import { useMessages } from 'components/hooks'; import Empty from 'components/common/Empty'; import Pager from 'components/common/Pager'; import styles from './DataTable.module.css'; +import { FilterQueryResult } from 'components/hooks/useFilterQuery'; const DEFAULT_SEARCH_DELAY = 600; export interface DataTableProps { - queryResult: { - result: { - page: number; - pageSize: number; - count: number; - data: any[]; - }; - params: { - query: string; - page: number; - }; - setParams: Dispatch>; - isLoading: boolean; - error: unknown; - }; + queryResult: FilterQueryResult; searchDelay?: number; allowSearch?: boolean; allowPaging?: boolean; @@ -38,7 +25,12 @@ export function DataTable({ children, }: DataTableProps) { const { formatMessage, labels, messages } = useMessages(); - const { result, error, isLoading, params, setParams } = queryResult || {}; + const { + result, + params, + setParams, + query: { error, isLoading }, + } = queryResult || {}; const { page, pageSize, count, data } = result || {}; const { query } = params || {}; const hasData = Boolean(!isLoading && data?.length); diff --git a/src/components/common/FilterLink.tsx b/src/components/common/FilterLink.tsx index a9030227..bc0a4d48 100644 --- a/src/components/common/FilterLink.tsx +++ b/src/components/common/FilterLink.tsx @@ -13,7 +13,7 @@ export interface FilterLinkProps { label?: string; externalUrl?: string; className?: string; - children: ReactNode; + children?: ReactNode; } export function FilterLink({ diff --git a/src/components/hooks/useFilterQuery.ts b/src/components/hooks/useFilterQuery.ts index 37c28b7e..2c520741 100644 --- a/src/components/hooks/useFilterQuery.ts +++ b/src/components/hooks/useFilterQuery.ts @@ -1,24 +1,25 @@ -import { useState } from 'react'; +import { useState, Dispatch, SetStateAction } from 'react'; import { useApi } from 'components/hooks/useApi'; -import { UseQueryOptions } from '@tanstack/react-query'; +import { SearchFilter, FilterResult } from 'lib/types'; -export function useFilterQuery(key: any[], fn, options?: UseQueryOptions) { - const [params, setParams] = useState({ +export interface FilterQueryResult { + result: FilterResult; + query: any; + params: SearchFilter; + setParams: Dispatch>; +} + +export function useFilterQuery(props = {}): FilterQueryResult { + const [params, setParams] = useState({ query: '', page: 1, }); const { useQuery } = useApi(); - - const { data, ...other } = useQuery([...key, params], fn.bind(null, params), options); + const { data, ...query } = useQuery>({ ...props }); return { - result: data as { - page: number; - pageSize: number; - count: number; - data: any[]; - }, - ...other, + result: data, + query, params, setParams, }; diff --git a/src/components/hooks/useReports.ts b/src/components/hooks/useReports.ts index d9292aeb..0fad2db1 100644 --- a/src/components/hooks/useReports.ts +++ b/src/components/hooks/useReports.ts @@ -8,10 +8,10 @@ export function useReports() { const { mutate } = useMutation(reportId => del(`/reports/${reportId}`)); const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } = useApiFilter(); - const { data, error, isLoading } = useQuery( - ['reports', { modified, filter, page, pageSize }], - () => get(`/reports`, { filter, page, pageSize }), - ); + const { data, error, isLoading } = useQuery({ + queryKey: ['reports', { modified, filter, page, pageSize }], + queryFn: () => get(`/reports`, { filter, page, pageSize }), + }); const deleteReport = id => { mutate(id, { diff --git a/src/components/hooks/useShareToken.ts b/src/components/hooks/useShareToken.ts index 088f643e..41ae7faf 100644 --- a/src/components/hooks/useShareToken.ts +++ b/src/components/hooks/useShareToken.ts @@ -6,12 +6,15 @@ const selector = (state: { shareToken: string }) => state.shareToken; export function useShareToken(shareId: string) { const shareToken = useStore(selector); const { get, useQuery } = useApi(); - const { isLoading, error } = useQuery(['share', shareId], async () => { - const data = await get(`/share/${shareId}`); + const { isLoading, error } = useQuery({ + queryKey: ['share', shareId], + queryFn: async () => { + const data = await get(`/share/${shareId}`); - setShareToken(data); + setShareToken(data); - return data; + return data; + }, }); return { shareToken, isLoading, error }; diff --git a/src/components/hooks/useWebsite.ts b/src/components/hooks/useWebsite.ts index 7b68335a..d18e96ba 100644 --- a/src/components/hooks/useWebsite.ts +++ b/src/components/hooks/useWebsite.ts @@ -2,7 +2,9 @@ import useApi from './useApi'; export function useWebsite(websiteId: string) { const { get, useQuery } = useApi(); - return useQuery(['websites', websiteId], () => get(`/websites/${websiteId}`), { + return useQuery({ + queryKey: ['websites', websiteId], + queryFn: () => get(`/websites/${websiteId}`), enabled: !!websiteId, }); } diff --git a/src/components/input/WebsiteSelect.js b/src/components/input/WebsiteSelect.js index 078389d3..23334215 100644 --- a/src/components/input/WebsiteSelect.js +++ b/src/components/input/WebsiteSelect.js @@ -6,7 +6,10 @@ import styles from './WebsiteSelect.module.css'; export function WebsiteSelect({ websiteId, onSelect }) { const { formatMessage, labels } = useMessages(); const { get, useQuery } = useApi(); - const { data } = useQuery(['websites:me'], () => get('/me/websites', { pageSize: 100 })); + const { data } = useQuery({ + queryKey: ['websites:me'], + queryFn: () => get('/me/websites', { pageSize: 100 }), + }); const renderValue = value => { return data?.data?.find(({ id }) => id === value)?.name; diff --git a/src/components/metrics/ActiveUsers.js b/src/components/metrics/ActiveUsers.js index 3074d0df..cd3bfb99 100644 --- a/src/components/metrics/ActiveUsers.js +++ b/src/components/metrics/ActiveUsers.js @@ -7,14 +7,12 @@ import styles from './ActiveUsers.module.css'; export function ActiveUsers({ websiteId, value, refetchInterval = 60000 }) { const { formatMessage, messages } = useMessages(); const { get, useQuery } = useApi(); - const { data } = useQuery( - ['websites:active', websiteId], - () => get(`/websites/${websiteId}/active`), - { - refetchInterval, - enabled: !!websiteId, - }, - ); + const { data } = useQuery({ + queryKey: ['websites:active', websiteId], + queryFn: () => get(`/websites/${websiteId}/active`), + refetchInterval, + enabled: !!websiteId, + }); const count = useMemo(() => { if (websiteId) { diff --git a/src/components/metrics/EventsChart.js b/src/components/metrics/EventsChart.js index f2cf48d1..610a3616 100644 --- a/src/components/metrics/EventsChart.js +++ b/src/components/metrics/EventsChart.js @@ -16,17 +16,20 @@ export function EventsChart({ websiteId, className, token }) { query: { url, event }, } = useNavigation(); - const { data, isLoading } = useQuery(['events', websiteId, modified, event], () => - get(`/websites/${websiteId}/events`, { - startAt: +startDate, - endAt: +endDate, - unit, - timezone, - url, - event, - token, - }), - ); + const { data, isLoading } = useQuery({ + queryKey: ['events', websiteId, modified, event], + queryFn: () => + get(`/websites/${websiteId}/events`, { + startAt: +startDate, + endAt: +endDate, + unit, + timezone, + url, + event, + token, + }), + enabled: !!websiteId, + }); const datasets = useMemo(() => { if (!data) return []; diff --git a/src/components/metrics/MetricsTable.js b/src/components/metrics/MetricsTable.js index 1d64b714..a251756f 100644 --- a/src/components/metrics/MetricsTable.js +++ b/src/components/metrics/MetricsTable.js @@ -35,8 +35,8 @@ export function MetricsTable({ const { get, useQuery } = useApi(); const { dir } = useLocale(); - const { data, isLoading, isFetched, error } = useQuery( - [ + const { data, isLoading, isFetched, error } = useQuery({ + queryKey: [ 'websites:metrics', { websiteId, @@ -53,11 +53,13 @@ export function MetricsTable({ city, }, ], - () => { + queryFn: () => { const filters = { url, title, referrer, os, browser, device, country, region, city }; filters[type] = undefined; + onDataLoad?.(); + return get(`/websites/${websiteId}/metrics`, { type, startAt: +startDate, @@ -65,8 +67,8 @@ export function MetricsTable({ ...filters, }); }, - { onSuccess: onDataLoad, retryDelay: delay || DEFAULT_ANIMATION_DURATION }, - ); + retryDelay: delay || DEFAULT_ANIMATION_DURATION, + }); const filteredData = useMemo(() => { if (data) { From 7c42f0da8290fce03d00c7a86660406497c044da Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Sun, 3 Dec 2023 03:07:03 -0800 Subject: [PATCH 4/5] Typescript refactor. --- package.json | 10 +-- src/app/(main)/{NavBar.js => NavBar.tsx} | 0 .../{UpdateNotice.js => UpdateNotice.tsx} | 0 .../{TestConsole.js => TestConsole.tsx} | 32 +++++----- .../dashboard/{Dashboard.js => Dashboard.tsx} | 28 ++++++--- .../{DashboardEdit.js => DashboardEdit.tsx} | 6 +- ...sButton.js => DashboardSettingsButton.tsx} | 0 ...DeleteButton.js => ReportDeleteButton.tsx} | 14 ++++- src/app/(main)/reports/ReportsDataTable.tsx | 12 +--- .../{ReportsHeader.js => ReportsHeader.tsx} | 0 .../{ReportsTable.js => ReportsTable.tsx} | 2 +- .../{BaseParameters.js => BaseParameters.tsx} | 13 +++- .../{FieldAddForm.js => FieldAddForm.tsx} | 18 ++++-- ...ggregateForm.js => FieldAggregateForm.tsx} | 12 +++- ...FieldFilterForm.js => FieldFilterForm.tsx} | 19 ++++-- ...FieldSelectForm.js => FieldSelectForm.tsx} | 17 +++++- ...lterSelectForm.js => FilterSelectForm.tsx} | 20 ++++-- .../{ParameterList.js => ParameterList.tsx} | 9 ++- .../[id]/{PopupForm.js => PopupForm.tsx} | 11 +++- .../reports/[id]/{Report.js => Report.tsx} | 16 +++-- .../[id]/{ReportBody.js => ReportBody.tsx} | 0 .../{ReportDetails.js => ReportDetails.tsx} | 2 +- .../{ReportHeader.js => ReportHeader.tsx} | 12 ++-- .../[id]/{ReportMenu.js => ReportMenu.tsx} | 0 ...ReportTemplates.js => ReportTemplates.tsx} | 0 ...aParameters.js => EventDataParameters.tsx} | 17 +++--- ...EventDataReport.js => EventDataReport.tsx} | 2 +- .../{EventDataTable.js => EventDataTable.tsx} | 0 .../{FunnelChart.js => FunnelChart.tsx} | 61 +++++++++++-------- ...nnelParameters.js => FunnelParameters.tsx} | 16 ++--- .../{FunnelReport.js => FunnelReport.tsx} | 0 .../{FunnelTable.js => FunnelTable.tsx} | 0 .../funnel/{UrlAddForm.js => UrlAddForm.tsx} | 7 ++- ...tsParameters.js => InsightsParameters.tsx} | 13 ++-- .../{InsightsReport.js => InsightsReport.tsx} | 2 +- .../{InsightsTable.js => InsightsTable.tsx} | 2 +- ...nParameters.js => RetentionParameters.tsx} | 7 +-- ...RetentionReport.js => RetentionReport.tsx} | 2 +- .../{RetentionTable.js => RetentionTable.tsx} | 2 +- ...teRangeSetting.js => DateRangeSetting.tsx} | 0 ...LanguageSetting.js => LanguageSetting.tsx} | 0 ...angeButton.js => PasswordChangeButton.tsx} | 0 ...sswordEditForm.js => PasswordEditForm.tsx} | 6 +- .../{ProfileHeader.js => ProfileHeader.tsx} | 0 ...ProfileSettings.js => ProfileSettings.tsx} | 0 .../{ThemeSetting.js => ThemeSetting.tsx} | 0 ...TimezoneSetting.js => TimezoneSetting.tsx} | 0 .../teams/{TeamAddForm.js => TeamAddForm.tsx} | 14 ++--- ...amDeleteButton.js => TeamDeleteButton.tsx} | 12 +++- .../{TeamDeleteForm.js => TeamDeleteForm.tsx} | 18 +++++- .../{TeamJoinForm.js => TeamJoinForm.tsx} | 4 +- ...TeamLeaveButton.js => TeamLeaveButton.tsx} | 10 ++- .../{TeamLeaveForm.js => TeamLeaveForm.tsx} | 35 +++++++---- .../{TeamsAddButton.js => TeamsAddButton.tsx} | 4 +- .../{TeamsDataTable.js => TeamsDataTable.tsx} | 4 +- .../teams/{TeamsHeader.js => TeamsHeader.tsx} | 0 ...TeamsJoinButton.js => TeamsJoinButton.tsx} | 0 .../teams/{TeamsTable.js => TeamsTable.tsx} | 2 +- .../teams/{WebsiteTags.js => WebsiteTags.tsx} | 10 ++- .../{TeamEditForm.js => TeamEditForm.tsx} | 0 ...veButton.js => TeamMemberRemoveButton.tsx} | 31 ++++++---- .../[id]/{TeamMembers.js => TeamMembers.tsx} | 2 +- ...amMembersTable.js => TeamMembersTable.tsx} | 10 ++- .../{TeamSettings.js => TeamSettings.tsx} | 6 +- ...bsiteAddForm.js => TeamWebsiteAddForm.tsx} | 16 ++++- ...eButton.js => TeamWebsiteRemoveButton.tsx} | 0 .../{TeamWebsites.js => TeamWebsites.tsx} | 8 ++- ...WebsitesTable.js => TeamWebsitesTable.tsx} | 12 +++- .../settings/teams/[id]/{page.js => page.tsx} | 0 .../{UserAddButton.js => UserAddButton.tsx} | 2 +- .../users/{UserAddForm.js => UserAddForm.tsx} | 6 +- ...erDeleteButton.js => UserDeleteButton.tsx} | 10 ++- .../{UserDeleteForm.js => UserDeleteForm.tsx} | 11 ++-- .../{UserEditForm.js => UserEditForm.tsx} | 26 ++++++-- src/app/(main)/settings/users/UserWebsites.js | 36 ----------- .../(main)/settings/users/UserWebsites.tsx | 26 ++++++++ .../{UsersDataTable.js => UsersDataTable.tsx} | 4 +- .../users/{UsersHeader.js => UsersHeader.tsx} | 2 +- .../users/{UsersTable.js => UsersTable.tsx} | 2 +- .../settings/users/[id]/UserSettings.js | 2 +- .../settings/users/[id]/{page.js => page.tsx} | 0 ...bsiteAddButton.js => WebsiteAddButton.tsx} | 2 +- .../settings/websites/WebsiteAddForm.tsx | 8 ++- .../settings/websites/WebsiteSettings.js | 2 +- .../{WebsitesHeader.js => WebsitesHeader.tsx} | 0 .../{WebsitesTable.js => WebsitesTable.tsx} | 12 +++- .../[id]/{ShareUrl.js => ShareUrl.tsx} | 10 +-- .../{TrackingCode.js => TrackingCode.tsx} | 8 ++- .../[id]/{WebsiteData.js => WebsiteData.tsx} | 8 ++- ...iteDeleteForm.js => WebsiteDeleteForm.tsx} | 14 ++++- ...WebsiteEditForm.js => WebsiteEditForm.tsx} | 14 ++++- ...bsiteResetForm.js => WebsiteResetForm.tsx} | 16 ++++- .../websites/[id]/{page.js => page.tsx} | 0 .../{WebsitesBrowse.js => WebsitesBrowse.tsx} | 2 +- .../{WebsiteChart.js => WebsiteChart.tsx} | 4 +- ...bsiteChartList.js => WebsiteChartList.tsx} | 10 ++- .../{WebsiteDetails.js => WebsiteDetails.tsx} | 7 +-- ...ilterButton.js => WebsiteFilterButton.tsx} | 12 +++- .../{WebsiteHeader.js => WebsiteHeader.tsx} | 11 +++- ...WebsiteMenuView.js => WebsiteMenuView.tsx} | 8 ++- ...iteMetricsBar.js => WebsiteMetricsBar.tsx} | 12 +++- ...bsiteTableView.js => WebsiteTableView.tsx} | 2 +- ...aMetricsBar.js => EventDataMetricsBar.tsx} | 2 +- .../{EventDataTable.js => EventDataTable.tsx} | 0 ...aValueTable.js => EventDataValueTable.tsx} | 2 +- .../[id]/event-data/{page.js => page.tsx} | 0 src/app/login/{LoginForm.js => LoginForm.tsx} | 9 +-- src/app/logout/{Logout.js => Logout.tsx} | 0 .../share/[...id]/{Footer.js => Footer.tsx} | 0 .../share/[...id]/{Header.js => Header.tsx} | 0 src/app/share/[...id]/{Share.js => Share.tsx} | 0 src/components/common/DataTable.tsx | 6 +- src/components/common/EmptyPlaceholder.tsx | 4 +- src/components/common/FilterButtons.tsx | 2 +- src/components/declarations.d.ts | 1 + src/components/hooks/useApiFilter.ts | 28 --------- src/components/hooks/useDateRange.ts | 2 +- src/components/hooks/useFilterQuery.ts | 19 ++++-- src/components/hooks/useNavigation.ts | 7 ++- src/components/hooks/useReports.ts | 30 ++++----- src/components/hooks/useShareToken.ts | 6 +- .../input/{DateFilter.js => DateFilter.tsx} | 25 +++++--- .../{LanguageButton.js => LanguageButton.tsx} | 6 +- .../{LogoutButton.js => LogoutButton.tsx} | 6 +- .../input/{MonthSelect.js => MonthSelect.tsx} | 10 +-- .../{ProfileButton.js => ProfileButton.tsx} | 0 .../{RefreshButton.js => RefreshButton.tsx} | 8 ++- .../{SettingsButton.js => SettingsButton.tsx} | 0 .../input/{ThemeButton.js => ThemeButton.tsx} | 0 ...iteDateFilter.js => WebsiteDateFilter.tsx} | 2 +- .../{WebsiteSelect.js => WebsiteSelect.tsx} | 8 ++- src/components/layout/Grid.js | 18 ------ src/components/layout/Grid.tsx | 34 +++++++++++ .../layout/{NavGroup.js => NavGroup.tsx} | 10 ++- .../layout/{SideNav.js => SideNav.tsx} | 11 +++- src/components/{messages.js => messages.ts} | 0 .../{ActiveUsers.js => ActiveUsers.tsx} | 12 +++- .../metrics/{BarChart.js => BarChart.tsx} | 31 +++++++--- .../{BrowsersTable.js => BrowsersTable.tsx} | 5 +- .../{CitiesTable.js => CitiesTable.tsx} | 7 +-- .../{CountriesTable.js => CountriesTable.tsx} | 19 ++++-- .../{DatePickerForm.js => DatePickerForm.tsx} | 2 +- .../{DevicesTable.js => DevicesTable.tsx} | 5 +- .../{EventsChart.js => EventsChart.tsx} | 9 ++- .../{EventsTable.js => EventsTable.tsx} | 7 +-- .../metrics/{FilterTags.js => FilterTags.tsx} | 4 +- .../{LanguagesTable.js => LanguagesTable.tsx} | 8 ++- .../metrics/{Legend.js => Legend.tsx} | 0 .../metrics/{ListTable.js => ListTable.tsx} | 17 +++++- .../metrics/{MetricCard.js => MetricCard.tsx} | 16 ++++- .../metrics/{MetricsBar.js => MetricsBar.tsx} | 10 ++- .../{MetricsTable.js => MetricsTable.tsx} | 39 +++++++----- .../metrics/{OSTable.js => OSTable.tsx} | 6 +- .../metrics/{PagesTable.js => PagesTable.tsx} | 9 ++- .../{PageviewsChart.js => PageviewsChart.tsx} | 16 +++-- ...etersTable.js => QueryParametersTable.tsx} | 8 ++- .../{RealtimeChart.js => RealtimeChart.tsx} | 20 +++--- .../{ReferrersTable.js => ReferrersTable.tsx} | 5 +- .../{RegionsTable.js => RegionsTable.tsx} | 7 +-- .../{ScreenTable.js => ScreenTable.tsx} | 5 +- .../metrics/{WorldMap.js => WorldMap.tsx} | 8 ++- src/lib/{charts.js => charts.tsx} | 12 ++-- src/lib/{crypto.js => crypto.ts} | 4 +- src/lib/{db.js => db.ts} | 4 +- src/lib/{filters.js => filters.ts} | 10 +-- src/lib/{format.js => format.ts} | 20 +++--- src/lib/{lang.js => lang.ts} | 4 +- src/lib/types.ts | 2 +- src/store/{app.js => app.ts} | 0 src/store/{cache.js => cache.ts} | 0 src/store/{dashboard.js => dashboard.ts} | 0 src/store/{version.js => version.ts} | 0 yarn.lock | 53 ++++++++-------- 173 files changed, 968 insertions(+), 549 deletions(-) rename src/app/(main)/{NavBar.js => NavBar.tsx} (100%) rename src/app/(main)/{UpdateNotice.js => UpdateNotice.tsx} (100%) rename src/app/(main)/console/{TestConsole.js => TestConsole.tsx} (85%) rename src/app/(main)/dashboard/{Dashboard.js => Dashboard.tsx} (80%) rename src/app/(main)/dashboard/{DashboardEdit.js => DashboardEdit.tsx} (95%) rename src/app/(main)/dashboard/{DashboardSettingsButton.js => DashboardSettingsButton.tsx} (100%) rename src/app/(main)/reports/{ReportDeleteButton.js => ReportDeleteButton.tsx} (81%) rename src/app/(main)/reports/{ReportsHeader.js => ReportsHeader.tsx} (100%) rename src/app/(main)/reports/{ReportsTable.js => ReportsTable.tsx} (94%) rename src/app/(main)/reports/[id]/{BaseParameters.js => BaseParameters.tsx} (83%) rename src/app/(main)/reports/[id]/{FieldAddForm.js => FieldAddForm.tsx} (76%) rename src/app/(main)/reports/[id]/{FieldAggregateForm.js => FieldAggregateForm.tsx} (86%) rename src/app/(main)/reports/[id]/{FieldFilterForm.js => FieldFilterForm.tsx} (86%) rename src/app/(main)/reports/[id]/{FieldSelectForm.js => FieldSelectForm.tsx} (65%) rename src/app/(main)/reports/[id]/{FilterSelectForm.js => FilterSelectForm.tsx} (67%) rename src/app/(main)/reports/[id]/{ParameterList.js => ParameterList.tsx} (82%) rename src/app/(main)/reports/[id]/{PopupForm.js => PopupForm.tsx} (59%) rename src/app/(main)/reports/[id]/{Report.js => Report.tsx} (56%) rename src/app/(main)/reports/[id]/{ReportBody.js => ReportBody.tsx} (100%) rename src/app/(main)/reports/[id]/{ReportDetails.js => ReportDetails.tsx} (90%) rename src/app/(main)/reports/[id]/{ReportHeader.js => ReportHeader.tsx} (92%) rename src/app/(main)/reports/[id]/{ReportMenu.js => ReportMenu.tsx} (100%) rename src/app/(main)/reports/create/{ReportTemplates.js => ReportTemplates.tsx} (100%) rename src/app/(main)/reports/event-data/{EventDataParameters.js => EventDataParameters.tsx} (91%) rename src/app/(main)/reports/event-data/{EventDataReport.js => EventDataReport.tsx} (89%) rename src/app/(main)/reports/event-data/{EventDataTable.js => EventDataTable.tsx} (100%) rename src/app/(main)/reports/funnel/{FunnelChart.js => FunnelChart.tsx} (52%) rename src/app/(main)/reports/funnel/{FunnelParameters.js => FunnelParameters.tsx} (84%) rename src/app/(main)/reports/funnel/{FunnelReport.js => FunnelReport.tsx} (100%) rename src/app/(main)/reports/funnel/{FunnelTable.js => FunnelTable.tsx} (100%) rename src/app/(main)/reports/funnel/{UrlAddForm.js => UrlAddForm.tsx} (86%) rename src/app/(main)/reports/insights/{InsightsParameters.js => InsightsParameters.tsx} (93%) rename src/app/(main)/reports/insights/{InsightsReport.js => InsightsReport.tsx} (90%) rename src/app/(main)/reports/insights/{InsightsTable.js => InsightsTable.tsx} (96%) rename src/app/(main)/reports/retention/{RetentionParameters.js => RetentionParameters.tsx} (87%) rename src/app/(main)/reports/retention/{RetentionReport.js => RetentionReport.tsx} (92%) rename src/app/(main)/reports/retention/{RetentionTable.js => RetentionTable.tsx} (96%) rename src/app/(main)/settings/profile/{DateRangeSetting.js => DateRangeSetting.tsx} (100%) rename src/app/(main)/settings/profile/{LanguageSetting.js => LanguageSetting.tsx} (100%) rename src/app/(main)/settings/profile/{PasswordChangeButton.js => PasswordChangeButton.tsx} (100%) rename src/app/(main)/settings/profile/{PasswordEditForm.js => PasswordEditForm.tsx} (91%) rename src/app/(main)/settings/profile/{ProfileHeader.js => ProfileHeader.tsx} (100%) rename src/app/(main)/settings/profile/{ProfileSettings.js => ProfileSettings.tsx} (100%) rename src/app/(main)/settings/profile/{ThemeSetting.js => ThemeSetting.tsx} (100%) rename src/app/(main)/settings/profile/{TimezoneSetting.js => TimezoneSetting.tsx} (100%) rename src/app/(main)/settings/teams/{TeamAddForm.js => TeamAddForm.tsx} (72%) rename src/app/(main)/settings/teams/{TeamDeleteButton.js => TeamDeleteButton.tsx} (79%) rename src/app/(main)/settings/teams/{TeamDeleteForm.js => TeamDeleteForm.tsx} (72%) rename src/app/(main)/settings/teams/{TeamJoinForm.js => TeamJoinForm.tsx} (85%) rename src/app/(main)/settings/teams/{TeamLeaveButton.js => TeamLeaveButton.tsx} (87%) rename src/app/(main)/settings/teams/{TeamLeaveForm.js => TeamLeaveForm.tsx} (60%) rename src/app/(main)/settings/teams/{TeamsAddButton.js => TeamsAddButton.tsx} (79%) rename src/app/(main)/settings/teams/{TeamsDataTable.js => TeamsDataTable.tsx} (88%) rename src/app/(main)/settings/teams/{TeamsHeader.js => TeamsHeader.tsx} (100%) rename src/app/(main)/settings/teams/{TeamsJoinButton.js => TeamsJoinButton.tsx} (100%) rename src/app/(main)/settings/teams/{TeamsTable.js => TeamsTable.tsx} (96%) rename src/app/(main)/settings/teams/{WebsiteTags.js => WebsiteTags.tsx} (83%) rename src/app/(main)/settings/teams/[id]/{TeamEditForm.js => TeamEditForm.tsx} (100%) rename src/app/(main)/settings/teams/[id]/{TeamMemberRemoveButton.js => TeamMemberRemoveButton.tsx} (59%) rename src/app/(main)/settings/teams/[id]/{TeamMembers.js => TeamMembers.tsx} (89%) rename src/app/(main)/settings/teams/[id]/{TeamMembersTable.js => TeamMembersTable.tsx} (90%) rename src/app/(main)/settings/teams/[id]/{TeamSettings.js => TeamSettings.tsx} (91%) rename src/app/(main)/settings/teams/[id]/{TeamWebsiteAddForm.js => TeamWebsiteAddForm.tsx} (86%) rename src/app/(main)/settings/teams/[id]/{TeamWebsiteRemoveButton.js => TeamWebsiteRemoveButton.tsx} (100%) rename src/app/(main)/settings/teams/[id]/{TeamWebsites.js => TeamWebsites.tsx} (86%) rename src/app/(main)/settings/teams/[id]/{TeamWebsitesTable.js => TeamWebsitesTable.tsx} (85%) rename src/app/(main)/settings/teams/[id]/{page.js => page.tsx} (100%) rename src/app/(main)/settings/users/{UserAddButton.js => UserAddButton.tsx} (92%) rename src/app/(main)/settings/users/{UserAddForm.js => UserAddForm.tsx} (92%) rename src/app/(main)/settings/users/{UserDeleteButton.js => UserDeleteButton.tsx} (85%) rename src/app/(main)/settings/users/{UserDeleteForm.js => UserDeleteForm.tsx} (72%) rename src/app/(main)/settings/users/{UserEditForm.js => UserEditForm.tsx} (82%) delete mode 100644 src/app/(main)/settings/users/UserWebsites.js create mode 100644 src/app/(main)/settings/users/UserWebsites.tsx rename src/app/(main)/settings/users/{UsersDataTable.js => UsersDataTable.tsx} (82%) rename src/app/(main)/settings/users/{UsersHeader.js => UsersHeader.tsx} (85%) rename src/app/(main)/settings/users/{UsersTable.js => UsersTable.tsx} (96%) rename src/app/(main)/settings/users/[id]/{page.js => page.tsx} (100%) rename src/app/(main)/settings/websites/{WebsiteAddButton.js => WebsiteAddButton.tsx} (92%) rename src/app/(main)/settings/websites/{WebsitesHeader.js => WebsitesHeader.tsx} (100%) rename src/app/(main)/settings/websites/{WebsitesTable.js => WebsitesTable.tsx} (90%) rename src/app/(main)/settings/websites/[id]/{ShareUrl.js => ShareUrl.tsx} (91%) rename src/app/(main)/settings/websites/[id]/{TrackingCode.js => TrackingCode.tsx} (87%) rename src/app/(main)/settings/websites/[id]/{WebsiteData.js => WebsiteData.tsx} (92%) rename src/app/(main)/settings/websites/[id]/{WebsiteDeleteForm.js => WebsiteDeleteForm.tsx} (82%) rename src/app/(main)/settings/websites/[id]/{WebsiteEditForm.js => WebsiteEditForm.tsx} (85%) rename src/app/(main)/settings/websites/[id]/{WebsiteResetForm.js => WebsiteResetForm.tsx} (78%) rename src/app/(main)/settings/websites/[id]/{page.js => page.tsx} (100%) rename src/app/(main)/websites/{WebsitesBrowse.js => WebsitesBrowse.tsx} (91%) rename src/app/(main)/websites/[id]/{WebsiteChart.js => WebsiteChart.tsx} (90%) rename src/app/(main)/websites/[id]/{WebsiteChartList.js => WebsiteChartList.tsx} (91%) rename src/app/(main)/websites/[id]/{WebsiteDetails.js => WebsiteDetails.tsx} (87%) rename src/app/(main)/websites/[id]/{WebsiteFilterButton.js => WebsiteFilterButton.tsx} (91%) rename src/app/(main)/websites/[id]/{WebsiteHeader.js => WebsiteHeader.tsx} (92%) rename src/app/(main)/websites/[id]/{WebsiteMenuView.js => WebsiteMenuView.tsx} (97%) rename src/app/(main)/websites/[id]/{WebsiteMetricsBar.js => WebsiteMetricsBar.tsx} (93%) rename src/app/(main)/websites/[id]/{WebsiteTableView.js => WebsiteTableView.tsx} (94%) rename src/app/(main)/websites/[id]/event-data/{EventDataMetricsBar.js => EventDataMetricsBar.tsx} (94%) rename src/app/(main)/websites/[id]/event-data/{EventDataTable.js => EventDataTable.tsx} (100%) rename src/app/(main)/websites/[id]/event-data/{EventDataValueTable.js => EventDataValueTable.tsx} (94%) rename src/app/(main)/websites/[id]/event-data/{page.js => page.tsx} (100%) rename src/app/login/{LoginForm.js => LoginForm.tsx} (90%) rename src/app/logout/{Logout.js => Logout.tsx} (100%) rename src/app/share/[...id]/{Footer.js => Footer.tsx} (100%) rename src/app/share/[...id]/{Header.js => Header.tsx} (100%) rename src/app/share/[...id]/{Share.js => Share.tsx} (100%) delete mode 100644 src/components/hooks/useApiFilter.ts rename src/components/input/{DateFilter.js => DateFilter.tsx} (89%) rename src/components/input/{LanguageButton.js => LanguageButton.tsx} (88%) rename src/components/input/{LogoutButton.js => LogoutButton.tsx} (80%) rename src/components/input/{MonthSelect.js => MonthSelect.tsx} (87%) rename src/components/input/{ProfileButton.js => ProfileButton.tsx} (100%) rename src/components/input/{RefreshButton.js => RefreshButton.tsx} (87%) rename src/components/input/{SettingsButton.js => SettingsButton.tsx} (100%) rename src/components/input/{ThemeButton.js => ThemeButton.tsx} (100%) rename src/components/input/{WebsiteDateFilter.js => WebsiteDateFilter.tsx} (95%) rename src/components/input/{WebsiteSelect.js => WebsiteSelect.tsx} (88%) delete mode 100644 src/components/layout/Grid.js create mode 100644 src/components/layout/Grid.tsx rename src/components/layout/{NavGroup.js => NavGroup.tsx} (90%) rename src/components/layout/{SideNav.js => SideNav.tsx} (82%) rename src/components/{messages.js => messages.ts} (100%) rename src/components/metrics/{ActiveUsers.js => ActiveUsers.tsx} (85%) rename src/components/metrics/{BarChart.js => BarChart.tsx} (83%) rename src/components/metrics/{BrowsersTable.js => BrowsersTable.tsx} (85%) rename src/components/metrics/{CitiesTable.js => CitiesTable.tsx} (85%) rename src/components/metrics/{CountriesTable.js => CountriesTable.tsx} (73%) rename src/components/metrics/{DatePickerForm.js => DatePickerForm.tsx} (96%) rename src/components/metrics/{DevicesTable.js => DevicesTable.tsx} (86%) rename src/components/metrics/{EventsChart.js => EventsChart.tsx} (92%) rename src/components/metrics/{EventsTable.js => EventsTable.tsx} (69%) rename src/components/metrics/{FilterTags.js => FilterTags.tsx} (92%) rename src/components/metrics/{LanguagesTable.js => LanguagesTable.tsx} (80%) rename src/components/metrics/{Legend.js => Legend.tsx} (100%) rename src/components/metrics/{ListTable.js => ListTable.tsx} (87%) rename src/components/metrics/{MetricCard.js => MetricCard.tsx} (77%) rename src/components/metrics/{MetricsBar.js => MetricsBar.tsx} (79%) rename src/components/metrics/{MetricsTable.js => MetricsTable.tsx} (80%) rename src/components/metrics/{OSTable.js => OSTable.tsx} (80%) rename src/components/metrics/{PagesTable.js => PagesTable.tsx} (83%) rename src/components/metrics/{PageviewsChart.js => PageviewsChart.tsx} (74%) rename src/components/metrics/{QueryParametersTable.js => QueryParametersTable.tsx} (88%) rename src/components/metrics/{RealtimeChart.js => RealtimeChart.tsx} (81%) rename src/components/metrics/{ReferrersTable.js => ReferrersTable.tsx} (83%) rename src/components/metrics/{RegionsTable.js => RegionsTable.tsx} (86%) rename src/components/metrics/{ScreenTable.js => ScreenTable.tsx} (70%) rename src/components/metrics/{WorldMap.js => WorldMap.tsx} (91%) rename src/lib/{charts.js => charts.tsx} (77%) rename src/lib/{crypto.js => crypto.ts} (85%) rename src/lib/{db.js => db.ts} (91%) rename src/lib/{filters.js => filters.ts} (86%) rename src/lib/{format.js => format.ts} (74%) rename src/lib/{lang.js => lang.ts} (96%) rename src/store/{app.js => app.ts} (100%) rename src/store/{cache.js => cache.ts} (100%) rename src/store/{dashboard.js => dashboard.ts} (100%) rename src/store/{version.js => version.ts} (100%) diff --git a/package.json b/package.json index feec3db8..a158696c 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@prisma/client": "5.6.0", "@prisma/extension-read-replicas": "^0.3.0", "@react-spring/web": "^9.7.3", - "@tanstack/react-query": "^4.33.0", + "@tanstack/react-query": "^5.12.2", "@umami/prisma-client": "^0.8.0", "@umami/redis-client": "^0.18.0", "chalk": "^4.1.1", @@ -94,12 +94,12 @@ "maxmind": "^4.3.6", "moment-timezone": "^0.5.35", "next": "13.5.6", - "next-basics": "^0.37.0", + "next-basics": "^0.39.0", "node-fetch": "^3.2.8", "npm-run-all": "^4.1.5", "prisma": "5.6.0", "react": "^18.2.0", - "react-basics": "^0.110.0", + "react-basics": "^0.114.0", "react-beautiful-dnd": "^13.1.0", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.4", @@ -127,8 +127,8 @@ "@svgr/rollup": "^8.1.0", "@svgr/webpack": "^8.1.0", "@types/node": "^20.9.0", - "@types/react": "^18.2.37", - "@types/react-dom": "^18.2.15", + "@types/react": "^18.2.41", + "@types/react-dom": "^18.2.17", "@typescript-eslint/eslint-plugin": "^6.7.3", "@typescript-eslint/parser": "^6.7.3", "cross-env": "^7.0.3", diff --git a/src/app/(main)/NavBar.js b/src/app/(main)/NavBar.tsx similarity index 100% rename from src/app/(main)/NavBar.js rename to src/app/(main)/NavBar.tsx diff --git a/src/app/(main)/UpdateNotice.js b/src/app/(main)/UpdateNotice.tsx similarity index 100% rename from src/app/(main)/UpdateNotice.js rename to src/app/(main)/UpdateNotice.tsx diff --git a/src/app/(main)/console/TestConsole.js b/src/app/(main)/console/TestConsole.tsx similarity index 85% rename from src/app/(main)/console/TestConsole.js rename to src/app/(main)/console/TestConsole.tsx index 7aae09b2..0bb807ff 100644 --- a/src/app/(main)/console/TestConsole.js +++ b/src/app/(main)/console/TestConsole.tsx @@ -1,18 +1,18 @@ 'use client'; +import { Button } from 'react-basics'; +import Head from 'next/head'; +import Link from 'next/link'; +import Script from 'next/script'; import WebsiteSelect from 'components/input/WebsiteSelect'; import Page from 'components/layout/Page'; import PageHeader from 'components/layout/PageHeader'; import EventsChart from 'components/metrics/EventsChart'; -import WebsiteChart from '../../(main)/websites/[id]/WebsiteChart'; +import WebsiteChart from 'app/(main)/websites/[id]/WebsiteChart'; import useApi from 'components/hooks/useApi'; -import Head from 'next/head'; -import Link from 'next/link'; import useNavigation from 'components/hooks/useNavigation'; -import Script from 'next/script'; -import { Button } from 'react-basics'; import styles from './TestConsole.module.css'; -export function TestConsole({ websiteId }) { +export function TestConsole({ websiteId }: { websiteId: string }) { const { get, useQuery } = useApi(); const { data, isLoading, error } = useQuery({ queryKey: ['websites:me'], @@ -20,14 +20,14 @@ export function TestConsole({ websiteId }) { }); const { router } = useNavigation(); - function handleChange(value) { + function handleChange(value: string) { router.push(`/console/${value}`); } function handleClick() { - window.umami.track({ url: '/page-view', referrer: 'https://www.google.com' }); - window.umami.track('track-event-no-data'); - window.umami.track('track-event-with-data', { + window['umami'].track({ url: '/page-view', referrer: 'https://www.google.com' }); + window['umami'].track('track-event-no-data'); + window['umami'].track('track-event-with-data', { test: 'test-data', boolean: true, booleanError: 'true', @@ -47,7 +47,7 @@ export function TestConsole({ websiteId }) { } function handleIdentifyClick() { - window.umami.identify({ + window['umami'].identify({ userId: 123, name: 'brian', number: Math.random() * 100, @@ -74,7 +74,7 @@ export function TestConsole({ websiteId }) { const website = data?.data.find(({ id }) => websiteId === id); return ( - + {website ? `${website.name} | Umami Console` : 'Umami Console'} @@ -116,7 +116,7 @@ export function TestConsole({ websiteId }) {
Click events
-

@@ -125,18 +125,18 @@ export function TestConsole({ websiteId }) { data-umami-event="button-click" data-umami-event-name="bob" data-umami-event-id="123" - variant="action" + variant="primary" > Send event with data

Javascript events
-

-

diff --git a/src/app/(main)/dashboard/Dashboard.js b/src/app/(main)/dashboard/Dashboard.tsx similarity index 80% rename from src/app/(main)/dashboard/Dashboard.js rename to src/app/(main)/dashboard/Dashboard.tsx index 6f0659bb..1afc0da2 100644 --- a/src/app/(main)/dashboard/Dashboard.js +++ b/src/app/(main)/dashboard/Dashboard.tsx @@ -11,23 +11,31 @@ import useApi from 'components/hooks/useApi'; import useDashboard from 'store/dashboard'; import useMessages from 'components/hooks/useMessages'; import useLocale from 'components/hooks/useLocale'; -import useApiFilter from 'components/hooks/useApiFilter'; +import useFilterQuery from 'components/hooks/useFilterQuery'; export function Dashboard() { const { formatMessage, labels, messages } = useMessages(); const { showCharts, editing } = useDashboard(); const { dir } = useLocale(); - const { get, useQuery } = useApi(); - const { page, handlePageChange } = useApiFilter(); + const { get } = useApi(); const pageSize = 10; - const { data: result, isLoading } = useQuery({ - queryKey: ['websites', page, pageSize], - queryFn: () => get('/websites', { includeTeams: 1, page, pageSize }), - }); - const { data, count } = result || {}; - const hasData = data && data?.length !== 0; - if (isLoading) { + const { query, params, setParams, result } = useFilterQuery({ + queryKey: ['dashboard:websites'], + queryFn: (params: any) => { + return get(`/websites`, { ...params, includeTeams: true, pageSize }); + }, + }); + + const handlePageChange = (page: number) => { + setParams({ ...params, page }); + }; + + const { data, count } = result || {}; + const hasData = !!(data as any)?.length; + const { page } = params; + + if (query.isLoading) { return ; } diff --git a/src/app/(main)/dashboard/DashboardEdit.js b/src/app/(main)/dashboard/DashboardEdit.tsx similarity index 95% rename from src/app/(main)/dashboard/DashboardEdit.js rename to src/app/(main)/dashboard/DashboardEdit.tsx index 0a8734e8..35638038 100644 --- a/src/app/(main)/dashboard/DashboardEdit.js +++ b/src/app/(main)/dashboard/DashboardEdit.tsx @@ -60,13 +60,13 @@ export function DashboardEdit() { return ( <>
- - -
diff --git a/src/app/(main)/dashboard/DashboardSettingsButton.js b/src/app/(main)/dashboard/DashboardSettingsButton.tsx similarity index 100% rename from src/app/(main)/dashboard/DashboardSettingsButton.js rename to src/app/(main)/dashboard/DashboardSettingsButton.tsx diff --git a/src/app/(main)/reports/ReportDeleteButton.js b/src/app/(main)/reports/ReportDeleteButton.tsx similarity index 81% rename from src/app/(main)/reports/ReportDeleteButton.js rename to src/app/(main)/reports/ReportDeleteButton.tsx index 35809a98..d3f1bb4f 100644 --- a/src/app/(main)/reports/ReportDeleteButton.js +++ b/src/app/(main)/reports/ReportDeleteButton.tsx @@ -3,13 +3,21 @@ import ConfirmDeleteForm from 'components/common/ConfirmDeleteForm'; import { useApi, useMessages } from 'components/hooks'; import { setValue } from 'store/cache'; -export function ReportDeleteButton({ reportId, reportName, onDelete }) { +export function ReportDeleteButton({ + reportId, + reportName, + onDelete, +}: { + reportId: string; + reportName: string; + onDelete?: () => void; +}) { const { formatMessage, labels } = useMessages(); const { del, useMutation } = useApi(); const { mutate } = useMutation(reportId => del(`/reports/${reportId}`)); - const handleConfirm = close => { - mutate(reportId, { + const handleConfirm = (close: () => void) => { + mutate(reportId as any, { onSuccess: () => { setValue('reports', Date.now()); onDelete?.(); diff --git a/src/app/(main)/reports/ReportsDataTable.tsx b/src/app/(main)/reports/ReportsDataTable.tsx index eeef203b..3ede4783 100644 --- a/src/app/(main)/reports/ReportsDataTable.tsx +++ b/src/app/(main)/reports/ReportsDataTable.tsx @@ -1,18 +1,10 @@ 'use client'; -import { useApi } from 'components/hooks'; +import { useReports } from 'components/hooks'; import ReportsTable from './ReportsTable'; -import useFilterQuery from 'components/hooks/useFilterQuery'; import DataTable from 'components/common/DataTable'; -import useCache from 'store/cache'; export default function ReportsDataTable({ websiteId }: { websiteId?: string }) { - const { get } = useApi(); - const modified = useCache(state => (state as any)?.reports); - const queryResult = useFilterQuery({ - queryKey: ['reports', { websiteId, modified }], - queryFn: (params: any) => - get(websiteId ? `/websites/${websiteId}/reports` : `/reports`, params), - }); + const queryResult = useReports(websiteId); return ( diff --git a/src/app/(main)/reports/ReportsHeader.js b/src/app/(main)/reports/ReportsHeader.tsx similarity index 100% rename from src/app/(main)/reports/ReportsHeader.js rename to src/app/(main)/reports/ReportsHeader.tsx diff --git a/src/app/(main)/reports/ReportsTable.js b/src/app/(main)/reports/ReportsTable.tsx similarity index 94% rename from src/app/(main)/reports/ReportsTable.js rename to src/app/(main)/reports/ReportsTable.tsx index 6b2a7932..882110ee 100644 --- a/src/app/(main)/reports/ReportsTable.js +++ b/src/app/(main)/reports/ReportsTable.tsx @@ -5,7 +5,7 @@ import useUser from 'components/hooks/useUser'; import { REPORT_TYPES } from 'lib/constants'; import ReportDeleteButton from './ReportDeleteButton'; -export function ReportsTable({ data = [], showDomain }) { +export function ReportsTable({ data = [], showDomain }: { data: any[]; showDomain?: boolean }) { const { formatMessage, labels } = useMessages(); const { user } = useUser(); const breakpoint = useBreakpoint(); diff --git a/src/app/(main)/reports/[id]/BaseParameters.js b/src/app/(main)/reports/[id]/BaseParameters.tsx similarity index 83% rename from src/app/(main)/reports/[id]/BaseParameters.js rename to src/app/(main)/reports/[id]/BaseParameters.tsx index a54ef4f3..9b6be5d8 100644 --- a/src/app/(main)/reports/[id]/BaseParameters.js +++ b/src/app/(main)/reports/[id]/BaseParameters.tsx @@ -6,12 +6,19 @@ import WebsiteSelect from 'components/input/WebsiteSelect'; import { useMessages } from 'components/hooks'; import { ReportContext } from './Report'; +export interface BaseParametersProps { + showWebsiteSelect?: boolean; + allowWebsiteSelect?: boolean; + showDateSelect?: boolean; + allowDateSelect?: boolean; +} + export function BaseParameters({ showWebsiteSelect = true, allowWebsiteSelect = true, showDateSelect = true, allowDateSelect = true, -}) { +}: BaseParametersProps) { const { report, updateReport } = useContext(ReportContext); const { formatMessage, labels } = useMessages(); @@ -19,11 +26,11 @@ export function BaseParameters({ const { websiteId, dateRange } = parameters || {}; const { value, startDate, endDate } = dateRange || {}; - const handleWebsiteSelect = websiteId => { + const handleWebsiteSelect = (websiteId: string) => { updateReport({ websiteId, parameters: { websiteId } }); }; - const handleDateChange = value => { + const handleDateChange = (value: string) => { updateReport({ parameters: { dateRange: { ...parseDateRange(value) } } }); }; diff --git a/src/app/(main)/reports/[id]/FieldAddForm.js b/src/app/(main)/reports/[id]/FieldAddForm.tsx similarity index 76% rename from src/app/(main)/reports/[id]/FieldAddForm.js rename to src/app/(main)/reports/[id]/FieldAddForm.tsx index 6923bceb..9db472d8 100644 --- a/src/app/(main)/reports/[id]/FieldAddForm.js +++ b/src/app/(main)/reports/[id]/FieldAddForm.tsx @@ -7,10 +7,20 @@ import FieldAggregateForm from './FieldAggregateForm'; import FieldFilterForm from './FieldFilterForm'; import styles from './FieldAddForm.module.css'; -export function FieldAddForm({ fields = [], group, onAdd, onClose }) { - const [selected, setSelected] = useState(); +export function FieldAddForm({ + fields = [], + group, + onAdd, + onClose, +}: { + fields?: any[]; + group: string; + onAdd: (group: string, value: string) => void; + onClose: () => void; +}) { + const [selected, setSelected] = useState<{ name: string; type: string; value: string }>(); - const handleSelect = value => { + const handleSelect = (value: any) => { const { type } = value; if (group === REPORT_PARAMETERS.groups || type === 'array' || type === 'boolean') { @@ -22,7 +32,7 @@ export function FieldAddForm({ fields = [], group, onAdd, onClose }) { setSelected(value); }; - const handleSave = value => { + const handleSave = (value: any) => { onAdd(group, value); onClose(); }; diff --git a/src/app/(main)/reports/[id]/FieldAggregateForm.js b/src/app/(main)/reports/[id]/FieldAggregateForm.tsx similarity index 86% rename from src/app/(main)/reports/[id]/FieldAggregateForm.js rename to src/app/(main)/reports/[id]/FieldAggregateForm.tsx index 34b67980..6b5bf636 100644 --- a/src/app/(main)/reports/[id]/FieldAggregateForm.js +++ b/src/app/(main)/reports/[id]/FieldAggregateForm.tsx @@ -1,7 +1,15 @@ import { Form, FormRow, Menu, Item } from 'react-basics'; import { useMessages } from 'components/hooks'; -export default function FieldAggregateForm({ name, type, onSelect }) { +export default function FieldAggregateForm({ + name, + type, + onSelect, +}: { + name: string; + type: string; + onSelect: (key: any) => void; +}) { const { formatMessage, labels } = useMessages(); const options = { @@ -27,7 +35,7 @@ export default function FieldAggregateForm({ name, type, onSelect }) { const items = options[type]; - const handleSelect = value => { + const handleSelect = (value: any) => { onSelect({ name, type, value }); }; diff --git a/src/app/(main)/reports/[id]/FieldFilterForm.js b/src/app/(main)/reports/[id]/FieldFilterForm.tsx similarity index 86% rename from src/app/(main)/reports/[id]/FieldFilterForm.js rename to src/app/(main)/reports/[id]/FieldFilterForm.tsx index ea80f82a..4af7febf 100644 --- a/src/app/(main)/reports/[id]/FieldFilterForm.js +++ b/src/app/(main)/reports/[id]/FieldFilterForm.tsx @@ -3,6 +3,15 @@ import { Form, FormRow, Item, Flexbox, Dropdown, Button } from 'react-basics'; import { useMessages, useFilters, useFormat, useLocale } from 'components/hooks'; import styles from './FieldFilterForm.module.css'; +export interface FieldFilterFormProps { + name: string; + label?: string; + type: string; + values?: any[]; + onSelect?: (key: any) => void; + allowFilterSelect?: boolean; +} + export default function FieldFilterForm({ name, label, @@ -10,7 +19,7 @@ export default function FieldFilterForm({ values, onSelect, allowFilterSelect = true, -}) { +}: FieldFilterFormProps) { const { formatMessage, labels } = useMessages(); const [filter, setFilter] = useState('eq'); const [value, setValue] = useState(); @@ -21,7 +30,7 @@ export default function FieldFilterForm({ const formattedValues = useMemo(() => { const formatted = {}; - const format = val => { + const format = (val: string) => { formatted[val] = formatValue(val, name); return formatted[val]; }; @@ -56,7 +65,7 @@ export default function FieldFilterForm({ items={filters} value={filter} renderValue={renderFilterValue} - onChange={setFilter} + onChange={(key: any) => setFilter(key)} > {({ value, label }) => { return {label}; @@ -69,12 +78,12 @@ export default function FieldFilterForm({ items={values} value={value} renderValue={renderValue} - onChange={setValue} + onChange={(key: any) => setValue(key)} style={{ minWidth: '250px', }} > - {value => { + {(value: string) => { return {formattedValues[value]}; }} diff --git a/src/app/(main)/reports/[id]/FieldSelectForm.js b/src/app/(main)/reports/[id]/FieldSelectForm.tsx similarity index 65% rename from src/app/(main)/reports/[id]/FieldSelectForm.js rename to src/app/(main)/reports/[id]/FieldSelectForm.tsx index e7661511..dfd402cf 100644 --- a/src/app/(main)/reports/[id]/FieldSelectForm.js +++ b/src/app/(main)/reports/[id]/FieldSelectForm.tsx @@ -1,15 +1,26 @@ import { Menu, Item, Form, FormRow } from 'react-basics'; import { useMessages } from 'components/hooks'; import styles from './FieldSelectForm.module.css'; +import { Key } from 'react'; -export default function FieldSelectForm({ items, onSelect, showType = true }) { +export interface FieldSelectFormProps { + fields?: any[]; + onSelect?: (key: any) => void; + showType?: boolean; +} + +export default function FieldSelectForm({ + fields = [], + onSelect, + showType = true, +}: FieldSelectFormProps) { const { formatMessage, labels } = useMessages(); return (
- onSelect(items[key])}> - {items.map(({ name, label, type }, index) => { + onSelect(fields[key as any])}> + {fields.map(({ name, label, type }: any, index: Key) => { return (
{label || name}
diff --git a/src/app/(main)/reports/[id]/FilterSelectForm.js b/src/app/(main)/reports/[id]/FilterSelectForm.tsx similarity index 67% rename from src/app/(main)/reports/[id]/FilterSelectForm.js rename to src/app/(main)/reports/[id]/FilterSelectForm.tsx index 8457b7ff..4f9b9264 100644 --- a/src/app/(main)/reports/[id]/FilterSelectForm.js +++ b/src/app/(main)/reports/[id]/FilterSelectForm.tsx @@ -5,7 +5,7 @@ import FieldSelectForm from './FieldSelectForm'; import FieldFilterForm from './FieldFilterForm'; import { useApi } from 'components/hooks'; -function useValues(websiteId, type) { +function useValues(websiteId: string, type: string) { const now = Date.now(); const { get, useQuery } = useApi(); const { data, error, isLoading } = useQuery({ @@ -22,12 +22,24 @@ function useValues(websiteId, type) { return { data, error, isLoading }; } -export default function FilterSelectForm({ websiteId, items, onSelect, allowFilterSelect }) { - const [field, setField] = useState(); +export interface FilterSelectFormProps { + websiteId: string; + items: any[]; + onSelect?: (key: any) => void; + allowFilterSelect?: boolean; +} + +export default function FilterSelectForm({ + websiteId, + items, + onSelect, + allowFilterSelect, +}: FilterSelectFormProps) { + const [field, setField] = useState<{ name: string; label: string; type: string }>(); const { data, isLoading } = useValues(websiteId, field?.name); if (!field) { - return ; + return ; } if (isLoading) { diff --git a/src/app/(main)/reports/[id]/ParameterList.js b/src/app/(main)/reports/[id]/ParameterList.tsx similarity index 82% rename from src/app/(main)/reports/[id]/ParameterList.js rename to src/app/(main)/reports/[id]/ParameterList.tsx index bf77dd9d..eb1a646a 100644 --- a/src/app/(main)/reports/[id]/ParameterList.js +++ b/src/app/(main)/reports/[id]/ParameterList.tsx @@ -1,10 +1,17 @@ +import { ReactNode } from 'react'; import { Icon, TooltipPopup } from 'react-basics'; import Icons from 'components/icons'; import Empty from 'components/common/Empty'; import { useMessages } from 'components/hooks'; import styles from './ParameterList.module.css'; -export function ParameterList({ items = [], children, onRemove }) { +export interface ParameterListProps { + items: any[]; + children?: ReactNode | ((item: any) => ReactNode); + onRemove: (index: number, e: any) => void; +} + +export function ParameterList({ items = [], children, onRemove }: ParameterListProps) { const { formatMessage, labels } = useMessages(); return ( diff --git a/src/app/(main)/reports/[id]/PopupForm.js b/src/app/(main)/reports/[id]/PopupForm.tsx similarity index 59% rename from src/app/(main)/reports/[id]/PopupForm.js rename to src/app/(main)/reports/[id]/PopupForm.tsx index 6b99b00a..f2666199 100644 --- a/src/app/(main)/reports/[id]/PopupForm.js +++ b/src/app/(main)/reports/[id]/PopupForm.tsx @@ -1,7 +1,16 @@ +import { CSSProperties, ReactNode } from 'react'; import classNames from 'classnames'; import styles from './PopupForm.module.css'; -export function PopupForm({ className, style, children }) { +export function PopupForm({ + className, + style, + children, +}: { + className?: string; + style?: CSSProperties; + children: ReactNode; +}) { return (
-
- {children} -
+
{children}
); } diff --git a/src/app/(main)/reports/[id]/ReportBody.js b/src/app/(main)/reports/[id]/ReportBody.tsx similarity index 100% rename from src/app/(main)/reports/[id]/ReportBody.js rename to src/app/(main)/reports/[id]/ReportBody.tsx diff --git a/src/app/(main)/reports/[id]/ReportDetails.js b/src/app/(main)/reports/[id]/ReportDetails.tsx similarity index 90% rename from src/app/(main)/reports/[id]/ReportDetails.js rename to src/app/(main)/reports/[id]/ReportDetails.tsx index df91719a..e4d4688a 100644 --- a/src/app/(main)/reports/[id]/ReportDetails.js +++ b/src/app/(main)/reports/[id]/ReportDetails.tsx @@ -12,7 +12,7 @@ const reports = { retention: RetentionReport, }; -export default function ReportDetails({ reportId }) { +export default function ReportDetails({ reportId }: { reportId: string }) { const { get, useQuery } = useApi(); const { data: report } = useQuery({ queryKey: ['reports', reportId], diff --git a/src/app/(main)/reports/[id]/ReportHeader.js b/src/app/(main)/reports/[id]/ReportHeader.tsx similarity index 92% rename from src/app/(main)/reports/[id]/ReportHeader.js rename to src/app/(main)/reports/[id]/ReportHeader.tsx index ed3b9736..6d226344 100644 --- a/src/app/(main)/reports/[id]/ReportHeader.js +++ b/src/app/(main)/reports/[id]/ReportHeader.tsx @@ -12,8 +12,10 @@ export function ReportHeader({ icon }) { const { showToast } = useToasts(); const { post, useMutation } = useApi(); const router = useRouter(); - const { mutate: create, isLoading: isCreating } = useMutation(data => post(`/reports`, data)); - const { mutate: update, isLoading: isUpdating } = useMutation(data => + const { mutate: create, isLoading: isCreating } = useMutation((data: any) => + post(`/reports`, data), + ); + const { mutate: update, isLoading: isUpdating } = useMutation((data: any) => post(`/reports/${data.id}`, data), ); @@ -26,7 +28,7 @@ export function ReportHeader({ icon }) { create(report, { onSuccess: async ({ id }) => { showToast({ message: formatMessage(messages.saved), variant: 'success' }); - router.push(`/reports/${id}`, null, { shallow: true }); + router.push(`/reports/${id}`); }, }); } else { @@ -38,11 +40,11 @@ export function ReportHeader({ icon }) { } }; - const handleNameChange = name => { + const handleNameChange = (name: string) => { updateReport({ name: name || defaultName }); }; - const handleDescriptionChange = description => { + const handleDescriptionChange = (description: string) => { updateReport({ description }); }; diff --git a/src/app/(main)/reports/[id]/ReportMenu.js b/src/app/(main)/reports/[id]/ReportMenu.tsx similarity index 100% rename from src/app/(main)/reports/[id]/ReportMenu.js rename to src/app/(main)/reports/[id]/ReportMenu.tsx diff --git a/src/app/(main)/reports/create/ReportTemplates.js b/src/app/(main)/reports/create/ReportTemplates.tsx similarity index 100% rename from src/app/(main)/reports/create/ReportTemplates.js rename to src/app/(main)/reports/create/ReportTemplates.tsx diff --git a/src/app/(main)/reports/event-data/EventDataParameters.js b/src/app/(main)/reports/event-data/EventDataParameters.tsx similarity index 91% rename from src/app/(main)/reports/event-data/EventDataParameters.js rename to src/app/(main)/reports/event-data/EventDataParameters.tsx index ac90fc2a..7a39131b 100644 --- a/src/app/(main)/reports/event-data/EventDataParameters.js +++ b/src/app/(main)/reports/event-data/EventDataParameters.tsx @@ -1,4 +1,4 @@ -import { useContext, useRef } from 'react'; +import { useContext } from 'react'; import { Form, FormRow, FormButtons, SubmitButton, PopupTrigger, Icon, Popup } from 'react-basics'; import Empty from 'components/common/Empty'; import Icons from 'components/icons'; @@ -29,7 +29,6 @@ function useFields(websiteId, startDate, endDate) { export function EventDataParameters() { const { report, runReport, updateReport, isRunning } = useContext(ReportContext); const { formatMessage, labels, messages } = useMessages(); - const ref = useRef(null); const { parameters } = report || {}; const { websiteId, dateRange, fields, filters, groups } = parameters || {}; const { startDate, endDate } = dateRange || {}; @@ -53,28 +52,28 @@ export function EventDataParameters() { runReport(values); }; - const handleAdd = (group, value) => { + const handleAdd = (group: string, value: any) => { const data = parameterData[group]; - if (!data.find(({ name }) => name === value.name)) { + if (!data.find(({ name }) => name === value?.name)) { updateReport({ parameters: { [group]: data.concat(value) } }); } }; - const handleRemove = (group, index) => { + const handleRemove = (group: string, index: number) => { const data = [...parameterData[group]]; data.splice(index, 1); updateReport({ parameters: { [group]: data } }); }; - const AddButton = ({ group }) => { + const AddButton = ({ group, onAdd }) => { return ( - {close => { + {(close: () => void) => { return ( ({ @@ -82,7 +81,7 @@ export function EventDataParameters() { type: DATA_TYPES[eventDataType], }))} group={group} - onAdd={handleAdd} + onAdd={onAdd} onClose={close} /> ); @@ -93,7 +92,7 @@ export function EventDataParameters() { }; return ( - + {!hasData && } {parametersSelected && diff --git a/src/app/(main)/reports/event-data/EventDataReport.js b/src/app/(main)/reports/event-data/EventDataReport.tsx similarity index 89% rename from src/app/(main)/reports/event-data/EventDataReport.js rename to src/app/(main)/reports/event-data/EventDataReport.tsx index e91cb4a2..8b4dc99c 100644 --- a/src/app/(main)/reports/event-data/EventDataReport.js +++ b/src/app/(main)/reports/event-data/EventDataReport.tsx @@ -11,7 +11,7 @@ const defaultParameters = { parameters: { fields: [], filters: [] }, }; -export default function EventDataReport({ reportId }) { +export default function EventDataReport({ reportId }: { reportId: string }) { return ( } /> diff --git a/src/app/(main)/reports/event-data/EventDataTable.js b/src/app/(main)/reports/event-data/EventDataTable.tsx similarity index 100% rename from src/app/(main)/reports/event-data/EventDataTable.js rename to src/app/(main)/reports/event-data/EventDataTable.tsx diff --git a/src/app/(main)/reports/funnel/FunnelChart.js b/src/app/(main)/reports/funnel/FunnelChart.tsx similarity index 52% rename from src/app/(main)/reports/funnel/FunnelChart.js rename to src/app/(main)/reports/funnel/FunnelChart.tsx index 7461afbc..923e8d56 100644 --- a/src/app/(main)/reports/funnel/FunnelChart.js +++ b/src/app/(main)/reports/funnel/FunnelChart.tsx @@ -1,13 +1,18 @@ -import { useCallback, useContext, useMemo } from 'react'; +import { JSX, useCallback, useContext, useMemo } from 'react'; import { Loading, StatusLight } from 'react-basics'; import useMessages from 'components/hooks/useMessages'; import useTheme from 'components/hooks/useTheme'; import BarChart from 'components/metrics/BarChart'; import { formatLongNumber } from 'lib/format'; -import styles from './FunnelChart.module.css'; import { ReportContext } from '../[id]/Report'; +import styles from './FunnelChart.module.css'; -export function FunnelChart({ className, loading }) { +export interface FunnelChartProps { + className?: string; + isLoading?: boolean; +} + +export function FunnelChart({ className, isLoading }: FunnelChartProps) { const { report } = useContext(ReportContext); const { formatMessage, labels } = useMessages(); const { colors } = useTheme(); @@ -15,33 +20,39 @@ export function FunnelChart({ className, loading }) { const { parameters, data } = report || {}; const renderXLabel = useCallback( - (label, index) => { + (label: string, index: number) => { return parameters.urls[index]; }, [parameters], ); - const renderTooltipPopup = useCallback((setTooltipPopup, model) => { - const { opacity, labelColors, dataPoints } = model.tooltip; + const renderTooltipPopup = useCallback( + ( + setTooltipPopup: (arg0: JSX.Element) => void, + model: { tooltip: { opacity: any; labelColors: any; dataPoints: any } }, + ) => { + const { opacity, labelColors, dataPoints } = model.tooltip; - if (!dataPoints?.length || !opacity) { - setTooltipPopup(null); - return; - } + if (!dataPoints?.length || !opacity) { + setTooltipPopup(null); + return; + } - setTooltipPopup( - <> -
- {formatLongNumber(dataPoints[0].raw.y)} {formatMessage(labels.visitors)} -
-
- - {formatLongNumber(dataPoints[0].raw.z)}% {formatMessage(labels.dropoff)} - -
- , - ); - }, []); + setTooltipPopup( + <> +
+ {formatLongNumber(dataPoints[0].raw.y)} {formatMessage(labels.visitors)} +
+
+ + {formatLongNumber(dataPoints[0].raw.z)}% {formatMessage(labels.dropoff)} + +
+ , + ); + }, + [], + ); const datasets = useMemo(() => { return [ @@ -54,7 +65,7 @@ export function FunnelChart({ className, loading }) { ]; }, [data, colors, formatMessage, labels]); - if (loading) { + if (isLoading) { return ; } @@ -63,7 +74,7 @@ export function FunnelChart({ className, loading }) { className={className} datasets={datasets} unit="day" - loading={loading} + isLoading={isLoading} renderXLabel={renderXLabel} renderTooltipPopup={renderTooltipPopup} XAxisType="category" diff --git a/src/app/(main)/reports/funnel/FunnelParameters.js b/src/app/(main)/reports/funnel/FunnelParameters.tsx similarity index 84% rename from src/app/(main)/reports/funnel/FunnelParameters.js rename to src/app/(main)/reports/funnel/FunnelParameters.tsx index 135b5db8..0bb45415 100644 --- a/src/app/(main)/reports/funnel/FunnelParameters.js +++ b/src/app/(main)/reports/funnel/FunnelParameters.tsx @@ -1,4 +1,4 @@ -import { useContext, useRef } from 'react'; +import { useContext } from 'react'; import { useMessages } from 'components/hooks'; import { Icon, @@ -21,13 +21,12 @@ import PopupForm from '../[id]/PopupForm'; export function FunnelParameters() { const { report, runReport, updateReport, isRunning } = useContext(ReportContext); const { formatMessage, labels } = useMessages(); - const ref = useRef(null); const { parameters } = report || {}; const { websiteId, dateRange, urls } = parameters || {}; const queryDisabled = !websiteId || !dateRange || urls?.length < 2; - const handleSubmit = (data, e) => { + const handleSubmit = (data: any, e: any) => { e.stopPropagation(); e.preventDefault(); if (!queryDisabled) { @@ -35,11 +34,11 @@ export function FunnelParameters() { } }; - const handleAddUrl = url => { + const handleAddUrl = (url: string) => { updateReport({ parameters: { urls: parameters.urls.concat(url) } }); }; - const handleRemoveUrl = (index, e) => { + const handleRemoveUrl = (index: number, e: any) => { e.stopPropagation(); const urls = [...parameters.urls]; urls.splice(index, 1); @@ -62,7 +61,7 @@ export function FunnelParameters() { }; return ( - + }> - + handleRemoveUrl(index, e)} + /> diff --git a/src/app/(main)/reports/funnel/FunnelReport.js b/src/app/(main)/reports/funnel/FunnelReport.tsx similarity index 100% rename from src/app/(main)/reports/funnel/FunnelReport.js rename to src/app/(main)/reports/funnel/FunnelReport.tsx diff --git a/src/app/(main)/reports/funnel/FunnelTable.js b/src/app/(main)/reports/funnel/FunnelTable.tsx similarity index 100% rename from src/app/(main)/reports/funnel/FunnelTable.js rename to src/app/(main)/reports/funnel/FunnelTable.tsx diff --git a/src/app/(main)/reports/funnel/UrlAddForm.js b/src/app/(main)/reports/funnel/UrlAddForm.tsx similarity index 86% rename from src/app/(main)/reports/funnel/UrlAddForm.js rename to src/app/(main)/reports/funnel/UrlAddForm.tsx index 3e77ce15..88c27ae9 100644 --- a/src/app/(main)/reports/funnel/UrlAddForm.js +++ b/src/app/(main)/reports/funnel/UrlAddForm.tsx @@ -3,7 +3,12 @@ import { useMessages } from 'components/hooks'; import { Button, Form, FormRow, TextField, Flexbox } from 'react-basics'; import styles from './UrlAddForm.module.css'; -export function UrlAddForm({ defaultValue = '', onAdd }) { +export interface UrlAddFormProps { + defaultValue?: string; + onAdd?: (url: string) => void; +} + +export function UrlAddForm({ defaultValue = '', onAdd }: UrlAddFormProps) { const [url, setUrl] = useState(defaultValue); const { formatMessage, labels } = useMessages(); diff --git a/src/app/(main)/reports/insights/InsightsParameters.js b/src/app/(main)/reports/insights/InsightsParameters.tsx similarity index 93% rename from src/app/(main)/reports/insights/InsightsParameters.js rename to src/app/(main)/reports/insights/InsightsParameters.tsx index 91dd09f8..cd643eed 100644 --- a/src/app/(main)/reports/insights/InsightsParameters.js +++ b/src/app/(main)/reports/insights/InsightsParameters.tsx @@ -1,4 +1,4 @@ -import { useContext, useRef } from 'react'; +import { useContext } from 'react'; import { useFormat, useMessages, useFilters } from 'components/hooks'; import { Form, @@ -24,7 +24,6 @@ export function InsightsParameters() { const { formatMessage, labels } = useMessages(); const { formatValue } = useFormat(); const { filterLabels } = useFilters(); - const ref = useRef(null); const { parameters } = report || {}; const { websiteId, dateRange, fields, filters } = parameters || {}; const { startDate, endDate } = dateRange || {}; @@ -72,7 +71,7 @@ export function InsightsParameters() { updateReport({ parameters: { [id]: data } }); }; - const AddButton = ({ id }) => { + const AddButton = ({ id, onAdd }) => { return ( @@ -84,8 +83,8 @@ export function InsightsParameters() { {id === 'fields' && ( )} @@ -93,7 +92,7 @@ export function InsightsParameters() { )} @@ -103,7 +102,7 @@ export function InsightsParameters() { }; return ( - + {parametersSelected && parameterGroups.map(({ id, label }) => { diff --git a/src/app/(main)/reports/insights/InsightsReport.js b/src/app/(main)/reports/insights/InsightsReport.tsx similarity index 90% rename from src/app/(main)/reports/insights/InsightsReport.js rename to src/app/(main)/reports/insights/InsightsReport.tsx index f99e187b..b90ff396 100644 --- a/src/app/(main)/reports/insights/InsightsReport.js +++ b/src/app/(main)/reports/insights/InsightsReport.tsx @@ -13,7 +13,7 @@ const defaultParameters = { parameters: { fields: [], filters: [] }, }; -export default function InsightsReport({ reportId }) { +export default function InsightsReport({ reportId }: { reportId: string }) { return ( } /> diff --git a/src/app/(main)/reports/insights/InsightsTable.js b/src/app/(main)/reports/insights/InsightsTable.tsx similarity index 96% rename from src/app/(main)/reports/insights/InsightsTable.js rename to src/app/(main)/reports/insights/InsightsTable.tsx index 4194fee8..a4517698 100644 --- a/src/app/(main)/reports/insights/InsightsTable.js +++ b/src/app/(main)/reports/insights/InsightsTable.tsx @@ -5,7 +5,7 @@ import { ReportContext } from '../[id]/Report'; import EmptyPlaceholder from 'components/common/EmptyPlaceholder'; export function InsightsTable() { - const [fields, setFields] = useState(); + const [fields, setFields] = useState([]); const { report } = useContext(ReportContext); const { formatMessage, labels } = useMessages(); const { formatValue } = useFormat(); diff --git a/src/app/(main)/reports/retention/RetentionParameters.js b/src/app/(main)/reports/retention/RetentionParameters.tsx similarity index 87% rename from src/app/(main)/reports/retention/RetentionParameters.js rename to src/app/(main)/reports/retention/RetentionParameters.tsx index 762a313d..fc168695 100644 --- a/src/app/(main)/reports/retention/RetentionParameters.js +++ b/src/app/(main)/reports/retention/RetentionParameters.tsx @@ -1,4 +1,4 @@ -import { useContext, useRef } from 'react'; +import { useContext } from 'react'; import { useMessages } from 'components/hooks'; import { Form, FormButtons, FormRow, SubmitButton } from 'react-basics'; import { ReportContext } from '../[id]/Report'; @@ -9,14 +9,13 @@ import { parseDateRange } from 'lib/date'; export function RetentionParameters() { const { report, runReport, isRunning, updateReport } = useContext(ReportContext); const { formatMessage, labels } = useMessages(); - const ref = useRef(null); const { parameters } = report || {}; const { websiteId, dateRange } = parameters || {}; const { startDate } = dateRange || {}; const queryDisabled = !websiteId || !dateRange; - const handleSubmit = (data, e) => { + const handleSubmit = (data: any, e: any) => { e.stopPropagation(); e.preventDefault(); @@ -30,7 +29,7 @@ export function RetentionParameters() { }; return ( - + diff --git a/src/app/(main)/reports/retention/RetentionReport.js b/src/app/(main)/reports/retention/RetentionReport.tsx similarity index 92% rename from src/app/(main)/reports/retention/RetentionReport.js rename to src/app/(main)/reports/retention/RetentionReport.tsx index ae42e76b..35f0fcb1 100644 --- a/src/app/(main)/reports/retention/RetentionReport.js +++ b/src/app/(main)/reports/retention/RetentionReport.tsx @@ -19,7 +19,7 @@ const defaultParameters = { }, }; -export default function RetentionReport({ reportId }) { +export default function RetentionReport({ reportId }: { reportId: string }) { return ( } /> diff --git a/src/app/(main)/reports/retention/RetentionTable.js b/src/app/(main)/reports/retention/RetentionTable.tsx similarity index 96% rename from src/app/(main)/reports/retention/RetentionTable.js rename to src/app/(main)/reports/retention/RetentionTable.tsx index a71fae6f..d2e7f129 100644 --- a/src/app/(main)/reports/retention/RetentionTable.js +++ b/src/app/(main)/reports/retention/RetentionTable.tsx @@ -18,7 +18,7 @@ export function RetentionTable({ days = DAYS }) { return ; } - const rows = data.reduce((arr, row) => { + const rows = data.reduce((arr: any[], row: { date: any; visitors: any; day: any }) => { const { date, visitors, day } = row; if (day === 0) { return arr.concat({ diff --git a/src/app/(main)/settings/profile/DateRangeSetting.js b/src/app/(main)/settings/profile/DateRangeSetting.tsx similarity index 100% rename from src/app/(main)/settings/profile/DateRangeSetting.js rename to src/app/(main)/settings/profile/DateRangeSetting.tsx diff --git a/src/app/(main)/settings/profile/LanguageSetting.js b/src/app/(main)/settings/profile/LanguageSetting.tsx similarity index 100% rename from src/app/(main)/settings/profile/LanguageSetting.js rename to src/app/(main)/settings/profile/LanguageSetting.tsx diff --git a/src/app/(main)/settings/profile/PasswordChangeButton.js b/src/app/(main)/settings/profile/PasswordChangeButton.tsx similarity index 100% rename from src/app/(main)/settings/profile/PasswordChangeButton.js rename to src/app/(main)/settings/profile/PasswordChangeButton.tsx diff --git a/src/app/(main)/settings/profile/PasswordEditForm.js b/src/app/(main)/settings/profile/PasswordEditForm.tsx similarity index 91% rename from src/app/(main)/settings/profile/PasswordEditForm.js rename to src/app/(main)/settings/profile/PasswordEditForm.tsx index 39ecfb77..7062ea59 100644 --- a/src/app/(main)/settings/profile/PasswordEditForm.js +++ b/src/app/(main)/settings/profile/PasswordEditForm.tsx @@ -6,10 +6,10 @@ import useMessages from 'components/hooks/useMessages'; export function PasswordEditForm({ onSave, onClose }) { const { formatMessage, labels, messages } = useMessages(); const { post, useMutation } = useApi(); - const { mutate, error, isLoading } = useMutation(data => post('/me/password', data)); + const { mutate, error, isLoading } = useMutation((data: any) => post('/me/password', data)); const ref = useRef(null); - const handleSubmit = async data => { + const handleSubmit = async (data: any) => { mutate(data, { onSuccess: async () => { onSave(); @@ -18,7 +18,7 @@ export function PasswordEditForm({ onSave, onClose }) { }); }; - const samePassword = value => { + const samePassword = (value: string) => { if (value !== ref?.current?.getValues('newPassword')) { return formatMessage(messages.noMatchPassword); } diff --git a/src/app/(main)/settings/profile/ProfileHeader.js b/src/app/(main)/settings/profile/ProfileHeader.tsx similarity index 100% rename from src/app/(main)/settings/profile/ProfileHeader.js rename to src/app/(main)/settings/profile/ProfileHeader.tsx diff --git a/src/app/(main)/settings/profile/ProfileSettings.js b/src/app/(main)/settings/profile/ProfileSettings.tsx similarity index 100% rename from src/app/(main)/settings/profile/ProfileSettings.js rename to src/app/(main)/settings/profile/ProfileSettings.tsx diff --git a/src/app/(main)/settings/profile/ThemeSetting.js b/src/app/(main)/settings/profile/ThemeSetting.tsx similarity index 100% rename from src/app/(main)/settings/profile/ThemeSetting.js rename to src/app/(main)/settings/profile/ThemeSetting.tsx diff --git a/src/app/(main)/settings/profile/TimezoneSetting.js b/src/app/(main)/settings/profile/TimezoneSetting.tsx similarity index 100% rename from src/app/(main)/settings/profile/TimezoneSetting.js rename to src/app/(main)/settings/profile/TimezoneSetting.tsx diff --git a/src/app/(main)/settings/teams/TeamAddForm.js b/src/app/(main)/settings/teams/TeamAddForm.tsx similarity index 72% rename from src/app/(main)/settings/teams/TeamAddForm.js rename to src/app/(main)/settings/teams/TeamAddForm.tsx index b8bb8c3a..5a242cb9 100644 --- a/src/app/(main)/settings/teams/TeamAddForm.js +++ b/src/app/(main)/settings/teams/TeamAddForm.tsx @@ -1,4 +1,3 @@ -import { useRef } from 'react'; import { Form, FormRow, @@ -12,11 +11,12 @@ import { setValue } from 'store/cache'; import useApi from 'components/hooks/useApi'; import useMessages from 'components/hooks/useMessages'; -export function TeamAddForm({ onSave, onClose }) { +export function TeamAddForm({ onSave, onClose }: { onSave: () => void; onClose: () => void }) { const { formatMessage, labels } = useMessages(); const { post, useMutation } = useApi(); - const { mutate, error, isLoading } = useMutation(data => post('/teams', data)); - const ref = useRef(null); + const { mutate, error, isPending } = useMutation({ + mutationFn: (data: any) => post('/teams', data), + }); const handleSubmit = async data => { mutate(data, { @@ -29,17 +29,17 @@ export function TeamAddForm({ onSave, onClose }) { }; return ( - + - + {formatMessage(labels.save)} - diff --git a/src/app/(main)/settings/teams/TeamDeleteButton.js b/src/app/(main)/settings/teams/TeamDeleteButton.tsx similarity index 79% rename from src/app/(main)/settings/teams/TeamDeleteButton.js rename to src/app/(main)/settings/teams/TeamDeleteButton.tsx index 5e4a41ea..36a1a1f8 100644 --- a/src/app/(main)/settings/teams/TeamDeleteButton.js +++ b/src/app/(main)/settings/teams/TeamDeleteButton.tsx @@ -2,7 +2,15 @@ import { Button, Icon, Icons, Modal, ModalTrigger, Text } from 'react-basics'; import useMessages from 'components/hooks/useMessages'; import TeamDeleteForm from './TeamDeleteForm'; -export function TeamDeleteButton({ teamId, teamName, onDelete }) { +export function TeamDeleteButton({ + teamId, + teamName, + onDelete, +}: { + teamId: string; + teamName: string; + onDelete?: () => void; +}) { const { formatMessage, labels } = useMessages(); return ( @@ -14,7 +22,7 @@ export function TeamDeleteButton({ teamId, teamName, onDelete }) { {formatMessage(labels.delete)} - {close => ( + {(close: any) => ( )} diff --git a/src/app/(main)/settings/teams/TeamDeleteForm.js b/src/app/(main)/settings/teams/TeamDeleteForm.tsx similarity index 72% rename from src/app/(main)/settings/teams/TeamDeleteForm.js rename to src/app/(main)/settings/teams/TeamDeleteForm.tsx index 9b80668a..00eedb17 100644 --- a/src/app/(main)/settings/teams/TeamDeleteForm.js +++ b/src/app/(main)/settings/teams/TeamDeleteForm.tsx @@ -3,10 +3,22 @@ import useApi from 'components/hooks/useApi'; import useMessages from 'components/hooks/useMessages'; import { setValue } from 'store/cache'; -export function TeamDeleteForm({ teamId, teamName, onSave, onClose }) { +export function TeamDeleteForm({ + teamId, + teamName, + onSave, + onClose, +}: { + teamId: string; + teamName: string; + onSave: () => void; + onClose: () => void; +}) { const { formatMessage, labels, messages, FormattedMessage } = useMessages(); const { del, useMutation } = useApi(); - const { mutate, error, isLoading } = useMutation(data => del(`/teams/${teamId}`, data)); + const { mutate, error, isPending } = useMutation({ + mutationFn: (data: any) => del(`/teams/${teamId}`, data), + }); const handleSubmit = async data => { mutate(data, { @@ -24,7 +36,7 @@ export function TeamDeleteForm({ teamId, teamName, onSave, onClose }) { {teamName} }} />

- + {formatMessage(labels.delete)} diff --git a/src/app/(main)/settings/teams/TeamJoinForm.js b/src/app/(main)/settings/teams/TeamJoinForm.tsx similarity index 85% rename from src/app/(main)/settings/teams/TeamJoinForm.js rename to src/app/(main)/settings/teams/TeamJoinForm.tsx index 528e1d75..5cd38225 100644 --- a/src/app/(main)/settings/teams/TeamJoinForm.js +++ b/src/app/(main)/settings/teams/TeamJoinForm.tsx @@ -12,10 +12,10 @@ import useApi from 'components/hooks/useApi'; import useMessages from 'components/hooks/useMessages'; import { setValue } from 'store/cache'; -export function TeamJoinForm({ onSave, onClose }) { +export function TeamJoinForm({ onSave, onClose }: { onSave: () => void; onClose: () => void }) { const { formatMessage, labels, getMessage } = useMessages(); const { post, useMutation } = useApi(); - const { mutate, error } = useMutation(data => post('/teams/join', data)); + const { mutate, error } = useMutation({ mutationFn: (data: any) => post('/teams/join', data) }); const ref = useRef(null); const handleSubmit = async data => { diff --git a/src/app/(main)/settings/teams/TeamLeaveButton.js b/src/app/(main)/settings/teams/TeamLeaveButton.tsx similarity index 87% rename from src/app/(main)/settings/teams/TeamLeaveButton.js rename to src/app/(main)/settings/teams/TeamLeaveButton.tsx index 7b98f082..97676e17 100644 --- a/src/app/(main)/settings/teams/TeamLeaveButton.js +++ b/src/app/(main)/settings/teams/TeamLeaveButton.tsx @@ -4,7 +4,15 @@ import useLocale from 'components/hooks/useLocale'; import useUser from 'components/hooks/useUser'; import TeamDeleteForm from './TeamLeaveForm'; -export function TeamLeaveButton({ teamId, teamName, onLeave }) { +export function TeamLeaveButton({ + teamId, + teamName, + onLeave, +}: { + teamId: string; + teamName: string; + onLeave?: () => void; +}) { const { formatMessage, labels } = useMessages(); const { dir } = useLocale(); const { user } = useUser(); diff --git a/src/app/(main)/settings/teams/TeamLeaveForm.js b/src/app/(main)/settings/teams/TeamLeaveForm.tsx similarity index 60% rename from src/app/(main)/settings/teams/TeamLeaveForm.js rename to src/app/(main)/settings/teams/TeamLeaveForm.tsx index a9b6922a..3b4d4703 100644 --- a/src/app/(main)/settings/teams/TeamLeaveForm.js +++ b/src/app/(main)/settings/teams/TeamLeaveForm.tsx @@ -3,22 +3,33 @@ import useApi from 'components/hooks/useApi'; import useMessages from 'components/hooks/useMessages'; import { setValue } from 'store/cache'; -export function TeamLeaveForm({ teamId, userId, teamName, onSave, onClose }) { +export function TeamLeaveForm({ + teamId, + userId, + teamName, + onSave, + onClose, +}: { + teamId: string; + userId: string; + teamName: string; + onSave: () => void; + onClose: () => void; +}) { const { formatMessage, labels, messages, FormattedMessage } = useMessages(); const { del, useMutation } = useApi(); - const { mutate, error, isLoading } = useMutation(() => del(`/teams/${teamId}/users/${userId}`)); + const { mutate, error, isPending } = useMutation({ + mutationFn: () => del(`/teams/${teamId}/users/${userId}`), + }); const handleSubmit = async () => { - mutate( - {}, - { - onSuccess: async () => { - setValue('team:members', Date.now()); - onSave(); - onClose(); - }, + mutate(null, { + onSuccess: async () => { + setValue('team:members', Date.now()); + onSave(); + onClose(); }, - ); + }); }; return ( @@ -27,7 +38,7 @@ export function TeamLeaveForm({ teamId, userId, teamName, onSave, onClose }) { {teamName} }} />

- + {formatMessage(labels.leave)} diff --git a/src/app/(main)/settings/teams/TeamsAddButton.js b/src/app/(main)/settings/teams/TeamsAddButton.tsx similarity index 79% rename from src/app/(main)/settings/teams/TeamsAddButton.js rename to src/app/(main)/settings/teams/TeamsAddButton.tsx index b7850812..09f9ecbb 100644 --- a/src/app/(main)/settings/teams/TeamsAddButton.js +++ b/src/app/(main)/settings/teams/TeamsAddButton.tsx @@ -3,7 +3,7 @@ import Icons from 'components/icons'; import useMessages from 'components/hooks/useMessages'; import TeamAddForm from './TeamAddForm'; -export function TeamsAddButton({ onAdd }) { +export function TeamsAddButton({ onAdd }: { onAdd?: () => void }) { const { formatMessage, labels } = useMessages(); return ( @@ -15,7 +15,7 @@ export function TeamsAddButton({ onAdd }) { {formatMessage(labels.createTeam)} - {close => } + {(close: () => void) => } ); diff --git a/src/app/(main)/settings/teams/TeamsDataTable.js b/src/app/(main)/settings/teams/TeamsDataTable.tsx similarity index 88% rename from src/app/(main)/settings/teams/TeamsDataTable.js rename to src/app/(main)/settings/teams/TeamsDataTable.tsx index a8c9e21d..2424e464 100644 --- a/src/app/(main)/settings/teams/TeamsDataTable.js +++ b/src/app/(main)/settings/teams/TeamsDataTable.tsx @@ -7,10 +7,10 @@ import useCache from 'store/cache'; export function TeamsDataTable() { const { get } = useApi(); - const modified = useCache(state => state?.teams); + const modified = useCache((state: any) => state?.teams); const queryResult = useFilterQuery({ queryKey: ['teams', { modified }], - queryFn: params => { + queryFn: (params: any) => { return get(`/teams`, { ...params, }); diff --git a/src/app/(main)/settings/teams/TeamsHeader.js b/src/app/(main)/settings/teams/TeamsHeader.tsx similarity index 100% rename from src/app/(main)/settings/teams/TeamsHeader.js rename to src/app/(main)/settings/teams/TeamsHeader.tsx diff --git a/src/app/(main)/settings/teams/TeamsJoinButton.js b/src/app/(main)/settings/teams/TeamsJoinButton.tsx similarity index 100% rename from src/app/(main)/settings/teams/TeamsJoinButton.js rename to src/app/(main)/settings/teams/TeamsJoinButton.tsx diff --git a/src/app/(main)/settings/teams/TeamsTable.js b/src/app/(main)/settings/teams/TeamsTable.tsx similarity index 96% rename from src/app/(main)/settings/teams/TeamsTable.js rename to src/app/(main)/settings/teams/TeamsTable.tsx index 1f7f1da4..70a7bebb 100644 --- a/src/app/(main)/settings/teams/TeamsTable.js +++ b/src/app/(main)/settings/teams/TeamsTable.tsx @@ -7,7 +7,7 @@ import { Button, GridColumn, GridTable, Icon, Icons, Text, useBreakpoint } from import TeamDeleteButton from './TeamDeleteButton'; import TeamLeaveButton from './TeamLeaveButton'; -export function TeamsTable({ data = [] }) { +export function TeamsTable({ data = [] }: { data: any[] }) { const { formatMessage, labels } = useMessages(); const { user } = useUser(); const breakpoint = useBreakpoint(); diff --git a/src/app/(main)/settings/teams/WebsiteTags.js b/src/app/(main)/settings/teams/WebsiteTags.tsx similarity index 83% rename from src/app/(main)/settings/teams/WebsiteTags.js rename to src/app/(main)/settings/teams/WebsiteTags.tsx index c17d5763..4a0f109d 100644 --- a/src/app/(main)/settings/teams/WebsiteTags.js +++ b/src/app/(main)/settings/teams/WebsiteTags.tsx @@ -1,7 +1,15 @@ import { Button, Icon, Icons, Text } from 'react-basics'; import styles from './WebsiteTags.module.css'; -export function WebsiteTags({ items = [], websites = [], onClick }) { +export function WebsiteTags({ + items = [], + websites = [], + onClick, +}: { + items: any[]; + websites: any[]; + onClick: (e: Event) => void; +}) { if (websites.length === 0) { return null; } diff --git a/src/app/(main)/settings/teams/[id]/TeamEditForm.js b/src/app/(main)/settings/teams/[id]/TeamEditForm.tsx similarity index 100% rename from src/app/(main)/settings/teams/[id]/TeamEditForm.js rename to src/app/(main)/settings/teams/[id]/TeamEditForm.tsx diff --git a/src/app/(main)/settings/teams/[id]/TeamMemberRemoveButton.js b/src/app/(main)/settings/teams/[id]/TeamMemberRemoveButton.tsx similarity index 59% rename from src/app/(main)/settings/teams/[id]/TeamMemberRemoveButton.js rename to src/app/(main)/settings/teams/[id]/TeamMemberRemoveButton.tsx index 603adae3..cef2977e 100644 --- a/src/app/(main)/settings/teams/[id]/TeamMemberRemoveButton.js +++ b/src/app/(main)/settings/teams/[id]/TeamMemberRemoveButton.tsx @@ -3,28 +3,37 @@ import useMessages from 'components/hooks/useMessages'; import { Icon, Icons, LoadingButton, Text } from 'react-basics'; import { setValue } from 'store/cache'; -export function TeamMemberRemoveButton({ teamId, userId, disabled, onSave }) { +export function TeamMemberRemoveButton({ + teamId, + userId, + disabled, + onSave, +}: { + teamId: string; + userId: string; + disabled?: boolean; + onSave?: () => void; +}) { const { formatMessage, labels } = useMessages(); const { del, useMutation } = useApi(); - const { mutate, isLoading } = useMutation(() => del(`/teams/${teamId}/users/${userId}`)); + const { mutate, isPending } = useMutation({ + mutationFn: () => del(`/teams/${teamId}/users/${userId}`), + }); const handleRemoveTeamMember = () => { - mutate( - {}, - { - onSuccess: () => { - setValue('team:members', Date.now()); - onSave?.(); - }, + mutate(null, { + onSuccess: () => { + setValue('team:members', Date.now()); + onSave?.(); }, - ); + }); }; return ( handleRemoveTeamMember()} disabled={disabled} - isLoading={isLoading} + isLoading={isPending} > diff --git a/src/app/(main)/settings/teams/[id]/TeamMembers.js b/src/app/(main)/settings/teams/[id]/TeamMembers.tsx similarity index 89% rename from src/app/(main)/settings/teams/[id]/TeamMembers.js rename to src/app/(main)/settings/teams/[id]/TeamMembers.tsx index 1b0c0d18..588a5a52 100644 --- a/src/app/(main)/settings/teams/[id]/TeamMembers.js +++ b/src/app/(main)/settings/teams/[id]/TeamMembers.tsx @@ -4,7 +4,7 @@ import useFilterQuery from 'components/hooks/useFilterQuery'; import DataTable from 'components/common/DataTable'; import useCache from 'store/cache'; -export function TeamMembers({ teamId, readOnly }) { +export function TeamMembers({ teamId, readOnly }: { teamId: string; readOnly: boolean }) { const { get } = useApi(); const modified = useCache(state => state?.['team:members']); const queryResult = useFilterQuery({ diff --git a/src/app/(main)/settings/teams/[id]/TeamMembersTable.js b/src/app/(main)/settings/teams/[id]/TeamMembersTable.tsx similarity index 90% rename from src/app/(main)/settings/teams/[id]/TeamMembersTable.js rename to src/app/(main)/settings/teams/[id]/TeamMembersTable.tsx index 9a402d44..a08c746b 100644 --- a/src/app/(main)/settings/teams/[id]/TeamMembersTable.js +++ b/src/app/(main)/settings/teams/[id]/TeamMembersTable.tsx @@ -4,7 +4,15 @@ import useUser from 'components/hooks/useUser'; import { ROLES } from 'lib/constants'; import TeamMemberRemoveButton from './TeamMemberRemoveButton'; -export function TeamMembersTable({ data = [], teamId, readOnly }) { +export function TeamMembersTable({ + data = [], + teamId, + readOnly, +}: { + data: any[]; + teamId: string; + readOnly: boolean; +}) { const { formatMessage, labels } = useMessages(); const { user } = useUser(); const breakpoint = useBreakpoint(); diff --git a/src/app/(main)/settings/teams/[id]/TeamSettings.js b/src/app/(main)/settings/teams/[id]/TeamSettings.tsx similarity index 91% rename from src/app/(main)/settings/teams/[id]/TeamSettings.js rename to src/app/(main)/settings/teams/[id]/TeamSettings.tsx index d7065f97..cf5ae35c 100644 --- a/src/app/(main)/settings/teams/[id]/TeamSettings.js +++ b/src/app/(main)/settings/teams/[id]/TeamSettings.tsx @@ -10,7 +10,7 @@ import TeamEditForm from './TeamEditForm'; import TeamMembers from './TeamMembers'; import TeamWebsites from './TeamWebsites'; -export function TeamSettings({ teamId }) { +export function TeamSettings({ teamId }: { teamId: string }) { const { formatMessage, labels, messages } = useMessages(); const { user } = useUser(); const [values, setValues] = useState(null); @@ -24,7 +24,7 @@ export function TeamSettings({ teamId }) { return get(`/teams/${teamId}`); } }, - cacheTime: 0, + gcTime: 0, }); const canEdit = data?.teamUser?.find( ({ userId, role }) => role === ROLES.teamOwner && userId === user.id, @@ -48,7 +48,7 @@ export function TeamSettings({ teamId }) { return ( - + setTab(value)} style={{ marginBottom: 30 }}> {formatMessage(labels.details)} {formatMessage(labels.members)} {formatMessage(labels.websites)} diff --git a/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js b/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.tsx similarity index 86% rename from src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js rename to src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.tsx index 004ae54f..64a0c58e 100644 --- a/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.js +++ b/src/app/(main)/settings/teams/[id]/TeamWebsiteAddForm.tsx @@ -7,11 +7,21 @@ import Empty from 'components/common/Empty'; import { setValue } from 'store/cache'; import { useUser } from 'components/hooks'; -export function TeamWebsiteAddForm({ teamId, onSave, onClose }) { +export function TeamWebsiteAddForm({ + teamId, + onSave, + onClose, +}: { + teamId: string; + onSave: () => void; + onClose: () => void; +}) { const { user } = useUser(); const { formatMessage, labels } = useMessages(); const { get, post, useQuery, useMutation } = useApi(); - const { mutate, error } = useMutation(data => post(`/teams/${teamId}/websites`, data)); + const { mutate, error } = useMutation({ + mutationFn: (data: any) => post(`/teams/${teamId}/websites`, data), + }); const { data: websites, isLoading } = useQuery({ queryKey: ['websites'], queryFn: () => get('/websites'), @@ -42,7 +52,7 @@ export function TeamWebsiteAddForm({ teamId, onSave, onClose }) { {!isLoading && !hasData && } {hasData && ( - + {row => ( { - queryResult.refetch(); + queryResult.query.refetch(); }; return ( @@ -43,7 +43,9 @@ export function TeamWebsites({ teamId }) { - {({ data }) => } + {({ data }) => ( + + )} ); diff --git a/src/app/(main)/settings/teams/[id]/TeamWebsitesTable.js b/src/app/(main)/settings/teams/[id]/TeamWebsitesTable.tsx similarity index 85% rename from src/app/(main)/settings/teams/[id]/TeamWebsitesTable.js rename to src/app/(main)/settings/teams/[id]/TeamWebsitesTable.tsx index 0f802212..29b95816 100644 --- a/src/app/(main)/settings/teams/[id]/TeamWebsitesTable.js +++ b/src/app/(main)/settings/teams/[id]/TeamWebsitesTable.tsx @@ -4,7 +4,15 @@ import useMessages from 'components/hooks/useMessages'; import useUser from 'components/hooks/useUser'; import TeamWebsiteRemoveButton from './TeamWebsiteRemoveButton'; -export function TeamWebsitesTable({ data = [], onRemove }) { +export function TeamWebsitesTable({ + data = [], + readOnly, + onRemove, +}: { + data: any[]; + readOnly: boolean; + onRemove: () => void; +}) { const { formatMessage, labels } = useMessages(); const { user } = useUser(); @@ -17,7 +25,7 @@ export function TeamWebsitesTable({ data = [], onRemove }) { const { id: teamId, teamUser } = row.teamWebsite[0].team; const { id: websiteId, userId } = row; const owner = teamUser[0]; - const canRemove = user.id === userId || user.id === owner.userId; + const canRemove = !readOnly && (user.id === userId || user.id === owner.userId); return ( <> {canRemove && ( diff --git a/src/app/(main)/settings/teams/[id]/page.js b/src/app/(main)/settings/teams/[id]/page.tsx similarity index 100% rename from src/app/(main)/settings/teams/[id]/page.js rename to src/app/(main)/settings/teams/[id]/page.tsx diff --git a/src/app/(main)/settings/users/UserAddButton.js b/src/app/(main)/settings/users/UserAddButton.tsx similarity index 92% rename from src/app/(main)/settings/users/UserAddButton.js rename to src/app/(main)/settings/users/UserAddButton.tsx index 0f4bf734..7f08107c 100644 --- a/src/app/(main)/settings/users/UserAddButton.js +++ b/src/app/(main)/settings/users/UserAddButton.tsx @@ -3,7 +3,7 @@ import UserAddForm from './UserAddForm'; import useMessages from 'components/hooks/useMessages'; import { setValue } from 'store/cache'; -export function UserAddButton({ onSave }) { +export function UserAddButton({ onSave }: { onSave?: () => void }) { const { formatMessage, labels, messages } = useMessages(); const { showToast } = useToasts(); diff --git a/src/app/(main)/settings/users/UserAddForm.js b/src/app/(main)/settings/users/UserAddForm.tsx similarity index 92% rename from src/app/(main)/settings/users/UserAddForm.js rename to src/app/(main)/settings/users/UserAddForm.tsx index 38c1bedd..11066a24 100644 --- a/src/app/(main)/settings/users/UserAddForm.js +++ b/src/app/(main)/settings/users/UserAddForm.tsx @@ -16,7 +16,9 @@ import useMessages from 'components/hooks/useMessages'; export function UserAddForm({ onSave, onClose }) { const { post, useMutation } = useApi(); - const { mutate, error, isLoading } = useMutation(data => post(`/users`, data)); + const { mutate, error, isPending } = useMutation({ + mutationFn: (data: any) => post(`/users`, data), + }); const { formatMessage, labels } = useMessages(); const handleSubmit = async data => { @@ -65,7 +67,7 @@ export function UserAddForm({ onSave, onClose }) { {formatMessage(labels.save)} - diff --git a/src/app/(main)/settings/users/UserDeleteButton.js b/src/app/(main)/settings/users/UserDeleteButton.tsx similarity index 85% rename from src/app/(main)/settings/users/UserDeleteButton.js rename to src/app/(main)/settings/users/UserDeleteButton.tsx index 22d93171..2b93c138 100644 --- a/src/app/(main)/settings/users/UserDeleteButton.js +++ b/src/app/(main)/settings/users/UserDeleteButton.tsx @@ -3,7 +3,15 @@ import useMessages from 'components/hooks/useMessages'; import useUser from 'components/hooks/useUser'; import UserDeleteForm from './UserDeleteForm'; -export function UserDeleteButton({ userId, username, onDelete }) { +export function UserDeleteButton({ + userId, + username, + onDelete, +}: { + userId: string; + username: string; + onDelete?: () => void; +}) { const { formatMessage, labels } = useMessages(); const { user } = useUser(); diff --git a/src/app/(main)/settings/users/UserDeleteForm.js b/src/app/(main)/settings/users/UserDeleteForm.tsx similarity index 72% rename from src/app/(main)/settings/users/UserDeleteForm.js rename to src/app/(main)/settings/users/UserDeleteForm.tsx index 5a47fdc1..eaa8e481 100644 --- a/src/app/(main)/settings/users/UserDeleteForm.js +++ b/src/app/(main)/settings/users/UserDeleteForm.tsx @@ -1,14 +1,13 @@ -import { useMutation } from '@tanstack/react-query'; import { Button, Form, FormButtons, SubmitButton } from 'react-basics'; import useApi from 'components/hooks/useApi'; import useMessages from 'components/hooks/useMessages'; export function UserDeleteForm({ userId, username, onSave, onClose }) { const { formatMessage, FormattedMessage, labels, messages } = useMessages(); - const { del } = useApi(); - const { mutate, error, isLoading } = useMutation(() => del(`/users/${userId}`)); + const { del, useMutation } = useApi(); + const { mutate, error, isPending } = useMutation({ mutationFn: () => del(`/users/${userId}`) }); - const handleSubmit = async data => { + const handleSubmit = async (data: any) => { mutate(data, { onSuccess: async () => { onSave(); @@ -23,10 +22,10 @@ export function UserDeleteForm({ userId, username, onSave, onClose }) { {username} }} />

- + {formatMessage(labels.delete)} - diff --git a/src/app/(main)/settings/users/UserEditForm.js b/src/app/(main)/settings/users/UserEditForm.tsx similarity index 82% rename from src/app/(main)/settings/users/UserEditForm.js rename to src/app/(main)/settings/users/UserEditForm.tsx index 157c9ad6..0b823a94 100644 --- a/src/app/(main)/settings/users/UserEditForm.js +++ b/src/app/(main)/settings/users/UserEditForm.tsx @@ -13,14 +13,30 @@ import useApi from 'components/hooks/useApi'; import { ROLES } from 'lib/constants'; import useMessages from 'components/hooks/useMessages'; -export function UserEditForm({ userId, data, onSave }) { +export function UserEditForm({ + userId, + data, + onSave, +}: { + userId: string; + data: any[]; + onSave: (data: any) => void; +}) { const { formatMessage, labels, messages } = useMessages(); const { post, useMutation } = useApi(); - const { mutate, error } = useMutation(({ username, password, role }) => - post(`/users/${userId}`, { username, password, role }), - ); + const { mutate, error } = useMutation({ + mutationFn: ({ + username, + password, + role, + }: { + username: string; + password: string; + role: string; + }) => post(`/users/${userId}`, { username, password, role }), + }); - const handleSubmit = async data => { + const handleSubmit = async (data: any) => { mutate(data, { onSuccess: async () => { onSave(data); diff --git a/src/app/(main)/settings/users/UserWebsites.js b/src/app/(main)/settings/users/UserWebsites.js deleted file mode 100644 index cd53c512..00000000 --- a/src/app/(main)/settings/users/UserWebsites.js +++ /dev/null @@ -1,36 +0,0 @@ -import Page from 'components/layout/Page'; -import useApi from 'components/hooks/useApi'; -import WebsitesTable from 'app/(main)/settings/websites/WebsitesTable'; -import useApiFilter from 'components/hooks/useApiFilter'; - -export function UserWebsites({ userId }) { - const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } = - useApiFilter(); - const { get, useQuery } = useApi(); - const { data, isLoading, error } = useQuery({ - queryKey: ['user:websites', userId, filter, page, pageSize], - queryFn: () => - get(`/users/${userId}/websites`, { - filter, - page, - pageSize, - }), - }); - const hasData = data && data.length !== 0; - - return ( - - {hasData && ( - - )} - - ); -} - -export default UserWebsites; diff --git a/src/app/(main)/settings/users/UserWebsites.tsx b/src/app/(main)/settings/users/UserWebsites.tsx new file mode 100644 index 00000000..2d06e82a --- /dev/null +++ b/src/app/(main)/settings/users/UserWebsites.tsx @@ -0,0 +1,26 @@ +import Page from 'components/layout/Page'; +import useApi from 'components/hooks/useApi'; +import WebsitesTable from 'app/(main)/settings/websites/WebsitesTable'; +import useFilterQuery from 'components/hooks/useFilterQuery'; +import DataTable from 'components/common/DataTable'; + +export function UserWebsites({ userId }) { + const { get } = useApi(); + const queryResult = useFilterQuery({ + queryKey: ['user:websites', userId], + queryFn: (params: any) => get(`/users/${userId}/websites`, params), + }); + const hasData = queryResult.result && queryResult.result.data.length !== 0; + + return ( + + {hasData && ( + + {({ data }) => } + + )} + + ); +} + +export default UserWebsites; diff --git a/src/app/(main)/settings/users/UsersDataTable.js b/src/app/(main)/settings/users/UsersDataTable.tsx similarity index 82% rename from src/app/(main)/settings/users/UsersDataTable.js rename to src/app/(main)/settings/users/UsersDataTable.tsx index 91125309..b7716451 100644 --- a/src/app/(main)/settings/users/UsersDataTable.js +++ b/src/app/(main)/settings/users/UsersDataTable.tsx @@ -8,10 +8,10 @@ import useCache from 'store/cache'; export function UsersDataTable() { const { get } = useApi(); - const modified = useCache(state => state?.users); + const modified = useCache((state: any) => state?.users); const queryResult = useFilterQuery({ queryKey: ['users', { modified }], - queryFn: params => get(`/users`, params), + queryFn: (params: { [key: string]: any }) => get(`/users`, params), }); return ( diff --git a/src/app/(main)/settings/users/UsersHeader.js b/src/app/(main)/settings/users/UsersHeader.tsx similarity index 85% rename from src/app/(main)/settings/users/UsersHeader.js rename to src/app/(main)/settings/users/UsersHeader.tsx index caf1f913..0901a6fb 100644 --- a/src/app/(main)/settings/users/UsersHeader.js +++ b/src/app/(main)/settings/users/UsersHeader.tsx @@ -3,7 +3,7 @@ import PageHeader from 'components/layout/PageHeader'; import useMessages from 'components/hooks/useMessages'; import UserAddButton from './UserAddButton'; -export function UsersHeader({ onAdd }) { +export function UsersHeader({ onAdd }: { onAdd?: () => void }) { const { formatMessage, labels } = useMessages(); return ( diff --git a/src/app/(main)/settings/users/UsersTable.js b/src/app/(main)/settings/users/UsersTable.tsx similarity index 96% rename from src/app/(main)/settings/users/UsersTable.js rename to src/app/(main)/settings/users/UsersTable.tsx index a0b5aba1..2b840b64 100644 --- a/src/app/(main)/settings/users/UsersTable.js +++ b/src/app/(main)/settings/users/UsersTable.tsx @@ -6,7 +6,7 @@ import useMessages from 'components/hooks/useMessages'; import useLocale from 'components/hooks/useLocale'; import UserDeleteButton from './UserDeleteButton'; -export function UsersTable({ data = [] }) { +export function UsersTable({ data = [] }: { data: any[] }) { const { formatMessage, labels } = useMessages(); const { dateLocale } = useLocale(); const breakpoint = useBreakpoint(); diff --git a/src/app/(main)/settings/users/[id]/UserSettings.js b/src/app/(main)/settings/users/[id]/UserSettings.js index f635bb05..3d8a92ea 100644 --- a/src/app/(main)/settings/users/[id]/UserSettings.js +++ b/src/app/(main)/settings/users/[id]/UserSettings.js @@ -21,7 +21,7 @@ export function UserSettings({ userId }) { return get(`/users/${userId}`); } }, - cacheTime: 0, + gcTime: 0, }); const handleSave = data => { diff --git a/src/app/(main)/settings/users/[id]/page.js b/src/app/(main)/settings/users/[id]/page.tsx similarity index 100% rename from src/app/(main)/settings/users/[id]/page.js rename to src/app/(main)/settings/users/[id]/page.tsx diff --git a/src/app/(main)/settings/websites/WebsiteAddButton.js b/src/app/(main)/settings/websites/WebsiteAddButton.tsx similarity index 92% rename from src/app/(main)/settings/websites/WebsiteAddButton.js rename to src/app/(main)/settings/websites/WebsiteAddButton.tsx index b1a69429..16681b0e 100644 --- a/src/app/(main)/settings/websites/WebsiteAddButton.js +++ b/src/app/(main)/settings/websites/WebsiteAddButton.tsx @@ -3,7 +3,7 @@ import WebsiteAddForm from './WebsiteAddForm'; import useMessages from 'components/hooks/useMessages'; import { setValue } from 'store/cache'; -export function WebsiteAddButton({ onSave }) { +export function WebsiteAddButton({ onSave }: { onSave?: () => void }) { const { formatMessage, labels, messages } = useMessages(); const { showToast } = useToasts(); diff --git a/src/app/(main)/settings/websites/WebsiteAddForm.tsx b/src/app/(main)/settings/websites/WebsiteAddForm.tsx index 99624103..9f3ba178 100644 --- a/src/app/(main)/settings/websites/WebsiteAddForm.tsx +++ b/src/app/(main)/settings/websites/WebsiteAddForm.tsx @@ -14,7 +14,9 @@ import useMessages from 'components/hooks/useMessages'; export function WebsiteAddForm({ onSave, onClose }: { onSave?: () => void; onClose?: () => void }) { const { formatMessage, labels, messages } = useMessages(); const { post, useMutation } = useApi(); - const { mutate, error, isLoading } = useMutation(data => post('/websites', data)); + const { mutate, error, isPending } = useMutation({ + mutationFn: (data: any) => post('/websites', data), + }); const handleSubmit = async (data: any) => { mutate(data, { @@ -26,7 +28,7 @@ export function WebsiteAddForm({ onSave, onClose }: { onSave?: () => void; onClo }; return ( - + @@ -48,7 +50,7 @@ export function WebsiteAddForm({ onSave, onClose }: { onSave?: () => void; onClo {formatMessage(labels.save)}
{onClose && ( - )} diff --git a/src/app/(main)/settings/websites/WebsiteSettings.js b/src/app/(main)/settings/websites/WebsiteSettings.js index 79ba08fc..ffda838c 100644 --- a/src/app/(main)/settings/websites/WebsiteSettings.js +++ b/src/app/(main)/settings/websites/WebsiteSettings.js @@ -20,7 +20,7 @@ export function WebsiteSettings({ websiteId, openExternal = false, analyticsUrl queryKey: ['website', websiteId], queryFn: () => get(`/websites/${websiteId}`), enabled: !!websiteId, - cacheTime: 0, + gcTime: 0, }); const [values, setValues] = useState(null); const [tab, setTab] = useState('details'); diff --git a/src/app/(main)/settings/websites/WebsitesHeader.js b/src/app/(main)/settings/websites/WebsitesHeader.tsx similarity index 100% rename from src/app/(main)/settings/websites/WebsitesHeader.js rename to src/app/(main)/settings/websites/WebsitesHeader.tsx diff --git a/src/app/(main)/settings/websites/WebsitesTable.js b/src/app/(main)/settings/websites/WebsitesTable.tsx similarity index 90% rename from src/app/(main)/settings/websites/WebsitesTable.js rename to src/app/(main)/settings/websites/WebsitesTable.tsx index eef3f7d4..02434448 100644 --- a/src/app/(main)/settings/websites/WebsitesTable.js +++ b/src/app/(main)/settings/websites/WebsitesTable.tsx @@ -1,8 +1,18 @@ +import { ReactNode } from 'react'; import Link from 'next/link'; import { Button, Text, Icon, Icons, GridTable, GridColumn, useBreakpoint } from 'react-basics'; import useMessages from 'components/hooks/useMessages'; import useUser from 'components/hooks/useUser'; +export interface WebsitesTableProps { + data: any[]; + showTeam?: boolean; + showActions?: boolean; + allowEdit?: boolean; + allowView?: boolean; + children?: ReactNode; +} + export function WebsitesTable({ data = [], showTeam, @@ -10,7 +20,7 @@ export function WebsitesTable({ allowEdit, allowView, children, -}) { +}: WebsitesTableProps) { const { formatMessage, labels } = useMessages(); const { user } = useUser(); const breakpoint = useBreakpoint(); diff --git a/src/app/(main)/settings/websites/[id]/ShareUrl.js b/src/app/(main)/settings/websites/[id]/ShareUrl.tsx similarity index 91% rename from src/app/(main)/settings/websites/[id]/ShareUrl.js rename to src/app/(main)/settings/websites/[id]/ShareUrl.tsx index 72ba217c..19149035 100644 --- a/src/app/(main)/settings/websites/[id]/ShareUrl.js +++ b/src/app/(main)/settings/websites/[id]/ShareUrl.tsx @@ -20,9 +20,9 @@ export function ShareUrl({ websiteId, data, analyticsUrl, onSave }) { const { name, shareId } = data; const [id, setId] = useState(shareId); const { post, useMutation } = useApi(); - const { mutate, error } = useMutation(({ shareId }) => - post(`/websites/${websiteId}`, { shareId }), - ); + const { mutate, error } = useMutation({ + mutationFn: (data: any) => post(`/websites/${websiteId}`, data), + }); const ref = useRef(null); const url = useMemo( () => @@ -32,7 +32,7 @@ export function ShareUrl({ websiteId, data, analyticsUrl, onSave }) { [id, name], ); - const handleSubmit = async data => { + const handleSubmit = async (data: any) => { mutate(data, { onSuccess: async () => { onSave(data); @@ -50,7 +50,7 @@ export function ShareUrl({ websiteId, data, analyticsUrl, onSave }) { setId(id); }; - const handleCheck = checked => { + const handleCheck = (checked: boolean) => { const data = { shareId: checked ? generateId() : null }; mutate(data, { onSuccess: async () => { diff --git a/src/app/(main)/settings/websites/[id]/TrackingCode.js b/src/app/(main)/settings/websites/[id]/TrackingCode.tsx similarity index 87% rename from src/app/(main)/settings/websites/[id]/TrackingCode.js rename to src/app/(main)/settings/websites/[id]/TrackingCode.tsx index 368368d7..b6bbdf89 100644 --- a/src/app/(main)/settings/websites/[id]/TrackingCode.js +++ b/src/app/(main)/settings/websites/[id]/TrackingCode.tsx @@ -2,7 +2,13 @@ import { TextArea } from 'react-basics'; import useMessages from 'components/hooks/useMessages'; import useConfig from 'components/hooks/useConfig'; -export function TrackingCode({ websiteId, analyticsUrl }) { +export function TrackingCode({ + websiteId, + analyticsUrl, +}: { + websiteId: string; + analyticsUrl: string; +}) { const { formatMessage, messages } = useMessages(); const config = useConfig(); diff --git a/src/app/(main)/settings/websites/[id]/WebsiteData.js b/src/app/(main)/settings/websites/[id]/WebsiteData.tsx similarity index 92% rename from src/app/(main)/settings/websites/[id]/WebsiteData.js rename to src/app/(main)/settings/websites/[id]/WebsiteData.tsx index 07dc9257..b4bfe609 100644 --- a/src/app/(main)/settings/websites/[id]/WebsiteData.js +++ b/src/app/(main)/settings/websites/[id]/WebsiteData.tsx @@ -3,7 +3,13 @@ import WebsiteDeleteForm from './WebsiteDeleteForm'; import WebsiteResetForm from './WebsiteResetForm'; import useMessages from 'components/hooks/useMessages'; -export function WebsiteData({ websiteId, onSave }) { +export function WebsiteData({ + websiteId, + onSave, +}: { + websiteId: string; + onSave?: (value: string) => void; +}) { const { formatMessage, labels, messages } = useMessages(); const handleReset = async () => { diff --git a/src/app/(main)/settings/websites/[id]/WebsiteDeleteForm.js b/src/app/(main)/settings/websites/[id]/WebsiteDeleteForm.tsx similarity index 82% rename from src/app/(main)/settings/websites/[id]/WebsiteDeleteForm.js rename to src/app/(main)/settings/websites/[id]/WebsiteDeleteForm.tsx index 1548bddb..c3b5d74a 100644 --- a/src/app/(main)/settings/websites/[id]/WebsiteDeleteForm.js +++ b/src/app/(main)/settings/websites/[id]/WebsiteDeleteForm.tsx @@ -12,10 +12,20 @@ import useMessages from 'components/hooks/useMessages'; const CONFIRM_VALUE = 'DELETE'; -export function WebsiteDeleteForm({ websiteId, onSave, onClose }) { +export function WebsiteDeleteForm({ + websiteId, + onSave, + onClose, +}: { + websiteId: string; + onSave?: () => void; + onClose?: () => void; +}) { const { formatMessage, labels, messages, FormattedMessage } = useMessages(); const { del, useMutation } = useApi(); - const { mutate, error } = useMutation(data => del(`/websites/${websiteId}`, data)); + const { mutate, error } = useMutation({ + mutationFn: (data: any) => del(`/websites/${websiteId}`, data), + }); const handleSubmit = async data => { mutate(data, { diff --git a/src/app/(main)/settings/websites/[id]/WebsiteEditForm.js b/src/app/(main)/settings/websites/[id]/WebsiteEditForm.tsx similarity index 85% rename from src/app/(main)/settings/websites/[id]/WebsiteEditForm.js rename to src/app/(main)/settings/websites/[id]/WebsiteEditForm.tsx index 18ad0ac9..9c05905c 100644 --- a/src/app/(main)/settings/websites/[id]/WebsiteEditForm.js +++ b/src/app/(main)/settings/websites/[id]/WebsiteEditForm.tsx @@ -4,10 +4,20 @@ import useApi from 'components/hooks/useApi'; import { DOMAIN_REGEX } from 'lib/constants'; import useMessages from 'components/hooks/useMessages'; -export function WebsiteEditForm({ websiteId, data, onSave }) { +export function WebsiteEditForm({ + websiteId, + data, + onSave, +}: { + websiteId: string; + data: any[]; + onSave?: (data: any) => void; +}) { const { formatMessage, labels, messages } = useMessages(); const { post, useMutation } = useApi(); - const { mutate, error } = useMutation(data => post(`/websites/${websiteId}`, data)); + const { mutate, error } = useMutation({ + mutationFn: (data: any) => post(`/websites/${websiteId}`, data), + }); const ref = useRef(null); const handleSubmit = async data => { diff --git a/src/app/(main)/settings/websites/[id]/WebsiteResetForm.js b/src/app/(main)/settings/websites/[id]/WebsiteResetForm.tsx similarity index 78% rename from src/app/(main)/settings/websites/[id]/WebsiteResetForm.js rename to src/app/(main)/settings/websites/[id]/WebsiteResetForm.tsx index 9886429b..76c2bc47 100644 --- a/src/app/(main)/settings/websites/[id]/WebsiteResetForm.js +++ b/src/app/(main)/settings/websites/[id]/WebsiteResetForm.tsx @@ -12,12 +12,22 @@ import useMessages from 'components/hooks/useMessages'; const CONFIRM_VALUE = 'RESET'; -export function WebsiteResetForm({ websiteId, onSave, onClose }) { +export function WebsiteResetForm({ + websiteId, + onSave, + onClose, +}: { + websiteId: string; + onSave?: () => void; + onClose?: () => void; +}) { const { formatMessage, labels, messages, FormattedMessage } = useMessages(); const { post, useMutation } = useApi(); - const { mutate, error } = useMutation(data => post(`/websites/${websiteId}/reset`, data)); + const { mutate, error } = useMutation({ + mutationFn: (data: any) => post(`/websites/${websiteId}/reset`, data), + }); - const handleSubmit = async data => { + const handleSubmit = async (data: any) => { mutate(data, { onSuccess: async () => { onSave(); diff --git a/src/app/(main)/settings/websites/[id]/page.js b/src/app/(main)/settings/websites/[id]/page.tsx similarity index 100% rename from src/app/(main)/settings/websites/[id]/page.js rename to src/app/(main)/settings/websites/[id]/page.tsx diff --git a/src/app/(main)/websites/WebsitesBrowse.js b/src/app/(main)/websites/WebsitesBrowse.tsx similarity index 91% rename from src/app/(main)/websites/WebsitesBrowse.js rename to src/app/(main)/websites/WebsitesBrowse.tsx index 3e8df2b2..c426cc06 100644 --- a/src/app/(main)/websites/WebsitesBrowse.js +++ b/src/app/(main)/websites/WebsitesBrowse.tsx @@ -17,7 +17,7 @@ export function WebsitesBrowse() { return ( <> - + setTab(tab)} style={{ marginBottom: 30 }}> {formatMessage(labels.myWebsites)} {formatMessage(labels.teamWebsites)} diff --git a/src/app/(main)/websites/[id]/WebsiteChart.js b/src/app/(main)/websites/[id]/WebsiteChart.tsx similarity index 90% rename from src/app/(main)/websites/[id]/WebsiteChart.js rename to src/app/(main)/websites/[id]/WebsiteChart.tsx index 15a7525f..eba155c1 100644 --- a/src/app/(main)/websites/[id]/WebsiteChart.js +++ b/src/app/(main)/websites/[id]/WebsiteChart.tsx @@ -3,7 +3,7 @@ import PageviewsChart from 'components/metrics/PageviewsChart'; import { useApi, useDateRange, useTimezone, useNavigation } from 'components/hooks'; import { getDateArray } from 'lib/date'; -export function WebsiteChart({ websiteId }) { +export function WebsiteChart({ websiteId }: { websiteId: string }) { const [dateRange] = useDateRange(websiteId); const { startDate, endDate, unit, modified } = dateRange; const [timezone] = useTimezone(); @@ -45,7 +45,7 @@ export function WebsiteChart({ websiteId }) { return { pageviews: [], sessions: [] }; }, [data, startDate, endDate, unit]); - return ; + return ; } export default WebsiteChart; diff --git a/src/app/(main)/websites/[id]/WebsiteChartList.js b/src/app/(main)/websites/[id]/WebsiteChartList.tsx similarity index 91% rename from src/app/(main)/websites/[id]/WebsiteChartList.js rename to src/app/(main)/websites/[id]/WebsiteChartList.tsx index bc2439de..b35b6f1f 100644 --- a/src/app/(main)/websites/[id]/WebsiteChartList.js +++ b/src/app/(main)/websites/[id]/WebsiteChartList.tsx @@ -8,7 +8,15 @@ import WebsiteHeader from './WebsiteHeader'; import { WebsiteMetricsBar } from './WebsiteMetricsBar'; import { useMessages, useLocale } from 'components/hooks'; -export default function WebsiteChartList({ websites, showCharts, limit }) { +export default function WebsiteChartList({ + websites, + showCharts, + limit, +}: { + websites: any[]; + showCharts?: boolean; + limit?: number; +}) { const { formatMessage, labels } = useMessages(); const { websiteOrder } = useDashboard(); const { dir } = useLocale(); diff --git a/src/app/(main)/websites/[id]/WebsiteDetails.js b/src/app/(main)/websites/[id]/WebsiteDetails.tsx similarity index 87% rename from src/app/(main)/websites/[id]/WebsiteDetails.js rename to src/app/(main)/websites/[id]/WebsiteDetails.tsx index c6ad1acc..4d3a18e7 100644 --- a/src/app/(main)/websites/[id]/WebsiteDetails.js +++ b/src/app/(main)/websites/[id]/WebsiteDetails.tsx @@ -11,7 +11,7 @@ import WebsiteHeader from './WebsiteHeader'; import WebsiteMetricsBar from './WebsiteMetricsBar'; import WebsiteTableView from './WebsiteTableView'; -export default function WebsiteDetails({ websiteId }) { +export default function WebsiteDetails({ websiteId }: { websiteId: string }) { const { data: website, isLoading, error } = useWebsite(websiteId); const pathname = usePathname(); const showLinks = !pathname.includes('/share/'); @@ -27,10 +27,7 @@ export default function WebsiteDetails({ websiteId }) { return ( <> - + {!website && } diff --git a/src/app/(main)/websites/[id]/WebsiteFilterButton.js b/src/app/(main)/websites/[id]/WebsiteFilterButton.tsx similarity index 91% rename from src/app/(main)/websites/[id]/WebsiteFilterButton.js rename to src/app/(main)/websites/[id]/WebsiteFilterButton.tsx index e96856f6..6a02cd47 100644 --- a/src/app/(main)/websites/[id]/WebsiteFilterButton.js +++ b/src/app/(main)/websites/[id]/WebsiteFilterButton.tsx @@ -3,7 +3,13 @@ import PopupForm from 'app/(main)/reports/[id]/PopupForm'; import FilterSelectForm from 'app/(main)/reports/[id]/FilterSelectForm'; import { useMessages, useNavigation } from 'components/hooks'; -export function WebsiteFilterButton({ websiteId, className }) { +export function WebsiteFilterButton({ + websiteId, + className, +}: { + websiteId: string; + className?: string; +}) { const { formatMessage, labels } = useMessages(); const { makeUrl, router } = useNavigation(); @@ -31,9 +37,9 @@ export function WebsiteFilterButton({ websiteId, className }) { {formatMessage(labels.filter)} - {close => { + {(close: () => void) => { return ( - + `${n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`} + format={n => `${+n < 0 ? '-' : ''}${formatShortTime(Math.abs(~~n), ['m', 's'], ' ')}`} /> )} diff --git a/src/app/(main)/websites/[id]/WebsiteTableView.js b/src/app/(main)/websites/[id]/WebsiteTableView.tsx similarity index 94% rename from src/app/(main)/websites/[id]/WebsiteTableView.js rename to src/app/(main)/websites/[id]/WebsiteTableView.tsx index 28a8fad6..e530f2ba 100644 --- a/src/app/(main)/websites/[id]/WebsiteTableView.js +++ b/src/app/(main)/websites/[id]/WebsiteTableView.tsx @@ -10,7 +10,7 @@ import CountriesTable from 'components/metrics/CountriesTable'; import EventsTable from 'components/metrics/EventsTable'; import EventsChart from 'components/metrics/EventsChart'; -export default function WebsiteTableView({ websiteId }) { +export default function WebsiteTableView({ websiteId }: { websiteId: string }) { const [countryData, setCountryData] = useState(); const tableProps = { websiteId, diff --git a/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.js b/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.tsx similarity index 94% rename from src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.js rename to src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.tsx index b02e166c..419472d5 100644 --- a/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.js +++ b/src/app/(main)/websites/[id]/event-data/EventDataMetricsBar.tsx @@ -5,7 +5,7 @@ import WebsiteDateFilter from 'components/input/WebsiteDateFilter'; import MetricsBar from 'components/metrics/MetricsBar'; import styles from './EventDataMetricsBar.module.css'; -export function EventDataMetricsBar({ websiteId }) { +export function EventDataMetricsBar({ websiteId }: { websiteId: string }) { const { formatMessage, labels } = useMessages(); const { get, useQuery } = useApi(); const [dateRange] = useDateRange(websiteId); diff --git a/src/app/(main)/websites/[id]/event-data/EventDataTable.js b/src/app/(main)/websites/[id]/event-data/EventDataTable.tsx similarity index 100% rename from src/app/(main)/websites/[id]/event-data/EventDataTable.js rename to src/app/(main)/websites/[id]/event-data/EventDataTable.tsx diff --git a/src/app/(main)/websites/[id]/event-data/EventDataValueTable.js b/src/app/(main)/websites/[id]/event-data/EventDataValueTable.tsx similarity index 94% rename from src/app/(main)/websites/[id]/event-data/EventDataValueTable.js rename to src/app/(main)/websites/[id]/event-data/EventDataValueTable.tsx index 4e50f5b9..7976ce36 100644 --- a/src/app/(main)/websites/[id]/event-data/EventDataValueTable.js +++ b/src/app/(main)/websites/[id]/event-data/EventDataValueTable.tsx @@ -6,7 +6,7 @@ import PageHeader from 'components/layout/PageHeader'; import Empty from 'components/common/Empty'; import { DATA_TYPES } from 'lib/constants'; -export function EventDataValueTable({ data = [], event }) { +export function EventDataValueTable({ data = [], event }: { data: any[]; event: string }) { const { formatMessage, labels } = useMessages(); const { makeUrl } = useNavigation(); diff --git a/src/app/(main)/websites/[id]/event-data/page.js b/src/app/(main)/websites/[id]/event-data/page.tsx similarity index 100% rename from src/app/(main)/websites/[id]/event-data/page.js rename to src/app/(main)/websites/[id]/event-data/page.tsx diff --git a/src/app/login/LoginForm.js b/src/app/login/LoginForm.tsx similarity index 90% rename from src/app/login/LoginForm.js rename to src/app/login/LoginForm.tsx index 59d145bf..78cf3dd3 100644 --- a/src/app/login/LoginForm.js +++ b/src/app/login/LoginForm.tsx @@ -1,5 +1,4 @@ 'use client'; -import { useMutation } from '@tanstack/react-query'; import { Form, FormRow, @@ -21,8 +20,10 @@ import styles from './LoginForm.module.css'; export function LoginForm() { const { formatMessage, labels, getMessage } = useMessages(); const router = useRouter(); - const { post } = useApi(); - const { mutate, error, isLoading } = useMutation(data => post('/auth/login', data)); + const { post, useMutation } = useApi(); + const { mutate, error, isPending } = useMutation({ + mutationFn: (data: any) => post('/auth/login', data), + }); const handleSubmit = async data => { mutate(data, { @@ -53,7 +54,7 @@ export function LoginForm() {
- + {formatMessage(labels.login)} diff --git a/src/app/logout/Logout.js b/src/app/logout/Logout.tsx similarity index 100% rename from src/app/logout/Logout.js rename to src/app/logout/Logout.tsx diff --git a/src/app/share/[...id]/Footer.js b/src/app/share/[...id]/Footer.tsx similarity index 100% rename from src/app/share/[...id]/Footer.js rename to src/app/share/[...id]/Footer.tsx diff --git a/src/app/share/[...id]/Header.js b/src/app/share/[...id]/Header.tsx similarity index 100% rename from src/app/share/[...id]/Header.js rename to src/app/share/[...id]/Header.tsx diff --git a/src/app/share/[...id]/Share.js b/src/app/share/[...id]/Share.tsx similarity index 100% rename from src/app/share/[...id]/Share.js rename to src/app/share/[...id]/Share.tsx diff --git a/src/components/common/DataTable.tsx b/src/components/common/DataTable.tsx index 621a44fe..00aba09c 100644 --- a/src/components/common/DataTable.tsx +++ b/src/components/common/DataTable.tsx @@ -36,11 +36,11 @@ export function DataTable({ const hasData = Boolean(!isLoading && data?.length); const noResults = Boolean(!isLoading && query && !hasData); - const handleSearch = query => { + const handleSearch = (query: string) => { setParams({ ...params, query, page: params.page ? page : 1 }); }; - const handlePageChange = page => { + const handlePageChange = (page: number) => { setParams({ ...params, query, page }); }; @@ -54,7 +54,7 @@ export function DataTable({ diff --git a/src/components/common/FilterButtons.tsx b/src/components/common/FilterButtons.tsx index e1860c78..a64a6482 100644 --- a/src/components/common/FilterButtons.tsx +++ b/src/components/common/FilterButtons.tsx @@ -4,7 +4,7 @@ import { ButtonGroup, Button, Flexbox } from 'react-basics'; export interface FilterButtonsProps { items: any[]; selectedKey?: Key; - onSelect: () => void; + onSelect: (key: any) => void; } export function FilterButtons({ items, selectedKey, onSelect }: FilterButtonsProps) { diff --git a/src/components/declarations.d.ts b/src/components/declarations.d.ts index 81533301..ca55157b 100644 --- a/src/components/declarations.d.ts +++ b/src/components/declarations.d.ts @@ -1,3 +1,4 @@ declare module '*.css'; declare module '*.svg'; declare module '*.json'; +declare module 'uuid'; diff --git a/src/components/hooks/useApiFilter.ts b/src/components/hooks/useApiFilter.ts deleted file mode 100644 index d411fd43..00000000 --- a/src/components/hooks/useApiFilter.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { useState } from 'react'; - -export function useApiFilter() { - const [filter, setFilter] = useState(); - const [filterType, setFilterType] = useState('All'); - const [page, setPage] = useState(1); - const [pageSize, setPageSize] = useState(10); - - const handleFilterChange = value => setFilter(value); - const handlePageChange = value => setPage(value); - const handlePageSizeChange = value => setPageSize(value); - - return { - filter, - setFilter, - filterType, - setFilterType, - page, - setPage, - pageSize, - setPageSize, - handleFilterChange, - handlePageChange, - handlePageSizeChange, - }; -} - -export default useApiFilter; diff --git a/src/components/hooks/useDateRange.ts b/src/components/hooks/useDateRange.ts index 6e70a368..71361b69 100644 --- a/src/components/hooks/useDateRange.ts +++ b/src/components/hooks/useDateRange.ts @@ -6,7 +6,7 @@ import websiteStore, { setWebsiteDateRange } from 'store/websites'; import appStore, { setDateRange } from 'store/app'; import useApi from './useApi'; -export function useDateRange(websiteId: string) { +export function useDateRange(websiteId?: string) { const { get } = useApi(); const { locale } = useLocale(); const websiteConfig = websiteStore(state => state[websiteId]?.dateRange); diff --git a/src/components/hooks/useFilterQuery.ts b/src/components/hooks/useFilterQuery.ts index 2c520741..6dc3abe7 100644 --- a/src/components/hooks/useFilterQuery.ts +++ b/src/components/hooks/useFilterQuery.ts @@ -1,24 +1,33 @@ +import { UseQueryOptions } from '@tanstack/react-query'; import { useState, Dispatch, SetStateAction } from 'react'; import { useApi } from 'components/hooks/useApi'; -import { SearchFilter, FilterResult } from 'lib/types'; +import { FilterResult, SearchFilter } from 'lib/types'; export interface FilterQueryResult { - result: FilterResult; + result: FilterResult; query: any; params: SearchFilter; setParams: Dispatch>; } -export function useFilterQuery(props = {}): FilterQueryResult { +export function useFilterQuery({ + queryKey, + queryFn, + ...options +}: UseQueryOptions): FilterQueryResult { const [params, setParams] = useState({ query: '', page: 1, }); const { useQuery } = useApi(); - const { data, ...query } = useQuery>({ ...props }); + const { data, ...query } = useQuery({ + queryKey: [...queryKey, params], + queryFn: (data: any) => queryFn({ ...data, ...params }), + ...options, + }); return { - result: data, + result: data as FilterResult, query, params, setParams, diff --git a/src/components/hooks/useNavigation.ts b/src/components/hooks/useNavigation.ts index 9f01cd80..fb9bffc5 100644 --- a/src/components/hooks/useNavigation.ts +++ b/src/components/hooks/useNavigation.ts @@ -2,7 +2,12 @@ import { useMemo } from 'react'; import { usePathname, useRouter, useSearchParams } from 'next/navigation'; import { buildUrl } from 'next-basics'; -export function useNavigation() { +export function useNavigation(): { + pathname: string; + query: { [key: string]: string }; + router: any; + makeUrl: (params: any, reset?: boolean) => string; +} { const router = useRouter(); const pathname = usePathname(); const params = useSearchParams(); diff --git a/src/components/hooks/useReports.ts b/src/components/hooks/useReports.ts index 0fad2db1..d2473002 100644 --- a/src/components/hooks/useReports.ts +++ b/src/components/hooks/useReports.ts @@ -1,19 +1,19 @@ import { useState } from 'react'; import useApi from './useApi'; -import useApiFilter from 'components/hooks/useApiFilter'; +import useFilterQuery from 'components/hooks/useFilterQuery'; -export function useReports() { +export function useReports(websiteId?: string) { const [modified, setModified] = useState(Date.now()); - const { get, useQuery, del, useMutation } = useApi(); - const { mutate } = useMutation(reportId => del(`/reports/${reportId}`)); - const { filter, page, pageSize, handleFilterChange, handlePageChange, handlePageSizeChange } = - useApiFilter(); - const { data, error, isLoading } = useQuery({ - queryKey: ['reports', { modified, filter, page, pageSize }], - queryFn: () => get(`/reports`, { filter, page, pageSize }), + const { get, del, useMutation } = useApi(); + const { mutate } = useMutation({ mutationFn: (reportId: string) => del(`/reports/${reportId}`) }); + const queryResult = useFilterQuery({ + queryKey: ['reports', { websiteId, modified }], + queryFn: (params: any) => { + return get(websiteId ? `/websites/${websiteId}/reports` : `/reports`, params); + }, }); - const deleteReport = id => { + const deleteReport = (id: any) => { mutate(id, { onSuccess: () => { setModified(Date.now()); @@ -22,16 +22,8 @@ export function useReports() { }; return { - reports: data, - error, - isLoading, + ...queryResult, deleteReport, - filter, - page, - pageSize, - handleFilterChange, - handlePageChange, - handlePageSizeChange, }; } diff --git a/src/components/hooks/useShareToken.ts b/src/components/hooks/useShareToken.ts index 41ae7faf..189657be 100644 --- a/src/components/hooks/useShareToken.ts +++ b/src/components/hooks/useShareToken.ts @@ -3,7 +3,11 @@ import useApi from './useApi'; const selector = (state: { shareToken: string }) => state.shareToken; -export function useShareToken(shareId: string) { +export function useShareToken(shareId: string): { + shareToken: any; + isLoading?: boolean; + error?: Error; +} { const shareToken = useStore(selector); const { get, useQuery } = useApi(); const { isLoading, error } = useQuery({ diff --git a/src/components/input/DateFilter.js b/src/components/input/DateFilter.tsx similarity index 89% rename from src/components/input/DateFilter.js rename to src/components/input/DateFilter.tsx index 9fde27ca..f7739f17 100644 --- a/src/components/input/DateFilter.js +++ b/src/components/input/DateFilter.tsx @@ -3,9 +3,20 @@ import { Icon, Modal, Dropdown, Item, Text, Flexbox } from 'react-basics'; import { endOfYear, isSameDay } from 'date-fns'; import DatePickerForm from 'components/metrics/DatePickerForm'; import useLocale from 'components/hooks/useLocale'; -import { formatDate } from 'lib/date'; -import Icons from 'components/icons'; import useMessages from 'components/hooks/useMessages'; +import Icons from 'components/icons'; +import { formatDate } from 'lib/date'; + +export interface DateFilterProps { + value: string; + startDate: Date; + endDate: Date; + className?: string; + onChange?: (value: string) => void; + selectedUnit?: string; + showAllTime?: boolean; + alignment?: 'start' | 'center' | 'end'; +} export function DateFilter({ value, @@ -16,7 +27,7 @@ export function DateFilter({ selectedUnit, showAllTime = false, alignment = 'end', -}) { +}: DateFilterProps) { const { formatMessage, labels } = useMessages(); const [showPicker, setShowPicker] = useState(false); @@ -65,7 +76,7 @@ export function DateFilter({ }, ].filter(n => n); - const renderValue = value => { + const renderValue = (value: string) => { return value.startsWith('range') ? ( { + const handleChange = (value: string) => { if (value === 'custom') { setShowPicker(true); return; @@ -86,7 +97,7 @@ export function DateFilter({ onChange(value); }; - const handlePickerChange = value => { + const handlePickerChange = (value: string) => { setShowPicker(false); onChange(value); }; @@ -102,7 +113,7 @@ export function DateFilter({ value={value} alignment={alignment} placeholder={formatMessage(labels.selectDate)} - onChange={handleChange} + onChange={key => handleChange(key as any)} > {({ label, value, divider }) => ( diff --git a/src/components/input/LanguageButton.js b/src/components/input/LanguageButton.tsx similarity index 88% rename from src/components/input/LanguageButton.js rename to src/components/input/LanguageButton.tsx index 3c0d0cd6..1151da0b 100644 --- a/src/components/input/LanguageButton.js +++ b/src/components/input/LanguageButton.tsx @@ -9,7 +9,7 @@ export function LanguageButton() { const { locale, saveLocale, dir } = useLocale(); const items = Object.keys(languages).map(key => ({ ...languages[key], value: key })); - function handleSelect(value, close, e) { + function handleSelect(value: string, close: () => void, e: MouseEvent) { e.stopPropagation(); saveLocale(value); close(); @@ -23,7 +23,7 @@ export function LanguageButton() { - {close => { + {(close: () => void) => { return (
{items.map(({ value, label }) => { @@ -31,7 +31,7 @@ export function LanguageButton() {
handleSelect(value, close, e)} > {label} {value === locale && ( diff --git a/src/components/input/LogoutButton.js b/src/components/input/LogoutButton.tsx similarity index 80% rename from src/components/input/LogoutButton.js rename to src/components/input/LogoutButton.tsx index 6ca358a1..c787f229 100644 --- a/src/components/input/LogoutButton.js +++ b/src/components/input/LogoutButton.tsx @@ -2,7 +2,11 @@ import { Button, Icon, Icons, TooltipPopup } from 'react-basics'; import Link from 'next/link'; import useMessages from 'components/hooks/useMessages'; -export function LogoutButton({ tooltipPosition = 'top' }) { +export function LogoutButton({ + tooltipPosition = 'top', +}: { + tooltipPosition?: 'top' | 'bottom' | 'left' | 'right'; +}) { const { formatMessage, labels } = useMessages(); return ( diff --git a/src/components/input/MonthSelect.js b/src/components/input/MonthSelect.tsx similarity index 87% rename from src/components/input/MonthSelect.js rename to src/components/input/MonthSelect.tsx index 312c6854..acb17dfe 100644 --- a/src/components/input/MonthSelect.js +++ b/src/components/input/MonthSelect.tsx @@ -20,7 +20,7 @@ export function MonthSelect({ date = new Date(), onChange }) { const year = date.getFullYear(); const ref = useRef(); - const handleChange = (close, date) => { + const handleChange = (close: () => void, date: Date) => { onChange(`range:${startOfMonth(date).getTime()}:${endOfMonth(date).getTime()}`); close(); }; @@ -53,12 +53,8 @@ export function MonthSelect({ date = new Date(), onChange }) { - {close => ( - + {(close: any) => ( + )} diff --git a/src/components/input/ProfileButton.js b/src/components/input/ProfileButton.tsx similarity index 100% rename from src/components/input/ProfileButton.js rename to src/components/input/ProfileButton.tsx diff --git a/src/components/input/RefreshButton.js b/src/components/input/RefreshButton.tsx similarity index 87% rename from src/components/input/RefreshButton.js rename to src/components/input/RefreshButton.tsx index 8b40cafa..01e80378 100644 --- a/src/components/input/RefreshButton.js +++ b/src/components/input/RefreshButton.tsx @@ -4,7 +4,13 @@ import useDateRange from 'components/hooks/useDateRange'; import Icons from 'components/icons'; import useMessages from 'components/hooks/useMessages'; -export function RefreshButton({ websiteId, isLoading }) { +export function RefreshButton({ + websiteId, + isLoading, +}: { + websiteId: string; + isLoading?: boolean; +}) { const { formatMessage, labels } = useMessages(); const [dateRange] = useDateRange(websiteId); diff --git a/src/components/input/SettingsButton.js b/src/components/input/SettingsButton.tsx similarity index 100% rename from src/components/input/SettingsButton.js rename to src/components/input/SettingsButton.tsx diff --git a/src/components/input/ThemeButton.js b/src/components/input/ThemeButton.tsx similarity index 100% rename from src/components/input/ThemeButton.js rename to src/components/input/ThemeButton.tsx diff --git a/src/components/input/WebsiteDateFilter.js b/src/components/input/WebsiteDateFilter.tsx similarity index 95% rename from src/components/input/WebsiteDateFilter.js rename to src/components/input/WebsiteDateFilter.tsx index 1725ca3b..cf1beaa1 100644 --- a/src/components/input/WebsiteDateFilter.js +++ b/src/components/input/WebsiteDateFilter.tsx @@ -5,7 +5,7 @@ import { Button, Icon, Icons } from 'react-basics'; import DateFilter from './DateFilter'; import styles from './WebsiteDateFilter.module.css'; -export function WebsiteDateFilter({ websiteId }) { +export function WebsiteDateFilter({ websiteId }: { websiteId: string }) { const [dateRange, setDateRange] = useDateRange(websiteId); const { value, startDate, endDate, selectedUnit } = dateRange; const isFutureDate = diff --git a/src/components/input/WebsiteSelect.js b/src/components/input/WebsiteSelect.tsx similarity index 88% rename from src/components/input/WebsiteSelect.js rename to src/components/input/WebsiteSelect.tsx index 23334215..e125e258 100644 --- a/src/components/input/WebsiteSelect.js +++ b/src/components/input/WebsiteSelect.tsx @@ -3,7 +3,13 @@ import useApi from 'components/hooks/useApi'; import useMessages from 'components/hooks/useMessages'; import styles from './WebsiteSelect.module.css'; -export function WebsiteSelect({ websiteId, onSelect }) { +export function WebsiteSelect({ + websiteId, + onSelect, +}: { + websiteId: string; + onSelect?: (key: any) => void; +}) { const { formatMessage, labels } = useMessages(); const { get, useQuery } = useApi(); const { data } = useQuery({ diff --git a/src/components/layout/Grid.js b/src/components/layout/Grid.js deleted file mode 100644 index 86b08887..00000000 --- a/src/components/layout/Grid.js +++ /dev/null @@ -1,18 +0,0 @@ -import classNames from 'classnames'; -import { mapChildren } from 'react-basics'; -import styles from './Grid.module.css'; - -export function Grid({ className, ...otherProps }) { - return
; -} - -export function GridRow(props) { - const { columns = 'two', className, children, ...otherProps } = props; - return ( -
- {mapChildren(children, child => { - return
{child}
; - })} -
- ); -} diff --git a/src/components/layout/Grid.tsx b/src/components/layout/Grid.tsx new file mode 100644 index 00000000..2a34fdc4 --- /dev/null +++ b/src/components/layout/Grid.tsx @@ -0,0 +1,34 @@ +import { CSSProperties } from 'react'; +import classNames from 'classnames'; +import { mapChildren } from 'react-basics'; +import styles from './Grid.module.css'; + +export interface GridProps { + className?: string; + style?: CSSProperties; + children?: any; +} + +export function Grid({ className, style, children }: GridProps) { + return ( +
+ {children} +
+ ); +} + +export function GridRow(props: { + [x: string]: any; + columns?: 'one' | 'two' | 'three' | 'one-two' | 'two-one'; + className?: string; + children?: any; +}) { + const { columns = 'two', className, children, ...otherProps } = props; + return ( +
+ {mapChildren(children, child => { + return
{child}
; + })} +
+ ); +} diff --git a/src/components/layout/NavGroup.js b/src/components/layout/NavGroup.tsx similarity index 90% rename from src/components/layout/NavGroup.js rename to src/components/layout/NavGroup.tsx index 361dffb5..e95b61fa 100644 --- a/src/components/layout/NavGroup.js +++ b/src/components/layout/NavGroup.tsx @@ -6,13 +6,21 @@ import Link from 'next/link'; import Icons from 'components/icons'; import styles from './NavGroup.module.css'; +export interface NavGroupProps { + title: string; + items: any[]; + defaultExpanded?: boolean; + allowExpand?: boolean; + minimized?: boolean; +} + export function NavGroup({ title, items, defaultExpanded = true, allowExpand = true, minimized = false, -}) { +}: NavGroupProps) { const pathname = usePathname(); const [expanded, setExpanded] = useState(defaultExpanded); diff --git a/src/components/layout/SideNav.js b/src/components/layout/SideNav.tsx similarity index 82% rename from src/components/layout/SideNav.js rename to src/components/layout/SideNav.tsx index c93881e4..f38bdba0 100644 --- a/src/components/layout/SideNav.js +++ b/src/components/layout/SideNav.tsx @@ -4,6 +4,15 @@ import { usePathname } from 'next/navigation'; import Link from 'next/link'; import styles from './SideNav.module.css'; +export interface SideNavProps { + selectedKey: string; + items: any[]; + shallow?: boolean; + scroll?: boolean; + className?: boolean; + onSelect?: () => void; +} + export function SideNav({ selectedKey, items, @@ -11,7 +20,7 @@ export function SideNav({ scroll = false, className, onSelect = () => {}, -}) { +}: SideNavProps) { const pathname = usePathname(); return ( get(`/websites/${websiteId}/active`), - refetchInterval, enabled: !!websiteId, + refetchInterval, }); const count = useMemo(() => { diff --git a/src/components/metrics/BarChart.js b/src/components/metrics/BarChart.tsx similarity index 83% rename from src/components/metrics/BarChart.js rename to src/components/metrics/BarChart.tsx index 8341fbc7..8bb3a7a7 100644 --- a/src/components/metrics/BarChart.js +++ b/src/components/metrics/BarChart.tsx @@ -10,12 +10,28 @@ import { DEFAULT_ANIMATION_DURATION } from 'lib/constants'; import { renderNumberLabels } from 'lib/charts'; import styles from './BarChart.module.css'; +export interface BarChartProps { + datasets?: any[]; + unit?: string; + animationDuration?: number; + stacked?: boolean; + isLoading?: boolean; + renderXLabel?: (label: string, index: number, values: any[]) => string; + renderYLabel?: (label: string, index: number, values: any[]) => string; + XAxisType?: string; + YAxisType?: string; + renderTooltipPopup?: (setTooltipPopup: (data: any) => void, model: any) => void; + onCreate?: (chart: any) => void; + onUpdate?: (chart: any) => void; + className?: string; +} + export function BarChart({ - datasets, + datasets = [], unit, animationDuration = DEFAULT_ANIMATION_DURATION, stacked = false, - loading = false, + isLoading = false, renderXLabel, renderYLabel, XAxisType = 'time', @@ -24,7 +40,7 @@ export function BarChart({ onCreate, onUpdate, className, -}) { +}: BarChartProps) { const canvas = useRef(); const chart = useRef(null); const [tooltip, setTooltipPopup] = useState(null); @@ -85,7 +101,7 @@ export function BarChart({ color: colors.chart.line, }, ticks: { - color: colors.text, + color: colors.chart.text, callback: renderYLabel || renderNumberLabels, }, }, @@ -106,16 +122,15 @@ export function BarChart({ const createChart = () => { Chart.defaults.font.family = 'Inter'; - const options = getOptions(); - chart.current = new Chart(canvas.current, { type: 'bar', data: { datasets, }, - options, }); + chart.current.options = getOptions(); + onCreate?.(chart.current); }; @@ -147,7 +162,7 @@ export function BarChart({ return (
- {loading && } + {isLoading && }
diff --git a/src/components/metrics/BrowsersTable.js b/src/components/metrics/BrowsersTable.tsx similarity index 85% rename from src/components/metrics/BrowsersTable.js rename to src/components/metrics/BrowsersTable.tsx index afc53663..e1c05435 100644 --- a/src/components/metrics/BrowsersTable.js +++ b/src/components/metrics/BrowsersTable.tsx @@ -1,9 +1,9 @@ import FilterLink from 'components/common/FilterLink'; -import MetricsTable from 'components/metrics/MetricsTable'; +import MetricsTable, { MetricsTableProps } from 'components/metrics/MetricsTable'; import useMessages from 'components/hooks/useMessages'; import useFormat from 'components/hooks/useFormat'; -export function BrowsersTable({ websiteId, ...props }) { +export function BrowsersTable(props: MetricsTableProps) { const { formatMessage, labels } = useMessages(); const { formatBrowser } = useFormat(); @@ -26,7 +26,6 @@ export function BrowsersTable({ websiteId, ...props }) { title={formatMessage(labels.browsers)} type="browser" metric={formatMessage(labels.visitors)} - websiteId={websiteId} renderLabel={renderLink} /> ); diff --git a/src/components/metrics/CitiesTable.js b/src/components/metrics/CitiesTable.tsx similarity index 85% rename from src/components/metrics/CitiesTable.js rename to src/components/metrics/CitiesTable.tsx index c5f1b1d7..69b89962 100644 --- a/src/components/metrics/CitiesTable.js +++ b/src/components/metrics/CitiesTable.tsx @@ -1,16 +1,16 @@ -import MetricsTable from './MetricsTable'; +import MetricsTable, { MetricsTableProps } from './MetricsTable'; import { emptyFilter } from 'lib/filters'; import FilterLink from 'components/common/FilterLink'; import useLocale from 'components/hooks/useLocale'; import useMessages from 'components/hooks/useMessages'; import useCountryNames from 'components/hooks/useCountryNames'; -export function CitiesTable({ websiteId, ...props }) { +export function CitiesTable(props: MetricsTableProps) { const { locale } = useLocale(); const { formatMessage, labels } = useMessages(); const countryNames = useCountryNames(locale); - const renderLabel = (city, country) => { + const renderLabel = (city: string, country: string) => { const name = countryNames[country]; return name ? `${city}, ${name}` : city; }; @@ -34,7 +34,6 @@ export function CitiesTable({ websiteId, ...props }) { title={formatMessage(labels.cities)} type="city" metric={formatMessage(labels.visitors)} - websiteId={websiteId} dataFilter={emptyFilter} renderLabel={renderLink} /> diff --git a/src/components/metrics/CountriesTable.js b/src/components/metrics/CountriesTable.tsx similarity index 73% rename from src/components/metrics/CountriesTable.js rename to src/components/metrics/CountriesTable.tsx index 6f3b75b0..99f9ca2f 100644 --- a/src/components/metrics/CountriesTable.js +++ b/src/components/metrics/CountriesTable.tsx @@ -1,15 +1,24 @@ import FilterLink from 'components/common/FilterLink'; import useCountryNames from 'components/hooks/useCountryNames'; import { useLocale, useMessages, useFormat } from 'components/hooks'; -import MetricsTable from './MetricsTable'; +import MetricsTable, { MetricsTableProps } from './MetricsTable'; -export function CountriesTable({ websiteId, ...props }) { +export function CountriesTable({ + onDataLoad, + ...props +}: { + onDataLoad: (data: any) => void; +} & MetricsTableProps) { const { locale } = useLocale(); const countryNames = useCountryNames(locale); const { formatMessage, labels } = useMessages(); const { formatCountry } = useFormat(); - function renderLink({ x: code }) { + const handleDataLoad = (data: any) => { + onDataLoad?.(data); + }; + + const renderLink = ({ x: code }) => { return ( ); - } + }; return ( ); } diff --git a/src/components/metrics/DatePickerForm.js b/src/components/metrics/DatePickerForm.tsx similarity index 96% rename from src/components/metrics/DatePickerForm.js rename to src/components/metrics/DatePickerForm.tsx index 5e1906c3..b4f0781e 100644 --- a/src/components/metrics/DatePickerForm.js +++ b/src/components/metrics/DatePickerForm.tsx @@ -39,7 +39,7 @@ export function DatePickerForm({ return (
- + setSelected(key as any)}> diff --git a/src/components/metrics/DevicesTable.js b/src/components/metrics/DevicesTable.tsx similarity index 86% rename from src/components/metrics/DevicesTable.js rename to src/components/metrics/DevicesTable.tsx index 606b020a..4ebdac1b 100644 --- a/src/components/metrics/DevicesTable.js +++ b/src/components/metrics/DevicesTable.tsx @@ -1,9 +1,9 @@ -import MetricsTable from './MetricsTable'; +import MetricsTable, { MetricsTableProps } from './MetricsTable'; import FilterLink from 'components/common/FilterLink'; import useMessages from 'components/hooks/useMessages'; import { useFormat } from 'components/hooks'; -export function DevicesTable({ websiteId, ...props }) { +export function DevicesTable(props: MetricsTableProps) { const { formatMessage, labels } = useMessages(); const { formatDevice } = useFormat(); @@ -26,7 +26,6 @@ export function DevicesTable({ websiteId, ...props }) { title={formatMessage(labels.devices)} type="device" metric={formatMessage(labels.visitors)} - websiteId={websiteId} renderLabel={renderLink} /> ); diff --git a/src/components/metrics/EventsChart.js b/src/components/metrics/EventsChart.tsx similarity index 92% rename from src/components/metrics/EventsChart.js rename to src/components/metrics/EventsChart.tsx index 610a3616..be6d4a0c 100644 --- a/src/components/metrics/EventsChart.js +++ b/src/components/metrics/EventsChart.tsx @@ -7,7 +7,13 @@ import { useApi, useLocale, useDateRange, useTimezone, useNavigation } from 'com import { EVENT_COLORS } from 'lib/constants'; import { renderDateLabels, renderStatusTooltipPopup } from 'lib/charts'; -export function EventsChart({ websiteId, className, token }) { +export interface EventsChartProps { + websiteId: string; + className?: string; + token?: string; +} + +export function EventsChart({ websiteId, className, token }: EventsChartProps) { const { get, useQuery } = useApi(); const [{ startDate, endDate, unit, modified }] = useDateRange(websiteId); const { locale } = useLocale(); @@ -71,7 +77,6 @@ export function EventsChart({ websiteId, className, token }) { className={className} datasets={datasets} unit={unit} - height={300} loading={isLoading} stacked renderXLabel={renderDateLabels(unit, locale)} diff --git a/src/components/metrics/EventsTable.js b/src/components/metrics/EventsTable.tsx similarity index 69% rename from src/components/metrics/EventsTable.js rename to src/components/metrics/EventsTable.tsx index a8ae82aa..26d9529b 100644 --- a/src/components/metrics/EventsTable.js +++ b/src/components/metrics/EventsTable.tsx @@ -1,10 +1,10 @@ -import MetricsTable from './MetricsTable'; +import MetricsTable, { MetricsTableProps } from './MetricsTable'; import useMessages from 'components/hooks/useMessages'; -export function EventsTable({ websiteId, ...props }) { +export function EventsTable(props: MetricsTableProps) { const { formatMessage, labels } = useMessages(); - function handleDataLoad(data) { + function handleDataLoad(data: any) { props.onDataLoad?.(data); } @@ -14,7 +14,6 @@ export function EventsTable({ websiteId, ...props }) { title={formatMessage(labels.events)} type="event" metric={formatMessage(labels.actions)} - websiteId={websiteId} onDataLoad={handleDataLoad} /> ); diff --git a/src/components/metrics/FilterTags.js b/src/components/metrics/FilterTags.tsx similarity index 92% rename from src/components/metrics/FilterTags.js rename to src/components/metrics/FilterTags.tsx index db8fdcbd..140385f5 100644 --- a/src/components/metrics/FilterTags.js +++ b/src/components/metrics/FilterTags.tsx @@ -18,7 +18,7 @@ export function FilterTags({ params }) { return null; } - function handleCloseFilter(param) { + function handleCloseFilter(param?: string) { if (!param) { router.push(makeUrl({ view }, true)); } else { @@ -44,7 +44,7 @@ export function FilterTags({ params }) {
); })} -