1
0

Комментарии, файлы и права проекта

- Система комментариев к задачам с вложенными ответами
- Редактирование и удаление комментариев
- Прикрепление файлов к задачам и комментариям (картинки, архивы до 10 МБ)
- Система прав проекта: админ проекта может удалять чужие комментарии и файлы
- Универсальный класс FileUpload для загрузки файлов
- Защита загрузки: только автор комментария может добавлять файлы
- Каскадное удаление: задача → комментарии → файлы
- Автообновление комментариев в реальном времени
This commit is contained in:
2026-01-15 06:40:47 +07:00
parent 8ac497df63
commit 3bfa1e9e1b
25 changed files with 3353 additions and 904 deletions

View File

@@ -113,7 +113,94 @@ export const taskImageApi = {
})
}
// ==================== COMMENT IMAGES ====================
export const commentImageApi = {
upload: (comment_id, file_data, file_name) => request('/api/comment', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'upload_image', comment_id, file_data, file_name })
}),
// Принимает строку (один файл) или массив (несколько файлов)
delete: (comment_id, file_names) => request('/api/comment', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'delete_image', comment_id, file_names })
})
}
// ==================== USERS ====================
export const usersApi = {
getAll: () => request('/api/user', { credentials: 'include' })
}
// ==================== SERVER ====================
export const serverApi = {
// Получение настроек сервера (timezone и т.д.) — публичный action
getSettings: () => request('/api/server', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'get_settings' })
})
}
// Хранилище серверных настроек
export const serverSettings = {
timezoneOffset: '+03:00', // дефолт, обновится при загрузке
// Инициализация — вызвать один раз при старте приложения
async init() {
try {
const result = await serverApi.getSettings()
if (result.success) {
this.timezoneOffset = result.data.timezone_offset
}
} catch (e) {
console.warn('Не удалось получить настройки сервера:', e)
}
},
// Парсинг даты с сервера с учётом таймзоны
parseDate(dateStr) {
if (!dateStr) return null
// Добавляем таймзону сервера для корректного парсинга
const normalized = dateStr.replace(' ', 'T')
// Если уже есть таймзона — не добавляем
if (normalized.includes('+') || normalized.includes('Z')) {
return new Date(normalized)
}
return new Date(normalized + this.timezoneOffset)
}
}
// ==================== COMMENTS ====================
export const commentsApi = {
// Получение комментариев задачи
getByTask: (id_task) => request(`/api/comment?id_task=${id_task}`, { credentials: 'include' }),
// Создание комментария (id_answer — опционально, для ответа на комментарий)
create: (id_task, text, id_answer = null) => request('/api/comment', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'create', id_task, text, id_answer })
}),
// Обновление комментария
update: (id, text) => request('/api/comment', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'update', id, text })
}),
// Удаление комментария
delete: (id) => request('/api/comment', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'delete', id })
})
}