v3.5.3: Убрана группировка бесконечных задач, добавлена иконка бесконечности, улучшен UI модального окна переноса
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 35s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 35s
This commit is contained in:
@@ -16,15 +16,6 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
const [selectedTaskForPostpone, setSelectedTaskForPostpone] = useState(null)
|
||||
const [postponeDate, setPostponeDate] = useState('')
|
||||
const [isPostponing, setIsPostponing] = useState(false)
|
||||
// Загружаем состояние раскрытия "Бесконечные" из localStorage (по умолчанию true)
|
||||
const [expandedInfinite, setExpandedInfinite] = useState(() => {
|
||||
try {
|
||||
const saved = localStorage.getItem('taskList_expandedInfinite')
|
||||
return saved ? JSON.parse(saved) : {}
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
const [toast, setToast] = useState(null)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -329,22 +320,6 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
}))
|
||||
}
|
||||
|
||||
const toggleInfiniteExpanded = (projectName) => {
|
||||
setExpandedInfinite(prev => {
|
||||
const newState = {
|
||||
...prev,
|
||||
[projectName]: !prev[projectName]
|
||||
}
|
||||
// Сохраняем в localStorage
|
||||
try {
|
||||
localStorage.setItem('taskList_expandedInfinite', JSON.stringify(newState))
|
||||
} catch (err) {
|
||||
console.error('Error saving expandedInfinite to localStorage:', err)
|
||||
}
|
||||
return newState
|
||||
})
|
||||
}
|
||||
|
||||
// Получаем все проекты из задачи (теперь они приходят в task.project_names)
|
||||
const getTaskProjects = (task) => {
|
||||
if (task.project_names && Array.isArray(task.project_names)) {
|
||||
@@ -357,13 +332,42 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
const isZeroPeriod = (intervalStr) => {
|
||||
if (!intervalStr) return false
|
||||
|
||||
const parts = intervalStr.trim().split(/\s+/)
|
||||
const trimmed = intervalStr.trim()
|
||||
|
||||
// Проверяем формат времени "00:00:00" или "0:00:00"
|
||||
if (/^\d{1,2}:\d{2}:\d{2}/.test(trimmed)) {
|
||||
const timeParts = trimmed.split(':')
|
||||
if (timeParts.length >= 3) {
|
||||
const hours = parseInt(timeParts[0], 10)
|
||||
const minutes = parseInt(timeParts[1], 10)
|
||||
const seconds = parseInt(timeParts[2], 10)
|
||||
return !isNaN(hours) && !isNaN(minutes) && !isNaN(seconds) &&
|
||||
hours === 0 && minutes === 0 && seconds === 0
|
||||
}
|
||||
}
|
||||
|
||||
// PostgreSQL может возвращать "0 day", "0 days", "0", и т.д.
|
||||
const parts = trimmed.split(/\s+/)
|
||||
if (parts.length < 1) return false
|
||||
|
||||
const value = parseInt(parts[0], 10)
|
||||
return !isNaN(value) && value === 0
|
||||
}
|
||||
|
||||
// Функция для проверки, является ли repetition_date нулевым
|
||||
const isZeroDate = (dateStr) => {
|
||||
if (!dateStr) return false
|
||||
|
||||
const trimmed = dateStr.trim()
|
||||
const parts = trimmed.split(/\s+/)
|
||||
if (parts.length < 2) return false
|
||||
|
||||
const value = parts[0]
|
||||
// Проверяем, является ли значение "0" (для формата "0 week", "0 month", "0 year")
|
||||
const numValue = parseInt(value, 10)
|
||||
return !isNaN(numValue) && numValue === 0
|
||||
}
|
||||
|
||||
// Группируем задачи по проектам
|
||||
const groupedTasks = useMemo(() => {
|
||||
const today = new Date()
|
||||
@@ -389,30 +393,28 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
nextShowDate.setHours(0, 0, 0, 0)
|
||||
isCompleted = nextShowDate.getTime() > today.getTime()
|
||||
isInfinite = false
|
||||
} else if (task.repetition_period && isZeroPeriod(task.repetition_period)) {
|
||||
// Если у задачи период повторения = 0 и нет next_show_at, она в бесконечных
|
||||
isInfinite = true
|
||||
isCompleted = false
|
||||
} else {
|
||||
// Если нет next_show_at и период не 0, задача в обычных
|
||||
// Бесконечная задача: repetition_period == 0 И (repetition_date == 0 ИЛИ отсутствует)
|
||||
// Для обратной совместимости: если repetition_period = 0, считаем бесконечной
|
||||
const hasZeroPeriod = task.repetition_period && isZeroPeriod(task.repetition_period)
|
||||
const hasZeroDate = task.repetition_date && isZeroDate(task.repetition_date)
|
||||
// Идеально: оба поля = 0, но для старых задач может быть только repetition_period = 0
|
||||
isInfinite = (hasZeroPeriod && hasZeroDate) || (hasZeroPeriod && !task.repetition_date)
|
||||
isCompleted = false
|
||||
isInfinite = false
|
||||
}
|
||||
|
||||
projects.forEach(projectName => {
|
||||
if (!groups[projectName]) {
|
||||
groups[projectName] = {
|
||||
notCompleted: [],
|
||||
completed: [],
|
||||
infinite: []
|
||||
completed: []
|
||||
}
|
||||
}
|
||||
|
||||
if (isInfinite) {
|
||||
groups[projectName].infinite.push(task)
|
||||
} else if (isCompleted) {
|
||||
if (isCompleted) {
|
||||
groups[projectName].completed.push(task)
|
||||
} else {
|
||||
// Бесконечные задачи теперь идут в обычный список
|
||||
groups[projectName].notCompleted.push(task)
|
||||
}
|
||||
})
|
||||
@@ -425,6 +427,27 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
const hasProgression = task.has_progression || task.progression_base != null
|
||||
const hasSubtasks = task.subtasks_count > 0
|
||||
const showDetailOnCheckmark = hasProgression || hasSubtasks
|
||||
|
||||
// Проверяем бесконечную задачу: repetition_period = 0 И (repetition_date = 0 ИЛИ отсутствует)
|
||||
// Для обратной совместимости: если repetition_period = 0, считаем бесконечной
|
||||
const hasZeroPeriod = task.repetition_period && isZeroPeriod(task.repetition_period)
|
||||
const hasZeroDate = task.repetition_date && isZeroDate(task.repetition_date)
|
||||
// Бесконечная задача: repetition_period = 0 И (repetition_date = 0 ИЛИ отсутствует)
|
||||
// Не проверяем next_show_at, так как для бесконечных задач он может быть установлен при выполнении
|
||||
const isInfinite = (hasZeroPeriod && hasZeroDate) || (hasZeroPeriod && !task.repetition_date)
|
||||
|
||||
// Отладка для задачи "Ролик"
|
||||
if (task.name === 'Ролик') {
|
||||
console.log('Task "Ролик":', {
|
||||
name: task.name,
|
||||
repetition_period: task.repetition_period,
|
||||
repetition_date: task.repetition_date,
|
||||
next_show_at: task.next_show_at,
|
||||
hasZeroPeriod,
|
||||
hasZeroDate,
|
||||
isInfinite
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -450,23 +473,42 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
{hasSubtasks && (
|
||||
<span className="task-subtasks-count">(+{task.subtasks_count})</span>
|
||||
)}
|
||||
{hasProgression && (
|
||||
<svg
|
||||
className="task-progression-icon"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
title="Задача с прогрессией"
|
||||
>
|
||||
<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"></polyline>
|
||||
<polyline points="17 6 23 6 23 12"></polyline>
|
||||
</svg>
|
||||
)}
|
||||
<span className="task-badge-bar">
|
||||
{hasProgression && (
|
||||
<svg
|
||||
className="task-progression-icon"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
title="Задача с прогрессией"
|
||||
>
|
||||
<polyline points="23 6 13.5 15.5 8.5 10.5 1 18"></polyline>
|
||||
<polyline points="17 6 23 6 23 12"></polyline>
|
||||
</svg>
|
||||
)}
|
||||
{isInfinite && (
|
||||
<svg
|
||||
className="task-infinite-icon"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
title="Бесконечная задача"
|
||||
>
|
||||
<path d="M12 12c0-2.5-1.5-4.5-3.5-4.5S5 9.5 5 12s1.5 4.5 3.5 4.5S12 14.5 12 12z"/>
|
||||
<path d="M12 12c0 2.5 1.5 4.5 3.5 4.5S19 14.5 19 12s-1.5-4.5-3.5-4.5S12 9.5 12 12z"/>
|
||||
</svg>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
{task.next_show_at && (() => {
|
||||
const showDate = new Date(task.next_show_at)
|
||||
@@ -559,12 +601,7 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
{projectNames.map(projectName => {
|
||||
const group = groupedTasks[projectName]
|
||||
const hasCompleted = group.completed.length > 0
|
||||
const hasInfinite = group.infinite.length > 0
|
||||
const isCompletedExpanded = expandedCompleted[projectName]
|
||||
// По умолчанию бесконечные раскрыты (true), если не сохранено иное
|
||||
const isInfiniteExpanded = expandedInfinite[projectName] !== undefined
|
||||
? expandedInfinite[projectName]
|
||||
: true
|
||||
|
||||
return (
|
||||
<div key={projectName} className="project-group">
|
||||
@@ -572,33 +609,13 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
<h3 className="project-group-title">{projectName}</h3>
|
||||
</div>
|
||||
|
||||
{/* Обычные задачи */}
|
||||
{/* Обычные задачи (включая бесконечные) */}
|
||||
{group.notCompleted.length > 0 && (
|
||||
<div className="task-group">
|
||||
{group.notCompleted.map(renderTaskItem)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Бесконечные задачи */}
|
||||
{hasInfinite && (
|
||||
<div className="completed-section">
|
||||
<button
|
||||
className="completed-toggle"
|
||||
onClick={() => toggleInfiniteExpanded(projectName)}
|
||||
>
|
||||
<span className="completed-toggle-icon">
|
||||
{isInfiniteExpanded ? '▼' : '▶'}
|
||||
</span>
|
||||
<span>Бесконечные ({group.infinite.length})</span>
|
||||
</button>
|
||||
{isInfiniteExpanded && (
|
||||
<div className="task-group completed-tasks">
|
||||
{group.infinite.map(renderTaskItem)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Выполненные задачи */}
|
||||
{hasCompleted && (
|
||||
<div className="completed-section">
|
||||
@@ -619,7 +636,7 @@ function TaskList({ onNavigate, data, loading, backgroundLoading, onRefresh }) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{group.notCompleted.length === 0 && !hasCompleted && !hasInfinite && (
|
||||
{group.notCompleted.length === 0 && !hasCompleted && (
|
||||
<div className="empty-group">Нет задач в этой группе</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user