Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion services/app/apps/codebattle/assets/css/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2213,7 +2213,7 @@ a.cb-text:hover {

.cb-separator {
width: 100%;
border: 2px solid gray;
border: 1px solid gray;
}

// react big calendar for tournaments
Expand Down Expand Up @@ -2517,3 +2517,7 @@ a.cb-text:hover {
.btn-link {
color: white;
}

.cb-subtle-background {
background: radial-gradient(circle at 50% 0%, #3a3b40 0%, $cb-bg-panel 100%);
}
8 changes: 5 additions & 3 deletions services/app/apps/codebattle/assets/js/widgets/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ const rollbarRedux = rollbarMiddleware(rollbar);
const store = configureStore({
reducer: rootReducer,
middleware: getDefaultMiddleware => getDefaultMiddleware({
serializableCheck: { ignoredActions: ['ERROR', PERSIST] },
}).concat(rollbarRedux),
serializableCheck: { ignoredActions: ['ERROR', PERSIST] },
}).concat(rollbarRedux),
});

const persistor = persistStore(store);
Expand Down Expand Up @@ -130,7 +130,9 @@ export const Lobby = () => (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Suspense>
<LobbyWidget />
<NiceModal.Provider>
<LobbyWidget />
</NiceModal.Provider>
</Suspense>
</PersistGate>
</Provider>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, { useState, useEffect, useCallback } from 'react';

import dayjs from '../../i18n/dayjs';

const ScheduleNavigationTab = ({
className,
events,
event,
setEvent,
}) => {
const [prev, setPrevEvent] = useState();
const [next, setNextEvent] = useState();

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 onClickPrev = useCallback(() => {
setEvent(prev);
}, [setEvent, prev]);
const onClickNext = useCallback(() => {
setEvent(next);
}, [setEvent, next]);

return (
<div className={className}>
<div className="d-flex">
{prev && (
<div
role="button"
onClick={onClickPrev}
onKeyPress={() => { }}
className="btn-link"
tabIndex="0"
>
<span className="pr-2">{'<<'}</span>
{prev.title}
</div>
)}
</div>
<div className="d-flex">
{next && (
<div
role="button"
onClick={onClickNext}
onKeyPress={() => { }}
className="btn-link"
tabIndex="0"
>
{next.title}
<span className="pl-2">{'>>'}</span>
</div>
)}
</div>
</div>
);
};

export default ScheduleNavigationTab;
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';

import cn from 'classnames';
import capitalize from 'lodash/capitalize';

import { getRankingPoints, getTasksCount, grades } from '@/config/grades';

const getGradeDescriptionClassName = highlight => (
cn(
'd-flex flex-column flex-lg-row flex-md-row flex-sm-row justify-content-between',
{
'text-monospace': highlight,
},
)
);

const GradeInfo = ({ grade, selected }) => (
<div className={getGradeDescriptionClassName(grade === selected)}>
<span className={grade === selected ? 'text-white' : ''}>
{capitalize(grade)}
{grade === selected && '(*)'}
</span>
<span className={cn('pl-3', { 'text-white': grade === selected })}>
[
{getRankingPoints(grade).join(', ')}
]
</span>
</div>
);

const TournamentDescription = ({
className,
tournament,
}) => (
<div className={className}>
{tournament.grade !== grades.open ? (
<>
<span className="text-white">Tournament Highlights:</span>
<div className="d-flex flex-column">
<span>Prizes: Codebattle T-shirt merch for a top-tier of League</span>
<span>{`Challenges: ${getTasksCount(tournament.grade)} unique algorithm problems`}</span>
<span>Impact: Advancing in the Codebattle programmer rankings</span>
</div>
<div className="d-flex justify-content-center w-100">
<div className="card cb-card mt-2">
<div className="card-header text-center">View League Ranking Points System</div>
<div className="card-body">
{[grades.rookie, grades.challenger, grades.pro, grades.elite, grades.masters, grades.grandSlam].map(grade => (
<GradeInfo grade={grade} selected={tournament.grade} />
))}
</div>
</div>
</div>
</>
) : (
<>
<span className="text-white">Tournament Description:</span>
{tournament.description}
</>
)}
</div>
);

export default TournamentDescription;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';

import { getRankingPoints, grades } from '@/config/grades';

import dayjs from '../../i18n/dayjs';

import TournamentTimer from './TournamentTimer';

const TournamentPreviewPanel = ({
className,
tournament,
start,
end,
}) => (
<div className={className}>
<div className="d-flex flex-column cb-bg-panel cb-rounded p-3">
<span>{`Start Date: ${dayjs(start).format('MMMM DD, YYYY')}`}</span>
<span>{`Time: ${dayjs(start).format('hh:mm A')} - ${dayjs(end).format('hh:mm A')}`}</span>
{tournament.grade !== grades.open
&& <span>{`First Place Points: ${getRankingPoints(tournament.grade)[0]} Ranking Points`}</span>}
<span><TournamentTimer date={start} label="Starts in: " /></span>
</div>
</div>
);

export default TournamentPreviewPanel;
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { useState, useEffect } from 'react';

import dayjs from '../../i18n/dayjs';

const TournamentTimer = ({ date = new Date(), label, children }) => {
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 <>{children}</>;
}

if (duration < 0) {
setStoped(true);
return <>{children}</>;
}

return (
<>
{label}
{' '}
<span className="text-monospace">{dayjs.duration(duration).format('HH:mm:ss')}</span>
</>
);
};

export default TournamentTimer;
33 changes: 33 additions & 0 deletions services/app/apps/codebattle/assets/js/widgets/config/grades.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export const grades = {
open: 'open',
rookie: 'rookie',
challenger: 'challenger',
pro: 'pro',
elite: 'elite',
masters: 'masters',
grandSlam: 'grand_slam',
};

export 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];
}
};

export 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;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const modalCodes = {
awardModal: 'award_modal',
eventStageModal: 'event_stage_modal',
calendarEventModal: 'calendar_event_modal',
tournamentModal: 'tournament_modal',
};

export default modalCodes;
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ function InfoWidget({ viewMode }) {
const task = useSelector(isTestingRoom ? builderTaskSelector : gameTaskSelector);
const { outputData, canShowOutput } = usePlayerOutputForInfoPanel(viewMode, roomMachineState);

if (task.type === 'css') {
if (task?.type === 'css') {
return (
<CssBattleInfoWidget
viewMode={viewMode}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import Gon from 'gon';
import Modal from 'react-bootstrap/Modal';
import { useDispatch, useSelector } from 'react-redux';

import * as lobbyMiddlewares from '../../middlewares/Lobby';
import * as selectors from '../../selectors';
import { actions } from '../../slices';
import { getLobbyUrl, makeGameUrl } from '../../utils/urlBuilders';
import * as lobbyMiddlewares from '@/middlewares/Lobby';
import * as selectors from '@/selectors';
import { actions } from '@/slices';
import { getLobbyUrl, makeGameUrl } from '@/utils/urlBuilders';
import useLobbyModals from '@/utils/useLobbyModals';

import ActiveGames from './ActiveGames';
import Announcement from './Announcement';
Expand Down Expand Up @@ -147,6 +148,8 @@ const LobbyWidget = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useLobbyModals();

return (
<div>
<Modal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,24 @@ const SeasonProfilePanel = ({
<div className="cb-bg-panel cb-rounded d-flex flex-column p-3 h-100 w-100 text-center">
<CodebattleLeagueDescription />
{seasonTournaments?.length || liveTournaments?.length ? (
<div className="cb-bg-highlight-panel">
<div>
{liveTournaments?.length !== 0 && (
<>
<div className="d-flex justify-content-center align-items-center pt-2 cb-bg-panel">
<span className="text-white text-uppercase h4">
Live Tournaments
</span>
</div>
{liveTournaments.map(tournament => (
<TournamentListItem
isAdmin={isAdmin}
key={tournament.id}
tournament={tournament}
icon={activeIcon}
/>
))}
<div className="d-flex flex-wrap">
{liveTournaments.map(tournament => (
<TournamentListItem
isAdmin={isAdmin}
key={tournament.id}
tournament={tournament}
icon={activeIcon}
/>
))}
</div>
</>
)}
{seasonTournaments?.length !== 0 && (
Expand All @@ -89,10 +91,7 @@ const SeasonProfilePanel = ({
Upcoming Tournaments
</span>
</div>
<div
className="d-flex flex-column cb-overflow-y-scroll position-relative"
style={{ maxHeight: '280px' }}
>
<div className="d-flex flex-wrap">
{seasonTournaments.map(tournament => (
<TournamentListItem
isAdmin={isAdmin}
Expand Down
Loading
Loading