Corp/backend/static/js/components/documentCrudHandler.js

275 lines
9.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @module documentCrudHandler
* @description Обработчик CRUD операций для документов с поддержкой RecipientPicker.
* Управляет созданием, редактированием и удалением документов через API.
*
* Использование:
* import { initDocumentCrud } from './documentCrudHandler.js';
* import { RecipientPicker } from './recipientPickerHandler.js';
*
* initDocumentCrud({
* apiListUrl: '/api/v1/documents/',
* apiDetailBase: '/api/v1/documents/',
* headers: { 'Authorization': 'Bearer TOKEN' },
* createPicker: recipientPickerInstance,
* editPicker: recipientPickerInstance
* });
*/
/**
* Переключает видимость блока получателей в зависимости от чекбокса "Отправить всем".
* @param {HTMLInputElement} checkbox - Чекбокс "sent_to_all"
* @param {HTMLElement} block - Блок с виджетом получателей
*/
function toggleRecipientsBlock(checkbox, block) {
block.hidden = checkbox.checked; // Скрыть, если "всем"
}
/**
* Добавляет ID получателей в FormData.
* @param {FormData} formData - Объект FormData
* @param {Array<number>} ids - Массив ID получателей
*/
function appendRecipientIds(formData, ids) {
// API ожидает повторяемые recipient_ids
for (const id of ids) {
formData.append('recipient_ids', String(id));
}
}
/**
* Инициализирует обработчики CRUD операций для документов.
* @param {Object} options - Опции инициализации
* @param {string} options.apiListUrl - URL API для списка документов
* @param {string} options.apiDetailBase - Базовый URL для операций с конкретным документом
* @param {Object} [options.headers={}] - HTTP заголовки для запросов
* @param {Object} options.createPicker - Экземпляр RecipientPicker для формы создания
* @param {Object} options.editPicker - Экземпляр RecipientPicker для формы редактирования
* @returns {Object} API с методом destroy
*/
export function initDocumentCrud(options) {
const {
apiListUrl,
apiDetailBase,
headers = {},
createPicker,
editPicker
} = options;
// Элементы формы создания
const createForm = document.getElementById('docCreateForm');
const createAllCheckbox = document.getElementById('createSentToAll');
const createBlock = document.getElementById('createRecipientsBlock');
// Элементы формы редактирования
const editModal = document.getElementById('docEditModal');
const editForm = document.getElementById('docEditForm');
const editAllCheckbox = document.getElementById('editSentToAll');
const editBlock = document.getElementById('editRecipientsBlock');
// Список документов
const listElement = document.getElementById('docList');
if (!createForm || !editForm || !listElement) {
console.warn('initDocumentCrud: обязательные элементы не найдены');
return { destroy: () => {} };
}
// Инициализация переключения блоков получателей
if (createAllCheckbox && createBlock) {
toggleRecipientsBlock(createAllCheckbox, createBlock);
createAllCheckbox.addEventListener('change', () => {
toggleRecipientsBlock(createAllCheckbox, createBlock);
});
}
if (editAllCheckbox && editBlock) {
toggleRecipientsBlock(editAllCheckbox, editBlock);
editAllCheckbox.addEventListener('change', () => {
toggleRecipientsBlock(editAllCheckbox, editBlock);
});
}
/**
* Обработчик создания документа.
*/
async function handleCreate(e) {
e.preventDefault();
const formData = new FormData(createForm);
formData.set('sent_to_all', createAllCheckbox.checked ? 'true' : 'false');
if (!createAllCheckbox.checked) {
const ids = createPicker.getIds();
if (ids.length === 0) {
alert('Выберите получателей или включите «Отправить всем».');
return;
}
appendRecipientIds(formData, ids);
} else {
formData.delete('recipient_ids');
}
try {
const response = await fetch(apiListUrl, {
method: 'POST',
headers: headers,
body: formData
});
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
await response.json();
window.location.reload();
} catch (error) {
alert('Не удалось создать документ: ' + error.message);
}
}
/**
* Открывает модальное окно редактирования документа.
* @param {HTMLElement} button - Кнопка с data-атрибутами документа
*/
async function openEditModal(button) {
const docId = button.getAttribute('data-doc-id');
editForm.elements.id.value = docId;
editForm.elements.title.value = button.getAttribute('data-doc-title') || '';
editForm.elements.description.value = button.getAttribute('data-doc-description') || '';
const sentToAll = button.getAttribute('data-doc-sent_to_all') === '1';
editAllCheckbox.checked = sentToAll;
toggleRecipientsBlock(editAllCheckbox, editBlock);
// Загружаем текущих получателей документа
try {
const response = await fetch(apiDetailBase + docId + '/', { headers });
if (response.ok) {
const data = await response.json();
const recipients = (data && data.recipients) ? data.recipients.map(r => ({
id: r.id,
display_name: r.display_name || r.full_name || r.email || ('#' + r.id),
email: r.email || ''
})) : [];
editPicker.setSelected(recipients);
} else {
editPicker.setSelected([]);
}
} catch (error) {
console.error('Ошибка загрузки получателей:', error);
editPicker.setSelected([]);
}
bootstrap.Modal.getOrCreateInstance(editModal).show();
}
/**
* Обработчик редактирования документа.
*/
async function handleEdit(e) {
e.preventDefault();
const docId = editForm.elements.id.value;
const formData = new FormData(editForm);
formData.set('sent_to_all', editAllCheckbox.checked ? 'true' : 'false');
if (!editAllCheckbox.checked) {
const ids = editPicker.getIds();
if (ids.length === 0) {
alert('Выберите получателей или включите «Отправить всем».');
return;
}
appendRecipientIds(formData, ids);
} else {
formData.delete('recipient_ids');
}
// Если файл не выбран — не отправляем ключ
if (!(editForm.elements.file && editForm.elements.file.files.length)) {
formData.delete('file');
}
try {
const response = await fetch(apiDetailBase + docId + '/', {
method: 'PATCH',
headers: headers,
body: formData
});
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
await response.json();
window.location.reload();
} catch (error) {
alert('Не удалось сохранить: ' + error.message);
}
}
/**
* Удаляет документ после подтверждения.
* @param {HTMLElement} button - Кнопка с data-doc-id
*/
async function handleDelete(button) {
if (!confirm('Удалить документ?')) return;
const docId = button.getAttribute('data-doc-id');
try {
const response = await fetch(apiDetailBase + docId + '/', {
method: 'DELETE',
headers: headers
});
if (response.status === 204 || response.ok) {
window.location.reload();
} else {
throw new Error('HTTP ' + response.status);
}
} catch (error) {
alert('Не удалось удалить документ: ' + error.message);
}
}
/**
* Делегированный обработчик кликов по списку документов.
*/
function handleListClick(e) {
const button = e.target.closest('button[data-action]');
if (!button) return;
const action = button.getAttribute('data-action');
if (action === 'edit') {
openEditModal(button);
} else if (action === 'delete') {
handleDelete(button);
}
}
// Установка обработчиков
createForm.addEventListener('submit', handleCreate);
editForm.addEventListener('submit', handleEdit);
listElement.addEventListener('click', handleListClick);
/**
* Функция для удаления всех обработчиков.
*/
function destroy() {
createForm.removeEventListener('submit', handleCreate);
editForm.removeEventListener('submit', handleEdit);
listElement.removeEventListener('click', handleListClick);
}
return { destroy };
}
// Экспорт для совместимости с неModular кодом
if (typeof window !== 'undefined') {
window.initDocumentCrud = initDocumentCrud;
}