Skip to content
This repository has been archived by the owner on Jan 3, 2025. It is now read-only.

Prevent accepting too many registrations at once (FE) #381

Merged
merged 6 commits into from
Dec 11, 2023
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
154 changes: 154 additions & 0 deletions Frontend/src/api/mocks/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,160 @@ export const OPEN_COMPETITION: CompetitionInfo = {
],
'class': 'competition',
}

export const LOW_COMPETITOR_LIMIT: CompetitionInfo = {
'id': 'LowLimit2023',
'name': 'Low Limit 2023',
'information': 'This Competition has a low Limit',
'venue': 'Pfarrheim St. Engelbert',
'contact':
'[Kölner Kubing 2023 Orgateam](mailto:koelnerkubing@googlegroups.com)',
'registration_open': dateFromNow(-1, -1),
'registration_close': dateFromNow(2),
'use_wca_registration': true,
'announced_at': '2023-08-22T17:10:32.000Z',
'base_entry_fee_lowest_denomination': 1600,
'currency_code': 'EUR',
'start_date': dateFromNow(2),
'end_date': dateFromNow(2, 1),
'enable_donations': true,
'competitor_limit': 3,
'extra_registration_requirements':
"### Deutsch:\r\n\r\n**1. Erstelle einen WCA-Account**\r\n(Dieser Schritt gilt NUR für Newcomer und Teilnehmer ohne WCA-Account): Erstelle [hier](https://www.worldcubeassociation.org/users/sign_up) einen WCA-Account.\r\n**2. Zahle die Anmeldegebühr**\r\nZahle die Anmeldegebühr in Höhe von 16 Euro via PayPal [*über diesen Link*](https://www.paypal.com/paypalme/FrederikHutfless/16eur) (https://www.paypal.com/paypalme/FrederikHutfless/16eur). \r\n**3. Fülle das Anmeldeformular aus**\r\nFülle das Anmeldeformular aus und schicke es ab: [klicke hier und scrolle ganz nach unten ans Ende der Seite](https://www.worldcubeassociation.org/competitions/KoelnerKubing2023/register).\r\n\r\n**Wichtig:** \r\n* Bitte aktiviere **nicht** den optionalen Käuferschutz, da hierfür eine Gebühr vom gezahlten Eintrittspreis abgezogen wird. \r\n* Falls der Name bei der Zahlung und der Name im Anmeldeformular nicht identisch sind, gib bitte den Namen des Teilnehmers im Anmeldeformular im letzten Schritt der Zahlungsprozedur an oder kontaktiere uns. *Eine Zahlung gilt erst dann als geleistet, wenn wir diese eindeutig zuordnen können.* \r\n* **Ohne (eindeutig zugeordnete) Zahlung gilt die Anmeldung als nicht vollständig und wird nicht bestätigt.**\r\n\r\nWenn das Teilnehmerlimit bereits erreicht wurde, erhältst du nach Anmeldung und Zahlung einen Platz auf der Warteliste. Danach erhältst du eine E-Mail, sobald ein Platz für dich frei werden sollte. Falls du keinen freien Teilnehmerplatz mehr erlangen solltest, wird die Anmeldegebühr selbstverständlich erstattet.\r\n\r\nSolltest du nicht mehr teilnehmen können und deine Anmeldung daher stornieren wollen, bitten wir dich darum, uns zu informieren. Dadurch kann ein Teilnehmer auf der Warteliste nachrücken! Du erhältst eine Erstattung der gesamten Anmeldegebühr (100%), wenn du deine Anmeldung vor dem 03. November 2023, 23:59 MESZ stornierst.\r\n\r\nWenn absehbar ist, dass die Warteliste bereits so lang ist, dass weitere Neuanmeldungen nicht mehr angenommen werden, behalten wir uns vor, die Anmeldung früher als angekündigt zu schließen.\r\n\r\nWir haben [häufig gestellte Fragen und Antworten hier](https://www.worldcubeassociation.org/competitions/KoelnerKubing2023#31967-faq) zusammengestellt.\r\n\r\n#### Für Gäste:\r\nJeder Gast zahlt 3€ (Euro) Eintitt vor Ort\r\n### English:\r\n\r\n**1. Create an WCA account**\r\n(This step is ONLY for newcomers and competitors without a WCA account:) Create a WCA-account [here](https://www.worldcubeassociation.org/users/sign_up).\r\n**2. Pay the registration fee**\r\nPay the registration fee of 16 Euro by following [*this link*](https://www.paypal.com/paypalme/FrederikHutfless/16eur) (https://www.paypal.com/paypalme/FrederikHutfless/16eur) and proceed with the payment via Paypal.\r\n**3. Fill in the registration form**\r\nFill and submit the registration form here: [click here and scroll all the way down to the bottom of the page](https://www.worldcubeassociation.org/competitions/KoelnerKubing2023/register).\r\n\r\n**Important:**\r\n* Please do **not** activate the optional buyer protection as this will be deducted as a fee from the amount you pay. \r\n* If the name of the payment and the name in the registration form are not identical, please enter the name of the competitor from on the registration form in the final step of the payment procedure or contact us. *A payment is only considered to be made once we can clearly match it.*\r\n* **The registration is not considered complete and will not be confirmed until a (clearly matched) payment is made.**\r\n\r\nIf you have registered and paid but the competitor limit has been reached, you will receive a spot on the waiting list. You will be notified via email once a spot for you becomes available. If you do not move up from the waiting list until registration closes, you will get a full refund.\r\n\r\nIf you find that you can not attend the competition anymore, please inform us via e-mail. That way, another competitor can fill your spot! You will receive a full refund (100%) of the entrance fee if you cancel your registration before November 03, 2023, 11:59 PM GMT+2.\r\n\r\nOnce the waiting list is long enough for us to anticipate that new registrations will likely not move up to the competitor's list, we may close the registration earlier than announced. \r\n\r\nWe compiled a set of [frequently asked questions and answers here](https://www.worldcubeassociation.org/competitions/KoelnerKubing2023#31967-faq).\r\n\r\n#### For guests:\r\nEach guest pays 3€ (Euro) entrance fee",
'on_the_spot_registration': false,
'on_the_spot_entry_fee_lowest_denomination': undefined,
'refund_policy_percent': 100,
'refund_policy_limit_date': dateFromNow(2),
'guests_entry_fee_lowest_denomination': 300,
'qualification_results': false,
'external_registration_page': '',
'event_restrictions': false,
'cancelled_at': undefined,
'waiting_list_deadline_date': dateFromNow(2, -1),
'event_change_deadline_date': dateFromNow(2, -1),
'guest_entry_status': 'free',
'allow_registration_edits': true,
'allow_registration_self_delete_after_acceptance': false,
'allow_registration_without_qualification': false,
'guests_per_registration_limit': undefined,
'force_comment_in_registration': false,
'url': 'https://www.worldcubeassociation.org/competitions/KoelnerKubing2023',
'website':
'https://www.worldcubeassociation.org/competitions/KoelnerKubing2023',
'short_name': 'Kölner Kubing 2023',
'city': 'Köln',
'venue_address': 'Pfarrer-Moll-Str. 54, 51105 Cologne, Germany',
'venue_details': 'Obergeschoss // upstairs',
'latitude_degrees': 50.929833,
'longitude_degrees': 6.995431,
'country_iso2': 'DE',
'event_ids': [
'333',
'222',
'444',
'555',
'666',
'777',
'333fm',
'333oh',
'clock',
'pyram',
'skewb',
],
'registration_opened?': true,
'main_event_id': '333',
'number_of_bookmarks': 5,
'using_stripe_payments?': undefined,
'uses_qualification?': false,
'uses_cutoff?': true,
'delegates': [
{
id: 2,
created_at: '2012-07-25T05:42:29.000Z',
updated_at: '2023-10-25T17:31:40.000Z',
name: 'Sébastien Auroux',
delegate_status: 'delegate',
wca_id: '2008AURO01',
gender: 'm',
country_iso2: 'DE',
url: 'https://www.worldcubeassociation.org/persons/2008AURO01',
country: {
id: 'Germany',
name: 'Germany',
continentId: '_Europe',
iso2: 'DE',
},
email: 'sauroux@worldcubeassociation.org',
location: 'Germany',
senior_delegate_id: 454,
class: 'user',
teams: [
{
id: 190,
friendly_id: 'wrt',
leader: true,
name: 'Sébastien Auroux',
senior_member: false,
wca_id: '2008AURO01',
avatar: {
url: 'https://avatars.worldcubeassociation.org/uploads/user/avatar/2008AURO01/1630621356.jpg',
thumb: {
url: 'https://avatars.worldcubeassociation.org/uploads/user/avatar/2008AURO01/1630621356_thumb.jpg',
},
},
},
],
avatar: {
url: 'https://avatars.worldcubeassociation.org/uploads/user/avatar/2008AURO01/1630621356.jpg',
pending_url:
'https://www.worldcubeassociation.org/assets/missing_avatar_thumb-d77f478a307a91a9d4a083ad197012a391d5410f6dd26cb0b0e3118a5de71438.png',
thumb_url:
'https://avatars.worldcubeassociation.org/uploads/user/avatar/2008AURO01/1630621356_thumb.jpg',
is_default: false,
},
},
],
'organizers': [
{
id: 80119,
created_at: '2017-11-04T21:20:56.000Z',
updated_at: '2023-10-30T09:30:27.000Z',
name: 'Dunhui Xiao (肖敦慧)',
delegate_status: undefined,
wca_id: '2018XIAO03',
gender: 'm',
country_iso2: 'DE',
url: 'https://www.worldcubeassociation.org/persons/2018XIAO03',
country: {
id: 'Germany',
name: 'Germany',
continentId: '_Europe',
iso2: 'DE',
},
class: 'user',
teams: [],
avatar: {
url: 'https://avatars.worldcubeassociation.org/uploads/user/avatar/2018XIAO03/1681755209.jpg',
pending_url:
'https://www.worldcubeassociation.org/assets/missing_avatar_thumb-d77f478a307a91a9d4a083ad197012a391d5410f6dd26cb0b0e3118a5de71438.png',
thumb_url:
'https://avatars.worldcubeassociation.org/uploads/user/avatar/2018XIAO03/1681755209_thumb.jpg',
is_default: false,
},
},
],
'tabs': [
{
id: 31963,
competition_id: 'KoelnerKubing2023',
name: 'Neulinge \u0026 Ergebnisse / Newcomers \u0026 Results',
content:
"# Deutsch\r\n## Informationen für Neulinge\r\nFalls dies dein erstes WCA Turnier sein sollte, beachte bitte die folgenden Punkte:\r\n\r\n- Alle Neulinge sind dazu verpflichtet ein **Ausweisdokument** an der Anmeldung vor zu zeigen (Regulation [2e](https://www.worldcubeassociation.org/regulations/#2e))). Aus dem Dokument müssen Name, Nationalität und Geburtsdatum hervorgehen.\r\n- Alle Neulinge sind eindringlich gebeten, am **Tutorial** (siehe Zeitplan) teilzunehmen.\r\n- Jeder Teilnehmer sollte bereits vor der Meisterschaft mindestens einmal die **[offiziellen Regeln der WCA](https://www.worldcubeassociation.org/regulations/translations/german/)** gelesen haben. Zusätzlich empfehlen wir euch einen Blick in das [WCA Competition Tutorial](https://www.worldcubeassociation.org/files/WCA_Competition_Tutorial.pdf) bzw. unsere deutschsprachige [Teilnehmer-Anleitung](https://www.germancubeassociation.de/anleitungen/). Dort findet ihr wichtige Infos zum Ablaub und zur Teilnahme am Wettbewerb. Weiterhin bietet zum Beispiel auch [dieses Video-Tutorial](https://www.youtube.com/watch?v=dPL3eV-A0ww) einen guten unterstützenden Einblick.\r\n- Keine Angst: während des Turniers besteht die Möglichkeit, sich mit dem offiziellen Equipment (Stackmat-Timer) vertraut zu machen und Fragen zu stellen.\r\n\r\n\r\nBei Unklarheiten oder wichtigen Fragen vor dem Turnier kannst du dich gerne [an das Organisationsteam wenden](mailto:KoelnerKubing@googlegroups.com).\r\n\r\nLive Ergebnisse sind über [WCA Live](https://live.worldcubeassociation.org/) verfügbar. Nach dem Wettkampf werden alle Ergebnisse in die Datenbank der WCA hochgeladen, und auf dieser Seite einzusehen sein.\r\n\r\n___\r\n\r\n# English\r\n## Newcomer information\r\nIf this is your first WCA competition, please pay attention to the following:\r\n\r\n- According to Regulation [2e)](https://www.worldcubeassociation.org/regulations/#2e), all newcomers are required to bring some form of **identification document** that shows name, citizenship and date of birth.\r\n- All newcomers are urgently asked to attend the **Tutorial** (see schedule).\r\n- Every competitor should have read the **[official WCA regulations](https://www.worldcubeassociation.org/regulations)** at least once before attending the competition! A more condensed version of the important regulations can be found at the [WCA Competition Tutorial](https://www.worldcubeassociation.org/files/WCA_Competition_Tutorial.pdf). We also recommend [this video guide](https://www.youtube.com/watch?v=dPL3eV-A0ww) for a more visual impression.\r\n- Don't be afraid: there will also be time to test the equipment (for example the official timing device, the Stackmat timer) and discuss the rules if you have questions at the competition.\r\n\r\nFeel free to [contact the organizer](mailto:KoelnerKubing@googlegroups.com) if you have any uncertainties.\r\n\r\nLive results are available via [WCA Live](https://live.worldcubeassociation.org/). All results will be uploaded to the WCA database after the competition, and will be available right here.",
display_order: 1,
},
],
'class': 'competition',
}
export const COMMENT_REQUIRED: CompetitionInfo = {
'id': 'FMCFrance2023',
'name': 'FMC France 2023',
Expand Down
4 changes: 4 additions & 0 deletions Frontend/src/api/mocks/get_competition_info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
CLOSED_COMPETITION,
COMMENT_REQUIRED,
FAVOURITES_COMPETITION,
LOW_COMPETITOR_LIMIT,
NOT_YET_OPEN,
OPEN_COMPETITION,
OPEN_WITH_PAYMENTS,
Expand Down Expand Up @@ -31,6 +32,9 @@ export default async function getCompetitionInfoMockWithRealFallback(
case 'PickeringFavouritesAutumn2023': {
return FAVOURITES_COMPETITION
}
case 'LowLimit2023': {
return LOW_COMPETITOR_LIMIT
}
default: {
// This allows non mocked response when debugging a certain competition
return getCompetitionInfo(competitionId)
Expand Down
2 changes: 1 addition & 1 deletion Frontend/src/api/mocks/get_permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function getPermissionsMock(): Permissions {
scope: [],
},
can_administer_competitions: {
scope: ['KoelnerKubing2023'],
scope: ['KoelnerKubing2023', 'LowLimit2023'],
},
}
case '6427':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default function RegistrationActions({
partitionedSelected,
refresh,
registrations,
spotsRemaining,
}) {
const { competitionInfo } = useContext(CompetitionContext)
const { isOrganizerOrDelegate } = useContext(PermissionsContext)
Expand All @@ -57,6 +58,20 @@ export default function RegistrationActions({
},
})

const attemptToApprove = () => {
const idsToAccept = [...pending, ...cancelled, ...waiting]
if (idsToAccept.length > spotsRemaining) {
setMessage(
`Accepting all these registrations would go over the competitor limit by ${
idsToAccept.length - spotsRemaining
}`,
'negative'
)
} else {
changeStatus(idsToAccept, 'accepted')
}
}

const changeStatus = async (attendees, status) => {
attendees.forEach((attendee) => {
updateRegistrationMutation(
Expand Down Expand Up @@ -112,15 +127,7 @@ export default function RegistrationActions({
{isOrganizerOrDelegate && (
<>
{anyApprovable && (
<Button
positive
onClick={() =>
changeStatus(
[...pending, ...cancelled, ...waiting],
'accepted'
)
}
>
<Button positive onClick={attemptToApprove}>
<UiIcon name="check" /> Approve
</Button>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,9 @@ export default function RegistrationAdministrationList() {

// some sticky/floating bar somewhere with totals/info would be better
// than putting this in the table headers which scroll out of sight
const spotsRemaining = `; ${
competitionInfo?.competitor_limit - accepted?.length
} spot(s) remaining`
const spotsRemaining =
(competitionInfo.competitor_limit ?? Infinity) - accepted.length
const spotsRemainingText = `; ${spotsRemaining} spot(s) remaining`

return isLoading ? (
<LoadingMessage />
Expand All @@ -143,7 +143,7 @@ export default function RegistrationAdministrationList() {
{competitionInfo.competitor_limit && (
<>
{`/${competitionInfo.competitor_limit}`}
{spotsRemaining}
{spotsRemainingText}
</>
)}
)
Expand All @@ -158,7 +158,7 @@ export default function RegistrationAdministrationList() {

<Header>
Waitlisted registrations ({waiting.length}
{competitionInfo.competitor_limit && spotsRemaining})
{competitionInfo.competitor_limit && spotsRemainingText})
</Header>
<RegistrationAdministrationTable
registrations={waiting}
Expand All @@ -185,6 +185,7 @@ export default function RegistrationAdministrationList() {
dispatch({ type: 'clear-selected' })
}}
registrations={registrations}
spotsRemaining={spotsRemaining}
/>
</>
)
Expand Down
6 changes: 6 additions & 0 deletions Frontend/src/ui/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ const DROPDOWNS = [
title: 'Comments Enforced',
reactRoute: true,
},
{
path: `${BASE_ROUTE}/LowLimit2023`,
icon: 'battery empty',
title: 'Low Competitor Limit',
reactRoute: true,
},
{
path: `${BASE_ROUTE}/PickeringFavouritesAutumn2023`,
icon: 'heart',
Expand Down
12 changes: 11 additions & 1 deletion app/controllers/registration_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,13 @@ def update

begin
registration = Registration.find("#{@competition_id}-#{@user_id}")
old_status = registration.competing_status
updated_registration = registration.update_competing_lane!({ status: status, comment: comment, event_ids: event_ids, admin_comment: admin_comment, guests: guests })
if old_status == 'accepted' && status != 'accepted'
Registration.decrement_competitors_count(@competition_id)
elsif old_status != 'accepted' && status == 'accepted'
Registration.increment_competitors_count(@competition_id)
end
render json: { status: 'ok', registration: {
user_id: updated_registration['user_id'],
guests: updated_registration.guests,
Expand Down Expand Up @@ -194,12 +200,16 @@ def list_admin

def import
file = params.require(:csv_data)
competition_id = params.require(:competition_id)
content = File.read(file)
if CsvImport.valid?(content)
registrations = CSV.parse(File.read(file), headers: true).map do |row|
CsvImport.parse_row_to_registration(row.to_h, params[:competition_id])
CsvImport.parse_row_to_registration(row.to_h, competition_id)
end
Registration.import(registrations)

Rails.cache.invalidate("#{competition_id}-accepted-count")

render json: { status: 'Successfully imported registration' }
else
render json: { error: 'Invalid csv' }, status: :internal_server_error
Expand Down
Loading