Отключены подзадачи для задач-тестов
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m27s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m27s
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "play-life-web",
|
||||
"version": "3.13.0",
|
||||
"version": "3.14.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -508,4 +508,3 @@
|
||||
color: #6b7280;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
@@ -311,18 +311,23 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
|
||||
use_progression: r.use_progression
|
||||
})))
|
||||
|
||||
// Загружаем подзадачи
|
||||
setSubtasks(data.subtasks.map(st => ({
|
||||
id: st.task.id,
|
||||
name: st.task.name || '',
|
||||
reward_message: st.task.reward_message || '',
|
||||
rewards: st.rewards.map(r => ({
|
||||
position: r.position,
|
||||
project_name: r.project_name,
|
||||
value: String(r.value),
|
||||
use_progression: r.use_progression
|
||||
}))
|
||||
})))
|
||||
// Загружаем подзадачи (только если задача не является тестом)
|
||||
if (data.task.config_id) {
|
||||
// Для задач-тестов не загружаем подзадачи
|
||||
setSubtasks([])
|
||||
} else {
|
||||
setSubtasks(data.subtasks.map(st => ({
|
||||
id: st.task.id,
|
||||
name: st.task.name || '',
|
||||
reward_message: st.task.reward_message || '',
|
||||
rewards: st.rewards.map(r => ({
|
||||
position: r.position,
|
||||
project_name: r.project_name,
|
||||
value: String(r.value),
|
||||
use_progression: r.use_progression
|
||||
}))
|
||||
})))
|
||||
}
|
||||
|
||||
// Загружаем информацию о связанном желании, если есть
|
||||
if (data.task.wishlist_id) {
|
||||
@@ -368,6 +373,8 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
|
||||
}
|
||||
// Тесты не могут иметь прогрессию
|
||||
setProgressionBase('')
|
||||
// Тесты не могут иметь подзадачи - очищаем их
|
||||
setSubtasks([])
|
||||
} else {
|
||||
setIsTest(false)
|
||||
setWordsCount('10')
|
||||
@@ -381,6 +388,13 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
|
||||
}
|
||||
}
|
||||
|
||||
// Очистка подзадач при переключении задачи в режим теста
|
||||
useEffect(() => {
|
||||
if (isTest && subtasks.length > 0) {
|
||||
setSubtasks([])
|
||||
}
|
||||
}, [isTest])
|
||||
|
||||
// Пересчет rewards при изменении reward_message (debounce)
|
||||
useEffect(() => {
|
||||
if (debounceTimer.current) {
|
||||
@@ -644,7 +658,7 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
|
||||
value: parseFloat(r.value) || 0,
|
||||
use_progression: !!(progressionBase && r.use_progression)
|
||||
})),
|
||||
subtasks: subtasks.map(st => ({
|
||||
subtasks: isTest ? [] : subtasks.map(st => ({
|
||||
id: st.id || undefined,
|
||||
name: st.name.trim() || null,
|
||||
reward_message: st.reward_message.trim() || null,
|
||||
@@ -1058,108 +1072,110 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<div className="subtasks-header">
|
||||
<label>Подзадачи</label>
|
||||
<button type="button" onClick={handleAddSubtask} className="add-subtask-button" title="Добавить подзадачу">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{subtasks.map((subtask, index) => (
|
||||
<div key={index} className="subtask-form-item">
|
||||
<div className="subtask-header-row">
|
||||
<input
|
||||
type="text"
|
||||
value={subtask.name}
|
||||
onChange={(e) => handleSubtaskChange(index, 'name', e.target.value)}
|
||||
placeholder="Название подзадачи"
|
||||
className="form-input subtask-name-input"
|
||||
{!isTest && (
|
||||
<div className="form-group">
|
||||
<div className="subtasks-header">
|
||||
<label>Подзадачи</label>
|
||||
<button type="button" onClick={handleAddSubtask} className="add-subtask-button" title="Добавить подзадачу">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{subtasks.map((subtask, index) => (
|
||||
<div key={index} className="subtask-form-item">
|
||||
<div className="subtask-header-row">
|
||||
<input
|
||||
type="text"
|
||||
value={subtask.name}
|
||||
onChange={(e) => handleSubtaskChange(index, 'name', e.target.value)}
|
||||
placeholder="Название подзадачи"
|
||||
className="form-input subtask-name-input"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveSubtask(index)}
|
||||
className="remove-subtask-button"
|
||||
title="Удалить подзадачу"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M3 6h18"></path>
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
|
||||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
|
||||
<line x1="10" y1="11" x2="10" y2="17"></line>
|
||||
<line x1="14" y1="11" x2="14" y2="17"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
value={subtask.reward_message}
|
||||
onChange={(e) => handleSubtaskRewardMessageChange(index, e.target.value)}
|
||||
placeholder="Сообщение награды (опционально)"
|
||||
className="form-textarea"
|
||||
rows={2}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveSubtask(index)}
|
||||
className="remove-subtask-button"
|
||||
title="Удалить подзадачу"
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M3 6h18"></path>
|
||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
|
||||
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
|
||||
<line x1="10" y1="11" x2="10" y2="17"></line>
|
||||
<line x1="14" y1="11" x2="14" y2="17"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
value={subtask.reward_message}
|
||||
onChange={(e) => handleSubtaskRewardMessageChange(index, e.target.value)}
|
||||
placeholder="Сообщение награды (опционально)"
|
||||
className="form-textarea"
|
||||
rows={2}
|
||||
/>
|
||||
{subtask.rewards && subtask.rewards.length > 0 && (
|
||||
<div className="subtask-rewards">
|
||||
{subtask.rewards.map((reward, rIndex) => (
|
||||
<div key={rIndex} className="reward-item">
|
||||
<span className="reward-number">{rIndex}</span>
|
||||
<input
|
||||
type="text"
|
||||
value={reward.project_name}
|
||||
onChange={(e) => {
|
||||
const newSubtasks = [...subtasks]
|
||||
newSubtasks[index].rewards[rIndex].project_name = e.target.value
|
||||
setSubtasks(newSubtasks)
|
||||
}}
|
||||
placeholder="Проект"
|
||||
className="form-input reward-project-input"
|
||||
list={`subtask-projects-${index}-${rIndex}`}
|
||||
/>
|
||||
<datalist id={`subtask-projects-${index}-${rIndex}`}>
|
||||
{projects.map(p => (
|
||||
<option key={p.project_id} value={p.project_name} />
|
||||
))}
|
||||
</datalist>
|
||||
<input
|
||||
type="number"
|
||||
step="any"
|
||||
value={reward.value}
|
||||
onChange={(e) => {
|
||||
const newSubtasks = [...subtasks]
|
||||
newSubtasks[index].rewards[rIndex].value = e.target.value
|
||||
setSubtasks(newSubtasks)
|
||||
}}
|
||||
placeholder="Score"
|
||||
className="form-input reward-score-input"
|
||||
/>
|
||||
{progressionBase && (
|
||||
<button
|
||||
type="button"
|
||||
tabIndex={0}
|
||||
className={`progression-button progression-button-subtask ${reward.use_progression ? 'progression-button-filled' : 'progression-button-outlined'}`}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault()
|
||||
{subtask.rewards && subtask.rewards.length > 0 && (
|
||||
<div className="subtask-rewards">
|
||||
{subtask.rewards.map((reward, rIndex) => (
|
||||
<div key={rIndex} className="reward-item">
|
||||
<span className="reward-number">{rIndex}</span>
|
||||
<input
|
||||
type="text"
|
||||
value={reward.project_name}
|
||||
onChange={(e) => {
|
||||
const newSubtasks = [...subtasks]
|
||||
newSubtasks[index].rewards[rIndex].use_progression = !newSubtasks[index].rewards[rIndex].use_progression
|
||||
newSubtasks[index].rewards[rIndex].project_name = e.target.value
|
||||
setSubtasks(newSubtasks)
|
||||
}}
|
||||
title={reward.use_progression ? 'Отключить прогрессию' : 'Включить прогрессию'}
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
placeholder="Проект"
|
||||
className="form-input reward-project-input"
|
||||
list={`subtask-projects-${index}-${rIndex}`}
|
||||
/>
|
||||
<datalist id={`subtask-projects-${index}-${rIndex}`}>
|
||||
{projects.map(p => (
|
||||
<option key={p.project_id} value={p.project_name} />
|
||||
))}
|
||||
</datalist>
|
||||
<input
|
||||
type="number"
|
||||
step="any"
|
||||
value={reward.value}
|
||||
onChange={(e) => {
|
||||
const newSubtasks = [...subtasks]
|
||||
newSubtasks[index].rewards[rIndex].value = e.target.value
|
||||
setSubtasks(newSubtasks)
|
||||
}}
|
||||
placeholder="Score"
|
||||
className="form-input reward-score-input"
|
||||
/>
|
||||
{progressionBase && (
|
||||
<button
|
||||
type="button"
|
||||
tabIndex={0}
|
||||
className={`progression-button progression-button-subtask ${reward.use_progression ? 'progression-button-filled' : 'progression-button-outlined'}`}
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault()
|
||||
const newSubtasks = [...subtasks]
|
||||
newSubtasks[index].rewards[rIndex].use_progression = !newSubtasks[index].rewards[rIndex].use_progression
|
||||
setSubtasks(newSubtasks)
|
||||
}}
|
||||
title={reward.use_progression ? 'Отключить прогрессию' : 'Включить прогрессию'}
|
||||
>
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Показываем ошибку валидации только если это ошибка валидации, не ошибка действия */}
|
||||
{error && (error.includes('обязательно') || error.includes('должны быть заполнены') || error.includes('нельзя одновременно')) && (
|
||||
|
||||
@@ -609,4 +609,3 @@
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.3);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user