HooStore-Backend/src/admin/routes/newsletter/page.tsx
2026-10-27 15:08:53 +07:00

126 lines
3.7 KiB
TypeScript

import { defineRouteConfig } from "@medusajs/admin-sdk"
import { EnvelopeSolid } from "@medusajs/icons"
import { Badge, Button, Container, Heading, Table, Text } from "@medusajs/ui"
import { useEffect, useState } from "react"
type Subscriber = {
id: string
email: string
status: "subscribed" | "unsubscribed"
source: string | null
created_at: string
}
const PAGE_SIZE = 25
const NewsletterPage = () => {
const [subscribers, setSubscribers] = useState<Subscriber[]>([])
const [count, setCount] = useState(0)
const [offset, setOffset] = useState(0)
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
setIsLoading(true)
fetch(
`/admin/newsletter/subscribers?limit=${PAGE_SIZE}&offset=${offset}`,
{ credentials: "include" }
)
.then((res) => {
if (!res.ok) throw new Error(`Request failed (${res.status})`)
return res.json()
})
.then((data) => {
setSubscribers(data.subscribers ?? [])
setCount(data.count ?? 0)
setError(null)
})
.catch((err) => setError(err.message))
.finally(() => setIsLoading(false))
}, [offset])
const pageCount = Math.max(Math.ceil(count / PAGE_SIZE), 1)
const page = Math.floor(offset / PAGE_SIZE) + 1
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<div>
<Heading>Newsletter</Heading>
<Text size="small" className="text-ui-fg-subtle">
{count} subscriber{count === 1 ? "" : "s"} signed up from the
storefront footer.
</Text>
</div>
<Button variant="secondary" asChild>
<a href="/admin/newsletter/subscribers?format=csv" download>
Export CSV
</a>
</Button>
</div>
{error && (
<Text className="px-6 py-4 text-ui-fg-error">
Could not load subscribers: {error}
</Text>
)}
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Email</Table.HeaderCell>
<Table.HeaderCell>Status</Table.HeaderCell>
<Table.HeaderCell>Source</Table.HeaderCell>
<Table.HeaderCell>Date</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{!isLoading && subscribers.length === 0 && (
<Table.Row>
<Table.Cell>No subscribers yet.</Table.Cell>
<Table.Cell />
<Table.Cell />
<Table.Cell />
</Table.Row>
)}
{subscribers.map((s) => (
<Table.Row key={s.id}>
<Table.Cell>{s.email}</Table.Cell>
<Table.Cell>
<Badge
size="2xsmall"
color={s.status === "subscribed" ? "green" : "grey"}
>
{s.status}
</Badge>
</Table.Cell>
<Table.Cell>{s.source ?? "-"}</Table.Cell>
<Table.Cell>
{new Date(s.created_at).toLocaleString()}
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>
<Table.Pagination
count={count}
pageSize={PAGE_SIZE}
pageIndex={page - 1}
pageCount={pageCount}
canPreviousPage={offset > 0}
canNextPage={offset + PAGE_SIZE < count}
previousPage={() => setOffset(Math.max(offset - PAGE_SIZE, 0))}
nextPage={() => setOffset(offset + PAGE_SIZE)}
/>
</Container>
)
}
export const config = defineRouteConfig({
label: "Newsletter",
icon: EnvelopeSolid,
})
export default NewsletterPage