diff --git a/apps/admin/app/dashboard/catalog/page.tsx b/apps/admin/app/dashboard/catalog/page.tsx index 8d6133bdd9..bb8d43642f 100644 --- a/apps/admin/app/dashboard/catalog/page.tsx +++ b/apps/admin/app/dashboard/catalog/page.tsx @@ -112,7 +112,7 @@ export default function CatalogPage() { const q = searchParams?.get('q') ?? undefined; const { - data: items = [], + data: result, isLoading, isError, } = useQuery({ @@ -120,6 +120,9 @@ export default function CatalogPage() { queryFn: () => getCatalogItems({ q }), }); + const items = result?.data ?? []; + const total = result?.total ?? 0; + return (
- {items.length.toLocaleString()} item{items.length !== 1 ? 's' : ''} + {items.length.toLocaleString()} of {total.toLocaleString()} item + {total !== 1 ? 's' : ''} {q ? ` matching "${q}"` : ''}
> diff --git a/apps/admin/app/dashboard/packs/page.tsx b/apps/admin/app/dashboard/packs/page.tsx index a2b826a5cb..d498628174 100644 --- a/apps/admin/app/dashboard/packs/page.tsx +++ b/apps/admin/app/dashboard/packs/page.tsx @@ -16,7 +16,9 @@ import { SearchInput } from 'admin-app/components/search-input'; import { type AdminPack, deletePack, getPacks } from 'admin-app/lib/api'; import { formatDate } from 'admin-app/lib/date'; import { queryKeys } from 'admin-app/lib/queryKeys'; +import { cn } from 'admin-app/lib/utils'; import { useSearchParams } from 'next/navigation'; +import { useState } from 'react'; function TableSkeleton() { return ( @@ -41,22 +43,28 @@ function TableSkeleton() { function PackRow({ pack }: { pack: AdminPack }) { const queryClient = useQueryClient(); + const isDeleted = pack.deleted; const { mutateAsync: handleDelete } = useMutation({ mutationFn: () => deletePack(pack.id), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: queryKeys.admin.packs() }); + queryClient.invalidateQueries({ queryKey: ['admin', 'packs'] }); }, }); return ( -{pack.name}
{pack.description && ({pack.description}
)} + {isDeleted && ( ++ Deleted {pack.deletedAt ? formatDate(new Date(pack.deletedAt)) : ''} +
+ )}Failed to load packs. Check that the API is reachable. @@ -159,7 +187,8 @@ export default function PacksPage() {
- {packs.length.toLocaleString()} pack{packs.length !== 1 ? 's' : ''} + {packs.length.toLocaleString()} of {total.toLocaleString()} pack + {total !== 1 ? 's' : ''} {q ? ` matching "${q}"` : ''}
> diff --git a/apps/admin/app/dashboard/page.tsx b/apps/admin/app/dashboard/page.tsx index d2543b8eae..7992c482ea 100644 --- a/apps/admin/app/dashboard/page.tsx +++ b/apps/admin/app/dashboard/page.tsx @@ -52,21 +52,25 @@ export default function DashboardPage() { queryFn: getStats, }); - const { data: users = [], isLoading: usersLoading } = useQuery({ + const { data: usersResult, isLoading: usersLoading } = useQuery({ queryKey: queryKeys.admin.users(5), queryFn: () => getUsers({ limit: 5 }), }); - const { data: packs = [], isLoading: packsLoading } = useQuery({ + const { data: packsResult, isLoading: packsLoading } = useQuery({ queryKey: queryKeys.admin.packs(5), queryFn: () => getPacks({ limit: 5 }), }); - const { data: catalog = [], isLoading: catalogLoading } = useQuery({ + const { data: catalogResult, isLoading: catalogLoading } = useQuery({ queryKey: queryKeys.admin.catalog(5), queryFn: () => getCatalogItems({ limit: 5 }), }); + const users = usersResult?.data ?? []; + const packs = packsResult?.data ?? []; + const catalog = catalogResult?.data ?? []; + const isLoading = statsLoading || usersLoading || packsLoading || catalogLoading; const isError = !isLoading && !stats; diff --git a/apps/admin/app/dashboard/trails/page.tsx b/apps/admin/app/dashboard/trails/page.tsx index c1f21a4bc2..f7c73ab163 100644 --- a/apps/admin/app/dashboard/trails/page.tsx +++ b/apps/admin/app/dashboard/trails/page.tsx @@ -1,20 +1,40 @@ 'use client'; +import { Badge } from '@packrat/web-ui/components/badge'; import { Button } from '@packrat/web-ui/components/button'; import { Input } from '@packrat/web-ui/components/input'; -import { useQuery } from '@tanstack/react-query'; -import { getTrailGeometry, type TrailGeometry } from 'admin-app/lib/api'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@packrat/web-ui/components/table'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { DeleteButton } from 'admin-app/components/delete-button'; +import { + deleteTrailCondition, + getTrailConditions, + getTrailGeometry, + searchTrails, + type TrailConditionReport, + type TrailGeometry, + type TrailSearchResult, +} from 'admin-app/lib/api'; +import { formatDate } from 'admin-app/lib/date'; import { queryKeys } from 'admin-app/lib/queryKeys'; +import { cn } from 'admin-app/lib/utils'; import dynamic from 'next/dynamic'; import { useState } from 'react'; -const LEADING_SLASH_RE = /^r?\//; - const TrailMap = dynamic(() => import('admin-app/components/trail-map').then((m) => m.TrailMap), { ssr: false, loading: () => , }); +// ─── Shared meta badge ──────────────────────────────────────────────────────── + function MetaBadge({ label, value }: { label: string; value: string | null | undefined }) { if (!value) return null; return ( @@ -25,51 +45,185 @@ function MetaBadge({ label, value }: { label: string; value: string | null | und ); } -export default function TrailViewerPage() { - const [input, setInput] = useState(''); - const [osmId, setOsmId] = useState(''); +// ─── Trail search section ───────────────────────────────────────────────────── - const { data, isLoading, isError, error } = useQuery- Enter an OSM relation ID to visualise its geometry and verify stitching. -
-