Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,7 @@ e.g.:
The following summarizes the various [apps](#adding-a-new-platform-ui-application) in the Platform UI.

- [Platform App](#platform-app)
- [Arena Manager](#arena-manager)
- [Dev Center](#dev-center)
- [Earn](#earn)
- [Gamification Admin](#gamification-admin)
Expand All @@ -565,6 +566,12 @@ It also renders the [Universal Navigation](https://github.com/topcoder-platform/
[Platform README](./src/apps/platform/README.md)
[Platform Routes](./src/apps/platform/src/platform.routes.tsx)

## Arena Manager

Application that hosts AI Arena problem-library and tournament-management workflows.

[Arena Manager Routes](./src/apps/arena-manager/src/arena-manager.routes.tsx)

## Dev Center

A community-led project to document how to work with Topcoder internal applications.
Expand Down
9 changes: 9 additions & 0 deletions craco.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ module.exports = {
} },
],

devServer: {
proxy: {
'/arena-manager/api': {
target: 'http://localhost:8081',
changeOrigin: true,
},
Comment thread
jpeg22 marked this conversation as resolved.
},
},

webpack: {
alias: {
// aliases used in JS/TS
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -249,5 +249,6 @@
"volta": {
"node": "22.13.0",
"yarn": "1.22.22"
}
},
"packageManager": "yarn@1.22.22"
}
1 change: 1 addition & 0 deletions src/apps/arena-manager/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './src'
32 changes: 32 additions & 0 deletions src/apps/arena-manager/src/ArenaManagerApp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { FC, useContext, useEffect, useMemo } from 'react'
import { Outlet, Routes } from 'react-router-dom'

import { routerContext, RouterContextData } from '~/libs/core'

import { toolTitle } from './arena-manager.routes'
import './lib/styles/index.scss'

/**
* Root component for the Arena Manager micro-app.
*/
const ArenaManagerApp: FC = () => {
const { getChildRoutes }: RouterContextData = useContext(routerContext)
// eslint-disable-next-line react-hooks/exhaustive-deps
const childRoutes = useMemo(() => getChildRoutes(toolTitle), [])

useEffect(() => {
document.body.classList.add('arena-manager-app')
return () => {
document.body.classList.remove('arena-manager-app')
}
}, [])

return (
<div>
<Outlet />
<Routes>{childRoutes}</Routes>
</div>
)
}

export default ArenaManagerApp
80 changes: 80 additions & 0 deletions src/apps/arena-manager/src/arena-manager.routes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { AppSubdomain, ToolTitle } from '~/config'
import {
lazyLoad,
PlatformRoute,
Rewrite,
} from '~/libs/core'

import {
aiHubTournamentRoute,
problemLibraryRouteId,
rootRoute,
tournamentLaunchRouteId,
tournamentsRouteId,
} from './config/routes.config'
import ActiveTournamentAiHubPage from './tournaments/ActiveTournamentAiHubPage'

const ArenaManagerApp = lazyLoad(
() => import('./ArenaManagerApp'),
)

const ProblemLibraryPage = lazyLoad(
() => import('./problem-library/ProblemLibraryPage'),
'ProblemLibraryPage',
)

const TournamentPage = lazyLoad(
() => import('./tournaments'),
'TournamentPage',
)

const TournamentLaunchPage = lazyLoad(
() => import('./tournaments/TournamentLaunchPage'),
'TournamentLaunchPage',
)

export const toolTitle: string = ToolTitle.arenaManager

export const arenaManagerRoutes: ReadonlyArray<PlatformRoute> = [
{
authRequired: false,
element: <ActiveTournamentAiHubPage />,
id: 'ai-hub-tournament',
route: aiHubTournamentRoute,
title: 'Active Tournament',
},
{
authRequired: true,
// TODO: Restrict viewing of Arena Manager pages by role when role policy is finalized.
// Example: rolesRequired: ['arena-admin']
children: [
{
element: <Rewrite to={problemLibraryRouteId} />,
route: '',
},
{
element: <ProblemLibraryPage />,
id: problemLibraryRouteId,
route: problemLibraryRouteId,
title: 'Problem Library',
},
{
element: <TournamentPage />,
id: tournamentsRouteId,
route: tournamentsRouteId,
title: 'Tournaments',
},
{
element: <TournamentLaunchPage />,
id: tournamentLaunchRouteId,
route: `${tournamentsRouteId}/:tourneyId/launch`,
title: 'Launch Tournament',
},
],
domain: AppSubdomain.arenaManager,
element: <ArenaManagerApp />,
id: toolTitle,
route: rootRoute,
title: toolTitle,
},
]
14 changes: 14 additions & 0 deletions src/apps/arena-manager/src/config/index.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Shared constants for the arena-manager app.
*/

export const MSG_NO_PROBLEMS_FOUND = 'No problems registered. Upload one to get started.'
export const MSG_NO_TOURNAMENTS_FOUND = 'No tournaments found.'

export const FALLBACK_PROBLEM_IDS = [
'P1-Sorting',
'P2-Maze',
'P3-Pathing',
'P4-Search',
'P5-Heuristic',
]
2 changes: 2 additions & 0 deletions src/apps/arena-manager/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './index.config'
export * from './routes.config'
22 changes: 22 additions & 0 deletions src/apps/arena-manager/src/config/routes.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Route IDs and root path for the arena-manager app.
*/
import { AppSubdomain, EnvironmentConfig } from '~/config'

export const rootRoute: string
= EnvironmentConfig.SUBDOMAIN === AppSubdomain.arenaManager
? ''
: `/${AppSubdomain.arenaManager}`

export const problemLibraryRouteId = 'problem-library'
export const tournamentsRouteId = 'tournaments'
export const tournamentLaunchRouteId = 'tournament-launch'
export const aiHubTournamentRoute = '/ai-hub/tournament'

export function getTournamentLaunchPath(tourneyId: string): string {
return `${rootRoute}/${tournamentsRouteId}/${tourneyId}/launch`
}

export function getActiveTournamentPath(): string {
return aiHubTournamentRoute
}
2 changes: 2 additions & 0 deletions src/apps/arena-manager/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { arenaManagerRoutes } from './arena-manager.routes'
export { rootRoute as arenaManagerRootRoute } from './config/routes.config'
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
@import '@libs/ui/styles/includes';

.modalOverlay {
align-items: center;
background: rgb(15 23 42 / 0.45);
display: flex;
inset: 0;
justify-content: center;
padding: $sp-4;
position: fixed;
z-index: 1200;
}

.modal {
background: $tc-white;
border-radius: 12px;
box-shadow: 0 10px 30px rgb(0 0 0 / 0.2);
max-width: 540px;
overflow: hidden;
width: 100%;
}

.modalHeader {
align-items: center;
border-bottom: 1px solid $black-20;
display: flex;
justify-content: space-between;
padding: $sp-4 $sp-5;
}

.modalTitle {
font-family: $font-barlow;
font-size: 22px;
font-weight: $font-weight-bold;
line-height: 30px;
margin: 0;
text-transform: none;
}

.modalClose {
background: transparent;
border: 0;
color: $black-60;
cursor: pointer;
font-size: 20px;
line-height: 1;
padding: 0;
}

.confirmBody {
padding: $sp-5;
}

.confirmText {
color: $black-80;
font-size: 16px;
line-height: 24px;
margin: 0;
}

.confirmActions {
display: flex;
gap: $sp-3;
justify-content: flex-end;
margin-top: $sp-5;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { FC, ReactNode } from 'react'

import { Button } from '~/libs/ui'

import styles from './DeleteConfirmationModal.module.scss'

interface DeleteConfirmationModalProps {
open: boolean
title: string
content: ReactNode
confirmLabel: string
cancelLabel?: string
confirmButtonClassName?: string
isProcessing?: boolean
onCancel: () => void
onConfirm: () => void
}

export const DeleteConfirmationModal: FC<DeleteConfirmationModalProps> = (
props: DeleteConfirmationModalProps,
) => {
if (!props.open) {
return null
}

return (
<div
className={styles.modalOverlay}
onClick={() => !props.isProcessing && props.onCancel()}
role='dialog'
aria-modal='true'
aria-label={props.title}
>
<div
className={styles.modal}
onClick={event => event.stopPropagation()}
>
<div className={styles.modalHeader}>
<h4 className={styles.modalTitle}>{props.title}</h4>
<button
className={styles.modalClose}
onClick={props.onCancel}
disabled={props.isProcessing}
aria-label='Close'
>
</button>
</div>
<div className={styles.confirmBody}>
<div className={styles.confirmText}>{props.content}</div>
<div className={styles.confirmActions}>
<Button
size='sm'
secondary
noCaps
onClick={props.onCancel}
disabled={props.isProcessing}
>
{props.cancelLabel || 'Cancel'}
</Button>
<Button
size='sm'
noCaps
onClick={props.onConfirm}
disabled={props.isProcessing}
className={props.confirmButtonClassName}
>
{props.confirmLabel}
</Button>
</div>
</div>
</div>
</div>
)
}

export default DeleteConfirmationModal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as DeleteConfirmationModal } from './DeleteConfirmationModal'
1 change: 1 addition & 0 deletions src/apps/arena-manager/src/lib/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './DeleteConfirmationModal'
3 changes: 3 additions & 0 deletions src/apps/arena-manager/src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './components'
export * from './models'
export * from './services'
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Generic API response wrapper from the arena-manager api.
*/
export interface ResponseObject<T> {
data: T
success: boolean
message: string
}
13 changes: 13 additions & 0 deletions src/apps/arena-manager/src/lib/models/SourceProblem.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Represents a programming problem in the problem library.
*/
export interface SourceProblem {
problemId: string
problemName: string
/** Whether the problem has successfully passed its Docker test run. */
isTested: boolean
/** Whether the problem is flagged as ready for use in a contest. */
isContestReady: boolean
/** Human-readable test status: 'Pending Test' | 'Passed' | 'Failed' */
status?: string
}
Loading
Loading