Files
teatea-pension/app/(app)/app/settings/organizations/page.tsx

111 lines
4.6 KiB
TypeScript

import { redirect } from "next/navigation";
import Link from "next/link";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { getCurrentAuthContext } from "@/modules/core/server/auth";
import { hasPermission } from "@/modules/core/server/permissions";
import { readData } from "@/modules/core/server/store";
import {
OrganizationManagementClient,
OrganizationRowActions,
} from "@/modules/settings/components/OrganizationManagementClient";
import { TableToolbar } from "@/modules/settings/components/TableToolbar";
import { getPage, getPageCount, getSearchParam, paginate, type SearchParams } from "@/modules/settings/lib/pagination";
type OrganizationsPageProps = {
searchParams?: Promise<SearchParams>;
};
export default async function OrganizationsPage({ searchParams }: OrganizationsPageProps): Promise<React.ReactElement> {
const context = await getCurrentAuthContext();
if (!context) {
redirect("/login");
}
if (!hasPermission(context.permissions, "organization:read")) {
redirect("/app/dashboard");
}
const params = (await searchParams) ?? {};
const query = getSearchParam(params, "q");
const page = getPage(params);
const data = await readData();
const normalizedQuery = query.trim().toLowerCase();
const filteredOrganizations = data.organizations.filter((organization) =>
[organization.name, organization.slug, organization.status].join(" ").toLowerCase().includes(normalizedQuery),
);
const pageCount = getPageCount(filteredOrganizations.length);
const visibleOrganizations = paginate(filteredOrganizations, page);
return (
<div className="flex flex-col gap-5">
<section>
<Badge variant="success">Organizations</Badge>
<h2 className="mt-3 text-xl font-semibold"></h2>
<p className="mt-1 text-sm text-muted-foreground"></p>
</section>
{hasPermission(context.permissions, "organization:manage") ? <OrganizationManagementClient /> : null}
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent className="p-0">
<TableToolbar
page={Math.min(page, pageCount)}
pageCount={pageCount}
pathname="/app/settings/organizations"
query={query}
searchPlaceholder="搜索机构名称、标识、状态"
total={filteredOrganizations.length}
/>
<div className="overflow-x-auto">
<table className="w-full min-w-[820px] text-sm">
<thead className="bg-secondary text-left text-xs text-muted-foreground">
<tr>
<th className="px-4 py-3 font-medium"></th>
<th className="px-4 py-3 font-medium">Slug</th>
<th className="px-4 py-3 font-medium"></th>
<th className="px-4 py-3 text-right font-medium"></th>
<th className="px-4 py-3 text-right font-medium"></th>
</tr>
</thead>
<tbody className="divide-y bg-card">
{visibleOrganizations.map((organization) => (
<tr className="table-row-enter hover:bg-secondary/45" key={organization.id}>
<td className="px-4 py-3">
<Link
className="font-medium text-primary underline-offset-4 hover:underline"
href={`/app/settings/organizations/${organization.id}`}
>
{organization.name}
</Link>
</td>
<td className="px-4 py-3 text-muted-foreground">{organization.slug}</td>
<td className="px-4 py-3">{organization.status === "active" ? "启用" : "停用"}</td>
<td className="px-4 py-3 text-right text-muted-foreground">
{new Date(organization.createdAt).toLocaleString("zh-CN")}
</td>
<td className="px-4 py-3">
<OrganizationRowActions organization={organization} />
</td>
</tr>
))}
{visibleOrganizations.length === 0 ? (
<tr>
<td className="px-4 py-8 text-center text-muted-foreground" colSpan={5}>
</td>
</tr>
) : null}
</tbody>
</table>
</div>
</CardContent>
</Card>
</div>
);
}