Обновление модального окна переноса задачи (v3.5.2)
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 39s

This commit is contained in:
poignatov
2026-01-09 13:51:50 +03:00
parent b57b0bc901
commit 1097a84d06
4 changed files with 197 additions and 118 deletions

View File

@@ -163,6 +163,54 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
}
}
// Функция для вычисления следующей даты по repetition_period
const calculateNextDateFromRepetitionPeriod = (repetitionPeriodStr) => {
if (!repetitionPeriodStr) return null
const parts = repetitionPeriodStr.trim().split(/\s+/)
if (parts.length < 2) return null
const value = parseInt(parts[0], 10)
if (isNaN(value) || value === 0) return null
const unit = parts[1].toLowerCase()
const now = new Date()
now.setHours(0, 0, 0, 0)
const nextDate = new Date(now)
switch (unit) {
case 'minute':
case 'minutes':
nextDate.setMinutes(nextDate.getMinutes() + value)
break
case 'hour':
case 'hours':
nextDate.setHours(nextDate.getHours() + value)
break
case 'day':
case 'days':
nextDate.setDate(nextDate.getDate() + value)
break
case 'week':
case 'weeks':
nextDate.setDate(nextDate.getDate() + value * 7)
break
case 'month':
case 'months':
nextDate.setMonth(nextDate.getMonth() + value)
break
case 'year':
case 'years':
nextDate.setFullYear(nextDate.getFullYear() + value)
break
default:
return null
}
return nextDate
}
// Форматирование даты в YYYY-MM-DD (локальное время, без смещения в UTC)
const formatDateToLocal = (date) => {
const year = date.getFullYear()
@@ -177,17 +225,26 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
// Устанавливаем дату по умолчанию
let defaultDate
const now = new Date()
now.setHours(0, 0, 0, 0)
if (task.repetition_date) {
// Для задач с repetition_date - вычисляем следующую подходящую дату
const nextDate = calculateNextDateFromRepetitionDate(task.repetition_date)
if (nextDate) {
defaultDate = nextDate
}
} else if (task.repetition_period && !isZeroPeriod(task.repetition_period)) {
// Для задач с repetition_period (не нулевым) - вычисляем следующую дату
const nextDate = calculateNextDateFromRepetitionPeriod(task.repetition_period)
if (nextDate) {
defaultDate = nextDate
}
}
if (!defaultDate) {
// Без repetition_date или если не удалось вычислить - завтра
defaultDate = new Date()
// Без repetition_date/repetition_period или если не удалось вычислить - завтра
defaultDate = new Date(now)
defaultDate.setDate(defaultDate.getDate() + 1)
}
@@ -197,11 +254,42 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
const handlePostponeSubmit = async () => {
if (!selectedTaskForPostpone || !postponeDate) return
await handlePostponeSubmitWithDate(postponeDate)
}
const handlePostponeClose = () => {
setSelectedTaskForPostpone(null)
setPostponeDate('')
}
const handleTodayClick = () => {
const today = new Date()
today.setHours(0, 0, 0, 0)
setPostponeDate(formatDateToLocal(today))
// Применяем дату сразу
if (selectedTaskForPostpone) {
handlePostponeSubmitWithDate(formatDateToLocal(today))
}
}
const handleTomorrowClick = () => {
const tomorrow = new Date()
tomorrow.setDate(tomorrow.getDate() + 1)
tomorrow.setHours(0, 0, 0, 0)
setPostponeDate(formatDateToLocal(tomorrow))
// Применяем дату сразу
if (selectedTaskForPostpone) {
handlePostponeSubmitWithDate(formatDateToLocal(tomorrow))
}
}
const handlePostponeSubmitWithDate = async (dateToUse) => {
if (!selectedTaskForPostpone || !dateToUse) return
setIsPostponing(true)
try {
// Преобразуем дату в ISO формат с временем
const dateObj = new Date(postponeDate)
const dateObj = new Date(dateToUse)
dateObj.setHours(0, 0, 0, 0)
const isoDate = dateObj.toISOString()
@@ -234,45 +322,6 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
}
}
const handlePostponeReset = async () => {
if (!selectedTaskForPostpone) return
setIsPostponing(true)
try {
const response = await authFetch(`${API_URL}/${selectedTaskForPostpone.id}/postpone`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ next_show_at: null }),
})
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
throw new Error(errorData.message || 'Ошибка при сбросе переноса задачи')
}
// Обновляем список
if (onRefresh) {
onRefresh()
}
// Закрываем модальное окно
setSelectedTaskForPostpone(null)
setPostponeDate('')
} catch (err) {
console.error('Error resetting postpone:', err)
alert(err.message || 'Ошибка при сбросе переноса задачи')
} finally {
setIsPostponing(false)
}
}
const handlePostponeClose = () => {
setSelectedTaskForPostpone(null)
setPostponeDate('')
}
const toggleCompletedExpanded = (projectName) => {
setExpandedCompleted(prev => ({
...prev,
@@ -588,47 +637,75 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
)}
{/* Модальное окно для переноса задачи */}
{selectedTaskForPostpone && (
<div className="task-postpone-modal-overlay" onClick={handlePostponeClose}>
<div className="task-postpone-modal" onClick={(e) => e.stopPropagation()}>
<div className="task-postpone-modal-header">
<h3>Перенести задачу</h3>
<button onClick={handlePostponeClose} className="task-postpone-close-button">
</button>
</div>
<div className="task-postpone-modal-content">
<p className="task-postpone-task-name">{selectedTaskForPostpone.name}</p>
<label className="task-postpone-label">
Дата показа:
<input
type="date"
value={postponeDate}
onChange={(e) => setPostponeDate(e.target.value)}
className="task-postpone-input"
min={new Date().toISOString().split('T')[0]}
/>
</label>
</div>
<div className="task-postpone-modal-actions">
<button
onClick={handlePostponeReset}
className="task-postpone-cancel-button"
disabled={isPostponing}
>
{isPostponing ? 'Сброс...' : 'Сбросить'}
</button>
<button
onClick={handlePostponeSubmit}
className="task-postpone-submit-button"
disabled={isPostponing || !postponeDate}
>
{isPostponing ? 'Перенос...' : 'Перенести'}
</button>
{selectedTaskForPostpone && (() => {
const todayStr = formatDateToLocal(new Date())
const tomorrow = new Date()
tomorrow.setDate(tomorrow.getDate() + 1)
const tomorrowStr = formatDateToLocal(tomorrow)
// Проверяем next_show_at задачи, а не значение в поле ввода
let nextShowAtStr = null
if (selectedTaskForPostpone.next_show_at) {
const nextShowAtDate = new Date(selectedTaskForPostpone.next_show_at)
nextShowAtStr = formatDateToLocal(nextShowAtDate)
}
const isToday = nextShowAtStr === todayStr
const isTomorrow = nextShowAtStr === tomorrowStr
return (
<div className="task-postpone-modal-overlay" onClick={handlePostponeClose}>
<div className="task-postpone-modal" onClick={(e) => e.stopPropagation()}>
<div className="task-postpone-modal-header">
<h3>{selectedTaskForPostpone.name}</h3>
<button onClick={handlePostponeClose} className="task-postpone-close-button">
</button>
</div>
<div className="task-postpone-modal-content">
<div className="task-postpone-input-group">
<input
type="date"
value={postponeDate}
onChange={(e) => setPostponeDate(e.target.value)}
className="task-postpone-input"
min={new Date().toISOString().split('T')[0]}
/>
{postponeDate && (
<button
onClick={handlePostponeSubmit}
disabled={isPostponing || !postponeDate}
className="task-postpone-submit-checkmark"
>
</button>
)}
</div>
<div className="task-postpone-quick-buttons">
{!isToday && (
<button
onClick={handleTodayClick}
className="task-postpone-quick-button"
disabled={isPostponing}
>
Сегодня
</button>
)}
{!isTomorrow && (
<button
onClick={handleTomorrowClick}
className="task-postpone-quick-button"
disabled={isPostponing}
>
Завтра
</button>
)}
</div>
</div>
</div>
</div>
</div>
)}
)
})()}
</div>
)
}