111 lines
4.6 KiB
TypeScript
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>
|
|
);
|
|
}
|