chore: initial project import
Some checks failed
CI - Production Readiness / Verify (push) Has been cancelled
Some checks failed
CI - Production Readiness / Verify (push) Has been cancelled
This commit is contained in:
73
app/contacts/export/page.tsx
Normal file
73
app/contacts/export/page.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import { ShellPage } from "@/components/page-templates";
|
||||
import { Button, SectionCard } from "@/components/ui";
|
||||
import { getSession } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default async function ExportContactsPage() {
|
||||
const session = await getSession();
|
||||
if (!session) {
|
||||
redirect("/login");
|
||||
}
|
||||
|
||||
if (session.role === "agent") {
|
||||
redirect("/unauthorized");
|
||||
}
|
||||
|
||||
const [segments, tags, count, lastUpdated] = await Promise.all([
|
||||
prisma.contactSegment.findMany({
|
||||
where: { tenantId: session.tenantId },
|
||||
orderBy: { name: "asc" },
|
||||
select: { id: true, name: true, _count: { select: { members: true } } }
|
||||
}),
|
||||
prisma.tag.findMany({ where: { tenantId: session.tenantId }, select: { name: true }, orderBy: { name: "asc" } }),
|
||||
prisma.contact.count({ where: { tenantId: session.tenantId } }),
|
||||
prisma.contact.findFirst({
|
||||
where: { tenantId: session.tenantId },
|
||||
orderBy: { updatedAt: "desc" },
|
||||
select: { updatedAt: true }
|
||||
})
|
||||
]);
|
||||
|
||||
const fields = ["fullName", "phoneNumber", "email", "countryCode", "optInStatus", "createdAt", "updatedAt"];
|
||||
|
||||
return (
|
||||
<ShellPage shell="admin" title="Export Contacts" description="Atur field dan filter sebelum export.">
|
||||
<SectionCard title="Export options">
|
||||
<div className="grid gap-4 md:max-w-xl">
|
||||
<p className="rounded-xl border border-line bg-surface-container p-3 text-sm text-on-surface-variant">
|
||||
Total kontak: {count} • Last updated: {lastUpdated?.updatedAt ? new Intl.DateTimeFormat("id-ID").format(lastUpdated.updatedAt) : "-"}
|
||||
</p>
|
||||
<input className="rounded-xl border border-line px-4 py-3" placeholder="Filter tags / segments" />
|
||||
<select className="rounded-xl border border-line px-4 py-3" defaultValue="">
|
||||
<option value="">Select segment (optional)</option>
|
||||
{segments.map((segment) => (
|
||||
<option key={segment.id} value={segment.id}>
|
||||
{segment.name} ({segment._count.members})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<label className="text-sm text-on-surface-variant">
|
||||
<span>Fields to export</span>
|
||||
<select className="mt-2 w-full rounded-xl border border-line px-4 py-3">
|
||||
{fields.map((field) => (
|
||||
<option key={field} value={field}>
|
||||
{field}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<p className="text-sm text-on-surface-variant">Available tags: {tags.length ? tags.map((tag) => tag.name).join(", ") : "-"}</p>
|
||||
<div>
|
||||
<Button href="/contacts">Export</Button>
|
||||
</div>
|
||||
<div>
|
||||
{segments.length === 0 ? (
|
||||
<p className="text-xs text-warning">Tambahkan segment untuk filtering export yang lebih presisi.</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</SectionCard>
|
||||
</ShellPage>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user