import React, { useState, useEffect } from 'react' import { useNavigate, useParams } from 'react-router-dom' import { useTranslation } from 'react-i18next' import { Box, Heading, Button, Input, VStack, HStack, Text, Field, Badge, IconButton, Flex, } from '@chakra-ui/react' import { useGetChainQuery, useGetTasksQuery, useCreateChainMutation, useUpdateChainMutation, } from '../../__data__/api/api' import { URLs } from '../../__data__/urls' import { LoadingSpinner } from '../../components/LoadingSpinner' import { ErrorAlert } from '../../components/ErrorAlert' import { toaster } from '../../components/ui/toaster' import type { ChallengeTask } from '../../types/challenge' export const ChainFormPage: React.FC = () => { const { id } = useParams<{ id: string }>() const navigate = useNavigate() const isEdit = !!id const { t } = useTranslation() const { data: chain, isLoading: isLoadingChain, error: loadError } = useGetChainQuery(id!, { skip: !id, }) const { data: allTasks, isLoading: isLoadingTasks } = useGetTasksQuery() const [createChain, { isLoading: isCreating }] = useCreateChainMutation() const [updateChain, { isLoading: isUpdating }] = useUpdateChainMutation() const [name, setName] = useState('') const [selectedTasks, setSelectedTasks] = useState([]) const [searchQuery, setSearchQuery] = useState('') const [isActive, setIsActive] = useState(true) useEffect(() => { if (chain) { setName(chain.name) setSelectedTasks(chain.tasks) setIsActive(chain.isActive !== false) } }, [chain]) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!name.trim()) { toaster.create({ title: t('challenge.admin.common.validation.error'), description: t('challenge.admin.chains.validation.enter.name'), type: 'error', }) return } if (selectedTasks.length === 0) { toaster.create({ title: t('challenge.admin.common.validation.error'), description: t('challenge.admin.chains.validation.add.task'), type: 'error', }) return } try { const taskIds = selectedTasks.map((task) => task.id) if (isEdit && id) { await updateChain({ id, data: { name: name.trim(), taskIds: taskIds, isActive, }, }).unwrap() toaster.create({ title: t('challenge.admin.common.success'), description: t('challenge.admin.chains.updated'), type: 'success', }) } else { await createChain({ name: name.trim(), taskIds: taskIds, isActive, }).unwrap() toaster.create({ title: t('challenge.admin.common.success'), description: t('challenge.admin.chains.created'), type: 'success', }) } navigate(URLs.chains) } catch (err: unknown) { const errorMessage = (err && typeof err === 'object' && 'data' in err && err.data && typeof err.data === 'object' && 'error' in err.data && err.data.error && typeof err.data.error === 'object' && 'message' in err.data.error && typeof err.data.error.message === 'string') ? err.data.error.message : t('challenge.admin.chains.save.error') toaster.create({ title: t('challenge.admin.common.error'), description: errorMessage, type: 'error', }) } } const handleAddTask = (task: ChallengeTask) => { if (!selectedTasks.find((t) => t.id === task.id)) { setSelectedTasks([...selectedTasks, task]) } } const handleRemoveTask = (taskId: string) => { setSelectedTasks(selectedTasks.filter((t) => t.id !== taskId)) } const handleMoveUp = (index: number) => { if (index === 0) return const newTasks = [...selectedTasks] ;[newTasks[index - 1], newTasks[index]] = [newTasks[index], newTasks[index - 1]] setSelectedTasks(newTasks) } const handleMoveDown = (index: number) => { if (index === selectedTasks.length - 1) return const newTasks = [...selectedTasks] ;[newTasks[index], newTasks[index + 1]] = [newTasks[index + 1], newTasks[index]] setSelectedTasks(newTasks) } if (isEdit && isLoadingChain) { return } if (isEdit && loadError) { return } if (isLoadingTasks) { return } if (!allTasks) { return } const isLoading = isCreating || isUpdating const availableTasks = allTasks.filter( (task) => !selectedTasks.find((t) => t.id === task.id) && task.title.toLowerCase().includes(searchQuery.toLowerCase()) ) return ( {isEdit ? t('challenge.admin.chains.edit.title') : t('challenge.admin.chains.create.title')} {/* Name */} {t('challenge.admin.chains.field.name')} setName(e.target.value)} placeholder={t('challenge.admin.chains.field.name.placeholder')} maxLength={255} disabled={isLoading} /> {/* Active flag */} setIsActive(e.target.checked)} disabled={isLoading} style={{ cursor: isLoading ? 'not-allowed' : 'pointer' }} /> {t('challenge.admin.chains.field.isActive')} {t('challenge.admin.chains.field.isActive.helper')} {/* Selected Tasks */} {t('challenge.admin.chains.selected.tasks')} ({selectedTasks.length}) {selectedTasks.length === 0 ? ( {t('challenge.admin.chains.selected.tasks.empty')} ) : ( {selectedTasks.map((task, index) => ( #{index + 1} {task.title} navigate(URLs.taskEdit(task.id))} disabled={isLoading} aria-label="Edit task" > ✎ handleMoveUp(index)} disabled={index === 0 || isLoading} aria-label="Move up" > ↑ handleMoveDown(index)} disabled={index === selectedTasks.length - 1 || isLoading} aria-label="Move down" > ↓ handleRemoveTask(task.id)} disabled={isLoading} aria-label="Remove" > ✕ ))} )} {/* Available Tasks */} {t('challenge.admin.chains.available.tasks')} setSearchQuery(e.target.value)} mb={3} /> {availableTasks.length === 0 ? ( {allTasks.length === selectedTasks.length ? t('challenge.admin.chains.all.tasks.added') : t('challenge.admin.common.not.found')} ) : ( {availableTasks.map((task) => ( handleAddTask(task)} > {task.title} ))} )} {/* Actions */} ) }