polish: clarify record detail actions

This commit is contained in:
JetSprow
2026-04-30 22:29:25 +10:00
parent 157f3841f6
commit b8a7cab1af
9 changed files with 108 additions and 59 deletions

View File

@@ -1,9 +1,10 @@
import { KeyRound, Server, UserRound, Waypoints } from "lucide-react"; import { Eye, KeyRound, Server, UserRound, Waypoints } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { batchTestNodeConnections } from "@/actions/admin/nodes"; import { batchTestNodeConnections } from "@/actions/admin/nodes";
import { BatchActionBar, BatchActionButton } from "@/components/admin/batch-action-bar"; import { BatchActionBar, BatchActionButton } from "@/components/admin/batch-action-bar";
import { EmptyState } from "@/components/shared/page-shell"; import { EmptyState } from "@/components/shared/page-shell";
import { StatusBadge } from "@/components/shared/status-badge"; import { StatusBadge } from "@/components/shared/status-badge";
import { buttonVariants } from "@/components/ui/button";
import { getNodeStatusLabel } from "@/lib/domain-labels"; import { getNodeStatusLabel } from "@/lib/domain-labels";
import { NodeActions } from "../node-actions"; import { NodeActions } from "../node-actions";
import { NodeForm } from "../node-form"; import { NodeForm } from "../node-form";
@@ -43,7 +44,6 @@ function InboundPreview({ node }: { node: NodeServerRow }) {
<div className="space-y-2"> <div className="space-y-2">
<div className="flex min-h-6 items-center justify-between gap-3"> <div className="flex min-h-6 items-center justify-between gap-3">
<p className="text-xs font-medium text-muted-foreground"></p> <p className="text-xs font-medium text-muted-foreground"></p>
<StatusBadge tone="neutral">{node._count.inbounds} </StatusBadge>
</div> </div>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{preview.map((inbound) => ( {preview.map((inbound) => (
@@ -58,7 +58,7 @@ function InboundPreview({ node }: { node: NodeServerRow }) {
))} ))}
{hiddenCount > 0 && ( {hiddenCount > 0 && (
<span className="inline-flex min-h-8 items-center rounded-lg border border-border bg-muted/25 px-2.5 text-xs font-medium text-muted-foreground"> <span className="inline-flex min-h-8 items-center rounded-lg border border-border bg-muted/25 px-2.5 text-xs font-medium text-muted-foreground">
+{hiddenCount}
</span> </span>
)} )}
</div> </div>
@@ -84,9 +84,7 @@ function NodeCard({ node, siteUrl }: { node: NodeServerRow; siteUrl: string | nu
<div className="min-w-0"> <div className="min-w-0">
<div className="flex min-h-6 flex-wrap items-center gap-2"> <div className="flex min-h-6 flex-wrap items-center gap-2">
<h3 className="min-w-0 truncate text-base font-semibold leading-6 tracking-tight"> <h3 className="min-w-0 truncate text-base font-semibold leading-6 tracking-tight">
<Link href={`/admin/nodes/${node.id}`} className="hover:underline"> {node.name}
{node.name}
</Link>
</h3> </h3>
<StatusBadge tone={node.status === "active" ? "success" : "neutral"}> <StatusBadge tone={node.status === "active" ? "success" : "neutral"}>
{getNodeStatusLabel(node.status)} {getNodeStatusLabel(node.status)}
@@ -99,6 +97,13 @@ function NodeCard({ node, siteUrl }: { node: NodeServerRow; siteUrl: string | nu
<InboundPreview node={node} /> <InboundPreview node={node} />
<div className="flex flex-wrap items-center gap-2 xl:justify-end"> <div className="flex flex-wrap items-center gap-2 xl:justify-end">
<Link
href={`/admin/nodes/${node.id}`}
className={buttonVariants({ variant: "outline", size: "sm" })}
>
<Eye className="size-3.5" />
</Link>
<NodeForm <NodeForm
node={{ node={{
id: node.id, id: node.id,

View File

@@ -1,6 +1,6 @@
import Link from "next/link"; import Link from "next/link";
import type { SubscriptionRiskEvent } from "@prisma/client"; import type { SubscriptionRiskEvent } from "@prisma/client";
import { ChevronDown } from "lucide-react"; import { ChevronDown, Eye } from "lucide-react";
import { LogDeleteButton } from "@/components/admin/log-delete-button"; import { LogDeleteButton } from "@/components/admin/log-delete-button";
import { import {
SubscriptionStatusBadge, SubscriptionStatusBadge,
@@ -10,6 +10,7 @@ import {
import { EmptyState } from "@/components/shared/page-shell"; import { EmptyState } from "@/components/shared/page-shell";
import { StatusBadge, type StatusTone } from "@/components/shared/status-badge"; import { StatusBadge, type StatusTone } from "@/components/shared/status-badge";
import { SubscriptionRiskReviewActions } from "@/components/subscriptions/subscription-risk-review-actions"; import { SubscriptionRiskReviewActions } from "@/components/subscriptions/subscription-risk-review-actions";
import { buttonVariants } from "@/components/ui/button";
import { formatDate, formatDateShort } from "@/lib/utils"; import { formatDate, formatDateShort } from "@/lib/utils";
import { SubscriptionRiskGeoDetails } from "./subscription-risk-geo-details"; import { SubscriptionRiskGeoDetails } from "./subscription-risk-geo-details";
import type { SubscriptionRiskEventRow } from "../risk-data"; import type { SubscriptionRiskEventRow } from "../risk-data";
@@ -114,14 +115,19 @@ function EventScope({ event }: { event: SubscriptionRiskEventRow }) {
return ( return (
<div className="space-y-2"> <div className="space-y-2">
<Link href={"/admin/subscriptions/" + event.subscription.id} className="break-words font-medium hover:underline"> <p className="break-words font-medium">{event.subscription.plan.name}</p>
{event.subscription.plan.name}
</Link>
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
<SubscriptionTypeBadge type={event.subscription.plan.type} /> <SubscriptionTypeBadge type={event.subscription.plan.type} />
<SubscriptionStatusBadge status={event.subscription.status} /> <SubscriptionStatusBadge status={event.subscription.status} />
</div> </div>
<p className="text-xs text-muted-foreground">{formatDateShort(event.subscription.endDate)}</p> <p className="text-xs text-muted-foreground">{formatDateShort(event.subscription.endDate)}</p>
<Link
href={"/admin/subscriptions/" + event.subscription.id}
className={buttonVariants({ variant: "outline", size: "xs" })}
>
<Eye className="size-3" />
</Link>
</div> </div>
); );
} }
@@ -133,11 +139,18 @@ function UserBlock({ event }: { event: SubscriptionRiskEventRow }) {
return ( return (
<div className="space-y-2"> <div className="space-y-2">
<Link href={"/admin/users/" + event.user.id} className="block break-all font-medium hover:underline"> <p className="break-all font-medium">{event.user.email}</p>
{event.user.email}
</Link>
<p className="break-words text-xs text-muted-foreground">{event.user.name || "未设置昵称"}</p> <p className="break-words text-xs text-muted-foreground">{event.user.name || "未设置昵称"}</p>
<UserStatusBadge status={event.user.status} /> <div className="flex flex-wrap items-center gap-2">
<UserStatusBadge status={event.user.status} />
<Link
href={"/admin/users/" + event.user.id}
className={buttonVariants({ variant: "outline", size: "xs" })}
>
<Eye className="size-3" />
</Link>
</div>
</div> </div>
); );
} }

View File

@@ -6,7 +6,7 @@ import type {
SubscriptionType, SubscriptionType,
UserStatus, UserStatus,
} from "@prisma/client"; } from "@prisma/client";
import { AlertTriangle, ShieldCheck, UserRound } from "lucide-react"; import { AlertTriangle, Eye, ShieldCheck, UserRound } from "lucide-react";
import { DataTableShell } from "@/components/admin/data-table-shell"; import { DataTableShell } from "@/components/admin/data-table-shell";
import { import {
DataTable, DataTable,
@@ -137,8 +137,15 @@ export function SubscriptionAccessRiskSection({
</div> </div>
<div className="space-y-1 text-sm"> <div className="space-y-1 text-sm">
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
<Link href={"/admin/users/" + owner.id} className="break-all font-medium hover:underline">{owner.email}</Link> <span className="break-all font-medium">{owner.email}</span>
<UserStatusBadge status={owner.status} /> <UserStatusBadge status={owner.status} />
<Link
href={"/admin/users/" + owner.id}
className={buttonVariants({ variant: "outline", size: "xs" })}
>
<Eye className="size-3" />
</Link>
</div> </div>
<p className="text-muted-foreground">{owner.name || "未设置昵称"}</p> <p className="text-muted-foreground">{owner.name || "未设置昵称"}</p>
<p className="break-all font-mono text-xs text-muted-foreground">{owner.id}</p> <p className="break-all font-mono text-xs text-muted-foreground">{owner.id}</p>

View File

@@ -1,4 +1,5 @@
import Link from "next/link"; import Link from "next/link";
import { Eye } from "lucide-react";
import { batchSubscriptionOperation } from "@/actions/admin/subscriptions"; import { batchSubscriptionOperation } from "@/actions/admin/subscriptions";
import { BatchActionBar, BatchActionButton } from "@/components/admin/batch-action-bar"; import { BatchActionBar, BatchActionButton } from "@/components/admin/batch-action-bar";
import { DataTableShell } from "@/components/admin/data-table-shell"; import { DataTableShell } from "@/components/admin/data-table-shell";
@@ -15,6 +16,7 @@ import {
SubscriptionStatusBadge, SubscriptionStatusBadge,
SubscriptionTypeBadge, SubscriptionTypeBadge,
} from "@/components/shared/domain-badges"; } from "@/components/shared/domain-badges";
import { buttonVariants } from "@/components/ui/button";
import { formatBytes, formatDateShort } from "@/lib/utils"; import { formatBytes, formatDateShort } from "@/lib/utils";
import { AdminSubscriptionActions } from "../subscription-actions"; import { AdminSubscriptionActions } from "../subscription-actions";
import type { StreamingServiceOption } from "../streaming-slot-dialog"; import type { StreamingServiceOption } from "../streaming-slot-dialog";
@@ -92,12 +94,7 @@ export function SubscriptionsTable({
className="mt-1 size-4 rounded border-border accent-primary" className="mt-1 size-4 rounded border-border accent-primary"
/> />
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<Link <p className="break-words text-sm font-semibold">{subscription.plan.name}</p>
href={`/admin/subscriptions/${subscription.id}`}
className="break-words text-sm font-semibold hover:underline"
>
{subscription.plan.name}
</Link>
<p className="mt-1 break-all text-xs text-muted-foreground">{subscription.user.email}</p> <p className="mt-1 break-all text-xs text-muted-foreground">{subscription.user.email}</p>
</div> </div>
<SubscriptionStatusBadge status={subscription.status} /> <SubscriptionStatusBadge status={subscription.status} />
@@ -118,7 +115,14 @@ export function SubscriptionsTable({
</div> </div>
</div> </div>
</div> </div>
<div className="flex justify-end"> <div className="flex flex-wrap justify-end gap-2">
<Link
href={`/admin/subscriptions/${subscription.id}`}
className={buttonVariants({ variant: "outline", size: "sm" })}
>
<Eye className="size-3.5" />
</Link>
<AdminSubscriptionActions <AdminSubscriptionActions
subscriptionId={subscription.id} subscriptionId={subscription.id}
status={subscription.status} status={subscription.status}
@@ -162,12 +166,7 @@ export function SubscriptionsTable({
</p> </p>
</DataTableCell> </DataTableCell>
<DataTableCell className="max-w-52 whitespace-normal break-words"> <DataTableCell className="max-w-52 whitespace-normal break-words">
<Link <p className="font-medium">{subscription.plan.name}</p>
href={`/admin/subscriptions/${subscription.id}`}
className="font-medium hover:underline"
>
{subscription.plan.name}
</Link>
</DataTableCell> </DataTableCell>
<DataTableCell> <DataTableCell>
<SubscriptionTypeBadge type={subscription.plan.type} /> <SubscriptionTypeBadge type={subscription.plan.type} />
@@ -188,7 +187,14 @@ export function SubscriptionsTable({
<SubscriptionStatusBadge status={subscription.status} /> <SubscriptionStatusBadge status={subscription.status} />
</DataTableCell> </DataTableCell>
<DataTableCell> <DataTableCell>
<div className="flex justify-end"> <div className="flex flex-wrap justify-end gap-2">
<Link
href={`/admin/subscriptions/${subscription.id}`}
className={buttonVariants({ variant: "outline", size: "sm" })}
>
<Eye className="size-3.5" />
</Link>
<AdminSubscriptionActions <AdminSubscriptionActions
subscriptionId={subscription.id} subscriptionId={subscription.id}
status={subscription.status} status={subscription.status}

View File

@@ -32,9 +32,7 @@ export function AdminSupportTable({ tickets }: AdminSupportTableProps) {
mobileCards={tickets.map((ticket) => ( mobileCards={tickets.map((ticket) => (
<article key={ticket.id} className="space-y-3 p-4"> <article key={ticket.id} className="space-y-3 p-4">
<div className="min-w-0"> <div className="min-w-0">
<Link href={`/admin/support/${ticket.id}`} className="break-words text-sm font-semibold hover:underline"> <p className="break-words text-sm font-semibold">{ticket.subject}</p>
{ticket.subject}
</Link>
<p className="mt-1 break-all text-xs text-muted-foreground">{ticket.user.email}</p> <p className="mt-1 break-all text-xs text-muted-foreground">{ticket.user.email}</p>
</div> </div>
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
@@ -72,9 +70,7 @@ export function AdminSupportTable({ tickets }: AdminSupportTableProps) {
{tickets.map((ticket) => ( {tickets.map((ticket) => (
<DataTableRow key={ticket.id}> <DataTableRow key={ticket.id}>
<DataTableCell className="max-w-64 whitespace-normal break-words"> <DataTableCell className="max-w-64 whitespace-normal break-words">
<Link href={`/admin/support/${ticket.id}`} className="font-medium hover:underline"> <p className="font-medium">{ticket.subject}</p>
{ticket.subject}
</Link>
{ticket.category && ( {ticket.category && (
<p className="mt-1 text-xs text-muted-foreground">{ticket.category}</p> <p className="mt-1 text-xs text-muted-foreground">{ticket.category}</p>
)} )}

View File

@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import Link from "next/link"; import Link from "next/link";
import { notFound } from "next/navigation"; import { notFound } from "next/navigation";
import { Eye } from "lucide-react";
import { PageHeader, PageShell, SectionHeader } from "@/components/shared/page-shell"; import { PageHeader, PageShell, SectionHeader } from "@/components/shared/page-shell";
import { DataTableShell } from "@/components/admin/data-table-shell"; import { DataTableShell } from "@/components/admin/data-table-shell";
import { import {
@@ -22,6 +23,7 @@ import {
orderKindLabels, orderKindLabels,
} from "@/components/shared/domain-badges"; } from "@/components/shared/domain-badges";
import { StatusBadge, type StatusTone } from "@/components/shared/status-badge"; import { StatusBadge, type StatusTone } from "@/components/shared/status-badge";
import { buttonVariants } from "@/components/ui/button";
import { import {
SupportTicketPriorityBadge, SupportTicketPriorityBadge,
SupportTicketStatusBadge, SupportTicketStatusBadge,
@@ -102,7 +104,15 @@ export default async function AdminUserDetailPage({
<SectionHeader <SectionHeader
title="账号资料" title="账号资料"
description="用于风控判断时快速确认用户基础信息。" description="用于风控判断时快速确认用户基础信息。"
actions={<Link href={"/admin/subscription-risk?q=" + encodeURIComponent(user.email)} className="text-sm font-medium text-primary hover:underline"></Link>} actions={
<Link
href={"/admin/subscription-risk?q=" + encodeURIComponent(user.email)}
className={buttonVariants({ variant: "outline", size: "sm" })}
>
<Eye className="size-3.5" />
</Link>
}
/> />
<div className="mt-4 grid gap-3 md:grid-cols-2"> <div className="mt-4 grid gap-3 md:grid-cols-2">
<div> <div>
@@ -136,15 +146,14 @@ export default async function AdminUserDetailPage({
<DataTableHeadCell></DataTableHeadCell> <DataTableHeadCell></DataTableHeadCell>
<DataTableHeadCell></DataTableHeadCell> <DataTableHeadCell></DataTableHeadCell>
<DataTableHeadCell></DataTableHeadCell> <DataTableHeadCell></DataTableHeadCell>
<DataTableHeadCell className="text-right"></DataTableHeadCell>
</DataTableHeaderRow> </DataTableHeaderRow>
</DataTableHead> </DataTableHead>
<DataTableBody> <DataTableBody>
{subscriptions.map((subscription) => ( {subscriptions.map((subscription) => (
<DataTableRow key={subscription.id}> <DataTableRow key={subscription.id}>
<DataTableCell> <DataTableCell>
<Link href={"/admin/subscriptions/" + subscription.id} className="font-medium hover:underline"> <p className="font-medium">{subscription.plan.name}</p>
{subscription.plan.name}
</Link>
</DataTableCell> </DataTableCell>
<DataTableCell><SubscriptionTypeBadge type={subscription.plan.type} /></DataTableCell> <DataTableCell><SubscriptionTypeBadge type={subscription.plan.type} /></DataTableCell>
<DataTableCell><SubscriptionStatusBadge status={subscription.status} /></DataTableCell> <DataTableCell><SubscriptionStatusBadge status={subscription.status} /></DataTableCell>
@@ -153,6 +162,17 @@ export default async function AdminUserDetailPage({
</DataTableCell> </DataTableCell>
<DataTableCell className="whitespace-nowrap text-muted-foreground">{formatDateShort(subscription.endDate)}</DataTableCell> <DataTableCell className="whitespace-nowrap text-muted-foreground">{formatDateShort(subscription.endDate)}</DataTableCell>
<DataTableCell className="whitespace-nowrap text-muted-foreground">{formatDateShort(subscription.createdAt)}</DataTableCell> <DataTableCell className="whitespace-nowrap text-muted-foreground">{formatDateShort(subscription.createdAt)}</DataTableCell>
<DataTableCell>
<div className="flex justify-end">
<Link
href={"/admin/subscriptions/" + subscription.id}
className={buttonVariants({ variant: "outline", size: "sm" })}
>
<Eye className="size-3.5" />
</Link>
</div>
</DataTableCell>
</DataTableRow> </DataTableRow>
))} ))}
</DataTableBody> </DataTableBody>
@@ -235,19 +255,29 @@ export default async function AdminUserDetailPage({
<DataTableHeadCell></DataTableHeadCell> <DataTableHeadCell></DataTableHeadCell>
<DataTableHeadCell></DataTableHeadCell> <DataTableHeadCell></DataTableHeadCell>
<DataTableHeadCell></DataTableHeadCell> <DataTableHeadCell></DataTableHeadCell>
<DataTableHeadCell className="text-right"></DataTableHeadCell>
</DataTableHeaderRow> </DataTableHeaderRow>
</DataTableHead> </DataTableHead>
<DataTableBody> <DataTableBody>
{supportTickets.map((ticket) => ( {supportTickets.map((ticket) => (
<DataTableRow key={ticket.id}> <DataTableRow key={ticket.id}>
<DataTableCell> <DataTableCell>
<Link href={"/admin/support/" + ticket.id} className="max-w-72 truncate font-medium hover:underline"> <p className="max-w-72 truncate font-medium">{ticket.subject}</p>
{ticket.subject}
</Link>
</DataTableCell> </DataTableCell>
<DataTableCell><SupportTicketStatusBadge status={ticket.status} /></DataTableCell> <DataTableCell><SupportTicketStatusBadge status={ticket.status} /></DataTableCell>
<DataTableCell><SupportTicketPriorityBadge priority={ticket.priority} /></DataTableCell> <DataTableCell><SupportTicketPriorityBadge priority={ticket.priority} /></DataTableCell>
<DataTableCell className="whitespace-nowrap text-muted-foreground">{formatDateShort(ticket.updatedAt)}</DataTableCell> <DataTableCell className="whitespace-nowrap text-muted-foreground">{formatDateShort(ticket.updatedAt)}</DataTableCell>
<DataTableCell>
<div className="flex justify-end">
<Link
href={"/admin/support/" + ticket.id}
className={buttonVariants({ variant: "outline", size: "sm" })}
>
<Eye className="size-3.5" />
</Link>
</div>
</DataTableCell>
</DataTableRow> </DataTableRow>
))} ))}
</DataTableBody> </DataTableBody>

View File

@@ -1,5 +1,5 @@
import Link from "next/link"; import Link from "next/link";
import { ShoppingBag } from "lucide-react"; import { CreditCard, ShoppingBag } from "lucide-react";
import { DataTableShell } from "@/components/shared/data-table-shell"; import { DataTableShell } from "@/components/shared/data-table-shell";
import { import {
DataTable, DataTable,
@@ -66,12 +66,13 @@ export function UserOrdersTable({ orders }: UserOrdersTableProps) {
{formatDateShort(order.createdAt)} {formatDateShort(order.createdAt)}
</DataTableCell> </DataTableCell>
<DataTableCell> <DataTableCell>
<div className="flex items-center justify-end gap-3"> <div className="flex flex-wrap items-center justify-end gap-2">
{order.status === "PENDING" && ( {order.status === "PENDING" && (
<Link <Link
href={`/pay/${order.id}`} href={`/pay/${order.id}`}
className="text-sm font-medium text-primary hover:underline" className={buttonVariants({ size: "sm" })}
> >
<CreditCard className="size-3.5" />
</Link> </Link>
)} )}

View File

@@ -1,7 +1,6 @@
import Link from "next/link";
import { format } from "date-fns"; import { format } from "date-fns";
import { zhCN } from "date-fns/locale"; import { zhCN } from "date-fns/locale";
import { ArrowUpRight, CalendarClock, Database, Radio, Server, Tv } from "lucide-react"; import { CalendarClock, Database, Radio, Server, Tv } from "lucide-react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress"; import { Progress } from "@/components/ui/progress";
import { StatusBadge } from "@/components/shared/status-badge"; import { StatusBadge } from "@/components/shared/status-badge";
@@ -104,14 +103,8 @@ export function ActiveSubscriptionCard({ sub, poolMap }: ActiveSubscriptionCardP
{sub.plan.type === "PROXY" ? <Radio className="size-4" /> : <Tv className="size-4" />} {sub.plan.type === "PROXY" ? <Radio className="size-4" /> : <Tv className="size-4" />}
</span> </span>
<div className="min-w-0 space-y-1.5"> <div className="min-w-0 space-y-1.5">
<CardTitle className="text-base"> <CardTitle className="truncate text-base">
<Link {sub.plan.name}
href={`/subscriptions/${sub.id}`}
className="group/link inline-flex max-w-full items-center gap-1.5 hover:text-primary"
>
<span className="truncate">{sub.plan.name}</span>
<ArrowUpRight className="size-3.5 opacity-45 transition-transform duration-300 group-hover/link:-translate-y-0.5 group-hover/link:translate-x-0.5 group-hover/link:opacity-100" />
</Link>
</CardTitle> </CardTitle>
<p className="inline-flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground"> <p className="inline-flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground">
<CalendarClock className="size-3.5" /> <CalendarClock className="size-3.5" />

View File

@@ -50,9 +50,7 @@ export function UserSupportTicketTable({ tickets }: UserSupportTicketTableProps)
{tickets.map((ticket) => ( {tickets.map((ticket) => (
<DataTableRow key={ticket.id}> <DataTableRow key={ticket.id}>
<DataTableCell> <DataTableCell>
<Link href={`/support/${ticket.id}`} className="font-medium hover:underline"> <p className="font-medium">{ticket.subject}</p>
{ticket.subject}
</Link>
{ticket.category && ( {ticket.category && (
<p className="mt-1 text-xs text-muted-foreground">{ticket.category}</p> <p className="mt-1 text-xs text-muted-foreground">{ticket.category}</p>
)} )}