Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 42 additions & 10 deletions apps/admin/app/dashboard/catalog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import { Badge } from '@packrat/web-ui/components/badge';
import { Button } from '@packrat/web-ui/components/button';
import { Skeleton } from '@packrat/web-ui/components/skeleton';
import {
Table,
Expand All @@ -17,7 +18,10 @@ import { SearchInput } from 'admin-app/components/search-input';
import { type AdminCatalogItem, deleteCatalogItem, getCatalogItems } from 'admin-app/lib/api';
import { formatDate } from 'admin-app/lib/date';
import { queryKeys } from 'admin-app/lib/queryKeys';
import { useSearchParams } from 'next/navigation';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { parseAsInteger, parseAsString, useQueryState } from 'nuqs';

const PAGE_SIZE = 50;

function TableSkeleton() {
return (
Expand Down Expand Up @@ -46,7 +50,7 @@ function CatalogRow({ item }: { item: AdminCatalogItem }) {
const { mutateAsync: handleDelete } = useMutation({
mutationFn: () => deleteCatalogItem(item.id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: queryKeys.admin.catalog() });
queryClient.invalidateQueries({ queryKey: queryKeys.admin.catalog.all });
},
});

Expand Down Expand Up @@ -108,18 +112,22 @@ function CatalogRow({ item }: { item: AdminCatalogItem }) {
}

export default function CatalogPage() {
const searchParams = useSearchParams();
const q = searchParams?.get('q') ?? undefined;
const [q] = useQueryState('q', parseAsString.withDefault(''));
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(0));
const offset = page * PAGE_SIZE;

const {
data: items = [],
isLoading,
isError,
} = useQuery({
queryKey: queryKeys.admin.catalog(q),
queryFn: () => getCatalogItems({ q }),
queryKey: queryKeys.admin.catalog.list({ q: q || undefined, page }),
queryFn: () => getCatalogItems({ q: q || undefined, limit: PAGE_SIZE, offset }),
});

const hasPrev = page > 0;
const hasNext = items.length === PAGE_SIZE;

return (
<div>
<div className="mb-6">
Expand Down Expand Up @@ -173,10 +181,34 @@ export default function CatalogPage() {
</TableBody>
</Table>
</div>
<p className="text-xs text-muted-foreground">
{items.length.toLocaleString()} item{items.length !== 1 ? 's' : ''}
{q ? ` matching "${q}"` : ''}
</p>
<div className="flex items-center justify-between">
<p className="text-xs text-muted-foreground">
{items.length === 0
? `No items${q ? ` matching "${q}"` : ''}`
: `${(offset + 1).toLocaleString()}–${(offset + items.length).toLocaleString()} items${q ? ` matching "${q}"` : ''}`}
</p>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() => setPage(page - 1)}
disabled={!hasPrev}
>
<ChevronLeft className="h-4 w-4" />
Prev
</Button>
<span className="text-xs text-muted-foreground">Page {page + 1}</span>
<Button
variant="outline"
size="sm"
onClick={() => setPage(page + 1)}
disabled={!hasNext}
>
Next
<ChevronRight className="h-4 w-4" />
</Button>
</div>
</div>
</>
)}
</div>
Expand Down
28 changes: 28 additions & 0 deletions apps/admin/app/dashboard/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client';

import { Button } from '@packrat/web-ui/components/button';
import { useEffect } from 'react';

export default function DashboardError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error('Dashboard error:', error);
}, [error]);

return (
<div className="flex flex-col items-center justify-center gap-4 py-20 text-center">
<h2 className="text-lg font-semibold">Failed to load</h2>
<p className="text-sm text-muted-foreground max-w-sm">
{error.message || 'Something went wrong loading this page.'}
</p>
<Button onClick={reset} variant="outline" size="sm">
Try again
</Button>
</div>
);
}
52 changes: 42 additions & 10 deletions apps/admin/app/dashboard/packs/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import { Badge } from '@packrat/web-ui/components/badge';
import { Button } from '@packrat/web-ui/components/button';
import { Skeleton } from '@packrat/web-ui/components/skeleton';
import {
Table,
Expand All @@ -16,7 +17,10 @@ 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 { useSearchParams } from 'next/navigation';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { parseAsInteger, parseAsString, useQueryState } from 'nuqs';

const PAGE_SIZE = 50;

function TableSkeleton() {
return (
Expand Down Expand Up @@ -45,7 +49,7 @@ function PackRow({ pack }: { pack: AdminPack }) {
const { mutateAsync: handleDelete } = useMutation({
mutationFn: () => deletePack(pack.id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: queryKeys.admin.packs() });
queryClient.invalidateQueries({ queryKey: queryKeys.admin.packs.all });
},
});

Expand Down Expand Up @@ -93,18 +97,22 @@ function PackRow({ pack }: { pack: AdminPack }) {
}

export default function PacksPage() {
const searchParams = useSearchParams();
const q = searchParams?.get('q') ?? undefined;
const [q] = useQueryState('q', parseAsString.withDefault(''));
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(0));
const offset = page * PAGE_SIZE;

const {
data: packs = [],
isLoading,
isError,
} = useQuery({
queryKey: queryKeys.admin.packs(q),
queryFn: () => getPacks({ q }),
queryKey: queryKeys.admin.packs.list({ q: q || undefined, page }),
queryFn: () => getPacks({ q: q || undefined, limit: PAGE_SIZE, offset }),
});

const hasPrev = page > 0;
const hasNext = packs.length === PAGE_SIZE;

return (
<div>
<div className="mb-6">
Expand Down Expand Up @@ -158,10 +166,34 @@ export default function PacksPage() {
</TableBody>
</Table>
</div>
<p className="text-xs text-muted-foreground">
{packs.length.toLocaleString()} pack{packs.length !== 1 ? 's' : ''}
{q ? ` matching "${q}"` : ''}
</p>
<div className="flex items-center justify-between">
<p className="text-xs text-muted-foreground">
{packs.length === 0
? `No packs${q ? ` matching "${q}"` : ''}`
: `${(offset + 1).toLocaleString()}–${(offset + packs.length).toLocaleString()} packs${q ? ` matching "${q}"` : ''}`}
</p>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() => setPage(page - 1)}
disabled={!hasPrev}
>
<ChevronLeft className="h-4 w-4" />
Prev
</Button>
<span className="text-xs text-muted-foreground">Page {page + 1}</span>
<Button
variant="outline"
size="sm"
onClick={() => setPage(page + 1)}
disabled={!hasNext}
>
Next
<ChevronRight className="h-4 w-4" />
</Button>
</div>
</div>
</>
)}
</div>
Expand Down
6 changes: 3 additions & 3 deletions apps/admin/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@ export default function DashboardPage() {
});

const { data: users = [], isLoading: usersLoading } = useQuery({
queryKey: queryKeys.admin.users(5),
queryKey: queryKeys.admin.users.list({ limit: 5 }),
queryFn: () => getUsers({ limit: 5 }),
});

const { data: packs = [], isLoading: packsLoading } = useQuery({
queryKey: queryKeys.admin.packs(5),
queryKey: queryKeys.admin.packs.list({ limit: 5 }),
queryFn: () => getPacks({ limit: 5 }),
});

const { data: catalog = [], isLoading: catalogLoading } = useQuery({
queryKey: queryKeys.admin.catalog(5),
queryKey: queryKeys.admin.catalog.list({ limit: 5 }),
queryFn: () => getCatalogItems({ limit: 5 }),
});

Expand Down
52 changes: 42 additions & 10 deletions apps/admin/app/dashboard/users/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import { Badge } from '@packrat/web-ui/components/badge';
import { Button } from '@packrat/web-ui/components/button';
import { Skeleton } from '@packrat/web-ui/components/skeleton';
import {
Table,
Expand All @@ -16,7 +17,10 @@ import { SearchInput } from 'admin-app/components/search-input';
import { type AdminUser, deleteUser, getUsers } from 'admin-app/lib/api';
import { formatDate } from 'admin-app/lib/date';
import { queryKeys } from 'admin-app/lib/queryKeys';
import { useSearchParams } from 'next/navigation';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { parseAsInteger, parseAsString, useQueryState } from 'nuqs';

const PAGE_SIZE = 50;

function TableSkeleton() {
return (
Expand Down Expand Up @@ -44,7 +48,7 @@ function UserRow({ user }: { user: AdminUser }) {
const { mutateAsync: handleDelete } = useMutation({
mutationFn: () => deleteUser(user.id),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: queryKeys.admin.users() });
queryClient.invalidateQueries({ queryKey: queryKeys.admin.users.all });
},
});

Expand Down Expand Up @@ -93,18 +97,22 @@ function UserRow({ user }: { user: AdminUser }) {
}

export default function UsersPage() {
const searchParams = useSearchParams();
const q = searchParams?.get('q') ?? undefined;
const [q] = useQueryState('q', parseAsString.withDefault(''));
const [page, setPage] = useQueryState('page', parseAsInteger.withDefault(0));
const offset = page * PAGE_SIZE;

const {
data: users = [],
isLoading,
isError,
} = useQuery({
queryKey: queryKeys.admin.users(q),
queryFn: () => getUsers({ q }),
queryKey: queryKeys.admin.users.list({ q: q || undefined, page }),
queryFn: () => getUsers({ q: q || undefined, limit: PAGE_SIZE, offset }),
});

const hasPrev = page > 0;
const hasNext = users.length === PAGE_SIZE;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

return (
<div>
<div className="mb-6">
Expand Down Expand Up @@ -155,10 +163,34 @@ export default function UsersPage() {
</TableBody>
</Table>
</div>
<p className="text-xs text-muted-foreground">
{users.length.toLocaleString()} user{users.length !== 1 ? 's' : ''}
{q ? ` matching "${q}"` : ''}
</p>
<div className="flex items-center justify-between">
<p className="text-xs text-muted-foreground">
{users.length === 0
? `No users${q ? ` matching "${q}"` : ''}`
: `${(offset + 1).toLocaleString()}–${(offset + users.length).toLocaleString()} users${q ? ` matching "${q}"` : ''}`}
</p>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() => setPage(page - 1)}
disabled={!hasPrev}
>
<ChevronLeft className="h-4 w-4" />
Prev
</Button>
<span className="text-xs text-muted-foreground">Page {page + 1}</span>
<Button
variant="outline"
size="sm"
onClick={() => setPage(page + 1)}
disabled={!hasNext}
>
Next
<ChevronRight className="h-4 w-4" />
</Button>
</div>
</div>
</>
)}
</div>
Expand Down
28 changes: 28 additions & 0 deletions apps/admin/app/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use client';

import { Button } from '@packrat/web-ui/components/button';
import { useEffect } from 'react';

export default function GlobalError({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error('App error:', error);
}, [error]);

return (
<div className="flex min-h-screen flex-col items-center justify-center gap-4 p-8 text-center">
<h2 className="text-xl font-semibold">Something went wrong</h2>
<p className="text-sm text-muted-foreground max-w-sm">
{error.message || 'An unexpected error occurred.'}
</p>
<Button onClick={reset} variant="outline" size="sm">
Try again
</Button>
</div>
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}
Loading
Loading