4.6.0: Расчет срока разблокировки желаний
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m39s

This commit is contained in:
poignatov
2026-01-31 18:43:25 +03:00
parent e955494dc8
commit 2428ca5fd0
11 changed files with 1365 additions and 70 deletions

View File

@@ -98,6 +98,7 @@ function WishlistForm({ onNavigate, wishlistId, editConditionIndex, newTaskId, b
start_date: cond.start_date || null,
display_order: idx,
user_id: cond.user_id || null,
weeks_text: cond.weeks_text || null,
})))
} else {
setUnlockConditions([])
@@ -253,6 +254,7 @@ function WishlistForm({ onNavigate, wishlistId, editConditionIndex, newTaskId, b
start_date: cond.start_date || null,
display_order: idx,
user_id: cond.user_id || null,
weeks_text: cond.weeks_text || null,
})))
} else {
setUnlockConditions([])
@@ -798,16 +800,24 @@ function WishlistForm({ onNavigate, wishlistId, editConditionIndex, newTaskId, b
const isOwnCondition = !cond.user_id || cond.user_id === user?.id
return (
<div key={idx} className="condition-item">
<span
className={`condition-item-text ${!isOwnCondition ? 'condition-item-other-user' : ''}`}
onClick={() => isOwnCondition && handleEditCondition(idx)}
style={{ cursor: isOwnCondition ? 'pointer' : 'default' }}
title={!isOwnCondition ? 'Чужая цель - нельзя редактировать' : ''}
>
{cond.type === 'task_completion'
? `Задача: ${tasks.find(t => t.id === cond.task_id)?.name || 'Не выбрана'}`
: `Баллы: ${cond.required_points} в ${projects.find(p => p.project_id === cond.project_id)?.project_name || cond.project_name || 'Не выбран'}${cond.start_date ? ` с ${new Date(cond.start_date + 'T00:00:00').toLocaleDateString('ru-RU')}` : ' за всё время'}`}
</span>
<div style={{ display: 'flex', flexDirection: 'column' }}>
<span
className={`condition-item-text ${!isOwnCondition ? 'condition-item-other-user' : ''}`}
onClick={() => isOwnCondition && handleEditCondition(idx)}
style={{ cursor: isOwnCondition ? 'pointer' : 'default', paddingBottom: '0.125rem' }}
title={!isOwnCondition ? 'Чужая цель - нельзя редактировать' : ''}
>
{cond.type === 'task_completion'
? tasks.find(t => t.id === cond.task_id)?.name || 'Не выбрана'
: `${cond.required_points} в ${projects.find(p => p.project_id === cond.project_id)?.project_name || cond.project_name || 'Не выбран'}${cond.start_date ? ` с ${new Date(cond.start_date + 'T00:00:00').toLocaleDateString('ru-RU')}` : ' за всё время'}`}
</span>
{cond.type === 'project_points' && cond.weeks_text && (
<div style={{ color: '#666', fontSize: '0.85em' }}>
<span>Срок: </span>
<span style={{ fontWeight: '600' }}>{cond.weeks_text}</span>
</div>
)}
</div>
{isOwnCondition && (
<button
type="button"
@@ -849,6 +859,7 @@ function WishlistForm({ onNavigate, wishlistId, editConditionIndex, newTaskId, b
editingCondition={editingConditionIndex !== null ? unlockConditions[editingConditionIndex] : null}
onCreateTask={handleCreateTaskFromCondition}
preselectedTaskId={newTaskId}
authFetch={authFetch}
/>
)}
@@ -1125,12 +1136,13 @@ function TaskAutocomplete({ tasks, value, onChange, onCreateTask, preselectedTas
}
// Компонент формы цели
function ConditionForm({ tasks, projects, onSubmit, onCancel, editingCondition, onCreateTask, preselectedTaskId }) {
function ConditionForm({ tasks, projects, onSubmit, onCancel, editingCondition, onCreateTask, preselectedTaskId, authFetch }) {
const [type, setType] = useState(editingCondition?.type || 'project_points')
const [taskId, setTaskId] = useState(editingCondition?.task_id || null)
const [projectId, setProjectId] = useState(editingCondition?.project_id?.toString() || '')
const [requiredPoints, setRequiredPoints] = useState(editingCondition?.required_points?.toString() || '')
const [startDate, setStartDate] = useState(editingCondition?.start_date || '')
const [calculatedWeeksText, setCalculatedWeeksText] = useState(null)
const isEditing = editingCondition !== null
@@ -1142,6 +1154,40 @@ function ConditionForm({ tasks, projects, onSubmit, onCancel, editingCondition,
}
}, [preselectedTaskId, editingCondition])
// Расчет недель при изменении проекта, баллов или даты
useEffect(() => {
const calculateWeeks = async () => {
if (type === 'project_points' && projectId && requiredPoints && authFetch) {
try {
const response = await authFetch('/api/wishlist/calculate-weeks', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
project_id: parseInt(projectId),
required_points: parseFloat(requiredPoints),
start_date: startDate || '',
condition_user_id: editingCondition?.user_id || null,
}),
})
if (response.ok) {
const data = await response.json()
setCalculatedWeeksText(data.weeks_text || null)
} else {
setCalculatedWeeksText(null)
}
} catch (err) {
console.error('Error calculating weeks:', err)
setCalculatedWeeksText(null)
}
} else {
setCalculatedWeeksText(null)
}
}
calculateWeeks()
}, [type, projectId, requiredPoints, startDate, editingCondition?.user_id, authFetch])
const handleSubmit = (e) => {
e.preventDefault()
e.stopPropagation() // Предотвращаем всплытие события
@@ -1173,7 +1219,12 @@ function ConditionForm({ tasks, projects, onSubmit, onCancel, editingCondition,
return (
<div className="condition-form-overlay" onClick={onCancel}>
<div className="condition-form" onClick={(e) => e.stopPropagation()}>
<h3>{isEditing ? 'Редактировать цель' : 'Добавить цель'}</h3>
<div className="condition-form-header">
<h3>{isEditing ? 'Редактировать цель' : 'Добавить цель'}</h3>
<button onClick={onCancel} className="condition-form-close-button">
</button>
</div>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>Тип условия</label>
@@ -1241,13 +1292,16 @@ function ConditionForm({ tasks, projects, onSubmit, onCancel, editingCondition,
)}
<div className="form-actions">
<button type="button" onClick={onCancel} className="cancel-button">
Отмена
</button>
<button type="submit" className="submit-button">
<button type="submit" className="submit-button condition-form-submit-button">
{isEditing ? 'Сохранить' : 'Добавить'}
</button>
</div>
{type === 'project_points' && calculatedWeeksText && (
<div className="calculated-weeks-info" style={{ marginTop: '4px', textAlign: 'left', color: '#666', fontSize: '0.85em' }}>
<span>Срок: </span>
<span style={{ fontWeight: '600' }}>{calculatedWeeksText}</span>
</div>
)}
</form>
</div>
</div>