'use client'; import React, { useEffect, useState, useRef } from 'react'; import { updateHomework, publishHomework, type Homework, type HomeworkFileItem, } from '@/api/homework'; import { getMyMaterials } from '@/api/materials'; import type { Material } from '@/api/materials'; import apiClient from '@/lib/api-client'; const MAX_FILE_SIZE_MB = 10; const MAX_FILES = 10; interface EditHomeworkDraftModalProps { isOpen: boolean; homework: Homework | null; onClose: () => void; onSuccess: () => void; } function getFileUrl(file: HomeworkFileItem | null): string { if (!file?.file) return ''; if (file.file.startsWith('http')) return file.file; const base = typeof window !== 'undefined' ? `${window.location.protocol}//${window.location.hostname}:8123` : ''; return file.file.startsWith('/') ? `${base}${file.file}` : `${base}/${file.file}`; } export function EditHomeworkDraftModal({ isOpen, homework, onClose, onSuccess, }: EditHomeworkDraftModalProps) { const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); const [deadline, setDeadline] = useState(''); const [existingFiles, setExistingFiles] = useState([]); const [newFiles, setNewFiles] = useState([]); const [uploadingFiles, setUploadingFiles] = useState>(new Set()); const [materials, setMaterials] = useState([]); const [materialsLoading, setMaterialsLoading] = useState(false); const [selectedMaterialIds, setSelectedMaterialIds] = useState>(new Set()); const [materialsSearch, setMaterialsSearch] = useState(''); const [saving, setSaving] = useState(false); const [publishing, setPublishing] = useState(false); const [error, setError] = useState(null); const fileInputRef = useRef(null); useEffect(() => { if (!isOpen || !homework) return; setTitle(homework.title || ''); setDescription(homework.description || ''); setDeadline(homework.deadline ? homework.deadline.slice(0, 16) : ''); setExistingFiles(homework.files?.filter(f => f.file_type === 'assignment') || []); setNewFiles([]); setSelectedMaterialIds(new Set()); setError(null); }, [isOpen, homework]); useEffect(() => { if (!isOpen) return; setMaterialsLoading(true); getMyMaterials() .then((list) => setMaterials(Array.isArray(list) ? list : [])) .catch(() => setMaterials([])) .finally(() => setMaterialsLoading(false)); }, [isOpen]); const handleFileChange = async (e: React.ChangeEvent) => { const files = Array.from(e.target.files || []); if (!files.length || !homework) return; const validFiles: File[] = []; for (const file of files) { if (file.size > MAX_FILE_SIZE_MB * 1024 * 1024) { setError(`Файл "${file.name}" больше ${MAX_FILE_SIZE_MB} МБ`); continue; } if (existingFiles.length + newFiles.length + validFiles.length >= MAX_FILES) { setError(`Максимум ${MAX_FILES} файлов`); break; } validFiles.push(file); } for (const file of validFiles) { const fileKey = `${file.name}-${Date.now()}`; setUploadingFiles((prev) => new Set(prev).add(fileKey)); setNewFiles((prev) => [...prev, file]); try { const formData = new FormData(); formData.append('homework', String(homework.id)); formData.append('file_type', 'assignment'); formData.append('file', file); const res = await apiClient.post('/homework/files/', formData); setExistingFiles((prev) => [...prev, res.data]); setNewFiles((prev) => prev.filter((f) => f !== file)); } catch (err) { setError(err instanceof Error ? err.message : 'Ошибка загрузки файла'); setNewFiles((prev) => prev.filter((f) => f !== file)); } finally { setUploadingFiles((prev) => { const next = new Set(prev); next.delete(fileKey); return next; }); } } if (fileInputRef.current) { fileInputRef.current.value = ''; } }; const handleRemoveFile = async (fileId: number) => { try { await apiClient.delete(`/homework/files/${fileId}/`); setExistingFiles((prev) => prev.filter((f) => f.id !== fileId)); } catch (err) { setError(err instanceof Error ? err.message : 'Ошибка удаления файла'); } }; const handleMaterialToggle = (materialId: string) => { setSelectedMaterialIds((prev) => { const next = new Set(prev); if (next.has(materialId)) { next.delete(materialId); } else { next.add(materialId); } return next; }); }; const attachMaterialFiles = async () => { if (!homework || selectedMaterialIds.size === 0) return; for (const materialId of selectedMaterialIds) { const material = materials.find((m) => String(m.id) === materialId); if (!material?.file) continue; try { const response = await fetch(material.file); const blob = await response.blob(); const filename = material.title || material.file.split('/').pop() || 'material'; const file = new File([blob], filename, { type: blob.type }); const formData = new FormData(); formData.append('homework', String(homework.id)); formData.append('file_type', 'assignment'); formData.append('file', file); const res = await apiClient.post('/homework/files/', formData); setExistingFiles((prev) => [...prev, res.data]); } catch { // Ignore material attach errors } } setSelectedMaterialIds(new Set()); }; const handleSave = async () => { if (!homework) return; try { setError(null); setSaving(true); await attachMaterialFiles(); await updateHomework(homework.id, { title: title.trim() || homework.title, description: description.trim(), deadline: deadline ? new Date(deadline).toISOString() : null, }); onSuccess(); } catch (e) { setError(e instanceof Error ? e.message : 'Ошибка сохранения'); } finally { setSaving(false); } }; const handlePublish = async () => { if (!homework) return; if (!title.trim()) { setError('Укажите название задания'); return; } if (!description.trim()) { setError('Укажите текст задания'); return; } try { setError(null); setPublishing(true); await attachMaterialFiles(); await updateHomework(homework.id, { title: title.trim(), description: description.trim(), deadline: deadline ? new Date(deadline).toISOString() : null, }); await publishHomework(homework.id); onSuccess(); onClose(); } catch (e) { setError(e instanceof Error ? e.message : 'Ошибка публикации'); } finally { setPublishing(false); } }; if (!isOpen || !homework) return null; const isLoading = saving || publishing || uploadingFiles.size > 0; const filteredMaterials = materials.filter((m) => { if (!materialsSearch.trim()) return true; const q = materialsSearch.toLowerCase(); return ( (m.title || '').toLowerCase().includes(q) || (m.description || '').toLowerCase().includes(q) ); }); return ( <>
{/* Header */}

Заполнить домашнее задание

{/* Content */}
{/* Title */}
setTitle(e.target.value)} placeholder="Введите название" disabled={isLoading} style={{ width: '100%', padding: '12px 16px', borderRadius: 12, border: '1px solid var(--md-sys-color-outline)', background: 'var(--md-sys-color-surface)', fontSize: 15, color: 'var(--md-sys-color-on-surface)', }} />
{/* Description */}