Модалка отправки отклика: кнопка в колонке действий, генерация с прелоадером, отправка по готовности текста
This commit is contained in:
1 parent
e4cd06a23d
commit
2d781737f2
1 file changed
+106
@@ -86,6 +86,12 @@
|
|||||||
max-height: 92vh; overflow: auto; }
|
max-height: 92vh; overflow: auto; }
|
||||||
.modal-box img { max-width: 100%; border: 1px solid var(--border);
|
.modal-box img { max-width: 100%; border: 1px solid var(--border);
|
||||||
border-radius: 8px; display: block; }
|
border-radius: 8px; display: block; }
|
||||||
|
.spinner { width: 18px; height: 18px; border: 2px solid var(--border);
|
||||||
|
border-top-color: var(--accent); border-radius: 50%;
|
||||||
|
animation: spin .8s linear infinite; flex-shrink: 0; }
|
||||||
|
@keyframes spin { to { transform: rotate(360deg); } }
|
||||||
|
.loading { display: flex; align-items: center; gap: 10px;
|
||||||
|
color: var(--muted); margin-bottom: 8px; }
|
||||||
::-webkit-scrollbar { width: 10px; height: 10px; }
|
::-webkit-scrollbar { width: 10px; height: 10px; }
|
||||||
::-webkit-scrollbar-track { background: var(--bg); }
|
::-webkit-scrollbar-track { background: var(--bg); }
|
||||||
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 5px; }
|
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 5px; }
|
||||||
@@ -240,6 +246,28 @@
|
|||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<!-- ============ МОДАЛКА: ОТПРАВКА ОТКЛИКА ============ -->
|
||||||
|
<div id="send-modal" class="modal hidden" onclick="if(event.target===this) closeSendModal()">
|
||||||
|
<div class="modal-box">
|
||||||
|
<div class="toolbar">
|
||||||
|
<h2 style="margin:0">Отправить отклик</h2>
|
||||||
|
<div class="spacer"></div>
|
||||||
|
<button class="secondary" onclick="closeSendModal()">Закрыть</button>
|
||||||
|
</div>
|
||||||
|
<div id="send-vacancy-title" class="muted" style="margin-bottom:8px"></div>
|
||||||
|
<div id="send-loading" class="loading">
|
||||||
|
<div class="spinner"></div>
|
||||||
|
<span>Текст генерируется...</span>
|
||||||
|
</div>
|
||||||
|
<textarea id="send-letter" style="min-height:140px" disabled
|
||||||
|
placeholder="Сопроводительное письмо появится здесь..."></textarea>
|
||||||
|
<div class="toolbar" style="margin-top:12px">
|
||||||
|
<button id="btn-send-letter" onclick="sendFromModal()" disabled>Отправить отклик</button>
|
||||||
|
<span id="send-modal-note" class="muted"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ============ МОДАЛКА: ПРОВЕРКА ОТКЛИКА ============ -->
|
<!-- ============ МОДАЛКА: ПРОВЕРКА ОТКЛИКА ============ -->
|
||||||
<div id="shot-modal" class="modal hidden" onclick="if(event.target===this) closeShot()">
|
<div id="shot-modal" class="modal hidden" onclick="if(event.target===this) closeShot()">
|
||||||
<div class="modal-box">
|
<div class="modal-box">
|
||||||
@@ -264,6 +292,7 @@ let vacancies = [];
|
|||||||
let generatedTexts = {};
|
let generatedTexts = {};
|
||||||
let currentShotId = null;
|
let currentShotId = null;
|
||||||
let sentTexts = new Set();
|
let sentTexts = new Set();
|
||||||
|
let sendModalId = null;
|
||||||
|
|
||||||
// ---------- Вкладки ----------
|
// ---------- Вкладки ----------
|
||||||
function showTab(name) {
|
function showTab(name) {
|
||||||
@@ -511,6 +540,7 @@ function renderVacancies() {
|
|||||||
<td>${exp}</td>
|
<td>${exp}</td>
|
||||||
<td>${status}</td>
|
<td>${status}</td>
|
||||||
<td class="actions-cell">
|
<td class="actions-cell">
|
||||||
|
<button class="secondary" onclick="openSendModal('${v.id}')">Отправить отклик</button>
|
||||||
<button class="secondary" onclick="openVacancy('${v.id}')">Открыть</button>
|
<button class="secondary" onclick="openVacancy('${v.id}')">Открыть</button>
|
||||||
<button class="secondary" onclick="markDone('${v.id}')">Отправил</button>
|
<button class="secondary" onclick="markDone('${v.id}')">Отправил</button>
|
||||||
</td>
|
</td>
|
||||||
@@ -589,6 +619,82 @@ function escapeHtml(s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------- Открытие, проверка и отправка ----------
|
// ---------- Открытие, проверка и отправка ----------
|
||||||
|
async function openSendModal(id) {
|
||||||
|
if (!currentAccount) { alert('Сначала выберите аккаунт'); return; }
|
||||||
|
const v = vacancies.find(x => x.id === id);
|
||||||
|
if (!v) return;
|
||||||
|
sendModalId = id;
|
||||||
|
document.getElementById('send-vacancy-title').textContent = `${v.title} — ${v.company || ''}`;
|
||||||
|
document.getElementById('send-letter').value = '';
|
||||||
|
document.getElementById('send-letter').disabled = true;
|
||||||
|
document.getElementById('btn-send-letter').disabled = true;
|
||||||
|
document.getElementById('send-modal-note').textContent = '';
|
||||||
|
document.getElementById('send-modal').classList.remove('hidden');
|
||||||
|
|
||||||
|
const cached = generatedTexts[id];
|
||||||
|
if (cached) {
|
||||||
|
// Текст уже сгенерирован ранее — показываем сразу
|
||||||
|
document.getElementById('send-letter').value = cached;
|
||||||
|
document.getElementById('send-letter').disabled = false;
|
||||||
|
document.getElementById('btn-send-letter').disabled = false;
|
||||||
|
document.getElementById('send-loading').classList.add('hidden');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генерируем текст (прелоадер виден)
|
||||||
|
document.getElementById('send-loading').classList.remove('hidden');
|
||||||
|
try {
|
||||||
|
const data = await api(`/api/accounts/${currentAccount}/generate`, {
|
||||||
|
method: 'POST', body: JSON.stringify({ ids: [id] }),
|
||||||
|
});
|
||||||
|
document.getElementById('send-loading').classList.add('hidden');
|
||||||
|
if (data.texts && data.texts[id]) {
|
||||||
|
generatedTexts[id] = data.texts[id];
|
||||||
|
document.getElementById('send-letter').value = data.texts[id];
|
||||||
|
document.getElementById('send-letter').disabled = false;
|
||||||
|
document.getElementById('btn-send-letter').disabled = false;
|
||||||
|
renderTexts();
|
||||||
|
} else if (data.errors && data.errors[id]) {
|
||||||
|
document.getElementById('send-modal-note').textContent = 'Ошибка: ' + data.errors[id];
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
document.getElementById('send-loading').classList.add('hidden');
|
||||||
|
document.getElementById('send-modal-note').textContent = 'Ошибка: ' + e.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeSendModal() {
|
||||||
|
document.getElementById('send-modal').classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendFromModal() {
|
||||||
|
if (!currentAccount || !sendModalId) return;
|
||||||
|
const message = document.getElementById('send-letter').value;
|
||||||
|
const btn = document.getElementById('btn-send-letter');
|
||||||
|
const note = document.getElementById('send-modal-note');
|
||||||
|
btn.disabled = true; note.textContent = 'Отправляю...';
|
||||||
|
try {
|
||||||
|
const data = await api(`/api/accounts/${currentAccount}/send-now`, {
|
||||||
|
method: 'POST', body: JSON.stringify({ id: sendModalId, message }),
|
||||||
|
});
|
||||||
|
if (data.screenshot) {
|
||||||
|
showShotModal(sendModalId, data.screenshot, data.note || 'Отклик отправлен', true);
|
||||||
|
sentTexts.add(sendModalId);
|
||||||
|
const v = vacancies.find(x => x.id === sendModalId);
|
||||||
|
if (v) v.responded = true;
|
||||||
|
renderVacancies();
|
||||||
|
renderTexts();
|
||||||
|
closeSendModal();
|
||||||
|
} else {
|
||||||
|
note.textContent = data.note || 'Не удалось отправить отклик';
|
||||||
|
btn.disabled = false;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
note.textContent = 'Ошибка: ' + e.message;
|
||||||
|
btn.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function showShotModal(id, screenshot, note, sent) {
|
function showShotModal(id, screenshot, note, sent) {
|
||||||
currentShotId = id;
|
currentShotId = id;
|
||||||
document.getElementById('shot-img').src = screenshot + '?t=' + Date.now();
|
document.getElementById('shot-img').src = screenshot + '?t=' + Date.now();
|
||||||
|
|||||||
Reference in new issue
Block a user