diff --git a/src/pages/ContestEditor.tsx b/src/pages/ContestEditor.tsx index 03bfd7e..3c66c31 100644 --- a/src/pages/ContestEditor.tsx +++ b/src/pages/ContestEditor.tsx @@ -12,7 +12,7 @@ import { } from '../redux/slices/contests'; import { useQuery } from '../hooks/useQuery'; import { Navigate, useNavigate } from 'react-router-dom'; -import { fetchMissionById } from '../redux/slices/missions'; +import { fetchMissionById, fetchMissions } from '../redux/slices/missions'; import { ReverseButton } from '../components/button/ReverseButton'; import { DropDownList, @@ -28,6 +28,54 @@ interface Mission { name: string; } +const highlightZ = (name: string, filter: string) => { + if (!filter) return name; + + const s = filter.toLowerCase(); + const t = name.toLowerCase(); + const n = t.length; + const m = s.length; + + const mark = Array(n).fill(false); + + // Проходимся с конца и ставим отметки + for (let i = n - 1; i >= 0; i--) { + if (i + m <= n && t.slice(i, i + m) === s) { + for (let j = i; j < i + m; j++) { + if (mark[j]) break; + mark[j] = true; + } + } + } + + // === Формируем единые жёлтые блоки === + const result: any[] = []; + let i = 0; + + while (i < n) { + if (!mark[i]) { + // обычный символ + result.push(name[i]); + i++; + } else { + // начинаем жёлтый блок + let j = i; + while (j < n && mark[j]) j++; + + const chunk = name.slice(i, j); + result.push( + + {chunk} + , + ); + + i = j; + } + } + + return result; +}; + function toUtc(localDateTime?: string): string { if (!localDateTime) return ''; @@ -58,7 +106,7 @@ const ContestEditor = () => { (state) => state.contests.createContest.status, ); - const [missionIdInput, setMissionIdInput] = useState(''); + const [missionFindInput, setMissionFindInput] = useState(''); const now = new Date(); const plus60 = new Date(now.getTime() + 60 * 60 * 1000); @@ -107,6 +155,8 @@ const ContestEditor = () => { (state) => state.contests.fetchContestById, ); + const globalMissions = useAppSelector((state) => state.missions.missions); + const myGroups = useAppSelector( (state) => state.groups.fetchMyGroups.groups, ).filter((group) => @@ -153,7 +203,15 @@ const ContestEditor = () => { }; const addMission = () => { - const id = Number(missionIdInput.trim()); + const mission = globalMissions + .filter((v) => !contest?.missionIds?.includes(v.id)) + .filter((v) => + (v.id + ' ' + v.name) + .toLocaleLowerCase() + .includes(missionFindInput.toLocaleLowerCase()), + )[0]; + if (!mission) return; + const id = mission.id; if (!id || contest.missionIds?.includes(id)) return; dispatch(fetchMissionById(id)) .unwrap() @@ -163,7 +221,7 @@ const ContestEditor = () => { ...prev, missionIds: [...(prev.missionIds ?? []), id], })); - setMissionIdInput(''); + setMissionFindInput(''); }) .catch((err) => { err; @@ -199,12 +257,13 @@ const ContestEditor = () => { useEffect(() => { if (refactor) { dispatch(fetchContestById(contestId)); + dispatch(fetchMyGroups()); + dispatch(fetchMissions({})); } }, [refactor]); useEffect(() => { if (refactor && contestByIdstatus == 'successful' && contestById) { - dispatch(fetchMyGroups()); setContest({ ...contestById, // groupIds: contestById.groups.map(group => group.groupId), @@ -445,24 +504,22 @@ const ContestEditor = () => { {/* Правая панель */} -
+
-

- {/* Блок для тегов */} -
+
{ - setMissionIdInput(v); + setMissionFindInput(v); }} - defaultState={missionIdInput} - placeholder="458" + defaultState={missionFindInput} + placeholder={`Наприме: \"458\" или \"Поиск наименьшего\"`} onKeyDown={(e) => { if (e.key == 'Enter') addMission(); }} @@ -472,18 +529,70 @@ const ContestEditor = () => { text="Добавить" className="h-[40px] w-[140px]" /> + + {/* Выпадающие задачи */} +
+
+
+ {globalMissions + .filter( + (v) => + !contest?.missionIds?.includes( + v.id, + ), + ) + .filter((v) => + (v.id + ' ' + v.name) + .toLocaleLowerCase() + .includes( + missionFindInput.toLocaleLowerCase(), + ), + ) + .map((v, i) => ( +
{ + setMissionFindInput( + v.id + + ' ' + + v.name, + ); + addMission(); + }} + > + {highlightZ( + '#' + + v.id + + ' ' + + v.name, + missionFindInput, + )} +
+ ))} +
+
+
-
+
{missions.map((v, i) => (
- {v.id} - {v.name} +
{'#' + v.id}
+
{v.name}