stones/src/app/contacts/page.tsx

144 lines
4.9 KiB
TypeScript

import { Metadata } from "next";
import Link from "next/link";
import { getUser } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { LogoutButton } from "@/components/auth/logout-button";
import { ContactsList } from "@/components/contacts/contacts-list";
export const metadata: Metadata = {
title: "Contacts - Stones Database",
description: "Manage contacts in the Stones Database",
};
interface ContactsPageProps {
searchParams: { [key: string]: string | string[] | undefined };
}
export default async function ContactsPage({ searchParams }: ContactsPageProps) {
const user = await getUser();
const page = Number(searchParams.page) || 1;
const search = typeof searchParams.search === 'string' ? searchParams.search : '';
const limit = 25;
const skip = (page - 1) * limit;
// Get contacts with pagination and search
const contacts = await prisma.contact.findMany({
where: {
OR: [
{ name: { contains: search, mode: 'insensitive' } },
{ ensName: { contains: search, mode: 'insensitive' } },
{ ethereumAddress: { contains: search, mode: 'insensitive' } },
{ email: { contains: search, mode: 'insensitive' } },
{ twitter: { contains: search, mode: 'insensitive' } },
{ discord: { contains: search, mode: 'insensitive' } },
],
},
skip,
take: limit,
orderBy: {
createdAt: "desc",
},
include: {
nftHoldings: {
take: 1,
},
daoMemberships: {
take: 1,
},
},
});
// Get total count for pagination with search
const totalContacts = await prisma.contact.count({
where: {
OR: [
{ name: { contains: search, mode: 'insensitive' } },
{ ensName: { contains: search, mode: 'insensitive' } },
{ ethereumAddress: { contains: search, mode: 'insensitive' } },
{ email: { contains: search, mode: 'insensitive' } },
{ twitter: { contains: search, mode: 'insensitive' } },
{ discord: { contains: search, mode: 'insensitive' } },
],
},
});
const totalPages = Math.ceil(totalContacts / limit);
return (
<div className="flex min-h-screen flex-col">
<header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="container flex h-14 items-center justify-between">
<div className="mr-4 flex">
<Link href="/" className="mr-6 flex items-center space-x-2">
<span className="font-bold">Stones Database</span>
</Link>
</div>
<nav className="flex items-center space-x-4">
<Link href="/contacts" className="text-sm font-medium">
Contacts
</Link>
<Link href="/dashboard" className="text-sm font-medium">
Dashboard
</Link>
{user && (
<div className="flex items-center gap-4">
<span className="text-sm text-muted-foreground">
Hello, {user.name}
</span>
<Link href="/settings" className="text-sm font-medium">
Settings
</Link>
<LogoutButton />
</div>
)}
</nav>
</div>
</header>
<main className="flex-1 container py-6">
<div className="flex items-center justify-between mb-6">
<h1 className="text-3xl font-bold">Contacts</h1>
<div className="flex gap-4">
<div className="relative w-full md:w-60">
<form action="/contacts" method="GET">
<Input
type="search"
name="search"
placeholder="Search contacts..."
className="pr-8"
defaultValue={search}
/>
{/* Preserve page parameter if it exists */}
{page > 1 && <input type="hidden" name="page" value={page} />}
</form>
</div>
<Link href="/contacts/new">
<Button>
Add Contact
</Button>
</Link>
<Link href={`/api/contacts/export?search=${encodeURIComponent(search)}`}>
<Button variant="outline">
Export Data
</Button>
</Link>
</div>
</div>
<ContactsList
contacts={contacts}
currentPage={page}
totalPages={totalPages}
search={search}
/>
</main>
<footer className="w-full border-t py-6">
<div className="container flex justify-center items-center">
<p className="text-center text-sm leading-loose text-muted-foreground">
© 2025 BoilerHaus. All rights reserved.
</p>
</div>
</footer>
</div>
);
}