improve empty task cover selection

This commit is contained in:
lizerk 2026-08-07 15:43:55 +03:00
parent fad399cd47
commit 73aa685b1d

View File

@ -3652,6 +3652,7 @@ function TasksPageContent() {
const pendingBoardViewStateRef = useRef<Map<string, boolean>>(new Map());
const columnNodeRefs = useRef<Map<number, HTMLElement>>(new Map());
const taskAttachmentInputRef = useRef<HTMLInputElement | null>(null);
const taskCoverUploadInputRef = useRef<HTMLInputElement | null>(null);
const taskFormAttachmentInputRef = useRef<HTMLInputElement | null>(null);
const taskChecklistDraftRef = useRef<HTMLInputElement | null>(null);
const taskFormChecklistDraftRef = useRef<HTMLInputElement | null>(null);
@ -5855,6 +5856,44 @@ function TasksPageContent() {
}
}, [activityOpen, board, loadBoard, loadTaskActivity, taskAttachmentsUploading, viewTask]);
const uploadViewedTaskCover = useCallback(async (file: File | null) => {
if (!board || !viewTask || !file || saving || taskAttachmentsUploading) return;
if (!isMediaFile(file)) {
setError("Для обложки можно загрузить только изображение или видео");
return;
}
setSaving(true);
setTaskAttachmentsUploading(true);
setError(null);
try {
const [attachment] = await apiClient.uploadTaskAttachments(viewTask.id, [file]);
if (!attachment) throw new Error("Файл не был загружен");
setTaskAttachments((current) => [...current, attachment]);
await apiClient.setTaskCover(viewTask.id, {
kind: "attachment",
source_id: attachment.id,
});
await loadBoard(board.id);
if (activityOpen) await loadTaskActivity(viewTask.id);
setCoverPickerOpen(false);
} catch (coverError) {
setError(getTaskError(coverError, "Не удалось загрузить и установить обложку задачи"));
} finally {
setTaskAttachmentsUploading(false);
setSaving(false);
}
}, [
activityOpen,
board,
loadBoard,
loadTaskActivity,
saving,
taskAttachmentsUploading,
viewTask,
]);
const downloadViewedTaskAttachment = useCallback(async (attachment: TaskAttachment) => {
if (!viewTask) return;
setError(null);
@ -9225,6 +9264,18 @@ function TasksPageContent() {
</div>
)}
>
<input
ref={taskCoverUploadInputRef}
type="file"
accept="image/*,video/*"
className="sr-only"
disabled={saving || taskAttachmentsUploading}
onChange={(event) => {
const file = event.currentTarget.files?.[0] || null;
event.currentTarget.value = "";
void uploadViewedTaskCover(file);
}}
/>
{coverPickerLoading ? (
<div className="app-surface-muted flex min-h-44 items-center justify-center rounded-xl border border-[var(--border-subtle)]">
<Loader2 size={20} className="animate-spin text-sky-500" />
@ -9338,12 +9389,28 @@ function TasksPageContent() {
&& taskComments.length === 0
&& taskExternalLinks.length === 0
&& taskCoverLinkedOptions.length === 0 ? (
<div className="app-surface-muted rounded-xl border border-dashed border-[var(--border-subtle)] px-4 py-8 text-center">
<ImagePlus size={22} className="app-text-muted mx-auto" />
<p className="app-text-muted mt-2 text-sm">
Добавьте фото, видео, чек-лист, комментарий, ссылку или связанный объект.
</p>
</div>
<button
type="button"
onClick={() => taskCoverUploadInputRef.current?.click()}
disabled={saving || taskAttachmentsUploading}
className="app-action-secondary flex w-full items-center gap-3 rounded-xl border border-dashed border-[var(--border-subtle)] px-4 py-4 text-left disabled:cursor-not-allowed disabled:opacity-60"
>
<span className="app-icon-button flex h-10 w-10 shrink-0 items-center justify-center rounded-xl">
{taskAttachmentsUploading ? (
<Loader2 size={19} className="animate-spin" />
) : (
<ImagePlus size={19} />
)}
</span>
<span className="min-w-0">
<span className="block text-sm font-medium text-[var(--foreground)]">
{taskAttachmentsUploading ? "Загрузка обложки..." : "Загрузить фото или видео"}
</span>
<span className="app-text-muted mt-0.5 block text-xs">
Медиафайл сразу станет обложкой задачи
</span>
</span>
</button>
) : null}
</div>
) : null}