shanshanzhong 5a493fce67
Some checks failed
Build and Release / Build (push) Has been cancelled
fix: 修复订阅跳转引号问题 + 设备组显示设备类型 + 统一用户名显示
- 去掉所有 Link search 中的 String() 包装(7处),修复 URL 引号问题
- View Owner 改用 useNavigate 强制导航,解决同页面跳转不生效
- 设备组详情成员列表添加 auth_type Badge 和 device_type Badge
- 设备组列表 owner 列统一为 [AUTH_TYPE Badge] + identifier 格式
- UserDetail 组件统一设备标识逻辑,去掉 shortenDeviceIdentifier

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-26 21:36:12 -07:00

150 lines
4.4 KiB
TypeScript

import { Badge } from "@workspace/ui/components/badge";
import { Button } from "@workspace/ui/components/button";
import {
ProTable,
type ProTableActions,
} from "@workspace/ui/composed/pro-table/pro-table";
import { getFamilyList } from "@workspace/ui/services/admin/user";
import { useRef } from "react";
import { useTranslation } from "react-i18next";
import { formatDate } from "@/utils/common";
import { getFamilyStatusLabel, isFamilyStatusActive } from "./enums";
import { FamilyDetailSheet } from "./family-detail-sheet";
interface FamilyManagementProps {
initialFamilyId?: number;
initialUserId?: number;
onChanged?: () => void;
}
export default function FamilyManagement({
initialFamilyId,
initialUserId,
onChanged,
}: Readonly<FamilyManagementProps>) {
const { t } = useTranslation("user");
const ref = useRef<ProTableActions>(null);
const initialFilters = {
family_id: initialFamilyId || undefined,
user_id: initialUserId || undefined,
};
return (
<ProTable<API.FamilySummary, API.GetFamilyListParams>
action={ref}
actions={{
render: (row) => [
<FamilyDetailSheet
familyId={row.family_id}
key={`detail-${row.family_id}`}
onChanged={() => {
ref.current?.refresh();
onChanged?.();
}}
trigger={<Button>{t("familyDetail", "Family Detail")}</Button>}
/>,
],
}}
columns={[
{
accessorKey: "family_id",
header: t("familyId", "Family ID"),
},
{
accessorKey: "owner_identifier",
header: t("owner", "Owner"),
cell: ({ row }) => (
<div className="flex items-center gap-1">
{row.original.owner_auth_type ? (
<Badge className="uppercase">
{row.original.owner_auth_type}
</Badge>
) : null}
<span>{row.original.owner_identifier}</span>
<span className="text-muted-foreground text-xs">
(ID: {row.original.owner_user_id})
</span>
</div>
),
},
{
accessorKey: "status",
header: t("status", "Status"),
cell: ({ row }) => {
const status = row.getValue("status") as string;
return isFamilyStatusActive(status) ? (
<Badge>{t("statusActive", "Active")}</Badge>
) : (
<Badge variant="secondary">
{getFamilyStatusLabel(t, status)}
</Badge>
);
},
},
{
accessorKey: "active_member_count",
header: t("memberCount", "Member Count"),
cell: ({ row }) =>
`${row.original.active_member_count}/${row.original.max_members}`,
},
{
accessorKey: "max_members",
header: t("familyMaxMembers", "Max Members"),
},
{
accessorKey: "created_at",
header: t("createdAt", "Created At"),
cell: ({ row }) => formatDate(row.getValue("created_at")),
},
{
accessorKey: "updated_at",
header: t("updatedAt", "Updated At"),
cell: ({ row }) => formatDate(row.getValue("updated_at")),
},
]}
header={{
title: t("familyManagement", "Family Group Management"),
}}
initialFilters={initialFilters}
key={String(initialFamilyId || initialUserId || "all")}
params={[
{
key: "keyword",
placeholder: t("search", "Search"),
},
{
key: "status",
placeholder: t("status", "Status"),
options: [
{ label: getFamilyStatusLabel(t, "active"), value: "active" },
{ label: getFamilyStatusLabel(t, "disabled"), value: "disabled" },
],
},
{
key: "owner_user_id",
placeholder: t("familyOwnerUserId", "Owner User ID"),
},
{
key: "family_id",
placeholder: t("familyId", "Family ID"),
},
{
key: "user_id",
placeholder: t("userId", "User ID"),
},
]}
request={async (pagination, filter) => {
const { data } = await getFamilyList({
...pagination,
...filter,
});
return {
list: data.data?.list || [],
total: data.data?.total || 0,
};
}}
/>
);
}