Отключены подзадачи для задач-тестов
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",
|
"name": "play-life-web",
|
||||||
"version": "3.13.0",
|
"version": "3.14.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -508,4 +508,3 @@
|
|||||||
color: #6b7280;
|
color: #6b7280;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -311,18 +311,23 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
|
|||||||
use_progression: r.use_progression
|
use_progression: r.use_progression
|
||||||
})))
|
})))
|
||||||
|
|
||||||
// Загружаем подзадачи
|
// Загружаем подзадачи (только если задача не является тестом)
|
||||||
setSubtasks(data.subtasks.map(st => ({
|
if (data.task.config_id) {
|
||||||
id: st.task.id,
|
// Для задач-тестов не загружаем подзадачи
|
||||||
name: st.task.name || '',
|
setSubtasks([])
|
||||||
reward_message: st.task.reward_message || '',
|
} else {
|
||||||
rewards: st.rewards.map(r => ({
|
setSubtasks(data.subtasks.map(st => ({
|
||||||
position: r.position,
|
id: st.task.id,
|
||||||
project_name: r.project_name,
|
name: st.task.name || '',
|
||||||
value: String(r.value),
|
reward_message: st.task.reward_message || '',
|
||||||
use_progression: r.use_progression
|
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) {
|
if (data.task.wishlist_id) {
|
||||||
@@ -368,6 +373,8 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
|
|||||||
}
|
}
|
||||||
// Тесты не могут иметь прогрессию
|
// Тесты не могут иметь прогрессию
|
||||||
setProgressionBase('')
|
setProgressionBase('')
|
||||||
|
// Тесты не могут иметь подзадачи - очищаем их
|
||||||
|
setSubtasks([])
|
||||||
} else {
|
} else {
|
||||||
setIsTest(false)
|
setIsTest(false)
|
||||||
setWordsCount('10')
|
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)
|
// Пересчет rewards при изменении reward_message (debounce)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (debounceTimer.current) {
|
if (debounceTimer.current) {
|
||||||
@@ -644,7 +658,7 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
|
|||||||
value: parseFloat(r.value) || 0,
|
value: parseFloat(r.value) || 0,
|
||||||
use_progression: !!(progressionBase && r.use_progression)
|
use_progression: !!(progressionBase && r.use_progression)
|
||||||
})),
|
})),
|
||||||
subtasks: subtasks.map(st => ({
|
subtasks: isTest ? [] : subtasks.map(st => ({
|
||||||
id: st.id || undefined,
|
id: st.id || undefined,
|
||||||
name: st.name.trim() || null,
|
name: st.name.trim() || null,
|
||||||
reward_message: st.reward_message.trim() || null,
|
reward_message: st.reward_message.trim() || null,
|
||||||
@@ -1058,108 +1072,110 @@ function TaskForm({ onNavigate, taskId, wishlistId, isTest: isTestFromProps = fa
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-group">
|
{!isTest && (
|
||||||
<div className="subtasks-header">
|
<div className="form-group">
|
||||||
<label>Подзадачи</label>
|
<div className="subtasks-header">
|
||||||
<button type="button" onClick={handleAddSubtask} className="add-subtask-button" title="Добавить подзадачу">
|
<label>Подзадачи</label>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
<button type="button" onClick={handleAddSubtask} className="add-subtask-button" title="Добавить подзадачу">
|
||||||
<line x1="12" y1="5" x2="12" y2="19"></line>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||||
</svg>
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||||
</button>
|
</svg>
|
||||||
</div>
|
</button>
|
||||||
{subtasks.map((subtask, index) => (
|
</div>
|
||||||
<div key={index} className="subtask-form-item">
|
{subtasks.map((subtask, index) => (
|
||||||
<div className="subtask-header-row">
|
<div key={index} className="subtask-form-item">
|
||||||
<input
|
<div className="subtask-header-row">
|
||||||
type="text"
|
<input
|
||||||
value={subtask.name}
|
type="text"
|
||||||
onChange={(e) => handleSubtaskChange(index, 'name', e.target.value)}
|
value={subtask.name}
|
||||||
placeholder="Название подзадачи"
|
onChange={(e) => handleSubtaskChange(index, 'name', e.target.value)}
|
||||||
className="form-input subtask-name-input"
|
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
|
{subtask.rewards && subtask.rewards.length > 0 && (
|
||||||
type="button"
|
<div className="subtask-rewards">
|
||||||
onClick={() => handleRemoveSubtask(index)}
|
{subtask.rewards.map((reward, rIndex) => (
|
||||||
className="remove-subtask-button"
|
<div key={rIndex} className="reward-item">
|
||||||
title="Удалить подзадачу"
|
<span className="reward-number">{rIndex}</span>
|
||||||
>
|
<input
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
type="text"
|
||||||
<path d="M3 6h18"></path>
|
value={reward.project_name}
|
||||||
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
|
onChange={(e) => {
|
||||||
<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()
|
|
||||||
const newSubtasks = [...subtasks]
|
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)
|
setSubtasks(newSubtasks)
|
||||||
}}
|
}}
|
||||||
title={reward.use_progression ? 'Отключить прогрессию' : 'Включить прогрессию'}
|
placeholder="Проект"
|
||||||
>
|
className="form-input reward-project-input"
|
||||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
list={`subtask-projects-${index}-${rIndex}`}
|
||||||
<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>
|
<datalist id={`subtask-projects-${index}-${rIndex}`}>
|
||||||
</svg>
|
{projects.map(p => (
|
||||||
</button>
|
<option key={p.project_id} value={p.project_name} />
|
||||||
)}
|
))}
|
||||||
</div>
|
</datalist>
|
||||||
))}
|
<input
|
||||||
</div>
|
type="number"
|
||||||
)}
|
step="any"
|
||||||
</div>
|
value={reward.value}
|
||||||
))}
|
onChange={(e) => {
|
||||||
</div>
|
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('нельзя одновременно')) && (
|
{error && (error.includes('обязательно') || error.includes('должны быть заполнены') || error.includes('нельзя одновременно')) && (
|
||||||
|
|||||||
@@ -609,4 +609,3 @@
|
|||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.3);
|
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user