diff --git a/apps/start/src/components/page/gsc-cannibalization.tsx b/apps/start/src/components/page/gsc-cannibalization.tsx index ff20b8b5..50b590dc 100644 --- a/apps/start/src/components/page/gsc-cannibalization.tsx +++ b/apps/start/src/components/page/gsc-cannibalization.tsx @@ -1,8 +1,9 @@ import type { IChartRange, IInterval } from '@openpanel/validation'; import { keepPreviousData, useQuery } from '@tanstack/react-query'; -import { AlertCircleIcon, ChevronsUpDownIcon } from 'lucide-react'; +import { AlertCircleIcon, ChevronsUpDownIcon, SearchIcon } from 'lucide-react'; import { useEffect, useMemo, useState } from 'react'; import { Pagination } from '@/components/pagination'; +import { Input } from '@/components/ui/input'; import { useAppContext } from '@/hooks/use-app-context'; import { useTRPC } from '@/integrations/trpc/react'; import { pushModal } from '@/modals'; @@ -27,6 +28,7 @@ export function GscCannibalization({ const { apiUrl } = useAppContext(); const [expanded, setExpanded] = useState>(new Set()); const [page, setPage] = useState(0); + const [search, setSearch] = useState(''); const pageSize = 15; const query = useQuery( @@ -54,7 +56,19 @@ export function GscCannibalization({ }); }; - const items = query.data ?? []; + const allItems = query.data ?? []; + + const items = useMemo(() => { + if (!search.trim()) { + return allItems; + } + const q = search.toLowerCase(); + return allItems.filter( + (item) => + item.query.toLowerCase().includes(q) || + item.pages.some((p) => p.page.toLowerCase().includes(q)) + ); + }, [allItems, search]); const pageCount = Math.ceil(items.length / pageSize) || 1; useEffect(() => { @@ -67,37 +81,52 @@ export function GscCannibalization({ const rangeStart = items.length ? page * pageSize + 1 : 0; const rangeEnd = Math.min((page + 1) * pageSize, items.length); - if (!(query.isLoading || items.length)) { + if (!(query.isLoading || allItems.length)) { return null; } return ( -
-
-
-

Keyword Cannibalization

+
+
+
+
+

Keyword Cannibalization

+ {items.length > 0 && ( + + {items.length} + + )} +
{items.length > 0 && ( - - {items.length} - +
+ + {items.length === 0 + ? '0 results' + : `${rangeStart}-${rangeEnd} of ${items.length}`} + + 0} + nextPage={() => setPage((p) => Math.min(pageCount - 1, p + 1))} + pageIndex={page} + previousPage={() => setPage((p) => Math.max(0, p - 1))} + /> +
)}
- {items.length > 0 && ( -
- - {items.length === 0 - ? '0 results' - : `${rangeStart}-${rangeEnd} of ${items.length}`} - - 0} - nextPage={() => setPage((p) => Math.min(pageCount - 1, p + 1))} - pageIndex={page} - previousPage={() => setPage((p) => Math.max(0, p - 1))} - /> -
- )} +
+ + { + setSearch(e.target.value); + setPage(0); + }} + placeholder="Search keywords" + type="search" + value={search} + /> +
{query.isLoading && diff --git a/apps/start/src/components/page/pages-insights.tsx b/apps/start/src/components/page/pages-insights.tsx index c89c1832..6db4f1c7 100644 --- a/apps/start/src/components/page/pages-insights.tsx +++ b/apps/start/src/components/page/pages-insights.tsx @@ -3,11 +3,13 @@ import { AlertTriangleIcon, EyeIcon, MousePointerClickIcon, + SearchIcon, TrendingUpIcon, } from 'lucide-react'; import { useMemo, useState } from 'react'; import { useOverviewOptions } from '@/components/overview/useOverviewOptions'; import { Pagination } from '@/components/pagination'; +import { Input } from '@/components/ui/input'; import { useAppContext } from '@/hooks/use-app-context'; import { useTRPC } from '@/integrations/trpc/react'; import { pushModal } from '@/modals'; @@ -69,6 +71,7 @@ export function PagesInsights({ projectId }: PagesInsightsProps) { const { range, interval, startDate, endDate } = useOverviewOptions(); const { apiUrl } = useAppContext(); const [page, setPage] = useState(0); + const [search, setSearch] = useState(''); const pageSize = 8; const dateInput = { @@ -217,45 +220,71 @@ export function PagesInsights({ projectId }: PagesInsightsProps) { const isLoading = gscPagesQuery.isLoading || analyticsQuery.isLoading; - const pageCount = Math.ceil(insights.length / pageSize) || 1; - const paginatedInsights = useMemo( - () => insights.slice(page * pageSize, (page + 1) * pageSize), - [insights, page, pageSize] - ); - const rangeStart = insights.length ? page * pageSize + 1 : 0; - const rangeEnd = Math.min((page + 1) * pageSize, insights.length); + const filteredInsights = useMemo(() => { + if (!search.trim()) { + return insights; + } + const q = search.toLowerCase(); + return insights.filter( + (i) => + i.path.toLowerCase().includes(q) || i.page.toLowerCase().includes(q) + ); + }, [insights, search]); - if (!isLoading && !insights.length) { + const pageCount = Math.ceil(filteredInsights.length / pageSize) || 1; + const paginatedInsights = useMemo( + () => filteredInsights.slice(page * pageSize, (page + 1) * pageSize), + [filteredInsights, page, pageSize] + ); + const rangeStart = filteredInsights.length ? page * pageSize + 1 : 0; + const rangeEnd = Math.min((page + 1) * pageSize, filteredInsights.length); + + if (!(isLoading || insights.length)) { return null; } return ( -
-
-
-

Opportunities

- {insights.length > 0 && ( - - {insights.length} - +
+
+
+
+

Opportunities

+ {filteredInsights.length > 0 && ( + + {filteredInsights.length} + + )} +
+ {filteredInsights.length > 0 && ( +
+ + {filteredInsights.length === 0 + ? '0 results' + : `${rangeStart}-${rangeEnd} of ${filteredInsights.length}`} + + 0} + nextPage={() => setPage((p) => Math.min(pageCount - 1, p + 1))} + pageIndex={page} + previousPage={() => setPage((p) => Math.max(0, p - 1))} + /> +
)}
- {insights.length > 0 && ( -
- - {insights.length === 0 - ? '0 results' - : `${rangeStart}-${rangeEnd} of ${insights.length}`} - - 0} - nextPage={() => setPage((p) => Math.min(pageCount - 1, p + 1))} - pageIndex={page} - previousPage={() => setPage((p) => Math.max(0, p - 1))} - /> -
- )} +
+ + { + setSearch(e.target.value); + setPage(0); + }} + placeholder="Search pages" + type="search" + value={search} + /> +
{isLoading && @@ -287,6 +316,16 @@ export function PagesInsights({ projectId }: PagesInsightsProps) { type="button" >
+ + + {config.label} +
- - - {config.label} + + {insight.metrics}

@@ -319,10 +351,6 @@ export function PagesInsights({ projectId }: PagesInsightsProps) { {insight.suggestion}

- - - {insight.metrics} - ); })} diff --git a/apps/start/src/routes/_app.$organizationId.$projectId.seo.tsx b/apps/start/src/routes/_app.$organizationId.$projectId.seo.tsx index 1cb36b23..6b8c10f4 100644 --- a/apps/start/src/routes/_app.$organizationId.$projectId.seo.tsx +++ b/apps/start/src/routes/_app.$organizationId.$projectId.seo.tsx @@ -732,10 +732,10 @@ function GscTable({ )}
{onSearchChange != null && ( -
+
onSearchChange(e.target.value)} placeholder={searchPlaceholder ?? 'Search'} type="search"