Files
lislgosms/src/apps/admin/enterprise-signatures/EnterpriseSignaturesTable.tsx
T

244 lines
10 KiB
TypeScript

import type { Dispatch, SetStateAction } from 'react';
import { ChevronDown, ChevronRight, Edit3, Plus } from 'lucide-react';
import type { ClientSmsSignature } from '@/api/adminApi';
import { Button, DeleteRiskAction, Pagination } from '@/components/ui';
import {
AuditStatusTag,
CarrierReportCount,
formatSignatureName,
readDrainagePayload,
signatureCardVisual,
} from './signature.helpers';
import type { DrainageInfo } from './signature.types';
type EnterpriseSignaturesTableProps = {
appliedDrainageKeyword: string;
currentPage: number;
expandedSignatureId: string;
filteredSignatures: ClientSmsSignature[];
loadData: () => Promise<void>;
onAddDrainage: (signature: ClientSmsSignature) => void;
setDeleteTarget: Dispatch<SetStateAction<{ kind: 'drainage'; signatureId: string; id: string; name: string } | null>>;
onEditDrainage: (signature: ClientSmsSignature, item: DrainageInfo) => void;
onOpenDrainageReport: (signature: ClientSmsSignature, item: DrainageInfo) => void;
setExpandedSignatureId: Dispatch<SetStateAction<string>>;
setPage: Dispatch<SetStateAction<number>>;
onEditSignature: (signature: ClientSmsSignature) => void;
onOpenSignatureReport: (signature: ClientSmsSignature) => void;
total: number;
totalPages: number;
visibleSignatures: ClientSmsSignature[];
};
export function EnterpriseSignaturesTable({
appliedDrainageKeyword,
currentPage,
expandedSignatureId,
filteredSignatures,
loadData,
onAddDrainage,
setDeleteTarget,
onEditDrainage,
onOpenDrainageReport,
setExpandedSignatureId,
setPage,
onEditSignature,
onOpenSignatureReport,
total,
totalPages,
visibleSignatures,
}: EnterpriseSignaturesTableProps) {
return (
<div className="signature-list admin-enterprise-signature-list">
<div className="enterprise-signature-table__head" role="row">
<span aria-hidden="true" />
<span>签名</span>
<span>企业</span>
<span>应用</span>
<span>审核状态</span>
<span>移动</span>
<span>联通</span>
<span>电信</span>
<span>引流信息</span>
<span>操作</span>
</div>
{visibleSignatures.map((signature) => {
const payload = readDrainagePayload(signature);
const visibleDrainageLinks = appliedDrainageKeyword
? payload.links.filter((item) =>
`${item.siteName} ${item.url} ${item.remark}`.includes(appliedDrainageKeyword),
)
: payload.links;
const cardVisual = signatureCardVisual(signature.auditStatus, signature.carrierReportSummary);
const expanded = expandedSignatureId === signature.id || Boolean(appliedDrainageKeyword);
const signatureTooltip = [
`签名:${formatSignatureName(signature.name)}`,
`企业:${signature.tenant?.name ?? signature.tenantId}`,
`应用:${signature.application?.name ?? '未绑定应用'}`,
`用途:${signature.purpose || '未填写'}`,
`审核状态:${signature.auditStatus}`,
`创建时间:${signature.createdAt ? new Date(signature.createdAt).toLocaleString('zh-CN') : '未知'}`,
`更新时间:${signature.updatedAt ? new Date(signature.updatedAt).toLocaleString('zh-CN') : '未知'}`,
].join('\n');
return (
<article
aria-label={`签名总体状态:${cardVisual.label}`}
className={`signature-card signature-card--${cardVisual.tone}`}
key={signature.id}
title={`总体状态:${cardVisual.label}`}
>
<div className="signature-summary">
<button
aria-label="展开签名"
onClick={() => setExpandedSignatureId(expanded ? '' : signature.id)}
type="button"
>
{expanded ? <ChevronDown size={18} /> : <ChevronRight size={18} />}
</button>
<div aria-label={signatureTooltip} className="enterprise-signature-table__signature" data-label="签名" title={signatureTooltip}>
<strong>{formatSignatureName(signature.name)}</strong>
</div>
<div data-label="企业">
<span className="signature-summary__regular-value">{signature.tenant?.name ?? signature.tenantId}</span>
</div>
<div data-label="应用">
<span className="signature-summary__regular-value">{signature.application?.name ?? '-'}</span>
</div>
<div data-label="审核状态">
<AuditStatusTag status={signature.auditStatus} />
</div>
<div data-label="移动">
<CarrierReportCount summary={signature.carrierReportSummary?.mobile} />
</div>
<div data-label="联通">
<CarrierReportCount summary={signature.carrierReportSummary?.unicom} />
</div>
<div data-label="电信">
<CarrierReportCount summary={signature.carrierReportSummary?.telecom} />
</div>
<div data-label="引流信息">
<strong>{payload.links.length} </strong>
</div>
<div className="signature-actions">
<Button
className="enterprise-signature-action-button"
icon={<Edit3 size={16} />}
onClick={() => onOpenSignatureReport(signature)}
size="sm"
variant="ghost"
>
报备状态
</Button>
<Button
className="enterprise-signature-action-button"
icon={<Edit3 size={16} />}
onClick={() => onEditSignature(signature)}
size="sm"
variant="ghost"
>
编辑
</Button>
<DeleteRiskAction
className="enterprise-signature-action-button"
onCompleted={() => void loadData()}
portal="admin"
targetId={signature.id}
targetType="signature"
/>
</div>
</div>
{expanded ? (
<div className="drainage-panel">
<h2>引流信息列表</h2>
{visibleDrainageLinks.length ? (
<div className="drainage-table">
<div className="drainage-table__head">
<span>引流url或号码</span>
<span>审核状态</span>
<span>移动</span>
<span>联通</span>
<span>电信</span>
<span>操作</span>
</div>
{visibleDrainageLinks.map((item) => {
const summary = signature.drainageCarrierReportSummary?.[item.id];
return (
<div className="drainage-table__row" key={item.id}>
<span className="drainage-table__url" title={item.url}>
{item.url}
</span>
<AuditStatusTag status={item.auditStatus ?? 'pending'} />
<CarrierReportCount summary={summary?.mobile} />
<CarrierReportCount summary={summary?.unicom} />
<CarrierReportCount summary={summary?.telecom} />
<span className="drainage-row-actions">
<Button
className="enterprise-signature-action-button"
disabled={item.auditStatus !== 'approved'}
onClick={() => onOpenDrainageReport(signature, item)}
size="sm"
variant="ghost"
>
报备状态
</Button>
<Button
className="enterprise-signature-action-button"
onClick={() => onEditDrainage(signature, item)}
size="sm"
variant="ghost"
>
编辑
</Button>
<Button
className="enterprise-signature-action-button"
onClick={() =>
setDeleteTarget({
kind: 'drainage',
signatureId: signature.id,
id: item.id,
name: item.url,
})
}
size="sm"
variant="danger"
>
删除
</Button>
</span>
</div>
);
})}
</div>
) : (
<p className="muted">暂无引流信息</p>
)}
<div className="drainage-panel__footer">
<Button
icon={<Plus size={16} />}
onClick={() => onAddDrainage(signature)}
size="sm"
variant="ghost"
>
添加引流信息
</Button>
</div>
</div>
) : null}
</article>
);
})}
<Pagination
nextDisabled={currentPage >= totalPages}
onNext={() => setPage((value) => Math.min(totalPages, value + 1))}
onPrevious={() => setPage((value) => Math.max(1, value - 1))}
page={currentPage}
totalPages={totalPages}
onPageChange={setPage}
previousDisabled={currentPage <= 1}
total={total}
/>
{filteredSignatures.length === 0 ? <div className="ui-table__empty">暂无企业签名</div> : null}
</div>
);
}