dont work
This commit is contained in:
@@ -8,6 +8,7 @@ import Home from './pages/Home';
|
||||
import Mission from './pages/Mission';
|
||||
import ArticleEditor from './pages/ArticleEditor';
|
||||
import Article from './pages/Article';
|
||||
import ContestEditor from './pages/ContestEditor';
|
||||
|
||||
function App() {
|
||||
return (
|
||||
@@ -20,6 +21,10 @@ function App() {
|
||||
path="/article/create/*"
|
||||
element={<ArticleEditor />}
|
||||
/>
|
||||
<Route
|
||||
path="/contest/create/*"
|
||||
element={<ContestEditor />}
|
||||
/>
|
||||
<Route path="/article/:articleId" element={<Article />} />
|
||||
<Route path="*" element={<Home />} />
|
||||
</Routes>
|
||||
|
||||
336
src/pages/ContestEditor.tsx
Normal file
336
src/pages/ContestEditor.tsx
Normal file
@@ -0,0 +1,336 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import Header from '../views/articleeditor/Header';
|
||||
import { PrimaryButton } from '../components/button/PrimaryButton';
|
||||
import { Input } from '../components/input/Input';
|
||||
import { useAppDispatch, useAppSelector } from '../redux/hooks';
|
||||
import {
|
||||
createContest,
|
||||
CreateContestBody,
|
||||
fetchContestById,
|
||||
} from '../redux/slices/contests';
|
||||
import DateRangeInput from '../components/input/DateRangeInput';
|
||||
import { useQuery } from '../hooks/useQuery';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { fetchMissionById, Mission } from '../redux/slices/missions';
|
||||
import { ReverseButton } from '../components/button/ReverseButton';
|
||||
|
||||
/**
|
||||
* Страница создания / редактирования контеста
|
||||
*/
|
||||
const ContestEditor = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
const status = useAppSelector(
|
||||
(state) => state.contests.createContest.status,
|
||||
);
|
||||
|
||||
const [missionIdInput, setMissionIdInput] = useState<string>('');
|
||||
|
||||
const query = useQuery();
|
||||
const back = query.get('back') ?? undefined;
|
||||
const contestId = Number(query.get('contestId') ?? undefined);
|
||||
const refactor = !!contestId;
|
||||
|
||||
const [contest, setContest] = useState<CreateContestBody>({
|
||||
name: '',
|
||||
description: '',
|
||||
scheduleType: 'AlwaysOpen',
|
||||
visibility: 'Public',
|
||||
startsAt: null,
|
||||
endsAt: null,
|
||||
attemptDurationMinutes: null,
|
||||
maxAttempts: null,
|
||||
allowEarlyFinish: false,
|
||||
groupId: null,
|
||||
missionIds: [],
|
||||
articleIds: [],
|
||||
});
|
||||
|
||||
const [missions, setMissions] = useState<Mission[]>([]);
|
||||
|
||||
const { contest: contestById, status: contestByIdstatus } = useAppSelector(
|
||||
(state) => state.contests.fetchContestById,
|
||||
);
|
||||
console.log(contestByIdstatus, contestById);
|
||||
useEffect(() => {
|
||||
if (status === 'successful') {
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
const handleChange = (key: keyof CreateContestBody, value: any) => {
|
||||
setContest((prev) => ({ ...prev, [key]: value }));
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
dispatch(createContest(contest));
|
||||
};
|
||||
|
||||
const addMission = () => {
|
||||
const id = Number(missionIdInput.trim());
|
||||
if (!id || contest.missionIds?.includes(id)) return;
|
||||
dispatch(fetchMissionById(id))
|
||||
.unwrap()
|
||||
.then((mission) => {
|
||||
setMissions((prev) => [...prev, mission]);
|
||||
setContest((prev) => ({
|
||||
...prev,
|
||||
missionIds: [...(prev.missionIds ?? []), id],
|
||||
}));
|
||||
setMissionIdInput('');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('Ошибка при загрузке миссии:', err);
|
||||
});
|
||||
};
|
||||
|
||||
const removeMission = (removeId: number) => {
|
||||
setContest({
|
||||
...contest,
|
||||
missionIds: contest.missionIds?.filter((v) => v !== removeId),
|
||||
});
|
||||
setMissions(missions.filter((v) => v.id != removeId));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (refactor) {
|
||||
dispatch(fetchContestById(contestId));
|
||||
}
|
||||
}, [refactor]);
|
||||
|
||||
useEffect(() => {
|
||||
if (refactor && contestByIdstatus == 'successful' && contestById) {
|
||||
setContest({
|
||||
...contestById,
|
||||
missionIds: [],
|
||||
visibility: 'Public',
|
||||
scheduleType: 'AlwaysOpen',
|
||||
});
|
||||
}
|
||||
}, [contestById]);
|
||||
|
||||
return (
|
||||
<div className="h-screen grid grid-rows-[60px,1fr] text-liquid-white">
|
||||
<Header backClick={() => navigate(back || '/home/contests')} />
|
||||
|
||||
<div className="grid grid-cols-2 h-full min-h-0">
|
||||
{/* Левая панешь */}
|
||||
<div className="overflow-y-auto min-h-0 overflow-hidden">
|
||||
<div className="p-4 border-r border-gray-700 flex flex-col h-full">
|
||||
<h2 className="text-lg font-semibold mb-3 text-gray-100"></h2>
|
||||
|
||||
<div className="">
|
||||
<div className="font-bold text-[30px] mb-[10px]">
|
||||
{refactor
|
||||
? `Редактирвоание контеста #${contestId} \"${contestById?.name}\"`
|
||||
: 'Создать контест'}
|
||||
</div>
|
||||
|
||||
<Input
|
||||
name="name"
|
||||
type="text"
|
||||
label="Название"
|
||||
className="mt-[10px]"
|
||||
placeholder="Введите название"
|
||||
onChange={(v) => handleChange('name', v)}
|
||||
defaultState={contest.name ?? ''}
|
||||
/>
|
||||
|
||||
<Input
|
||||
name="description"
|
||||
type="text"
|
||||
label="Описание"
|
||||
className="mt-[10px]"
|
||||
placeholder="Введите описание"
|
||||
onChange={(v) => handleChange('description', v)}
|
||||
defaultState={contest.description ?? ''}
|
||||
/>
|
||||
|
||||
<div className="grid grid-cols-2 gap-[10px] mt-[10px]">
|
||||
<div>
|
||||
<label className="block text-sm mb-1">
|
||||
Тип расписания
|
||||
</label>
|
||||
<select
|
||||
className="w-full p-2 rounded-md bg-liquid-darker border border-liquid-lighter"
|
||||
value={contest.scheduleType}
|
||||
onChange={(e) =>
|
||||
handleChange(
|
||||
'scheduleType',
|
||||
e.target
|
||||
.value as CreateContestBody['scheduleType'],
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="AlwaysOpen">
|
||||
Всегда открыт
|
||||
</option>
|
||||
<option value="FixedWindow">
|
||||
Фиксированные даты
|
||||
</option>
|
||||
<option value="RollingWindow">
|
||||
Скользящее окно
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm mb-1">
|
||||
Видимость
|
||||
</label>
|
||||
<select
|
||||
className="w-full p-2 rounded-md bg-liquid-darker border border-liquid-lighter"
|
||||
value={contest.visibility}
|
||||
onChange={(e) =>
|
||||
handleChange(
|
||||
'visibility',
|
||||
e.target
|
||||
.value as CreateContestBody['visibility'],
|
||||
)
|
||||
}
|
||||
>
|
||||
<option value="Public">
|
||||
Публичный
|
||||
</option>
|
||||
<option value="GroupPrivate">
|
||||
Групповой
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Даты начала и конца */}
|
||||
<div className="grid grid-cols-2 gap-[10px] mt-[10px]">
|
||||
<DateRangeInput
|
||||
startValue={contest.startsAt || ''}
|
||||
endValue={contest.endsAt || ''}
|
||||
onChange={handleChange}
|
||||
className="mt-[10px]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Продолжительность и лимиты */}
|
||||
<div className="grid grid-cols-2 gap-[10px] mt-[10px]">
|
||||
<Input
|
||||
name="attemptDurationMinutes"
|
||||
type="number"
|
||||
label="Длительность попытки (мин)"
|
||||
placeholder="Например: 60"
|
||||
onChange={(v) =>
|
||||
handleChange(
|
||||
'attemptDurationMinutes',
|
||||
Number(v),
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Input
|
||||
name="maxAttempts"
|
||||
type="number"
|
||||
label="Макс. попыток"
|
||||
placeholder="Например: 3"
|
||||
onChange={(v) =>
|
||||
handleChange('maxAttempts', Number(v))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Разрешить раннее завершение */}
|
||||
<div className="flex items-center gap-[10px] mt-[15px]">
|
||||
<input
|
||||
id="allowEarlyFinish"
|
||||
type="checkbox"
|
||||
checked={!!contest.allowEarlyFinish}
|
||||
onChange={(e) =>
|
||||
handleChange(
|
||||
'allowEarlyFinish',
|
||||
e.target.checked,
|
||||
)
|
||||
}
|
||||
/>
|
||||
<label htmlFor="allowEarlyFinish">
|
||||
Разрешить раннее завершение
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Кнопки */}
|
||||
<div className="flex flex-row w-full items-center justify-end mt-[20px] gap-[20px]">
|
||||
{refactor ? (
|
||||
<>
|
||||
<PrimaryButton
|
||||
onClick={handleSubmit}
|
||||
text="Сохранить"
|
||||
disabled={status === 'loading'}
|
||||
/>
|
||||
<ReverseButton
|
||||
color="error"
|
||||
onClick={handleSubmit}
|
||||
text="Удалить"
|
||||
disabled={status === 'loading'}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<PrimaryButton
|
||||
onClick={handleSubmit}
|
||||
text="Создать"
|
||||
disabled={status === 'loading'}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Правая панель */}
|
||||
<div className="overflow-y-auto min-h-0 overflow-hidden">
|
||||
<div className="p-4 border-r border-gray-700 flex flex-col h-full">
|
||||
<h2 className="text-lg font-semibold mb-3 text-gray-100"></h2>
|
||||
|
||||
{/* Блок для тегов */}
|
||||
<div className="mt-[20px] max-w-[600px]">
|
||||
<div className="grid grid-cols-[1fr,140px] items-end gap-2">
|
||||
<Input
|
||||
name="missionId"
|
||||
autocomplete="missionId"
|
||||
className="mt-[20px] max-w-[600px]"
|
||||
type="number"
|
||||
label="ID миссии"
|
||||
onChange={(v) => {
|
||||
setMissionIdInput(v);
|
||||
}}
|
||||
defaultState={missionIdInput}
|
||||
placeholder="458"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key == 'Enter') addMission();
|
||||
}}
|
||||
/>
|
||||
<PrimaryButton
|
||||
onClick={addMission}
|
||||
text="Добавить"
|
||||
className="h-[40px] w-[140px]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-[10px] mt-2">
|
||||
{missions.map((v, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-1 bg-liquid-lighter px-3 py-1 rounded-full"
|
||||
>
|
||||
<span>{v.id}</span>
|
||||
<span>{v.name}</span>
|
||||
<button
|
||||
onClick={() => removeMission(v.id)}
|
||||
className="text-liquid-red font-bold ml-[5px]"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContestEditor;
|
||||
@@ -15,7 +15,7 @@ export interface Mission {
|
||||
updatedAt: string;
|
||||
timeLimitMilliseconds: number;
|
||||
memoryLimitBytes: number;
|
||||
statements: null;
|
||||
statements: string;
|
||||
}
|
||||
|
||||
export interface Member {
|
||||
@@ -24,18 +24,22 @@ export interface Member {
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface Group {
|
||||
groupId: number;
|
||||
groupName: string;
|
||||
}
|
||||
|
||||
export interface Contest {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
scheduleType: string;
|
||||
scheduleType: 'AlwaysOpen' | 'FixedWindow' | 'RollingWindow';
|
||||
startsAt: string;
|
||||
endsAt: string;
|
||||
attemptDurationMinutes: number | null;
|
||||
maxAttempts: number | null;
|
||||
allowEarlyFinish: boolean | null;
|
||||
groupId: number | null;
|
||||
groupName: string | null;
|
||||
attemptDurationMinutes: number;
|
||||
maxAttempts: number;
|
||||
allowEarlyFinish: boolean;
|
||||
groups: Group[];
|
||||
missions: Mission[];
|
||||
articles: any[];
|
||||
members: Member[];
|
||||
@@ -47,20 +51,18 @@ interface ContestsResponse {
|
||||
}
|
||||
|
||||
export interface CreateContestBody {
|
||||
name?: string | null;
|
||||
description?: string | null;
|
||||
name: string;
|
||||
description: string;
|
||||
scheduleType: 'AlwaysOpen' | 'FixedWindow' | 'RollingWindow';
|
||||
visibility: 'Public' | 'GroupPrivate';
|
||||
startsAt?: string | null;
|
||||
endsAt?: string | null;
|
||||
attemptDurationMinutes?: number | null;
|
||||
maxAttempts?: number | null;
|
||||
allowEarlyFinish?: boolean | null;
|
||||
groupId?: number | null;
|
||||
missionIds?: number[] | null;
|
||||
articleIds?: number[] | null;
|
||||
participantIds?: number[] | null;
|
||||
organizerIds?: number[] | null;
|
||||
startsAt: string;
|
||||
endsAt: string;
|
||||
attemptDurationMinutes: number;
|
||||
maxAttempts: number;
|
||||
allowEarlyFinish: boolean;
|
||||
groupId: number;
|
||||
missionIds: number[];
|
||||
articleIds: number[];
|
||||
}
|
||||
|
||||
// =====================
|
||||
@@ -77,12 +79,22 @@ interface ContestsState {
|
||||
error: string | null;
|
||||
};
|
||||
fetchContestById: {
|
||||
contest: Contest | null;
|
||||
contest: Contest;
|
||||
status: Status;
|
||||
error: string | null;
|
||||
};
|
||||
createContest: {
|
||||
contest: Contest | null;
|
||||
contest: Contest;
|
||||
status: Status;
|
||||
error: string | null;
|
||||
};
|
||||
// 🆕 Добавляем updateContest и deleteContest
|
||||
updateContest: {
|
||||
contest: Contest;
|
||||
status: Status;
|
||||
error: string | null;
|
||||
};
|
||||
deleteContest: {
|
||||
status: Status;
|
||||
error: string | null;
|
||||
};
|
||||
@@ -107,12 +119,63 @@ const initialState: ContestsState = {
|
||||
error: null,
|
||||
},
|
||||
fetchContestById: {
|
||||
contest: null,
|
||||
contest: {
|
||||
id: 0,
|
||||
name: '',
|
||||
description: '',
|
||||
scheduleType: 'AlwaysOpen',
|
||||
startsAt: '',
|
||||
endsAt: '',
|
||||
attemptDurationMinutes: 0,
|
||||
maxAttempts: 0,
|
||||
allowEarlyFinish: false,
|
||||
groups: [],
|
||||
missions: [],
|
||||
articles: [],
|
||||
members: [],
|
||||
},
|
||||
status: 'idle',
|
||||
error: null,
|
||||
},
|
||||
createContest: {
|
||||
contest: null,
|
||||
contest: {
|
||||
id: 0,
|
||||
name: '',
|
||||
description: '',
|
||||
scheduleType: 'AlwaysOpen',
|
||||
startsAt: '',
|
||||
endsAt: '',
|
||||
attemptDurationMinutes: 0,
|
||||
maxAttempts: 0,
|
||||
allowEarlyFinish: false,
|
||||
groups: [],
|
||||
missions: [],
|
||||
articles: [],
|
||||
members: [],
|
||||
},
|
||||
status: 'idle',
|
||||
error: null,
|
||||
},
|
||||
updateContest: {
|
||||
contest: {
|
||||
id: 0,
|
||||
name: '',
|
||||
description: '',
|
||||
scheduleType: 'AlwaysOpen',
|
||||
startsAt: '',
|
||||
endsAt: '',
|
||||
attemptDurationMinutes: 0,
|
||||
maxAttempts: 0,
|
||||
allowEarlyFinish: false,
|
||||
groups: [],
|
||||
missions: [],
|
||||
articles: [],
|
||||
members: [],
|
||||
},
|
||||
status: 'idle',
|
||||
error: null,
|
||||
},
|
||||
deleteContest: {
|
||||
status: 'idle',
|
||||
error: null,
|
||||
},
|
||||
@@ -191,13 +254,51 @@ export const createContest = createAsyncThunk(
|
||||
},
|
||||
);
|
||||
|
||||
// 🆕 Обновление контеста
|
||||
export const updateContest = createAsyncThunk(
|
||||
'contests/update',
|
||||
async (
|
||||
{
|
||||
contestId,
|
||||
...contestData
|
||||
}: { contestId: number } & CreateContestBody,
|
||||
{ rejectWithValue },
|
||||
) => {
|
||||
try {
|
||||
const response = await axios.patch<Contest>(
|
||||
`/contests/${contestId}`,
|
||||
contestData,
|
||||
);
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
return rejectWithValue(
|
||||
err.response?.data?.message || 'Failed to update contest',
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// 🆕 Удаление контеста
|
||||
export const deleteContest = createAsyncThunk(
|
||||
'contests/delete',
|
||||
async (contestId: number, { rejectWithValue }) => {
|
||||
try {
|
||||
await axios.delete(`/contests/${contestId}`);
|
||||
return contestId;
|
||||
} catch (err: any) {
|
||||
return rejectWithValue(
|
||||
err.response?.data?.message || 'Failed to delete contest',
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Контесты, созданные мной
|
||||
export const fetchMyContests = createAsyncThunk(
|
||||
'contests/fetchMyContests',
|
||||
async (_, { rejectWithValue }) => {
|
||||
try {
|
||||
const response = await axios.get<Contest[]>('/contests/my');
|
||||
// Возвращаем просто массив контестов
|
||||
return response.data;
|
||||
} catch (err: any) {
|
||||
return rejectWithValue(
|
||||
@@ -238,8 +339,15 @@ const contestsSlice = createSlice({
|
||||
name: 'contests',
|
||||
initialState,
|
||||
reducers: {
|
||||
clearSelectedContest: (state) => {
|
||||
state.fetchContestById.contest = null;
|
||||
// 🆕 Сброс статусов
|
||||
setContestStatus: (
|
||||
state,
|
||||
action: PayloadAction<{ key: keyof ContestsState; status: Status }>,
|
||||
) => {
|
||||
const { key, status } = action.payload;
|
||||
if (state[key]) {
|
||||
(state[key] as any).status = status;
|
||||
}
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
@@ -295,7 +403,48 @@ const contestsSlice = createSlice({
|
||||
state.createContest.error = action.payload;
|
||||
});
|
||||
|
||||
// fetchMyContests
|
||||
// 🆕 updateContest
|
||||
builder.addCase(updateContest.pending, (state) => {
|
||||
state.updateContest.status = 'loading';
|
||||
state.updateContest.error = null;
|
||||
});
|
||||
builder.addCase(
|
||||
updateContest.fulfilled,
|
||||
(state, action: PayloadAction<Contest>) => {
|
||||
state.updateContest.status = 'successful';
|
||||
state.updateContest.contest = action.payload;
|
||||
},
|
||||
);
|
||||
builder.addCase(updateContest.rejected, (state, action: any) => {
|
||||
state.updateContest.status = 'failed';
|
||||
state.updateContest.error = action.payload;
|
||||
});
|
||||
|
||||
// 🆕 deleteContest
|
||||
builder.addCase(deleteContest.pending, (state) => {
|
||||
state.deleteContest.status = 'loading';
|
||||
state.deleteContest.error = null;
|
||||
});
|
||||
builder.addCase(
|
||||
deleteContest.fulfilled,
|
||||
(state, action: PayloadAction<number>) => {
|
||||
state.deleteContest.status = 'successful';
|
||||
// Удалим контест из списков
|
||||
state.fetchContests.contests =
|
||||
state.fetchContests.contests.filter(
|
||||
(c) => c.id !== action.payload,
|
||||
);
|
||||
state.fetchMyContests.contests =
|
||||
state.fetchMyContests.contests.filter(
|
||||
(c) => c.id !== action.payload,
|
||||
);
|
||||
},
|
||||
);
|
||||
builder.addCase(deleteContest.rejected, (state, action: any) => {
|
||||
state.deleteContest.status = 'failed';
|
||||
state.deleteContest.error = action.payload;
|
||||
});
|
||||
|
||||
// fetchMyContests
|
||||
builder.addCase(fetchMyContests.pending, (state) => {
|
||||
state.fetchMyContests.status = 'loading';
|
||||
@@ -342,5 +491,5 @@ const contestsSlice = createSlice({
|
||||
// Экспорты
|
||||
// =====================
|
||||
|
||||
export const { clearSelectedContest } = contestsSlice.actions;
|
||||
export const { setContestStatus } = contestsSlice.actions;
|
||||
export const contestsReducer = contestsSlice.reducer;
|
||||
|
||||
@@ -60,7 +60,6 @@ const ContestsBlock: FC<ContestsBlockProps> = ({
|
||||
id={v.id}
|
||||
name={v.name}
|
||||
startAt={v.startsAt}
|
||||
statusRegister={'reg'}
|
||||
duration={
|
||||
new Date(v.endsAt).getTime() -
|
||||
new Date(v.startsAt).getTime()
|
||||
|
||||
@@ -93,7 +93,7 @@ const ContestItem: React.FC<ContestItemProps> = ({
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/contest/editor?back=/home/account/articles&articleId=${id}`,
|
||||
`/contest/create?back=/home/account/contests&contestId=${id}`,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -4,9 +4,13 @@ import { PrimaryButton } from '../../../components/button/PrimaryButton';
|
||||
import { SecondaryButton } from '../../../components/button/SecondaryButton';
|
||||
import { Input } from '../../../components/input/Input';
|
||||
import { useAppDispatch, useAppSelector } from '../../../redux/hooks';
|
||||
import { createContest } from '../../../redux/slices/contests';
|
||||
import {
|
||||
createContest,
|
||||
setContestStatus,
|
||||
} from '../../../redux/slices/contests';
|
||||
import { CreateContestBody } from '../../../redux/slices/contests';
|
||||
import DateRangeInput from '../../../components/input/DateRangeInput';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface ModalCreateContestProps {
|
||||
active: boolean;
|
||||
@@ -18,6 +22,7 @@ const ModalCreateContest: FC<ModalCreateContestProps> = ({
|
||||
setActive,
|
||||
}) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const navigate = useNavigate();
|
||||
const status = useAppSelector(
|
||||
(state) => state.contests.createContest.status,
|
||||
);
|
||||
@@ -27,21 +32,29 @@ const ModalCreateContest: FC<ModalCreateContestProps> = ({
|
||||
description: '',
|
||||
scheduleType: 'AlwaysOpen',
|
||||
visibility: 'Public',
|
||||
startsAt: null,
|
||||
endsAt: null,
|
||||
attemptDurationMinutes: null,
|
||||
maxAttempts: null,
|
||||
startsAt: '',
|
||||
endsAt: '',
|
||||
attemptDurationMinutes: 0,
|
||||
maxAttempts: 0,
|
||||
allowEarlyFinish: false,
|
||||
groupId: null,
|
||||
missionIds: null,
|
||||
articleIds: null,
|
||||
participantIds: null,
|
||||
organizerIds: null,
|
||||
groupId: 0,
|
||||
missionIds: [],
|
||||
articleIds: [],
|
||||
});
|
||||
|
||||
const contest = useAppSelector(
|
||||
(state) => state.contests.createContest.contest,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (status === 'successful') {
|
||||
setActive(false);
|
||||
console.log('navigate');
|
||||
navigate(
|
||||
`/contest/create?back=/home/account/contests&contestId=${contest.id}`,
|
||||
);
|
||||
dispatch(
|
||||
setContestStatus({ key: 'createContest', status: 'idle' }),
|
||||
);
|
||||
}
|
||||
}, [status]);
|
||||
|
||||
@@ -176,7 +189,9 @@ const ModalCreateContest: FC<ModalCreateContestProps> = ({
|
||||
{/* Кнопки */}
|
||||
<div className="flex flex-row w-full items-center justify-end mt-[20px] gap-[20px]">
|
||||
<PrimaryButton
|
||||
onClick={handleSubmit}
|
||||
onClick={() => {
|
||||
handleSubmit();
|
||||
}}
|
||||
text="Создать"
|
||||
disabled={status === 'loading'}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user