680 lines
28 KiB
JavaScript
680 lines
28 KiB
JavaScript
import AddCircleOutlineOutlined from "@mui/icons-material/AddCircleOutlineOutlined";
|
|
import AutoAwesomeOutlined from "@mui/icons-material/AutoAwesomeOutlined";
|
|
import EditOutlined from "@mui/icons-material/EditOutlined";
|
|
import RestartAltOutlined from "@mui/icons-material/RestartAltOutlined";
|
|
import SendOutlined from "@mui/icons-material/SendOutlined";
|
|
import SettingsOutlined from "@mui/icons-material/SettingsOutlined";
|
|
import MenuItem from "@mui/material/MenuItem";
|
|
import TextField from "@mui/material/TextField";
|
|
import Tooltip from "@mui/material/Tooltip";
|
|
import { AdminFormDialog, AdminFormSection } from "@/shared/ui/AdminFormDialog.jsx";
|
|
import { Button } from "@/shared/ui/Button.jsx";
|
|
import { DataState } from "@/shared/ui/DataState.jsx";
|
|
import { DataTable } from "@/shared/ui/DataTable.jsx";
|
|
import { IconButton } from "@/shared/ui/IconButton.jsx";
|
|
import { useAppUserDetail } from "@/features/app-users/components/AppUserDetailContext.jsx";
|
|
import { formatMillis } from "@/shared/utils/time.js";
|
|
import {
|
|
prettyIdSourceLabels,
|
|
prettyIdSourceOptions,
|
|
prettyIdStatusLabels,
|
|
prettyIdStatusOptions,
|
|
prettyLevelTrackOptions,
|
|
prettyPoolStatusLabels,
|
|
prettyPoolStatusOptions,
|
|
prettyRuleOptions,
|
|
} from "@/features/app-users/constants.js";
|
|
import { usePrettyIdsPage } from "@/features/app-users/hooks/usePrettyIdsPage.js";
|
|
import styles from "@/features/app-users/app-users.module.css";
|
|
|
|
export function AppUserPrettyIdsPage() {
|
|
const page = usePrettyIdsPage();
|
|
const pools = page.poolQuery.data.items || [];
|
|
const prettyIds = page.idQuery.data.items || [];
|
|
|
|
return (
|
|
<section className={styles.root}>
|
|
<div className={`${styles.contentPanel} ${styles.prettyContentPanel}`}>
|
|
<div className={styles.prettyWorkspace}>
|
|
<section className={`${styles.prettyPane} ${styles.prettyPoolPane}`}>
|
|
<PaneHeader
|
|
actions={
|
|
<>
|
|
{page.abilities.canPrettyGenerate ? (
|
|
<Button
|
|
startIcon={<AutoAwesomeOutlined fontSize="small" />}
|
|
onClick={() => page.openGenerate(null)}
|
|
>
|
|
批量生成
|
|
</Button>
|
|
) : null}
|
|
{page.abilities.canPrettyUpdate ? (
|
|
<Button
|
|
startIcon={<AddCircleOutlineOutlined fontSize="small" />}
|
|
variant="primary"
|
|
onClick={page.openCreatePool}
|
|
>
|
|
新增池
|
|
</Button>
|
|
) : null}
|
|
</>
|
|
}
|
|
count={page.poolQuery.data.total}
|
|
title="靓号池"
|
|
/>
|
|
<div className={styles.prettyFilters}>
|
|
<TextField
|
|
className={styles.compactControl}
|
|
label="等级轨道"
|
|
select
|
|
size="small"
|
|
value={page.poolTrack}
|
|
onChange={(event) => page.changePoolTrack(event.target.value)}
|
|
>
|
|
<MenuItem value="">全部等级轨道</MenuItem>
|
|
{prettyLevelTrackOptions.map(([value, label]) => (
|
|
<MenuItem key={value} value={value}>
|
|
{label}
|
|
</MenuItem>
|
|
))}
|
|
</TextField>
|
|
<TextField
|
|
className={styles.compactControl}
|
|
label="状态"
|
|
select
|
|
size="small"
|
|
value={page.poolStatus}
|
|
onChange={(event) => page.changePoolStatus(event.target.value)}
|
|
>
|
|
{prettyPoolStatusOptions.map(([value, label]) => (
|
|
<MenuItem key={value || "all"} value={value}>
|
|
{label}
|
|
</MenuItem>
|
|
))}
|
|
</TextField>
|
|
<Button startIcon={<RestartAltOutlined fontSize="small" />} onClick={page.resetPoolFilters}>
|
|
重置
|
|
</Button>
|
|
</div>
|
|
<DataState
|
|
error={page.poolQuery.error}
|
|
loading={page.poolQuery.loading}
|
|
onRetry={page.poolQuery.reload}
|
|
>
|
|
<DataTable
|
|
className={styles.compactTableFrame}
|
|
columns={poolColumns(page)}
|
|
items={pools}
|
|
minWidth="720px"
|
|
pagination={
|
|
page.poolQuery.data.total > 0
|
|
? {
|
|
page: page.poolPage,
|
|
pageSize: page.poolQuery.data.pageSize || 20,
|
|
total: page.poolQuery.data.total,
|
|
onPageChange: page.setPoolPage,
|
|
}
|
|
: undefined
|
|
}
|
|
rowKey={(pool) => pool.poolId}
|
|
/>
|
|
</DataState>
|
|
</section>
|
|
|
|
<section className={`${styles.prettyPane} ${styles.prettyIDPane}`}>
|
|
<PaneHeader
|
|
actions={
|
|
page.abilities.canPrettyGrant ? (
|
|
<Button
|
|
startIcon={<SendOutlined fontSize="small" />}
|
|
variant="primary"
|
|
onClick={page.openGrant}
|
|
>
|
|
后台发放
|
|
</Button>
|
|
) : null
|
|
}
|
|
count={page.idQuery.data.total}
|
|
title="靓号列表"
|
|
/>
|
|
<div className={styles.prettyFilters}>
|
|
<TextField
|
|
className={styles.search}
|
|
label="搜索"
|
|
size="small"
|
|
value={page.idKeyword}
|
|
onChange={(event) => page.changeIDKeyword(event.target.value)}
|
|
/>
|
|
<TextField
|
|
className={styles.compactControl}
|
|
label="靓号池"
|
|
select
|
|
size="small"
|
|
value={page.idPoolId}
|
|
onChange={(event) => page.changeIDPool(event.target.value)}
|
|
>
|
|
<MenuItem value="">全部靓号池</MenuItem>
|
|
{page.poolOptions.map((option) => (
|
|
<MenuItem key={option.value} value={option.value}>
|
|
{option.label}
|
|
</MenuItem>
|
|
))}
|
|
</TextField>
|
|
<TextField
|
|
className={styles.compactControl}
|
|
label="来源"
|
|
select
|
|
size="small"
|
|
value={page.idSource}
|
|
onChange={(event) => page.changeIDSource(event.target.value)}
|
|
>
|
|
{prettyIdSourceOptions.map(([value, label]) => (
|
|
<MenuItem key={value || "all"} value={value}>
|
|
{label}
|
|
</MenuItem>
|
|
))}
|
|
</TextField>
|
|
<TextField
|
|
className={styles.compactControl}
|
|
label="状态"
|
|
select
|
|
size="small"
|
|
value={page.idStatus}
|
|
onChange={(event) => page.changeIDStatus(event.target.value)}
|
|
>
|
|
{prettyIdStatusOptions.map(([value, label]) => (
|
|
<MenuItem key={value || "all"} value={value}>
|
|
{label}
|
|
</MenuItem>
|
|
))}
|
|
</TextField>
|
|
<TextField
|
|
className={styles.userIDControl}
|
|
label="占用用户"
|
|
size="small"
|
|
value={page.assignedUserId}
|
|
onChange={(event) => page.changeAssignedUserId(event.target.value)}
|
|
/>
|
|
<Button startIcon={<RestartAltOutlined fontSize="small" />} onClick={page.resetIDFilters}>
|
|
重置
|
|
</Button>
|
|
</div>
|
|
<DataState
|
|
error={page.idQuery.error}
|
|
loading={page.idQuery.loading}
|
|
onRetry={page.idQuery.reload}
|
|
>
|
|
<DataTable
|
|
className={styles.compactTableFrame}
|
|
columns={idColumns(page)}
|
|
items={prettyIds}
|
|
minWidth="980px"
|
|
pagination={
|
|
page.idQuery.data.total > 0
|
|
? {
|
|
page: page.idPage,
|
|
pageSize: page.idQuery.data.pageSize || 20,
|
|
total: page.idQuery.data.total,
|
|
onPageChange: page.setIdPage,
|
|
}
|
|
: undefined
|
|
}
|
|
rowKey={(item) => item.prettyId}
|
|
/>
|
|
</DataState>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
|
|
<PoolDialog page={page} />
|
|
<GenerateDialog page={page} />
|
|
<GrantDialog page={page} />
|
|
<StatusDialog page={page} />
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function PaneHeader({ actions, count, title }) {
|
|
return (
|
|
<div className={styles.prettyPaneHeader}>
|
|
<div className={styles.prettyPaneTitle}>
|
|
<h2>{title}</h2>
|
|
<span>共 {Number(count || 0)} 条</span>
|
|
</div>
|
|
<div className={styles.toolbarActions}>{actions}</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function poolColumns(page) {
|
|
return [
|
|
{
|
|
key: "pool",
|
|
label: "靓号池",
|
|
width: "minmax(220px, 1.2fr)",
|
|
render: (pool) => (
|
|
<div className={styles.stack}>
|
|
<span className={styles.primaryText}>{pool.name || "-"}</span>
|
|
<span className={styles.meta}>{pool.poolId}</span>
|
|
<span className={styles.meta}>更新 {formatMillis(pool.updatedAtMs || pool.createdAtMs)}</span>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: "rule",
|
|
label: "规则 / 等级",
|
|
width: "minmax(160px, 0.9fr)",
|
|
render: (pool) => (
|
|
<div className={styles.stack}>
|
|
<span>{ruleLabel(pool.ruleType)}</span>
|
|
<span className={styles.meta}>
|
|
{trackLabel(pool.levelTrack)} {pool.minLevel}-{pool.maxLevel}
|
|
</span>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: "status",
|
|
label: "状态",
|
|
width: "minmax(110px, 0.6fr)",
|
|
render: (pool) => <StatusBadge labels={prettyPoolStatusLabels} status={pool.status} />,
|
|
},
|
|
{
|
|
key: "actions",
|
|
label: "操作",
|
|
width: "104px",
|
|
render: (pool) => (
|
|
<div className={styles.rowActions}>
|
|
{page.abilities.canPrettyUpdate ? (
|
|
<ActionIcon label="编辑" onClick={() => page.openEditPool(pool)}>
|
|
<EditOutlined fontSize="small" />
|
|
</ActionIcon>
|
|
) : null}
|
|
{page.abilities.canPrettyGenerate ? (
|
|
<ActionIcon label="生成" onClick={() => page.openGenerate(pool)}>
|
|
<AutoAwesomeOutlined fontSize="small" />
|
|
</ActionIcon>
|
|
) : null}
|
|
</div>
|
|
),
|
|
},
|
|
];
|
|
}
|
|
|
|
function idColumns(page) {
|
|
return [
|
|
{
|
|
key: "display",
|
|
label: "靓号",
|
|
width: "minmax(190px, 1fr)",
|
|
render: (item) => (
|
|
<div className={styles.stack}>
|
|
<div className={styles.nameLine}>
|
|
<span className={styles.name}>{item.displayUserId || "-"}</span>
|
|
</div>
|
|
<div className={styles.meta}>{item.prettyId}</div>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: "pool",
|
|
label: "来源 / 池",
|
|
width: "minmax(180px, 0.9fr)",
|
|
render: (item) => (
|
|
<div className={styles.stack}>
|
|
<span>{prettyIdSourceLabels[item.source] || item.source || "-"}</span>
|
|
<span className={styles.meta}>{item.pool?.name || item.poolId || "-"}</span>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: "status",
|
|
label: "状态",
|
|
width: "minmax(110px, 0.6fr)",
|
|
render: (item) => <StatusBadge labels={prettyIdStatusLabels} status={item.status} />,
|
|
},
|
|
{
|
|
key: "assigned",
|
|
label: "占用 / 时间",
|
|
width: "minmax(190px, 0.9fr)",
|
|
render: (item) => (
|
|
<div className={styles.stack}>
|
|
<AssignedUserCell item={item} />
|
|
<span className={styles.meta}>
|
|
{item.assignedAtMs ? formatMillis(item.assignedAtMs) : formatMillis(item.createdAtMs)}
|
|
</span>
|
|
<span className={styles.meta}>
|
|
{item.releasedAtMs ? `释放 ${formatMillis(item.releasedAtMs)}` : "-"}
|
|
</span>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: "actions",
|
|
label: "操作",
|
|
width: "minmax(96px, 0.5fr)",
|
|
render: (item) =>
|
|
page.abilities.canPrettyUpdate && canChangePrettyStatus(item) ? (
|
|
<div className={styles.rowActions}>
|
|
<ActionIcon label="状态" onClick={() => page.openStatus(item)}>
|
|
<SettingsOutlined fontSize="small" />
|
|
</ActionIcon>
|
|
</div>
|
|
) : (
|
|
"-"
|
|
),
|
|
},
|
|
];
|
|
}
|
|
|
|
function AssignedUserCell({ item }) {
|
|
const { openAppUserDetail } = useAppUserDetail();
|
|
const assignedUserId = formatEntityID(item.assignedUserId);
|
|
if (!item.assignedUserId || !openAppUserDetail) {
|
|
return <span>{assignedUserId}</span>;
|
|
}
|
|
|
|
return (
|
|
<button
|
|
className={styles.countryButton}
|
|
type="button"
|
|
onClick={(event) => {
|
|
event.stopPropagation();
|
|
openAppUserDetail({
|
|
displayUserId: assignedUserId,
|
|
prettyDisplayUserId: item.displayUserId,
|
|
prettyId: item.prettyId,
|
|
userId: item.assignedUserId,
|
|
});
|
|
}}
|
|
>
|
|
{assignedUserId}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
function PoolDialog({ page }) {
|
|
const editing = Boolean(page.activePool);
|
|
return (
|
|
<AdminFormDialog
|
|
loading={page.loadingAction === "pool"}
|
|
open={page.activeDialog === "pool"}
|
|
submitDisabled={!page.abilities.canPrettyUpdate || page.loadingAction === "pool"}
|
|
title={editing ? "编辑靓号池" : "新增靓号池"}
|
|
onClose={page.closeDialog}
|
|
onSubmit={page.submitPool}
|
|
>
|
|
<AdminFormSection title="池配置">
|
|
<TextField
|
|
label="名称"
|
|
required
|
|
value={page.poolForm.name}
|
|
onChange={(event) => page.setPoolForm({ ...page.poolForm, name: event.target.value })}
|
|
/>
|
|
<TextField
|
|
label="等级轨道"
|
|
required
|
|
select
|
|
value={page.poolForm.levelTrack}
|
|
onChange={(event) => page.setPoolForm({ ...page.poolForm, levelTrack: event.target.value })}
|
|
>
|
|
{prettyLevelTrackOptions.map(([value, label]) => (
|
|
<MenuItem key={value} value={value}>
|
|
{label}
|
|
</MenuItem>
|
|
))}
|
|
</TextField>
|
|
<div className={styles.formGridTwo}>
|
|
<TextField
|
|
label="最低等级"
|
|
required
|
|
type="number"
|
|
value={page.poolForm.minLevel}
|
|
onChange={(event) => page.setPoolForm({ ...page.poolForm, minLevel: event.target.value })}
|
|
/>
|
|
<TextField
|
|
label="最高等级"
|
|
required
|
|
type="number"
|
|
value={page.poolForm.maxLevel}
|
|
onChange={(event) => page.setPoolForm({ ...page.poolForm, maxLevel: event.target.value })}
|
|
/>
|
|
</div>
|
|
<div className={styles.formGridTwo}>
|
|
<TextField
|
|
label="规则"
|
|
required
|
|
select
|
|
value={page.poolForm.ruleType}
|
|
onChange={(event) => page.setPoolForm({ ...page.poolForm, ruleType: event.target.value })}
|
|
>
|
|
{prettyRuleOptions.map(([value, label]) => (
|
|
<MenuItem key={value} value={value}>
|
|
{label}
|
|
</MenuItem>
|
|
))}
|
|
</TextField>
|
|
<TextField
|
|
label="状态"
|
|
required
|
|
select
|
|
value={page.poolForm.status}
|
|
onChange={(event) => page.setPoolForm({ ...page.poolForm, status: event.target.value })}
|
|
>
|
|
{prettyPoolStatusOptions
|
|
.filter(([value]) => value)
|
|
.map(([value, label]) => (
|
|
<MenuItem key={value} value={value}>
|
|
{label}
|
|
</MenuItem>
|
|
))}
|
|
</TextField>
|
|
</div>
|
|
<TextField
|
|
label="排序"
|
|
type="number"
|
|
value={page.poolForm.sortOrder}
|
|
onChange={(event) => page.setPoolForm({ ...page.poolForm, sortOrder: event.target.value })}
|
|
/>
|
|
<TextField
|
|
label="规则配置"
|
|
minRows={3}
|
|
multiline
|
|
value={page.poolForm.ruleConfigJson}
|
|
onChange={(event) => page.setPoolForm({ ...page.poolForm, ruleConfigJson: event.target.value })}
|
|
/>
|
|
</AdminFormSection>
|
|
</AdminFormDialog>
|
|
);
|
|
}
|
|
|
|
function GenerateDialog({ page }) {
|
|
return (
|
|
<AdminFormDialog
|
|
loading={page.loadingAction === "generate"}
|
|
open={page.activeDialog === "generate"}
|
|
submitDisabled={!page.abilities.canPrettyGenerate || page.loadingAction === "generate"}
|
|
title="批量生成靓号"
|
|
onClose={page.closeDialog}
|
|
onSubmit={page.submitGenerate}
|
|
>
|
|
<AdminFormSection title="生成配置">
|
|
<TextField
|
|
label="靓号池"
|
|
required
|
|
select
|
|
value={page.generateForm.poolId}
|
|
onChange={(event) => page.setGenerateForm({ ...page.generateForm, poolId: event.target.value })}
|
|
>
|
|
{page.poolOptions.map((option) => (
|
|
<MenuItem key={option.value} value={option.value}>
|
|
{option.label}
|
|
</MenuItem>
|
|
))}
|
|
</TextField>
|
|
<TextField
|
|
label="规则"
|
|
required
|
|
select
|
|
value={page.generateForm.ruleType}
|
|
onChange={(event) => page.setGenerateForm({ ...page.generateForm, ruleType: event.target.value })}
|
|
>
|
|
{prettyRuleOptions.map(([value, label]) => (
|
|
<MenuItem key={value} value={value}>
|
|
{label}
|
|
</MenuItem>
|
|
))}
|
|
</TextField>
|
|
<TextField
|
|
label="生成数量"
|
|
required
|
|
type="number"
|
|
value={page.generateForm.count}
|
|
onChange={(event) => page.setGenerateForm({ ...page.generateForm, count: event.target.value })}
|
|
/>
|
|
<TextField
|
|
label="规则配置"
|
|
minRows={3}
|
|
multiline
|
|
value={page.generateForm.ruleConfigJson}
|
|
onChange={(event) =>
|
|
page.setGenerateForm({ ...page.generateForm, ruleConfigJson: event.target.value })
|
|
}
|
|
/>
|
|
</AdminFormSection>
|
|
</AdminFormDialog>
|
|
);
|
|
}
|
|
|
|
function GrantDialog({ page }) {
|
|
return (
|
|
<AdminFormDialog
|
|
loading={page.loadingAction === "grant"}
|
|
open={page.activeDialog === "grant"}
|
|
submitDisabled={!page.abilities.canPrettyGrant || page.loadingAction === "grant"}
|
|
title="后台发放靓号"
|
|
onClose={page.closeDialog}
|
|
onSubmit={page.submitGrant}
|
|
>
|
|
<AdminFormSection title="发放信息">
|
|
<TextField
|
|
label="用户 ID"
|
|
required
|
|
value={page.grantForm.targetUserId}
|
|
onChange={(event) => page.setGrantForm({ ...page.grantForm, targetUserId: event.target.value })}
|
|
/>
|
|
<TextField
|
|
label="靓号内容"
|
|
required
|
|
value={page.grantForm.displayUserId}
|
|
onChange={(event) => page.setGrantForm({ ...page.grantForm, displayUserId: event.target.value })}
|
|
/>
|
|
<TextField
|
|
label="有效天数"
|
|
type="number"
|
|
value={page.grantForm.durationDays}
|
|
onChange={(event) => page.setGrantForm({ ...page.grantForm, durationDays: event.target.value })}
|
|
/>
|
|
<TextField
|
|
label="原因"
|
|
value={page.grantForm.reason}
|
|
onChange={(event) => page.setGrantForm({ ...page.grantForm, reason: event.target.value })}
|
|
/>
|
|
</AdminFormSection>
|
|
</AdminFormDialog>
|
|
);
|
|
}
|
|
|
|
function StatusDialog({ page }) {
|
|
return (
|
|
<AdminFormDialog
|
|
loading={page.loadingAction === "status"}
|
|
open={page.activeDialog === "status"}
|
|
submitDisabled={!page.abilities.canPrettyUpdate || page.loadingAction === "status"}
|
|
title="修改靓号状态"
|
|
onClose={page.closeDialog}
|
|
onSubmit={page.submitStatus}
|
|
>
|
|
<AdminFormSection title="状态信息">
|
|
<TextField disabled label="靓号" value={page.activePrettyID?.displayUserId || ""} />
|
|
<TextField
|
|
label="状态"
|
|
required
|
|
select
|
|
value={page.statusForm.status}
|
|
onChange={(event) => page.setStatusForm({ ...page.statusForm, status: event.target.value })}
|
|
>
|
|
{prettyIdStatusOptions
|
|
.filter(([value]) => ["available", "disabled", "reserved"].includes(value))
|
|
.map(([value, label]) => (
|
|
<MenuItem key={value} value={value}>
|
|
{label}
|
|
</MenuItem>
|
|
))}
|
|
</TextField>
|
|
<TextField
|
|
label="原因"
|
|
value={page.statusForm.reason}
|
|
onChange={(event) => page.setStatusForm({ ...page.statusForm, reason: event.target.value })}
|
|
/>
|
|
</AdminFormSection>
|
|
</AdminFormDialog>
|
|
);
|
|
}
|
|
|
|
function ActionIcon({ children, label, onClick }) {
|
|
return (
|
|
<Tooltip arrow title={label}>
|
|
<span>
|
|
<IconButton label={label} onClick={onClick}>
|
|
{children}
|
|
</IconButton>
|
|
</span>
|
|
</Tooltip>
|
|
);
|
|
}
|
|
|
|
function StatusBadge({ labels, status }) {
|
|
return (
|
|
<span className={`status-badge status-badge--${statusTone(status)}`}>
|
|
<span className="status-point" />
|
|
{labels[status] || status || "-"}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
function statusTone(status) {
|
|
if (status === "active" || status === "available") {
|
|
return "running";
|
|
}
|
|
if (status === "assigned" || status === "reserved") {
|
|
return "warning";
|
|
}
|
|
if (status === "released" || status === "deleted") {
|
|
return "stopped";
|
|
}
|
|
return "danger";
|
|
}
|
|
|
|
function trackLabel(track) {
|
|
return prettyLevelTrackOptions.find(([value]) => value === track)?.[1] || track || "-";
|
|
}
|
|
|
|
function ruleLabel(ruleType) {
|
|
return prettyRuleOptions.find(([value]) => value === ruleType)?.[1] || ruleType || "-";
|
|
}
|
|
|
|
function formatEntityID(value) {
|
|
if (value === 0 || value === "0" || value === "" || value === undefined || value === null) {
|
|
return "-";
|
|
}
|
|
return String(value);
|
|
}
|
|
|
|
function canChangePrettyStatus(item) {
|
|
const assigned = item.assignedUserId;
|
|
const unassigned =
|
|
assigned === 0 || assigned === "0" || assigned === "" || assigned === undefined || assigned === null;
|
|
return ["available", "disabled", "reserved"].includes(item.status) && unassigned;
|
|
}
|