import React from "react"; import HelpTip from "./HelpTip.jsx"; import { getPresetLabel } from "../utils/presetLabels.js"; export default function TaskSettingsTab({ selectedTaskName, taskForm, setTaskForm, activePreset, setActivePreset, applyTaskPreset, formatAccountLabel, accountById, competitorText, setCompetitorText, roleMode, applyRoleMode, normalizeIntervals, taskStatus, perAccountInviteSum, hasSelectedTask, inviteAccessStatus, inviteAccessCheckedAt, confirmAccessStatus, confirmAccessCheckedAt, formatTimestamp, checkInviteAccess, checkConfirmAccess, accounts, showNotification, copyToClipboard, sanitizeTaskForm, hasPerAccountInviteLimits, fileImportResult, importInviteFile, fileImportForm, setFileImportForm, criticalErrorAccounts }) { const [customPresetName, setCustomPresetName] = React.useState(""); const [customPresets, setCustomPresets] = React.useState(() => { try { const raw = localStorage.getItem("tia_custom_presets_v1"); return raw ? JSON.parse(raw) : []; } catch { return []; } }); const changedSections = (nextForm) => { const sections = []; const hasAny = (keys) => keys.some((k) => JSON.stringify(taskForm[k]) !== JSON.stringify(nextForm[k])); if (hasAny(["name", "ourGroup"])) sections.push("Основное"); if (hasAny(["maxCompetitorBots", "maxOurBots", "separateConfirmRoles", "maxConfirmBots", "rolesMode", "requireSameBotInBoth"])) sections.push("Роли ботов и вступление"); if (hasAny(["inviteViaAdmins", "inviteAdminMasterId", "inviteAdminAnonymous", "inviteAdminAllowFlood", "inviteLinkOnFail"])) sections.push("Инвайт через админов"); if (hasAny(["minIntervalMinutes", "maxIntervalMinutes", "dailyLimit", "maxInvitesPerCycle", "warmupEnabled", "historyLimit"])) sections.push("Интервалы и лимиты"); return sections; }; const confirmApply = (nextForm, label, applyFn, sectionsOverride = null) => { const sections = Array.isArray(sectionsOverride) ? sectionsOverride : changedSections(nextForm); const msg = sections.length ? `Пресет "${label}" изменит текущие настройки.\n\nБудут изменены разделы:\n- ${sections.join("\n- ")}\n\nПрименить?` : `Пресет "${label}" не меняет текущие настройки.\nПрименить?`; if (!window.confirm(msg)) return; applyFn(); }; const saveCustomPreset = () => { const name = customPresetName.trim(); if (!name) { showNotification("Введите имя пресета.", "error"); return; } const next = [...customPresets.filter((p) => p.name !== name), { name, form: taskForm }]; setCustomPresets(next); localStorage.setItem("tia_custom_presets_v1", JSON.stringify(next)); setCustomPresetName(""); showNotification(`Сохранен пользовательский пресет: ${name}`, "success"); }; const applyCustomPreset = (preset) => { const nextForm = sanitizeTaskForm({ ...taskForm, ...preset.form }); confirmApply(nextForm, preset.name, () => { setTaskForm(nextForm); setActivePreset(`custom:${preset.name}`); showNotification(`Применен пользовательский пресет: ${preset.name}`, "success"); }); }; const deleteCustomPreset = (name) => { const next = customPresets.filter((preset) => preset.name !== name); setCustomPresets(next); localStorage.setItem("tia_custom_presets_v1", JSON.stringify(next)); if (activePreset === `custom:${name}`) { setActivePreset(""); } showNotification(`Удален пользовательский пресет: ${name}`, "success"); }; const renameCustomPreset = (name) => { const nextName = window.prompt("Новое имя пресета:", name); if (!nextName) return; const trimmed = nextName.trim(); if (!trimmed) return; const preset = customPresets.find((p) => p.name === name); if (!preset) return; const next = [ ...customPresets.filter((p) => p.name !== name && p.name !== trimmed), { ...preset, name: trimmed } ]; setCustomPresets(next); localStorage.setItem("tia_custom_presets_v1", JSON.stringify(next)); if (activePreset === `custom:${name}`) { setActivePreset(`custom:${trimmed}`); } showNotification(`Переименован пресет: ${name} → ${trimmed}`, "success"); }; const exportCustomPresets = () => { try { const payload = { version: 1, exportedAt: new Date().toISOString(), presets: customPresets }; const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = "custom-presets.json"; link.click(); URL.revokeObjectURL(url); showNotification("Пользовательские пресеты экспортированы.", "success"); } catch (error) { showNotification(error.message || String(error), "error"); } }; const importCustomPresets = async (event) => { const file = event.target.files && event.target.files[0]; event.target.value = ""; if (!file) return; try { const text = await file.text(); const parsed = JSON.parse(text); const list = Array.isArray(parsed) ? parsed : Array.isArray(parsed.presets) ? parsed.presets : []; const valid = list .filter((item) => item && typeof item.name === "string" && item.name.trim() && item.form && typeof item.form === "object") .map((item) => ({ name: item.name.trim(), form: item.form })); if (!valid.length) { showNotification("Файл не содержит корректных пресетов.", "error"); return; } const replaceAll = window.confirm("Заменить все текущие пользовательские пресеты?\n\nОК — заменить полностью\nОтмена — объединить с текущими"); const map = new Map(); if (!replaceAll) { customPresets.forEach((preset) => map.set(preset.name, preset)); } valid.forEach((preset) => map.set(preset.name, preset)); const next = Array.from(map.values()); setCustomPresets(next); localStorage.setItem("tia_custom_presets_v1", JSON.stringify(next)); showNotification(`Импортировано пресетов: ${valid.length}. Режим: ${replaceAll ? "замена" : "объединение"}.`, "success"); } catch (error) { showNotification(error.message || String(error), "error"); } }; const inviteChecks = Array.isArray(inviteAccessStatus) ? inviteAccessStatus : []; const inviteChecksById = React.useMemo(() => { const map = new Map(); inviteChecks.forEach((item) => { map.set(Number(item.accountId), item); }); return map; }, [inviteChecks]); const masterId = Number(taskForm.inviteAdminMasterId || 0); const masterAccount = masterId ? (accountById.get(masterId) || accounts.find((item) => Number(item.id) === masterId)) : null; const masterCheck = masterId ? inviteChecksById.get(masterId) : null; const checkedCount = inviteChecks.length; const canInviteCount = inviteChecks.filter((item) => item && item.canInvite).length; const confirmChecks = Array.isArray(confirmAccessStatus) ? confirmAccessStatus : []; const confirmCheckedCount = confirmChecks.length; const confirmOkCount = confirmChecks.filter((item) => item && item.ok).length; const diagnostics = [ { title: "Режим", value: taskForm.inviteViaAdmins ? "включен" : "выключен", tone: taskForm.inviteViaAdmins ? "ok" : "warn" }, { title: "Мастер-админ", value: masterId ? formatAccountLabel(masterAccount || { id: masterId, phone: `ID ${masterId}` }) : "не выбран", tone: masterId ? "ok" : "fail" }, { title: "Проверка прав", value: checkedCount ? `выполнена (${checkedCount} аккаунтов)` : "не выполнялась", tone: checkedCount ? "ok" : "warn" }, { title: "Права мастера", value: !masterId ? "нельзя проверить без выбора мастера" : !checkedCount ? "нет данных (нажмите «Проверить права»)" : masterCheck ? (masterCheck.canInvite ? "OK: может приглашать" : `ошибка: ${masterCheck.reason || "нет права приглашать"}`) : "мастер не вошел в результаты проверки", tone: !masterId ? "warn" : !checkedCount ? "warn" : (masterCheck && masterCheck.canInvite ? "ok" : "fail") }, { title: "Инвайтеры с правом", value: checkedCount ? `${canInviteCount}/${checkedCount}` : "—", tone: !checkedCount ? "warn" : canInviteCount > 0 ? "ok" : "fail" } ]; return (

Настройки задачи

Для: {selectedTaskName}
Основное
Пресеты:
Мастер-админ: {taskForm.inviteAdminMasterId ? formatAccountLabel(accountById.get(taskForm.inviteAdminMasterId)) : "не выбран"}
Выбранный пресет: {getPresetLabel(activePreset)}. При применении пресета будут изменены: «Роли ботов и вступление», «Инвайт через админов», «Интервалы и лимиты».
setCustomPresetName(event.target.value)} placeholder="Имя пользовательского пресета" /> {customPresets.map((preset) => ( ))}