155 lines
6.7 KiB
JavaScript
155 lines
6.7 KiB
JavaScript
import React from "react";
|
||
|
||
export default function TasksSidebar({
|
||
createTask,
|
||
taskSearch,
|
||
setTaskSearch,
|
||
taskFilter,
|
||
setTaskFilter,
|
||
taskSort,
|
||
setTaskSort,
|
||
filteredTasks,
|
||
taskStatusMap,
|
||
selectedTaskId,
|
||
selectTask,
|
||
deleteTask,
|
||
hasSelectedTask,
|
||
formatCountdown,
|
||
formatTimestamp,
|
||
accountById,
|
||
formatAccountLabel
|
||
}) {
|
||
return (
|
||
<section className="card sticky">
|
||
<div className="row-header">
|
||
<h3>Задачи</h3>
|
||
<button className="ghost" type="button" onClick={createTask}>Новая задача</button>
|
||
</div>
|
||
<div className="task-controls">
|
||
<div className="task-search">
|
||
<input
|
||
type="text"
|
||
value={taskSearch}
|
||
onChange={(event) => setTaskSearch(event.target.value)}
|
||
placeholder="Поиск по названию или ссылке"
|
||
/>
|
||
</div>
|
||
<div className="task-filters">
|
||
<button
|
||
type="button"
|
||
className={`chip ${taskFilter === "all" ? "active" : ""}`}
|
||
onClick={() => setTaskFilter("all")}
|
||
>
|
||
Все
|
||
</button>
|
||
<button
|
||
type="button"
|
||
className={`chip ${taskFilter === "running" ? "active" : ""}`}
|
||
onClick={() => setTaskFilter("running")}
|
||
>
|
||
Запущены
|
||
</button>
|
||
<button
|
||
type="button"
|
||
className={`chip ${taskFilter === "stopped" ? "active" : ""}`}
|
||
onClick={() => setTaskFilter("stopped")}
|
||
>
|
||
Остановлены
|
||
</button>
|
||
</div>
|
||
<div className="row-inline">
|
||
<label className="select-inline">
|
||
<span>Сортировка</span>
|
||
<select value={taskSort} onChange={(event) => setTaskSort(event.target.value)}>
|
||
<option value="activity">Активные сверху</option>
|
||
<option value="queue">По очереди</option>
|
||
<option value="limit">По лимиту</option>
|
||
<option value="lastMessage">По последнему сообщению</option>
|
||
<option value="id">По ID</option>
|
||
</select>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div className="task-list">
|
||
{filteredTasks.length === 0 && <div className="empty">Совпадений нет.</div>}
|
||
{filteredTasks.map((task) => {
|
||
const status = taskStatusMap[task.id];
|
||
const statusLabel = status ? (status.running ? "Запущено" : "Остановлено") : "—";
|
||
const statusClass = status ? (status.running ? "ok" : "off") : "off";
|
||
const unconfirmedCount = status ? Number(status.unconfirmedCount || 0) : 0;
|
||
const queueLabel = status ? `Очередь: ${status.queueCount}` : "Очередь: —";
|
||
const warmupStart = Number(task.warmupStartLimit || 0);
|
||
const warmupEnabled = Boolean(task.warmupEnabled);
|
||
const warmupDelta = status && warmupEnabled && warmupStart > 0 && Number(status.dailyLimit || 0) > warmupStart
|
||
? Number(status.dailyLimit || 0) - warmupStart
|
||
: 0;
|
||
const dailyLabel = status
|
||
? `Лимит сегодня: ${status.dailyUsed}/${status.dailyLimit}${warmupDelta > 0 ? ` (старт ${warmupStart} +${warmupDelta} прогрев)` : ""}`
|
||
: "Лимит сегодня: —";
|
||
const cycleLabel = status && status.running ? `Цикл: ${formatCountdown(status.nextRunAt)}` : "Цикл: —";
|
||
const lastMessageRaw = status && status.monitorInfo && status.monitorInfo.lastMessageAt
|
||
? status.monitorInfo.lastMessageAt
|
||
: "";
|
||
const lastMessage = formatTimestamp(lastMessageRaw);
|
||
const lastSource = status && status.monitorInfo && status.monitorInfo.lastSource
|
||
? status.monitorInfo.lastSource
|
||
: "—";
|
||
const monitoring = Boolean(status && status.monitorInfo && status.monitorInfo.monitoring);
|
||
const monitorAccountIds = status && status.monitorInfo && status.monitorInfo.accountIds
|
||
? status.monitorInfo.accountIds
|
||
: (status && status.monitorInfo && status.monitorInfo.accountId ? [status.monitorInfo.accountId] : []);
|
||
const monitorLabels = monitorAccountIds
|
||
.map((id) => {
|
||
const account = accountById.get(id);
|
||
return account ? formatAccountLabel(account) : String(id);
|
||
})
|
||
.filter(Boolean);
|
||
const monitorLabel = monitorLabels.length
|
||
? (monitorLabels.length > 2 ? `${monitorLabels.length} аккаунта` : monitorLabels.join(", "))
|
||
: "—";
|
||
const tooltip = [
|
||
`Статус: ${statusLabel}`,
|
||
`Очередь: ${status ? status.queueCount : "—"}`,
|
||
`Лимит сегодня: ${status ? `${status.dailyUsed}/${status.dailyLimit}${warmupDelta > 0 ? ` (старт ${warmupStart} +${warmupDelta} прогрев)` : ""}` : "—"}`,
|
||
`Мониторинг: ${monitoring ? "активен" : "нет"}`,
|
||
`Мониторит: ${monitorLabel}`,
|
||
`Последнее: ${lastMessage}`,
|
||
`Источник: ${lastSource}`
|
||
].join(" | ");
|
||
return (
|
||
<button
|
||
key={task.id}
|
||
type="button"
|
||
className={`task-item ${selectedTaskId === task.id ? "active" : ""}`}
|
||
onClick={() => selectTask(task.id)}
|
||
title={tooltip}
|
||
>
|
||
<div className="task-info">
|
||
<div className="task-title-row">
|
||
<div className="task-title">{task.name || `Задача #${task.id}`}</div>
|
||
<div className="task-badge-row">
|
||
<div className={`task-badge ${statusClass}`}>{statusLabel}</div>
|
||
{unconfirmedCount > 0 && (
|
||
<div className="task-badge warn" title={`Не подтверждено: ${unconfirmedCount}`}>
|
||
Не подтверждено: {unconfirmedCount}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
<div className="task-meta-row">
|
||
<span className="task-meta">{queueLabel}</span>
|
||
<span className="task-meta">{dailyLabel}</span>
|
||
<span className="task-meta">{cycleLabel}</span>
|
||
</div>
|
||
</div>
|
||
</button>
|
||
);
|
||
})}
|
||
</div>
|
||
<div className="sidebar-actions">
|
||
<button className="danger" type="button" onClick={deleteTask} disabled={!hasSelectedTask}>Удалить задачу</button>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|