diff --git a/Frontend/package.json b/Frontend/package.json index 5a17e221..5884052a 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -14,31 +14,37 @@ "homepage": "https://github.com/thewca/wca-registration/Frontend/README.md", "scripts": { "build:dev": "NODE_ENV=\"development\" ASSET_PATH=\"/dist\" API_URL=\"http://localhost:3001/api/v1\" AUTH_URL=\"http://localhost:3001/jwt\" node src/build.js", - "build:staging": "NODE_ENV=\"production\" ASSET_PATH=\"https://d1qizdh27al0a7.cloudfront.net/staging/dist\" API_URL=\"https://staging.registration.worldcubeassociation.org/api/v1\" AUTH_URL=\"https://staging.registration.worldcubeassociation.org/jwt\" node src/build.js", - "build:prod": "NODE_ENV=\"production\" ASSET_PATH=\"https://d1qizdh27al0a7.cloudfront.net/dist\" API_URL=\"https://registration.worldcubeassociation.org/api/v1\" AUTH_URL=\"https://registration.worldcubeassociation.org/jwt\" node src/build.js", + "build:staging": "NODE_ENV=\"staging\" ASSET_PATH=\"https://d1qizdh27al0a7.cloudfront.net/staging/dist\" API_URL=\"https://staging.registration.worldcubeassociation.org/api/v1\" AUTH_URL=\"https://staging.registration.worldcubeassociation.org/jwt\" node src/build.js", + "build:prod": "NODE_ENV=\"production\" ASSET_PATH=\"https://d1qizdh27al0a7.cloudfront.net/dist\" API_URL=\"https://registration.worldcubeassociation.org/api/v1\" AUTH_URL=\"https://test-registration.worldcubeassociation.org/api/v10/auth/jwt\" node src/build.js", "watch": "node src/watch.mjs", "lint": "eslint src --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint src --ext .js,.jsx,.ts,.tsx --fix" }, "dependencies": { + "@dinero.js/currencies": "^2.0.0-alpha.14", "@tanstack/react-query": "^4.29.19", "@tanstack/react-query-devtools": "^4.29.19", "@thewca/wca-components": "0.5.0", "@wca/helpers": "^1.1.4", + "dinero.js": "^2.0.0-alpha.14", "esbuild": "^0.18.6", "esbuild-sass-plugin": "^2.10.0", "events": "^3.3.0", "fomantic-ui-css": "^2.9.2", + "marked": "^5.1.1", + "moment": "^2.29.4", "postcss": "^8.4.23", "postcss-modules": "^6.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.11.2", - "semantic-ui-react": "^2.1.4" + "semantic-ui-react": "^2.1.4", + "sync-fetch": "^0.5.2" }, "devDependencies": { "@tanstack/eslint-plugin-query": "^4.29.9", "@types/node": "^20.2.5", + "@types/sync-fetch": "^0.4.0", "eslint": "^8.40.0", "eslint-kit": "^9.3.0", "prettier": "^2.8.8" diff --git a/Frontend/src/api/auth/get_permissions.ts b/Frontend/src/api/auth/get_permissions.ts index 9f6fc988..e908cb78 100644 --- a/Frontend/src/api/auth/get_permissions.ts +++ b/Frontend/src/api/auth/get_permissions.ts @@ -1,111 +1,32 @@ -// This function simulates a call to the user permissions route on the monolith. -// This will return your own permissions, not someone else, the argument is -// just here for the mock -import { USER_IS_BANNED, USER_PROFILE_INCOMPLETE } from '../helper/error_codes' +import externalServiceFetch from '../helper/external_service_fetch' +import getPermissionsMock from '../mocks/get_permissions' -function getPermissions(userId: string) { - switch (userId) { - case '1': - return { - can_attend_competitions: { - scope: '*', - }, - can_organize_competitions: { - scope: ['BudapestSummer2023'], - }, - can_administer_competitions: { - scope: ['BudapestSummer2023'], - }, - } - case '2': - return { - can_attend_competitions: { - scope: '*', - }, - can_organize_competitions: { - scope: [], - }, - can_administer_competitions: { - scope: [], - }, - } - case '6427': - return { - can_attend_competitions: { - scope: '*', - }, - can_organize_competitions: { - scope: [], - }, - can_administer_competitions: { - scope: [], - }, - } - case '15073': - return { - can_attend_competitions: { - scope: '*', - }, - can_organize_competitions: { - scope: '*', - }, - can_administer_competitions: { - scope: '*', - }, - } - case '209943': - return { - can_attend_competitions: { - scope: [], - reasons: USER_IS_BANNED, - }, - can_organize_competitions: { - scope: [], - }, - can_administer_competitions: { - scope: [], - }, - } - case '999999': - return { - can_attend_competitions: { - scope: [], - reasons: USER_PROFILE_INCOMPLETE, - }, - can_organize_competitions: { - scope: [], - }, - can_administer_competitions: { - scope: [], - }, - } - default: - return { - can_attend_competitions: { - scope: [], - }, - can_organize_competitions: { - scope: [], - }, - can_administer_competitions: { - scope: [], - }, - } +interface Permissions { + can_attend_competitions: { scope: Scope } + can_organize_competitions: { scope: Scope } + can_administer_competitions: { scope: Scope } +} +type Scope = '*' | string[] + +function getPermissions() { + if (process.env.NODE_ENV === 'production') { + return externalServiceFetch( + 'https://test-registration.worldcubeassociation.org/api/v10/users/me/permissions' + ) as Permissions } + return getPermissionsMock() } -export function canAdminCompetition(userId: string, competitionId: string) { - const permissions = getPermissions(userId) +export function canAdminCompetition(competitionId: string) { + const permissions = getPermissions() return ( permissions.can_administer_competitions.scope === '*' || - (permissions.can_administer_competitions.scope as string[]).includes( - competitionId - ) + permissions.can_administer_competitions.scope.includes(competitionId) ) } -export function canAttendCompetitions(userId: string) { - const permissions = getPermissions(userId) +export function canAttendCompetitions() { + const permissions = getPermissions() return permissions.can_attend_competitions.scope === '*' } diff --git a/Frontend/src/api/competition/get/get_competition_info.ts b/Frontend/src/api/competition/get/get_competition_info.ts index 9a986cfd..fcf99625 100644 --- a/Frontend/src/api/competition/get/get_competition_info.ts +++ b/Frontend/src/api/competition/get/get_competition_info.ts @@ -5,6 +5,6 @@ export default async function getCompetitionInfo( competitionId: string ): Promise { return externalServiceFetch( - `https://www.worldcubeassociation.org/api/v0/competitions/${competitionId}` + `https://test-registration.worldcubeassociation.org/api/v10/competitions/${competitionId}` ) } diff --git a/Frontend/src/api/helper/external_service_fetch.ts b/Frontend/src/api/helper/external_service_fetch.ts index 716b0172..b3805467 100644 --- a/Frontend/src/api/helper/external_service_fetch.ts +++ b/Frontend/src/api/helper/external_service_fetch.ts @@ -1,8 +1,9 @@ +import fetch from 'sync-fetch' import { BackendError } from './backend_fetch' -export default async function externalServiceFetch(route: string) { - const response = await fetch(route) - const body = await response.json() +export default function externalServiceFetch(route: string) { + const response = fetch(route) + const body = response.json() if (response.ok) { return body } diff --git a/Frontend/src/api/mocks/get_permissions.ts b/Frontend/src/api/mocks/get_permissions.ts new file mode 100644 index 00000000..e9442636 --- /dev/null +++ b/Frontend/src/api/mocks/get_permissions.ts @@ -0,0 +1,94 @@ +import { USER_KEY } from '../../ui/App' +import { USER_IS_BANNED, USER_PROFILE_INCOMPLETE } from '../helper/error_codes' + +export default function getPermissionsMock() { + const userId = localStorage.getItem(USER_KEY) + switch (userId) { + case '1': + return { + can_attend_competitions: { + scope: '*', + }, + can_organize_competitions: { + scope: ['BudapestSummer2023'], + }, + can_administer_competitions: { + scope: ['BudapestSummer2023'], + }, + } + case '2': + return { + can_attend_competitions: { + scope: '*', + }, + can_organize_competitions: { + scope: [], + }, + can_administer_competitions: { + scope: [], + }, + } + case '6427': + return { + can_attend_competitions: { + scope: '*', + }, + can_organize_competitions: { + scope: [], + }, + can_administer_competitions: { + scope: [], + }, + } + case '15073': + return { + can_attend_competitions: { + scope: '*', + }, + can_organize_competitions: { + scope: '*', + }, + can_administer_competitions: { + scope: '*', + }, + } + case '209943': + return { + can_attend_competitions: { + scope: [], + reasons: USER_IS_BANNED, + }, + can_organize_competitions: { + scope: [], + }, + can_administer_competitions: { + scope: [], + }, + } + case '999999': + return { + can_attend_competitions: { + scope: [], + reasons: USER_PROFILE_INCOMPLETE, + }, + can_organize_competitions: { + scope: [], + }, + can_administer_competitions: { + scope: [], + }, + } + default: + return { + can_attend_competitions: { + scope: [], + }, + can_organize_competitions: { + scope: [], + }, + can_administer_competitions: { + scope: [], + }, + } + } +} diff --git a/Frontend/src/api/types.d.ts b/Frontend/src/api/types.d.ts index 95bda6c9..b53791e2 100644 --- a/Frontend/src/api/types.d.ts +++ b/Frontend/src/api/types.d.ts @@ -18,31 +18,53 @@ interface UpdateRegistrationBody { guests?: number } -type GetRegistrationBody = Record +interface Tabs { + id: number + competition_id: string + name: string + content: string + display_order: number +} // This needs to be moved to WCA-helpers interface CompetitionInfo { - id: string - name: string - registration_open: string - registration_close: string - announced_at: string - start_date: string - end_date: string - competitor_limit?: number - cancelled_at?: string - url: string - website: string - short_name: string - city: string - venue_address: string - venue_details: string - latitude_degrees: number - longitude_degrees: number - country_iso2: string - event_ids: EventId[] + 'id': string + 'name': string + 'registration_open': string + 'registration_close': string + 'announced_at': string + 'start_date': string + 'end_date': string + 'competitor_limit'?: number + 'cancelled_at'?: string + 'refund_policy_limit_date'?: string + 'event_change_deadline_date'?: string + 'waiting_list_deadline_date'?: string + 'base_entry_fee_lowest_denomination'?: string + 'currency_code'?: string + 'on_the_spot_registration': boolean + 'on_the_spot_entry_fee_lowest_denomination': string + 'extra_registration_requirements': string + 'url': string + 'website': string + 'short_name': string + 'city': string + 'venue_address': string + 'venue_details': string + 'latitude_degrees': number + 'longitude_degrees': number + 'country_iso2': string + 'registration_opened?': boolean + 'event_ids': EventId[] + 'main_event_id': EventId + 'guests_per_registration_limit'?: number + 'guest_entry_status': 'free' | 'restricted' | 'unclear' + 'allow_registration_edits': boolean + 'allow_registration_without_qualification': boolean + 'allow_registration_self_delete_after_acceptance': boolean // TODO: create a User Type - delegates: object[] - organizers: object[] - class: string + 'delegates': object[] + 'organizers': object[] + 'tabs': Tabs[] + 'class': string } diff --git a/Frontend/src/api/user/get/get_user_info.ts b/Frontend/src/api/user/get/get_user_info.ts index 94e1b621..d02fca36 100644 --- a/Frontend/src/api/user/get/get_user_info.ts +++ b/Frontend/src/api/user/get/get_user_info.ts @@ -19,6 +19,6 @@ export default async function getCompetitorInfo( userId: string ): Promise { return externalServiceFetch( - `https://www.worldcubeassociation.org/api/v0/users/${userId}` + `https://api.worldcubeassociation.org/users/${userId}` ) } diff --git a/Frontend/src/build.js b/Frontend/src/build.js index ec201973..73b03682 100644 --- a/Frontend/src/build.js +++ b/Frontend/src/build.js @@ -5,7 +5,11 @@ const statsPlugin = require('./statsplugin') esbuild .build({ - entryPoints: ['src/index.jsx'], + entryPoints: [ + process.env.NODE_ENV === 'production' + ? 'src/index.jsx' + : 'src/index.dev.jsx', + ], bundle: true, outfile: 'dist/bundle.js', metafile: true, diff --git a/Frontend/src/index.dev.jsx b/Frontend/src/index.dev.jsx new file mode 100644 index 00000000..1ab86b3d --- /dev/null +++ b/Frontend/src/index.dev.jsx @@ -0,0 +1,102 @@ +// External Styles (this is probably not the best way to load this?) +import 'fomantic-ui-css/semantic.css' +import './global.scss' +import '@thewca/wca-components/dist/index.esm.css' +import React from 'react' +import { createRoot } from 'react-dom/client' +import { createBrowserRouter, Outlet, RouterProvider } from 'react-router-dom' +import { Container } from 'semantic-ui-react' +import HomePage from './pages/home' +import Register from './pages/register' +import RegistrationAdministration from './pages/registration_administration' +import RegistrationEdit from './pages/registration_edit' +import Registrations from './pages/registrations' +import TestLogin from './pages/test/login' +import TestLogout from './pages/test/logout' +import App from './ui/App' +import Competition from './ui/Competition' +import CustomTab from './ui/CustomTab' +import PageFooter from './ui/Footer' +import PageHeader from './ui/Header' +import FlashMessage from './ui/messages/flashMessage' +import PageTabs from './ui/Tabs' + +const router = createBrowserRouter([ + { + path: '/', + element: ( + + + +
+ +
+ +
+ ), + children: [ + { + // Test Route to simulate different users + path: '/login/:login_id', + element: , + }, + { + // Test Route to simulate different users + path: '/logout', + element: , + }, + { + path: '', + element: ( +

+ Choose a Test Competition from the Menu +

+ ), + }, + { + path: '/competitions/:competition_id', + element: ( + + + + + + + ), + children: [ + { + path: '/competitions/:competition_id', + element: , + }, + { + path: '/competitions/:competition_id/register', + element: , + }, + { + path: '/competitions/:competition_id/tabs/:tab_id', + element: , + }, + { + path: '/competitions/:competition_id/registrations', + element: , + }, + { + path: '/competitions/:competition_id/:user_id/edit', + element: , + }, + { + path: '/competitions/:competition_id/registrations/edit', + element: , + }, + ], + }, + ], + }, +]) + +// Clear the existing HTML content +document.body.innerHTML = '
' + +// Render your React component instead +const root = createRoot(document.querySelector('#app')) +root.render() diff --git a/Frontend/src/index.jsx b/Frontend/src/index.jsx index 27c13e35..fde83a5a 100644 --- a/Frontend/src/index.jsx +++ b/Frontend/src/index.jsx @@ -1,6 +1,4 @@ // External Styles (this is probably not the best way to load this?) -import 'fomantic-ui-css/semantic.css' -import './global.scss' import '@thewca/wca-components/dist/index.esm.css' import React from 'react' import { createRoot } from 'react-dom/client' @@ -11,49 +9,24 @@ import Register from './pages/register' import RegistrationAdministration from './pages/registration_administration' import RegistrationEdit from './pages/registration_edit' import Registrations from './pages/registrations' -import TestLogin from './pages/test/login' -import TestLogout from './pages/test/logout' import App from './ui/App' import Competition from './ui/Competition' -import PageFooter from './ui/Footer' -import PageHeader from './ui/Header' +import CustomTab from './ui/CustomTab' import FlashMessage from './ui/messages/flashMessage' import PageTabs from './ui/Tabs' const router = createBrowserRouter([ { - path: '/', + path: '/competitions', element: ( - -
- -
- +
), children: [ { - // Test Route to simulate different users - path: '/login/:login_id', - element: , - }, - { - // Test Route to simulate different users - path: '/logout', - element: , - }, - { - path: '', - element: ( -

- Choose a Test Competition from the Menu -

- ), - }, - { - path: '/:competition_id', + path: '/competitions/:competition_id', element: ( @@ -64,23 +37,27 @@ const router = createBrowserRouter([ ), children: [ { - path: '/:competition_id', + path: '/competitions/:competition_id', element: , }, { - path: '/:competition_id/register', + path: '/competitions/:competition_id/register', element: , }, { - path: '/:competition_id/registrations', + path: '/competitions/:competition_id/tabs/:tab_id', + element: , + }, + { + path: '/competitions/:competition_id/registrations', element: , }, { - path: '/:competition_id/:user_id/edit', + path: '/competitions/:competition_id/:user_id/edit', element: , }, { - path: '/:competition_id/registrations/edit', + path: '/competitions/:competition_id/registrations/edit', element: , }, ], @@ -89,9 +66,6 @@ const router = createBrowserRouter([ }, ]) -// Clear the existing HTML content -document.body.innerHTML = '
' - -// Render your React component instead -const root = createRoot(document.querySelector('#app')) +// Render the React component into the body of the monolith +const root = createRoot(document.querySelector('#registration-app')) root.render() diff --git a/Frontend/src/pages/register/components/RegistrationPanel.jsx b/Frontend/src/pages/register/components/RegistrationPanel.jsx index 01983a7f..2d2a44eb 100644 --- a/Frontend/src/pages/register/components/RegistrationPanel.jsx +++ b/Frontend/src/pages/register/components/RegistrationPanel.jsx @@ -105,7 +105,9 @@ export default function RegistrationPanel() {
- Registration Fee of $$$ | Waitlist: 0 People + Registration Fee of{' '} + {competitionInfo.base_entry_fee_lowest_denomination} | Waitlist: 0 + People
@@ -152,7 +154,9 @@ export default function RegistrationPanel() { value={guests} onChange={(e, data) => setGuests(data.value)} selection - options={[...new Array(10)].map((_, index) => { + options={[ + ...new Array(competitionInfo.guests_per_registration_limit ?? 99), + ].map((_, index) => { return { key: `registration-guest-dropdown-${index}`, text: index, @@ -167,10 +171,13 @@ export default function RegistrationPanel() {
Your Registration Status: {registration.registration_status} + {competitionInfo.allow_registration_edits + ? 'Update Your Registration below' + : 'Registration Editing is disabled'}
{!loggedIn ? (

You have to log in to Register for a Competition

- ) : ( - <> + ) : // eslint-disable-next-line unicorn/no-nested-ternary + competitionInfo['registration_opened?'] ? ( +
Hi, {user}
{canAttendCompetitions(user) ? ( ) : ( )} - +
+ ) : ( +
+ + {moment(competitionInfo.registration_open).diff(moment.now()) < 0 + ? `Competition Registration closed on ${moment( + competitionInfo.registration_close + ).format('ll')}` + : `Competition Registration will open in ${moment( + competitionInfo.registration_open + ).fromNow()} on ${moment( + competitionInfo.registration_open + ).format('lll')}`} + +
)}
) diff --git a/Frontend/src/pages/register/index.module.scss b/Frontend/src/pages/register/index.module.scss index c246a0f6..806557a3 100644 --- a/Frontend/src/pages/register/index.module.scss +++ b/Frontend/src/pages/register/index.module.scss @@ -29,3 +29,7 @@ line-height: 3em; } } +.competition-not-open{ + font-size: $sub-header-size; + padding: 30px; +} \ No newline at end of file diff --git a/Frontend/src/pages/registration_administration/index.jsx b/Frontend/src/pages/registration_administration/index.jsx index af376374..7e1ba91f 100644 --- a/Frontend/src/pages/registration_administration/index.jsx +++ b/Frontend/src/pages/registration_administration/index.jsx @@ -3,7 +3,6 @@ import { CAN_ADMINISTER_COMPETITIONS, canAdminCompetition, } from '../../api/auth/get_permissions' -import { AuthContext } from '../../api/helper/context/auth_context' import { CompetitionContext } from '../../api/helper/context/competition_context' import PermissionMessage from '../../ui/messages/permissionMessage' import RegistrationAdministrationList from './components/RegistrationAdministrationList' @@ -11,10 +10,9 @@ import styles from './index.module.scss' export default function RegistrationAdministration() { const { competitionInfo } = useContext(CompetitionContext) - const { user } = useContext(AuthContext) return (
- {canAdminCompetition(user, competitionInfo.id) ? ( + {canAdminCompetition(competitionInfo.id) ? ( ) : ( diff --git a/Frontend/src/pages/registration_edit/index.jsx b/Frontend/src/pages/registration_edit/index.jsx index f4bffd0b..8fecda6c 100644 --- a/Frontend/src/pages/registration_edit/index.jsx +++ b/Frontend/src/pages/registration_edit/index.jsx @@ -3,7 +3,6 @@ import { CAN_ADMINISTER_COMPETITIONS, canAdminCompetition, } from '../../api/auth/get_permissions' -import { AuthContext } from '../../api/helper/context/auth_context' import { CompetitionContext } from '../../api/helper/context/competition_context' import PermissionMessage from '../../ui/messages/permissionMessage' import RegistrationEditor from './components/RegistrationEditor' @@ -11,10 +10,9 @@ import styles from './index.module.scss' export default function RegistrationEdit() { const { competitionInfo } = useContext(CompetitionContext) - const { user } = useContext(AuthContext) return (
- {canAdminCompetition(user, competitionInfo.id) ? ( + {canAdminCompetition(competitionInfo.id) ? ( ) : ( diff --git a/Frontend/src/ui/Competition.jsx b/Frontend/src/ui/Competition.jsx index 930292aa..032de796 100644 --- a/Frontend/src/ui/Competition.jsx +++ b/Frontend/src/ui/Competition.jsx @@ -1,5 +1,8 @@ +import * as currencies from '@dinero.js/currencies' import { useQuery } from '@tanstack/react-query' import { CubingIcon, UiIcon } from '@thewca/wca-components' +import { dinero, toDecimal } from 'dinero.js' +import moment from 'moment' import React from 'react' import { useNavigate, useParams } from 'react-router-dom' import { Button, Image } from 'semantic-ui-react' @@ -28,14 +31,26 @@ export default function Competition({ children }) {
{competitionInfo.name} |{' '} - Open + {competitionInfo['registration_opened?'] ? ( + Open + ) : ( + Close + )}
{competitionInfo.venue_address}
- {new Date(competitionInfo.start_date).toDateString()},{' '} - + {moment(competitionInfo.start_date).format('LL')},{' '} + Add to Google Calendar
@@ -45,11 +60,25 @@ export default function Competition({ children }) {
- Registration Fee of $$$ + + Registration Fee:{' '} + {toDecimal( + dinero({ + amount: + competitionInfo.base_entry_fee_lowest_denomination, + currency: currencies[competitionInfo.currency_code], + }), + ({ value, currency }) => `${currency.code} ${value}` + ) ?? 'No Entry Fee'} +
@@ -68,7 +97,7 @@ export default function Competition({ children }) { Main Event: diff --git a/Frontend/src/ui/CustomTab.jsx b/Frontend/src/ui/CustomTab.jsx new file mode 100644 index 00000000..4f86646b --- /dev/null +++ b/Frontend/src/ui/CustomTab.jsx @@ -0,0 +1,17 @@ +import { marked } from 'marked' +import React, { useContext } from 'react' +import { useParams } from 'react-router-dom' +import { CompetitionContext } from '../api/helper/context/competition_context' +import styles from './customtab.module.scss' + +export default function CustomTab() { + const { tab_id } = useParams() + const { competitionInfo } = useContext(CompetitionContext) + const tab = competitionInfo.tabs.find((tab) => tab.id.toString() === tab_id) + return ( + + ) +} diff --git a/Frontend/src/ui/Header.jsx b/Frontend/src/ui/Header.jsx index a0ea47b1..2d76106d 100644 --- a/Frontend/src/ui/Header.jsx +++ b/Frontend/src/ui/Header.jsx @@ -9,15 +9,27 @@ const DROPDOWNS = [ title: 'Registration System', items: [ { - path: '/BudapestSummer2023', + path: '/competitions/BanjaLukaCubeDay2023', icon: 'frog', - title: 'Test Competition 1', + title: 'Open Competition 1', reactRoute: true, }, { - path: '/HessenOpen2023', + path: '/competitions/DarmstadtDodecahedronDays2023', icon: 'fish', - title: 'Test Competition 2', + title: 'Open Competition 2', + reactRoute: true, + }, + { + path: '/competitions/HessenOpen2023', + icon: 'close', + title: 'Closed Competition', + reactRoute: true, + }, + { + path: '/competitions/BrizZonSylwesterOpen2023', + icon: 'time', + title: 'Not yet open Competition', reactRoute: true, }, ], diff --git a/Frontend/src/ui/Tabs.jsx b/Frontend/src/ui/Tabs.jsx index 26c2897a..85e7b483 100644 --- a/Frontend/src/ui/Tabs.jsx +++ b/Frontend/src/ui/Tabs.jsx @@ -3,24 +3,22 @@ import React, { useContext, useMemo } from 'react' import { useNavigate } from 'react-router-dom' import { Menu, Tab } from 'semantic-ui-react' import { canAdminCompetition } from '../api/auth/get_permissions' -import { AuthContext } from '../api/helper/context/auth_context' import { CompetitionContext } from '../api/helper/context/competition_context' import styles from './tabs.module.scss' export default function PageTabs() { const { competitionInfo } = useContext(CompetitionContext) - const { user } = useContext(AuthContext) const navigate = useNavigate() const panes = useMemo(() => { - const adminPanes = [] - if (canAdminCompetition(user, competitionInfo.id)) { - adminPanes.push({ + const optionalTabs = [] + if (canAdminCompetition(competitionInfo.id)) { + optionalTabs.push({ menuItem: ( - navigate(`/${competitionInfo.id}/registrations/edit`) + navigate(`/competitions/${competitionInfo.id}/registrations/edit`) } > @@ -30,30 +28,33 @@ export default function PageTabs() { render: () => {}, }) } - return [ - { + if (new Date(competitionInfo.registration_open) < Date.now()) { + optionalTabs.push({ menuItem: ( navigate(`/${competitionInfo.id}`)} + onClick={() => + navigate(`/competitions/${competitionInfo.id}/registrations`) + } > - - General Info + + Competitors ), render: () => {}, - }, - ...adminPanes, + }) + } + return [ { menuItem: ( navigate(`/${competitionInfo.id}/registrations`)} + onClick={() => navigate(`/competitions/${competitionInfo.id}`)} > - - Competitors + + General Info ), render: () => {}, @@ -63,7 +64,9 @@ export default function PageTabs() { navigate(`/${competitionInfo.id}/register`)} + onClick={() => + navigate(`/competitions/${competitionInfo.id}/register`) + } > Register @@ -71,8 +74,30 @@ export default function PageTabs() { ), render: () => {}, }, + ...optionalTabs, + ...competitionInfo.tabs.map((tab) => { + return { + menuItem: ( + + navigate(`/competitions/${competitionInfo.id}/tabs/${tab.id}`) + } + > + {tab.name} + + ), + render: () => {}, + } + }), ] - }, [competitionInfo.id, navigate, user]) + }, [ + competitionInfo.id, + competitionInfo.registration_open, + competitionInfo.tabs, + navigate, + ]) return ( 1.4) nokogiri (1.15.2-x86_64-linux) racc (~> 1.4) parallel (1.23.0) @@ -244,6 +246,7 @@ GEM parser (>= 3.2.1.0) ruby-prof (1.6.3) ruby-progressbar (1.13.0) + sqlite3 (1.6.3-aarch64-linux) sqlite3 (1.6.3-x86_64-linux) thor (1.2.2) timeout (0.4.0) @@ -266,6 +269,7 @@ GEM zeitwerk (2.6.8) PLATFORMS + aarch64-linux x86_64-linux DEPENDENCIES diff --git a/config/routes.rb b/config/routes.rb index 3068875c..859c0170 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -7,12 +7,10 @@ end get '/healthcheck', to: 'healthcheck#index' - # auth route for testing - # Uncomment this ones we integrate with the monolith - # unless Rails.env.production? - # get '/jwt', to: 'jwt_dev#index' - # end - get '/jwt', to: 'jwt_dev#index' + # auth route for testing, we can remove this for staging when we have fully integrated with the monolith + unless ENV.fetch("CODE_ENVIRONMENT", "development") == "production" + get '/jwt', to: 'jwt_dev#index' + end get '/api/v1/register', to: 'registration#entry' post '/api/v1/register', to: 'registration#create' patch '/api/v1/register', to: 'registration#update'