+
+
+
+ {icon || getIconForGrade(tournament.grade)}
+
+
-
-
- {tournament.name}
-
-
- {tournament.state !== 'upcoming' && (
-
-
- {mapTournamentTitleByState[tournament.state]}
+
+
+
+ {tournament.grade !== grades.open && (
+
+
+ {getRankingPoints(tournament.grade)[0]}
+ Ranking Points
)}
- {tournamentStates.canceled !== tournament.state
- && tournament.state !== 'upcoming' && (
- <>
-
+
+ {tournament.state !== 'upcoming' && (
+
+
+ {mapTournamentTitleByState[tournament.state]}
+
+ )}
+ {tournamentStates.canceled !== tournament.state
+ && tournament.state !== 'upcoming' && (
+
{tournament.playersCount}
- >
- )}
+ )}
+
{showStartsAt(tournament.state) && (
<>
-
+
- {dayjs(tournament.startsAt).format('MMMM D, YYYY [at] h:mma')}
+
+ {dayjs(tournament.startsAt).format(getDateFormat(tournament.grade))}
+
>
)}
@@ -136,16 +191,14 @@ const TournamentListItem = ({ tournament, icon, isAdmin = false }) => (
<>
- {dayjs(tournament.lastRoundEndedAt).format(
- 'MMMM D, YYYY [at] h:mma',
- )}
+ {dayjs(tournament.lastRoundEndedAt).format(getDateFormat(tournament.grade))}
>
)}
-
+
+
-
);
diff --git a/services/app/apps/codebattle/assets/js/widgets/pages/lobby/TournamentModal.jsx b/services/app/apps/codebattle/assets/js/widgets/pages/lobby/TournamentModal.jsx
new file mode 100644
index 000000000..06017ad2c
--- /dev/null
+++ b/services/app/apps/codebattle/assets/js/widgets/pages/lobby/TournamentModal.jsx
@@ -0,0 +1,81 @@
+import React, { memo } from 'react';
+
+import NiceModal, { useModal } from '@ebay/nice-modal-react';
+import cn from 'classnames';
+import i18n from 'i18next';
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import { useSelector } from 'react-redux';
+
+import TournamentDescription from '@/components/TournamentDescription';
+import TournamentPreviewPanel from '@/components/TournamentPreviewPanel';
+import { grades } from '@/config/grades';
+import ModalCodes from '@/config/modalCodes';
+import { currentUserIsAdminSelector } from '@/selectors';
+
+import dayjs from '../../../i18n/dayjs';
+
+export const TournamentModal = NiceModal.create(({ tournament }) => {
+ const isAdmin = useSelector(currentUserIsAdminSelector);
+
+ const modal = useModal(ModalCodes.tournamentModal);
+
+ const isUpcoming = tournament?.grade === 'upcoming';
+ const start = dayjs(tournament.startsAt).toDate();
+ const end = dayjs(tournament.startsAt).add(1, 'hour').toDate();
+
+ if (!tournament) {
+ return <>>;
+ }
+
+ return (
+
+
+
+ {tournament.grade !== grades.open && Codebattle League 2025 }
+ {i18n.t('Tournament: %{name}', { name: tournament.name })}
+
+
+
+
+
+
+
+
+
+ {tournament.id && (
+
+ {i18n.t('Open Tournament')}
+
+ )}
+
+ {i18n.t('Close')}
+
+
+
+ );
+});
+
+export default memo(TournamentModal);
diff --git a/services/app/apps/codebattle/assets/js/widgets/pages/schedule/EventModal.jsx b/services/app/apps/codebattle/assets/js/widgets/pages/schedule/EventModal.jsx
index ccf789d85..c5d7c98aa 100644
--- a/services/app/apps/codebattle/assets/js/widgets/pages/schedule/EventModal.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/pages/schedule/EventModal.jsx
@@ -1,100 +1,23 @@
import React, {
- memo, useCallback, useEffect, useState,
+ memo, useCallback, useState,
} from 'react';
import NiceModal, { useModal } from '@ebay/nice-modal-react';
import cn from 'classnames';
import i18n from 'i18next';
-import capitalize from 'lodash/capitalize';
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
import { useSelector } from 'react-redux';
+import ScheduleNavigationTab from '@/components/ScheduleNavigationBar';
+import TournamentDescription from '@/components/TournamentDescription';
+import TournamentPreviewPanel from '@/components/TournamentPreviewPanel';
+import { grades } from '@/config/grades';
+import ModalCodes from '@/config/modalCodes';
import { currentUserIsAdminSelector } from '@/selectors';
-import dayjs from '../../../i18n/dayjs';
-import ModalCodes from '../../config/modalCodes';
-
-import { grades } from './TournamentSchedule';
-
-const getRankingPoints = grade => {
- switch (grade) {
- case grades.rookie: return [8, 4, 2];
- case grades.challenger: return [16, 8, 4, 2];
- case grades.pro: return [128, 64, 32, 16, 8, 4, 2];
- case grades.elite: return [256, 128, 64, 32, 16, 8, 4, 2];
- case grades.masters: return [1024, 512, 256, 128, 64, 32, 16, 8, 4, 2];
- case grades.grandSlam: return [2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2];
- default: return [0];
- }
-};
-
-const getTasksCount = grade => {
- switch (grade) {
- case grades.rookie: return 4;
- case grades.challenger: return 6;
- case grades.pro: return 8;
- case grades.elite: return 10;
- case grades.masters: return 12;
- case grades.grandSlam: return 14;
- default: return 0;
- }
-};
-
-const GradeInfo = ({ grade, selected }) => (
-
-
- {capitalize(grade)}
- {grade === selected && '(*)'}
-
-
- [
- {getRankingPoints(grade).join(', ')}
- ]
-
-
-);
-
-const Timer = ({ date = new Date(), label }) => {
- const [duration, setDuration] = useState(0);
- const [stoped, setStoped] = useState(0);
-
- useEffect(() => {
- if (stoped) {
- return () => { };
- }
-
- const interval = setInterval(() => {
- setDuration(dayjs(date).diff(dayjs()));
- }, 100);
-
- return () => {
- clearInterval(interval);
- };
- }, [date, stoped, setDuration]);
-
- if (stoped || duration > 1000 * 60 * 60 * 24) {
- return <>>;
- }
-
- if (duration < 0) {
- setStoped(true);
- return <>>;
- }
-
- return (
- <>
- {label}
- {' '}
-
{dayjs.duration(duration).format('HH:mm:ss')}
- >
- );
-};
-
export const EventModal = NiceModal.create(({ event: selectedEvent, events, clearEvent }) => {
const [currentEvent, setCurrentEvent] = useState();
- const [prevEvent, setPrevEvent] = useState();
- const [nextEvent, setNextEvent] = useState();
const isAdmin = useSelector(currentUserIsAdminSelector);
@@ -102,34 +25,6 @@ export const EventModal = NiceModal.create(({ event: selectedEvent, events, clea
const event = currentEvent || selectedEvent;
const isUpcoming = event?.resourse?.grade === 'upcoming';
-
- useEffect(() => {
- if (event) {
- const sortedEvents = events.sort((a, b) => dayjs(a.start).diff(dayjs(b.start)));
- const eventIndex = sortedEvents.findIndex(e => e.resourse.id === event.resourse.id);
-
- if (eventIndex === -1) return;
-
- if (eventIndex < 1) {
- setPrevEvent(undefined);
- } else {
- setPrevEvent(sortedEvents[eventIndex - 1]);
- }
-
- if (eventIndex > events.length - 2) {
- setNextEvent(undefined);
- } else {
- setNextEvent(sortedEvents[eventIndex + 1]);
- }
- }
- }, [event, events, setPrevEvent, setNextEvent]);
-
- const clickPrev = useCallback(() => {
- setCurrentEvent(prevEvent);
- }, [setCurrentEvent, prevEvent]);
- const clickNext = useCallback(() => {
- setCurrentEvent(nextEvent);
- }, [setCurrentEvent, nextEvent]);
const handleClose = useCallback(() => {
modal.hide();
clearEvent();
@@ -150,72 +45,22 @@ export const EventModal = NiceModal.create(({ event: selectedEvent, events, clea
-
-
- {prevEvent && (
-
{ }}
- className="btn-link"
- tabIndex="0"
- >
- {'<<'}
- {prevEvent.title}
-
- )}
-
-
- {nextEvent && (
-
{ }}
- className="btn-link"
- tabIndex="0"
- >
- {nextEvent.title}
- {'>>'}
-
- )}
-
-
-
-
- {`Start Date: ${dayjs(event.start).format('MMMM DD, YYYY')}`}
- {`Time: ${dayjs(event.start).format('hh:mm A')} - ${dayjs(event.end).format('hh:mm A')}`}
- {event.resourse.grade !== grades.open
- && {`First Place Points: ${getRankingPoints(event.resourse.grade)[0]} Ranking Points`} }
-
-
-
-
- {event.resourse.grade !== grades.open ? (
- <>
-
Tournament Highlights:
-
- Prizes: Codebattle T-shirt merch for a top-tier of League
- {`Challenges: ${getTasksCount(event.resourse.grade)} unique algorithm problems`}
- Impact: Advancing in the Codebattle programmer rankings
-
-
-
-
View League Ranking Points System
-
- {[grades.rookie, grades.challenger, grades.pro, grades.elite, grades.masters, grades.grandSlam].map(grade => (
-
- ))}
-
-
-
- >
- ) : (
- <>
-
Tournament Description:
- {event.resourse.description}
- >
- )}
-
+
+
+
diff --git a/services/app/apps/codebattle/assets/js/widgets/pages/schedule/ScheduleLegend.jsx b/services/app/apps/codebattle/assets/js/widgets/pages/schedule/ScheduleLegend.jsx
index 2bb106bd3..4bfb6a135 100644
--- a/services/app/apps/codebattle/assets/js/widgets/pages/schedule/ScheduleLegend.jsx
+++ b/services/app/apps/codebattle/assets/js/widgets/pages/schedule/ScheduleLegend.jsx
@@ -12,14 +12,20 @@ export const states = {
};
const sectionBtnClassName = cn(
- 'btn btn-secondary border-0 cb-btn-secondary cb-rounded mx-2',
+ 'btn btn-secondary cb-btn-secondary cb-rounded w-100 m-2',
);
const ScheduleLegend = ({ onChangeContext, loading, context }) => {
const isAdmin = useSelector(currentUserIsAdminSelector);
return (
-
+
{
return [diff, 'millisecond'];
}
- if (t.grade === 'rookie') {
+ if (t.grade === grades.rookie) {
return [15, 'minute'];
}
@@ -58,10 +49,7 @@ const getEventFromTournamentData = t => ({
.add(...getEndOffsetParams(t))
.toDate(),
resourse: {
- id: t.id,
- description: t.description,
- state: t.state,
- grade: t.grade,
+ ...t,
},
});
diff --git a/services/app/apps/codebattle/assets/js/widgets/utils/useLobbyModals.js b/services/app/apps/codebattle/assets/js/widgets/utils/useLobbyModals.js
new file mode 100644
index 000000000..0b96084c1
--- /dev/null
+++ b/services/app/apps/codebattle/assets/js/widgets/utils/useLobbyModals.js
@@ -0,0 +1,21 @@
+import { useEffect } from 'react';
+
+import NiceModal, { unregister } from '@ebay/nice-modal-react';
+
+import ModalCodes from '@/config/modalCodes';
+import TournamentModal from '@/pages/lobby/TournamentModal';
+
+const useLobbyModals = () => {
+ useEffect(() => {
+ NiceModal.register(ModalCodes.tournamentModal, TournamentModal);
+
+ const unregisterModals = () => {
+ unregister(ModalCodes.tournamentModal);
+ };
+
+ return unregisterModals;
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+};
+
+export default useLobbyModals;
diff --git a/services/app/apps/codebattle/webpack/webpack.base.config.js b/services/app/apps/codebattle/webpack/webpack.base.config.js
index 85e1e2f77..cdc333937 100644
--- a/services/app/apps/codebattle/webpack/webpack.base.config.js
+++ b/services/app/apps/codebattle/webpack/webpack.base.config.js
@@ -131,6 +131,7 @@ module.exports = {
'../assets/js/widgets/middlewares',
),
'@/pages': path.resolve(__dirname, '../assets/js/widgets/pages'),
+ '@/config': path.resolve(__dirname, '../assets/js/widgets/config'),
'@/selectors': path.resolve(__dirname, '../assets/js/widgets/selectors'),
'@/slices': path.resolve(__dirname, '../assets/js/widgets/slices'),
'@/utils': path.resolve(__dirname, '../assets/js/widgets/utils'),