diff --git a/Frontend/package.json b/Frontend/package.json index e68be4cb..dfbbf6de 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -15,8 +15,8 @@ "scripts": { "build:types": "TYPES_ONLY=true node src/build.mjs", "build:dev": "NODE_ENV=\"development\" ASSET_PATH=\"/dist\" API_URL=\"http://localhost:3001/api/v1\" AUTH_URL=\"http://localhost:3001/jwt\" node src/build.mjs", - "build:staging": "POLL_URL=\"https://1rq8d7dif3.execute-api.us-west-2.amazonaws.com/v1/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.mjs", - "build:prod": "POLL_URL=\"https://1rq8d7dif3.execute-api.us-west-2.amazonaws.com/v1/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.mjs", + "build:staging": "POLL_URL=\"https://1rq8d7dif3.execute-api.us-west-2.amazonaws.com/v1/staging\" NODE_ENV=\"staging\" ASSET_PATH=\"https://d1qizdh27al0a7.cloudfront.net/staging/dist\" API_URL=\"https://staging.registration.worldcubeassociation.org/api/v1\" WCA_URL=\"https://staging.worldcubeassociation.org\" node src/build.mjs", + "build:prod": "POLL_URL=\"https://1rq8d7dif3.execute-api.us-west-2.amazonaws.com/v1/prod\" NODE_ENV=\"production\" ASSET_PATH=\"https://d1qizdh27al0a7.cloudfront.net/dist\" API_URL=\"https://registration.worldcubeassociation.org/api/v1\" WCA_URL=\"https://worldcubeassociation.org\" node src/build.mjs", "watch": "node src/watch.mjs", "lint": "eslint src --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint src --ext .js,.jsx,.ts,.tsx --fix" diff --git a/Frontend/src/api/auth/get_jwt.ts b/Frontend/src/api/auth/get_jwt.ts index 2105848d..aac35d24 100644 --- a/Frontend/src/api/auth/get_jwt.ts +++ b/Frontend/src/api/auth/get_jwt.ts @@ -1,5 +1,6 @@ import { JWT_KEY } from '../../ui/providers/UserProvider' import { BackendError } from '../helper/backend_fetch' +import { tokenRoute } from '../helper/routes' import getJWTMock from '../mocks/get_jwt' export async function getJWT(reauthenticate = false): Promise { @@ -9,9 +10,7 @@ export async function getJWT(reauthenticate = false): Promise { // the jwt token is cached in local storage, if it has expired, we need to reauthenticate const cachedToken = localStorage.getItem(JWT_KEY) if (reauthenticate || cachedToken === null) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore AUTH_URL is injected at build time - const response = await fetch(process.env.AUTH_URL) + const response = await fetch(tokenRoute) const body = await response.json() if (response.ok) { const token = response.headers.get('authorization') diff --git a/Frontend/src/api/auth/get_permissions.ts b/Frontend/src/api/auth/get_permissions.ts index 9656e15e..6ad032cd 100644 --- a/Frontend/src/api/auth/get_permissions.ts +++ b/Frontend/src/api/auth/get_permissions.ts @@ -1,8 +1,9 @@ import externalServiceFetch from '../helper/external_service_fetch' +import { permissionsRoute } from '../helper/routes' import getPermissionsMock from '../mocks/get_permissions' export interface Permissions { - can_attend_competitions: { scope: Scope; reasons?: number } + can_attend_competitions: { scope: Scope; until?: string } can_organize_competitions: { scope: Scope } can_administer_competitions: { scope: Scope } } @@ -10,9 +11,7 @@ type Scope = '*' | string[] export async function getPermissions(): Promise { if (process.env.NODE_ENV === 'production') { - return externalServiceFetch( - 'https://test-registration.worldcubeassociation.org/api/v10/users/me/permissions' - ) + return externalServiceFetch(permissionsRoute) } return getPermissionsMock() } diff --git a/Frontend/src/api/competition/get/get_competition_info.ts b/Frontend/src/api/competition/get/get_competition_info.ts index fcf99625..2cbd73ea 100644 --- a/Frontend/src/api/competition/get/get_competition_info.ts +++ b/Frontend/src/api/competition/get/get_competition_info.ts @@ -1,10 +1,9 @@ import externalServiceFetch from '../../helper/external_service_fetch' +import { competitionInfoRoute } from '../../helper/routes' import { CompetitionInfo } from '../../types' export default async function getCompetitionInfo( competitionId: string ): Promise { - return externalServiceFetch( - `https://test-registration.worldcubeassociation.org/api/v10/competitions/${competitionId}` - ) + return externalServiceFetch(competitionInfoRoute(competitionId)) } diff --git a/Frontend/src/api/competition/get/get_competition_wcif.ts b/Frontend/src/api/competition/get/get_competition_wcif.ts new file mode 100644 index 00000000..96e31d31 --- /dev/null +++ b/Frontend/src/api/competition/get/get_competition_wcif.ts @@ -0,0 +1,9 @@ +import { Competition } from '@wca/helpers' +import externalServiceFetch from '../../helper/external_service_fetch' +import { competitionWCIFRoute } from '../../helper/routes' + +export default async function getCompetitionWcif( + competitionId: string +): Promise { + return externalServiceFetch(competitionWCIFRoute(competitionId)) +} diff --git a/Frontend/src/api/helper/error_codes.ts b/Frontend/src/api/helper/error_codes.ts index 6e054774..a901e7d5 100644 --- a/Frontend/src/api/helper/error_codes.ts +++ b/Frontend/src/api/helper/error_codes.ts @@ -1,20 +1,21 @@ // see app/helpers/error_codes.rb -// TODO generate these errors out of some kind of shared error file export const INVALID_TOKEN = -1 export const EXPIRED_TOKEN = -2 export const MISSING_AUTHENTICATION = -3 -export const SERVICE_DOWN = -4 - export const COMPETITION_NOT_FOUND = -1000 export const COMPETITION_API_5XX = -1001 -export const COMPETITION_CLOSED = -1002 -export const COMPETITION_INVALID_EVENTS = -1003 - -export const USER_IMPERSONATION = -2000 export const USER_IS_BANNED = -2001 export const USER_PROFILE_INCOMPLETE = -2002 export const USER_INSUFFICIENT_PERMISSIONS = -2003 -export const USER_NOT_LOGGED_IN = -2004 - +export const REGISTRATION_NOT_FOUND = -3000 export const PAYMENT_NOT_ENABLED = -3001 export const PAYMENT_NOT_READY = -3002 +export const INVALID_REQUEST_DATA = -4000 +export const EVENT_EDIT_DEADLINE_PASSED = -4001 +export const GUEST_LIMIT_EXCEEDED = -4002 +export const USER_COMMENT_TOO_LONG = -4003 +export const INVALID_EVENT_SELECTION = -4004 +export const REQUIRED_COMMENT_MISSING = -4005 +export const COMPETITOR_LIMIT_REACHED = -4006 +export const INVALID_REGISTRATION_STATUS = -4007 +export const REGISTRATION_CLOSED = -4008 diff --git a/Frontend/src/api/helper/external_service_fetch.ts b/Frontend/src/api/helper/external_service_fetch.ts index 716b0172..1522099b 100644 --- a/Frontend/src/api/helper/external_service_fetch.ts +++ b/Frontend/src/api/helper/external_service_fetch.ts @@ -1,7 +1,10 @@ import { BackendError } from './backend_fetch' -export default async function externalServiceFetch(route: string) { - const response = await fetch(route) +export default async function externalServiceFetch( + route: string, + options = {} +) { + const response = await fetch(route, options) const body = await response.json() if (response.ok) { return body diff --git a/Frontend/src/api/helper/routes.ts b/Frontend/src/api/helper/routes.ts new file mode 100644 index 00000000..8389fe18 --- /dev/null +++ b/Frontend/src/api/helper/routes.ts @@ -0,0 +1,21 @@ +export const tokenRoute = `${process.env.WCA_URL}/api/v0/users/token` +export const permissionsRoute = `${process.env.WCA_URL}/api/v0/users/me/permissions` +export const paymentConfigRoute = `${process.env.WCA_URL}/payment/config` +export const paymentFinishRoute = (competitionId: string, userId: string) => + `${process.env.WCA_URL}/payment/${competitionId}-${userId}/finish` +// TODO: Move this to swagger once finalised +export const paymentIdRoute = (id: string) => + `${process.env.API_URL}/${id}/payment` +export const meRoute = `${process.env.WCA_URL}/api/v0/users/me` +// This will break when urls get really big, maybe we should switch to POST? +export const usersInfoRoute = (ids: string[]) => + `${process.env.WCA_URL}/api/v0/users?${ids + .map((id) => 'ids[]=' + id) + .join('&')}` +// Hardcoded because these are currently not mocked +export const competitionInfoRoute = (id: string) => + `https://api.worldcubeassociation.org/competitions/${id}` +export const competitionWCIFRoute = (id: string) => + `https://api.worldcubeassociation.org/competitions/${id}/wcif/public` +export const userInfoRoute = (id: string) => + `https://api.worldcubeassociation.org/users/${id}` diff --git a/Frontend/src/api/mocks/get_payment_intent.ts b/Frontend/src/api/mocks/get_payment_intent.ts deleted file mode 100644 index 8efaaa0b..00000000 --- a/Frontend/src/api/mocks/get_payment_intent.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { PaymentInfo } from '../registration/get/get_payment_intent' -import getStripeConfigMock from './get_stripe_config' - -export default async function getPaymentIntentMock(): Promise { - const stripeConfig = getStripeConfigMock() - - // Get a Payment Intent from Stripe - const stripe = require('stripe')('sk_test_CY2eQJchZKUrPGQtJ3Z60ycA') - - const paymentIntent = await stripe.paymentIntents.create( - { - amount: 2000, - currency: 'eur', - automatic_payment_methods: { enabled: true }, - }, - { stripeAccount: stripeConfig.connected_account_id } - ) - return { - client_secret_id: paymentIntent.client_secret, - } -} diff --git a/Frontend/src/api/mocks/get_permissions.ts b/Frontend/src/api/mocks/get_permissions.ts index f39b0345..f0f1297c 100644 --- a/Frontend/src/api/mocks/get_permissions.ts +++ b/Frontend/src/api/mocks/get_permissions.ts @@ -1,6 +1,5 @@ import { USER_KEY } from '../../ui/providers/UserProvider' import { Permissions } from '../auth/get_permissions' -import { USER_IS_BANNED, USER_PROFILE_INCOMPLETE } from '../helper/error_codes' export default function getPermissionsMock(): Permissions { const userId = localStorage.getItem(USER_KEY) @@ -57,7 +56,6 @@ export default function getPermissionsMock(): Permissions { return { can_attend_competitions: { scope: [], - reasons: USER_IS_BANNED, }, can_organize_competitions: { scope: [], @@ -70,7 +68,6 @@ export default function getPermissionsMock(): Permissions { return { can_attend_competitions: { scope: [], - reasons: USER_PROFILE_INCOMPLETE, }, can_organize_competitions: { scope: [], diff --git a/Frontend/src/api/mocks/get_stripe_config.ts b/Frontend/src/api/mocks/get_stripe_config.ts deleted file mode 100644 index b92c1cde..00000000 --- a/Frontend/src/api/mocks/get_stripe_config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { StripeConfig } from '../payment/get/get_stripe_config' - -export default function getStripeConfigMock(): StripeConfig { - return { - stripe_publishable_key: 'pk_test_N0KdZIOedIrP8C4bD5XLUxOY', - connected_account_id: 'acct_1NYpaMGZClrCFkEy', - } -} diff --git a/Frontend/src/api/payment/get/get_stripe_config.ts b/Frontend/src/api/payment/get/get_stripe_config.ts index ffdc8e30..65ec1287 100644 --- a/Frontend/src/api/payment/get/get_stripe_config.ts +++ b/Frontend/src/api/payment/get/get_stripe_config.ts @@ -1,20 +1,22 @@ import externalServiceFetch from '../../helper/external_service_fetch' -import getStripeConfigMock from '../../mocks/get_stripe_config' +import { paymentConfigRoute } from '../../helper/routes' export interface StripeConfig { stripe_publishable_key: string connected_account_id: string + client_secret: string } export default async function getStripeConfig( - competitionId: string + competitionId: string, + paymentId: string ): Promise { - if (process.env.NODE_ENV === 'production') { - // This should live in the payment service? - return externalServiceFetch( - `https://test-registration.worldcubeassociation.org/api/v10/payment/${competitionId}/config` - ) - } - - return getStripeConfigMock() + return externalServiceFetch(paymentConfigRoute, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: { + competitionId, + paymentId, + }, + }) } diff --git a/Frontend/src/api/registration/get/get_payment_intent.ts b/Frontend/src/api/registration/get/get_payment_intent.ts index df03e810..4a099f50 100644 --- a/Frontend/src/api/registration/get/get_payment_intent.ts +++ b/Frontend/src/api/registration/get/get_payment_intent.ts @@ -1,19 +1,17 @@ import backendFetch from '../../helper/backend_fetch' -import getPaymentIntentMock from '../../mocks/get_payment_intent' +import { paymentIdRoute } from '../../helper/routes' export interface PaymentInfo { - client_secret_id: string + // This is the MySQL payment id that can be give to the payment service + // to get the relevant data, not the Stripe ID! + payment_id: string } // We get the user_id out of the JWT key, which is why we only send the // competition_id -export default async function getPaymentIntent( +export default async function getPaymentId( competitionId: string ): Promise { - if (process.env.NODE_ENV === 'production') { - // This should live in the payment service? - return backendFetch(`/${competitionId}/payment`, 'GET', { - needsAuthentication: true, - }) as Promise - } - return getPaymentIntentMock() + return backendFetch(paymentIdRoute(competitionId), 'GET', { + needsAuthentication: true, + }) as Promise } diff --git a/Frontend/src/api/registration/get/get_registrations.ts b/Frontend/src/api/registration/get/get_registrations.ts index 14d8caed..eebd79f7 100644 --- a/Frontend/src/api/registration/get/get_registrations.ts +++ b/Frontend/src/api/registration/get/get_registrations.ts @@ -1,56 +1,36 @@ -import { EventId } from '@wca/helpers' import createClient from 'openapi-fetch' +import { getJWT } from '../../auth/get_jwt' import backendFetch, { BackendError } from '../../helper/backend_fetch' -import { paths } from '../../schema' -import getCompetitorInfo, { User } from '../../user/get/get_user_info' +import { EXPIRED_TOKEN } from '../../helper/error_codes' +import { components, paths } from '../../schema' +import getCompetitorInfo from '../../user/get/get_user_info' -const { get } = createClient({ +const { GET } = createClient({ // TODO: Change this once we are fully migrated from backend fetch // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore baseUrl: process.env.API_URL.slice(0, -7), }) -type RegistrationStatus = 'waiting' | 'accepted' | 'deleted' -type PaymentStatus = 'initialized' | 'waiting' | 'completed' - -interface Registration { - user_id: string - user: User - event_ids: EventId[] -} - -export interface RegistrationAdmin { - user_id: string - event_ids: EventId[] - registration_status: RegistrationStatus - payment_status: PaymentStatus - registered_on: string - comment: string - admin_comment: string - guests: number - user: User -} - export async function getConfirmedRegistrations( competitionID: string -): Promise { +): Promise { //TODO: Because there is currently no bulk user fetch route we need to manually add user data here - const { data, error, response } = await get( + const { data, response } = await GET( '/api/v1/registrations/{competition_id}', { params: { path: { competition_id: competitionID } }, } ) const regList = [] - if (error) { - throw new BackendError(error.error, response.status) + if (!response.ok) { + throw new BackendError(500, response.status) } - for (const registration of data) { + for (const registration of data!) { const user = (await getCompetitorInfo(registration.user_id)).user regList.push({ user_id: registration.user_id, - event_ids: registration.event_ids, + competing: registration.competing, user, }) } @@ -59,19 +39,30 @@ export async function getConfirmedRegistrations( export async function getAllRegistrations( competitionID: string -): Promise { +): Promise { //TODO: Because there is currently no bulk user fetch route we need to manually add user data here - const registrations = (await backendFetch( - `/registrations/${competitionID}/admin`, - 'GET', + const { data, error, response } = await GET( + '/api/v1/registrations/{competition_id}/admin', { - needsAuthentication: true, + params: { path: { competition_id: competitionID } }, + headers: { Authorization: await getJWT() }, } - )) as RegistrationAdmin[] + ) const regList = [] - for (const registration of registrations) { - registration.user = (await getCompetitorInfo(registration.user_id)).user - regList.push(registration) + if (error) { + if (error.error === EXPIRED_TOKEN) { + await getJWT(true) + return getAllRegistrations(competitionID) + } + throw new BackendError(error.error, response.status) + } + for (const registration of data!) { + const user = (await getCompetitorInfo(registration.user_id)).user + regList.push({ + user_id: registration.user_id, + competing: registration.competing, + user, + }) } return regList } @@ -79,10 +70,10 @@ export async function getAllRegistrations( export async function getSingleRegistration( userId: string, competitionId: string -): Promise<{ registration: RegistrationAdmin }> { +): Promise<{ registration: components['schemas']['registrationAdmin'] }> { return backendFetch( `/register?user_id=${userId}&competition_id=${competitionId}`, 'GET', { needsAuthentication: true } - ) as Promise<{ registration: RegistrationAdmin }> + ) as Promise<{ registration: components['schemas']['registrationAdmin'] }> } diff --git a/Frontend/src/api/registration/patch/update_registration.ts b/Frontend/src/api/registration/patch/update_registration.ts index 6e5ecb08..256cec46 100644 --- a/Frontend/src/api/registration/patch/update_registration.ts +++ b/Frontend/src/api/registration/patch/update_registration.ts @@ -1,10 +1,32 @@ -import backendFetch from '../../helper/backend_fetch' -import { UpdateRegistrationBody } from '../../types' -import { RegistrationAdmin } from '../get/get_registrations' +import createClient from 'openapi-fetch' +import { getJWT } from '../../auth/get_jwt' +import { BackendError } from '../../helper/backend_fetch' +import { EXPIRED_TOKEN } from '../../helper/error_codes' +import { components, paths } from '../../schema' -export async function updateRegistration(body: UpdateRegistrationBody) { - return backendFetch('/register', 'PATCH', { +const { PATCH } = createClient({ + // TODO: Change this once we are fully migrated from backend fetch + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + baseUrl: process.env.API_URL.slice(0, -7), +}) + +export async function updateRegistration( + body: components['schemas']['updateRegistrationBody'] +): Promise<{ + status?: string + registration?: components['schemas']['registrationAdmin'] +}> { + const { data, error, response } = await PATCH('/api/v1/register', { + headers: { Authorization: await getJWT() }, body, - needsAuthentication: true, - }) as Promise<{ registration: RegistrationAdmin }> + }) + if (error) { + if (error.error === EXPIRED_TOKEN) { + await getJWT(true) + return updateRegistration(body) + } + throw new BackendError(error.error, response.status) + } + return data } diff --git a/Frontend/src/api/registration/post/submit_registration.ts b/Frontend/src/api/registration/post/submit_registration.ts index e366a60c..86a4ff33 100644 --- a/Frontend/src/api/registration/post/submit_registration.ts +++ b/Frontend/src/api/registration/post/submit_registration.ts @@ -4,7 +4,7 @@ import { BackendError } from '../../helper/backend_fetch' import { EXPIRED_TOKEN } from '../../helper/error_codes' import { components, paths } from '../../schema' -const { post } = createClient({ +const { POST } = createClient({ // TODO: Change this once we are fully migrated from backend fetch // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -13,11 +13,8 @@ const { post } = createClient({ export default async function submitEventRegistration( body: components['schemas']['submitRegistrationBody'] ): Promise { - const token = await getJWT() - const { data, error, response } = await post('/api/v1/register', { - // TODO: I think this is a bug in open-api fetch https://github.com/drwpow/openapi-typescript/issues/1230 - params: { header: { Authorization: token } }, - headers: { Authorization: token }, + const { data, error, response } = await POST('/api/v1/register', { + headers: { Authorization: await getJWT() }, body, }) if (error) { diff --git a/Frontend/src/api/schema.d.ts b/Frontend/src/api/schema.d.ts index c77d0aef..87f11458 100644 --- a/Frontend/src/api/schema.d.ts +++ b/Frontend/src/api/schema.d.ts @@ -14,30 +14,12 @@ export interface paths { }; }; responses: { - /** @description PASSING comp service down but registrations exist */ + /** @description -> PASSING comp service down but registrations exist */ 200: { content: { "application/json": components["schemas"]["registration"][]; }; }; - /** @description PASSING Competition ID doesnt exist */ - 404: { - content: { - "application/json": components["schemas"]["error_response"]; - }; - }; - /** @description PASSING Competition service unavailable - 500 error */ - 500: { - content: { - "application/json": components["schemas"]["error_response"]; - }; - }; - /** @description PASSING Competition service unavailable - 502 error */ - 502: { - content: { - "application/json": components["schemas"]["error_response"]; - }; - }; }; }; }; @@ -50,15 +32,17 @@ export interface paths { }; }; responses: { - /** @description PASSING organizer has access to comp 2 */ + /** @description -> PASSING organizer has access to comp 2 */ 200: { content: { "application/json": components["schemas"]["registrationAdmin"][]; }; }; - /** @description PASSING organizer cannot access registrations for comps they arent organizing - multi comp auth */ + /** @description -> PASSING organizer cannot access registrations for comps they arent organizing - multi comp auth */ 401: { - content: never; + content: { + "application/json": components["schemas"]["error_response"]; + }; }; }; }; @@ -68,25 +52,82 @@ export interface paths { post: { requestBody: { content: { - "application/json": components["schemas"]["registration"]; + "application/json": components["schemas"]["submitRegistrationBody"]; }; }; responses: { - /** @description PASSING only required fields included */ + /** @description -> PASSING competitor submits basic registration */ 202: { - content: never; + content: { + "application/json": components["schemas"]["success_response"]; + }; }; - /** @description PASSING empty payload provided */ + /** @description -> PASSING empty payload provided */ 400: { - content: never; + content: { + "application/json": components["schemas"]["error_response"]; + }; }; - /** @description PASSING user impersonation (no admin permission, JWWT token user_id does not match registration user_id) */ + /** @description -> PASSING user impersonation (no admin permission, JWT token user_id does not match registration user_id) */ 401: { - content: never; + content: { + "application/json": components["schemas"]["error_response"]; + }; }; - /** @description PASSING comp not open */ + /** @description -> PASSING user cant register while registration is closed */ 403: { - content: never; + content: { + "application/json": components["schemas"]["error_response"]; + }; + }; + /** @description -> PASSING competition does not exist */ + 404: { + content: { + "application/json": components["schemas"]["error_response"]; + }; + }; + /** @description PASSING user registration exceeds guest limit */ + 422: { + content: { + "application/json": components["schemas"]["error_response"]; + }; + }; + }; + }; + /** update or cancel an attendee registration */ + patch: { + requestBody: { + content: { + "application/json": components["schemas"]["updateRegistrationBody"]; + }; + }; + responses: { + /** @description PASSING user changes comment */ + 200: { + content: { + "application/json": { + status?: string; + registration?: components["schemas"]["registrationAdmin"]; + }; + }; + }; + /** @description PASSING user requests invalid status change to their own reg */ + 401: { + content: { + "application/json": components["schemas"]["error_response"]; + }; + }; + /** @description PASSING user changes events / other stuff past deadline */ + 403: { + content: { + "application/json": components["schemas"]["error_response"]; + }; + }; + /** @description PASSING user does not include required comment */ + 422: { + content: { + "application/json": components["schemas"]["error_response"]; + }; }; }; }; @@ -106,15 +147,20 @@ export interface components { }; registration: { user_id: string; - event_ids: EventId[]; + competing: { + event_ids: EventId[]; + }; }; registrationAdmin: { user_id: string; - event_ids: EventId[]; - comment?: string | null; - admin_comment?: string | null; + competing: { + event_ids: EventId[]; + registered_on: string; + registration_status: string; + comment?: string | null; + admin_comment?: string | null; + }; guests?: number | null; - email?: string; }; submitRegistrationBody: { user_id: string; @@ -127,9 +173,13 @@ export interface components { }; updateRegistrationBody: { user_id: string; - event_ids: EventId[]; - comment?: string; - admin_comment?: string; + competition_id: string; + competing?: { + event_ids?: EventId[]; + status?: string; + comment?: string; + admin_comment?: string; + }; guests?: number; }; }; diff --git a/Frontend/src/api/types.d.ts b/Frontend/src/api/types.d.ts index a66c1901..e41c36df 100644 --- a/Frontend/src/api/types.d.ts +++ b/Frontend/src/api/types.d.ts @@ -1,15 +1,4 @@ -import { Event, EventId, Venue } from '@wca/helpers' - -// These will be generated by the swagger file generated with Rswag -interface UpdateRegistrationBody { - user_id: string - competition_id: string - status?: string - event_ids?: EventId[] - comment?: string - admin_comment?: string - guests?: number -} +import { EventId } from '@wca/helpers' interface Tabs { id: number @@ -60,18 +49,12 @@ interface CompetitionInfo { 'using_stripe_payments?': boolean 'external_registration_page'?: string 'event_ids': EventId[] - 'events_with_rounds': Event[] '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 - 'schedule_wcif': { - startDate: string - numberOfDays: number - venues: Venue[] - } 'delegates': UserFull[] 'organizers': UserFull[] 'contact': string diff --git a/Frontend/src/api/user/get/get_me.ts b/Frontend/src/api/user/get/get_me.ts index 3a400534..ece2e88c 100644 --- a/Frontend/src/api/user/get/get_me.ts +++ b/Frontend/src/api/user/get/get_me.ts @@ -1,12 +1,11 @@ import { UserFull } from '../../helper/context/user_context' import externalServiceFetch from '../../helper/external_service_fetch' +import { meRoute } from '../../helper/routes' import getMeMock from '../../mocks/get_me' export default async function getMe(): Promise { if (process.env.NODE_ENV === 'production') { - const userRequest = await externalServiceFetch( - `https://test-registration.worldcubeassociation.org/api/v10/users/me` - ) + const userRequest = await externalServiceFetch(meRoute) return userRequest.user } return getMeMock() diff --git a/Frontend/src/api/user/get/get_user_info.ts b/Frontend/src/api/user/get/get_user_info.ts index d02fca36..05ed5685 100644 --- a/Frontend/src/api/user/get/get_user_info.ts +++ b/Frontend/src/api/user/get/get_user_info.ts @@ -1,4 +1,5 @@ import externalServiceFetch from '../../helper/external_service_fetch' +import { userInfoRoute } from '../../helper/routes' export interface User { id: string @@ -18,7 +19,5 @@ export interface UserInfo { export default async function getCompetitorInfo( userId: string ): Promise { - return externalServiceFetch( - `https://api.worldcubeassociation.org/users/${userId}` - ) + return externalServiceFetch(userInfoRoute(userId)) } diff --git a/Frontend/src/build.mjs b/Frontend/src/build.mjs index 1548738b..bad2c9cd 100644 --- a/Frontend/src/build.mjs +++ b/Frontend/src/build.mjs @@ -47,7 +47,7 @@ if (!process.env.TYPES_ONLY) { ], define: { 'process.env.API_URL': `"${process.env.API_URL}"`, - 'process.env.AUTH_URL': `"${process.env.AUTH_URL}"`, + 'process.env.WCA_URL': `"${process.env.WCA_URL}"`, 'process.env.NODE_ENV': `"${process.env.NODE_ENV}"`, 'process.env.POLL_URL': `"${process.env.POLL_URL}"`, }, diff --git a/Frontend/src/pages/events/index.jsx b/Frontend/src/pages/events/index.jsx index dd128e35..1dd335a1 100644 --- a/Frontend/src/pages/events/index.jsx +++ b/Frontend/src/pages/events/index.jsx @@ -1,6 +1,8 @@ +import { useQuery } from '@tanstack/react-query' import { getEventName, getFormatName } from '@wca/helpers' import React, { useContext } from 'react' import { + Message, Table, TableBody, TableCell, @@ -8,12 +10,34 @@ import { TableHeaderCell, TableRow, } from 'semantic-ui-react' +import getCompetitionWcif from '../../api/competition/get/get_competition_wcif' import { CompetitionContext } from '../../api/helper/context/competition_context' +import { setMessage } from '../../ui/events/messages' +import LoadingMessage from '../../ui/messages/loadingMessage' import styles from './index.module.scss' export default function Events() { const { competitionInfo } = useContext(CompetitionContext) - return ( + const { + isLoading, + isError, + data: wcif, + } = useQuery({ + queryKey: ['wcif', competitionInfo.id], + queryFn: () => getCompetitionWcif(competitionInfo.id), + retry: false, + onError: (err) => { + setMessage(err.message, 'error') + }, + }) + + if (isError) { + return Loading Events failed, please try again + } + + return isLoading ? ( + + ) : (
@@ -33,7 +57,7 @@ export default function Events() { - {competitionInfo.events_with_rounds.map((event) => { + {wcif.events.map((event) => { return event.rounds.map((round, i) => { return ( diff --git a/Frontend/src/pages/register/components/CompetingStep.jsx b/Frontend/src/pages/register/components/CompetingStep.jsx index 9a8f1e75..e526b381 100644 --- a/Frontend/src/pages/register/components/CompetingStep.jsx +++ b/Frontend/src/pages/register/components/CompetingStep.jsx @@ -40,10 +40,10 @@ export default function CompetingStep({ nextStep }) { }, }) useEffect(() => { - if (registrationRequest?.registration.registration_status) { + if (registrationRequest?.registration?.competing) { setRegistration(registrationRequest.registration) - setComment(registrationRequest.registration.comment ?? '') - setSelectedEvents(registrationRequest.registration.event_ids) + setComment(registrationRequest.registration.competing.comment ?? '') + setSelectedEvents(registrationRequest.registration.competing.event_ids) setGuests(registrationRequest.registration.guests) } }, [registrationRequest]) @@ -223,9 +223,11 @@ export default function CompetingStep({ nextStep }) { updateRegistrationMutation({ user_id: registration.user_id, competition_id: competitionInfo.id, - comment, - guests, - event_ids: selectedEvents, + competing: { + comment, + guests, + event_ids: selectedEvents, + }, }) }} > @@ -239,7 +241,9 @@ export default function CompetingStep({ nextStep }) { updateRegistrationMutation({ user_id: registration.user_id, competition_id: competitionInfo.id, - status: 'deleted', + competing: { + status: 'deleted', + }, }) }} > @@ -262,7 +266,7 @@ export default function CompetingStep({ nextStep }) {