fixing verification for both a team registration and a indivual turnament registration
This commit is contained in:
parent
06ba7fe85a
commit
1a4d1cf73c
4 changed files with 415 additions and 164 deletions
|
|
@ -177,8 +177,8 @@ impl TournamentSignupConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.entry_fields = normalize_signup_fields(self.entry_fields);
|
self.entry_fields = normalize_signup_fields(self.entry_fields);
|
||||||
ensure_attendance_id_field(&mut self.entry_fields);
|
|
||||||
self.participant_fields = normalize_signup_fields(self.participant_fields);
|
self.participant_fields = normalize_signup_fields(self.participant_fields);
|
||||||
|
ensure_attendance_field_for_mode(&mut self);
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -218,26 +218,53 @@ fn normalize_signup_fields(mut fields: Vec<TournamentSignupField>) -> Vec<Tourna
|
||||||
fields
|
fields
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_attendance_id_field(fields: &mut Vec<TournamentSignupField>) {
|
fn remove_attendance_id_field(
|
||||||
|
fields: &mut Vec<TournamentSignupField>,
|
||||||
|
) -> Option<TournamentSignupField> {
|
||||||
let mut attendance_index = None;
|
let mut attendance_index = None;
|
||||||
for (index, field) in fields.iter_mut().enumerate() {
|
for (index, field) in fields.iter().enumerate() {
|
||||||
if field.id == ATTENDANCE_ID_FIELD_ID {
|
if field.id == ATTENDANCE_ID_FIELD_ID {
|
||||||
attendance_index = Some(index);
|
attendance_index = Some(index);
|
||||||
sanitize_attendance_id_field(field);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match attendance_index {
|
attendance_index.map(|index| {
|
||||||
Some(index) => {
|
let mut field = fields.remove(index);
|
||||||
if index != 0 {
|
sanitize_attendance_id_field(&mut field);
|
||||||
let field = fields.remove(index);
|
field
|
||||||
fields.insert(0, field);
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
None => {
|
fn insert_attendance_field_front(
|
||||||
fields.insert(0, default_attendance_id_field());
|
fields: &mut Vec<TournamentSignupField>,
|
||||||
}
|
mut field: TournamentSignupField,
|
||||||
|
) {
|
||||||
|
sanitize_attendance_id_field(&mut field);
|
||||||
|
fields.insert(0, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_attendance_field_for_mode(config: &mut TournamentSignupConfig) {
|
||||||
|
let mut attendance = remove_attendance_id_field(&mut config.entry_fields)
|
||||||
|
.or_else(|| remove_attendance_id_field(&mut config.participant_fields))
|
||||||
|
.unwrap_or_else(default_attendance_id_field);
|
||||||
|
|
||||||
|
if config.mode == "team" {
|
||||||
|
config
|
||||||
|
.entry_fields
|
||||||
|
.retain(|field| field.id != ATTENDANCE_ID_FIELD_ID);
|
||||||
|
config
|
||||||
|
.participant_fields
|
||||||
|
.retain(|field| field.id != ATTENDANCE_ID_FIELD_ID);
|
||||||
|
insert_attendance_field_front(&mut config.participant_fields, attendance);
|
||||||
|
} else {
|
||||||
|
config
|
||||||
|
.entry_fields
|
||||||
|
.retain(|field| field.id != ATTENDANCE_ID_FIELD_ID);
|
||||||
|
config
|
||||||
|
.participant_fields
|
||||||
|
.retain(|field| field.id != ATTENDANCE_ID_FIELD_ID);
|
||||||
|
insert_attendance_field_front(&mut config.entry_fields, attendance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use rocket::serde::json::Json;
|
||||||
use rocket::Route;
|
use rocket::Route;
|
||||||
use serde_json::{Map, Value};
|
use serde_json::{Map, Value};
|
||||||
use sqlx::{Postgres, Transaction};
|
use sqlx::{Postgres, Transaction};
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
pub fn routes() -> Vec<Route> {
|
pub fn routes() -> Vec<Route> {
|
||||||
rocket::routes![
|
rocket::routes![
|
||||||
|
|
@ -1178,47 +1178,122 @@ pub async fn create_registration_by_slug(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let attendance_id_value = entry_values
|
let is_team = config.mode == "team";
|
||||||
.get(ATTENDANCE_ID_FIELD_ID)
|
let mut lead_participant: Option<(i32, Person)> = None;
|
||||||
.map(|value| value.trim().to_string())
|
|
||||||
.filter(|value| !value.is_empty())
|
|
||||||
.ok_or_else(|| ApiError::bad_request("Ange ditt deltagar-ID från närvarolistan."))?;
|
|
||||||
|
|
||||||
let attendance_id: i32 = attendance_id_value.parse().map_err(|_| {
|
if is_team {
|
||||||
ApiError::bad_request("Deltagar-ID måste vara ett heltal från närvarolistan.")
|
entry_values.remove(ATTENDANCE_ID_FIELD_ID);
|
||||||
|
let mut seen_attendance_ids: HashSet<i32> = HashSet::new();
|
||||||
|
|
||||||
|
for (index, values) in participant_values.iter_mut().enumerate() {
|
||||||
|
let attendance_raw = values
|
||||||
|
.get(ATTENDANCE_ID_FIELD_ID)
|
||||||
|
.map(|value| value.trim().to_string())
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
ApiError::bad_request(format!(
|
||||||
|
"Spelare #{number}: ange deltagar-ID från närvarolistan.",
|
||||||
|
number = index + 1
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let attendance_id: i32 = attendance_raw.parse().map_err(|_| {
|
||||||
|
ApiError::bad_request(format!(
|
||||||
|
"Spelare #{number}: deltagar-ID måste vara ett heltal från närvarolistan.",
|
||||||
|
number = index + 1
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if !seen_attendance_ids.insert(attendance_id) {
|
||||||
|
return Err(ApiError::bad_request(format!(
|
||||||
|
"Spelare #{number}: deltagar-ID används redan av en annan spelare i laget.",
|
||||||
|
number = index + 1
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let person = sqlx::query_as::<_, Person>(
|
||||||
|
r#"
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
first_name,
|
||||||
|
last_name,
|
||||||
|
grade,
|
||||||
|
parent_name,
|
||||||
|
parent_phone_number,
|
||||||
|
checked_in,
|
||||||
|
inside,
|
||||||
|
visitor,
|
||||||
|
sleeping_spot
|
||||||
|
FROM persons
|
||||||
|
WHERE id = $1
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(attendance_id)
|
||||||
|
.fetch_optional(&mut *tx)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| {
|
||||||
|
ApiError::bad_request(format!(
|
||||||
|
"Spelare #{number}: deltagar-ID:t finns inte i närvarolistan.",
|
||||||
|
number = index + 1
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
values.insert(
|
||||||
|
ATTENDANCE_ID_FIELD_ID.to_string(),
|
||||||
|
attendance_id.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if lead_participant.is_none() {
|
||||||
|
lead_participant = Some((attendance_id, person.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let attendance_id_value = entry_values
|
||||||
|
.get(ATTENDANCE_ID_FIELD_ID)
|
||||||
|
.map(|value| value.trim().to_string())
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
.ok_or_else(|| ApiError::bad_request("Ange ditt deltagar-ID från närvarolistan."))?;
|
||||||
|
|
||||||
|
let attendance_id: i32 = attendance_id_value.parse().map_err(|_| {
|
||||||
|
ApiError::bad_request("Deltagar-ID måste vara ett heltal från närvarolistan.")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let person = sqlx::query_as::<_, Person>(
|
||||||
|
r#"
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
first_name,
|
||||||
|
last_name,
|
||||||
|
grade,
|
||||||
|
parent_name,
|
||||||
|
parent_phone_number,
|
||||||
|
checked_in,
|
||||||
|
inside,
|
||||||
|
visitor,
|
||||||
|
sleeping_spot
|
||||||
|
FROM persons
|
||||||
|
WHERE id = $1
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(attendance_id)
|
||||||
|
.fetch_optional(&mut *tx)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| {
|
||||||
|
ApiError::bad_request("Det angivna deltagar-ID:t finns inte i närvarolistan.")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
entry_values.insert(
|
||||||
|
ATTENDANCE_ID_FIELD_ID.to_string(),
|
||||||
|
attendance_id.to_string(),
|
||||||
|
);
|
||||||
|
lead_participant = Some((attendance_id, person));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (lead_attendance_id, lead_person) = lead_participant.ok_or_else(|| {
|
||||||
|
ApiError::bad_request("Minst en deltagare med deltagar-ID krävs för anmälan.")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let person = sqlx::query_as::<_, Person>(
|
let entry_label = build_entry_label(lead_attendance_id, &lead_person, &config, &entry_values);
|
||||||
r#"
|
|
||||||
SELECT
|
|
||||||
id,
|
|
||||||
first_name,
|
|
||||||
last_name,
|
|
||||||
grade,
|
|
||||||
parent_name,
|
|
||||||
parent_phone_number,
|
|
||||||
checked_in,
|
|
||||||
inside,
|
|
||||||
visitor,
|
|
||||||
sleeping_spot
|
|
||||||
FROM persons
|
|
||||||
WHERE id = $1
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.bind(attendance_id)
|
|
||||||
.fetch_optional(&mut *tx)
|
|
||||||
.await?
|
|
||||||
.ok_or_else(|| {
|
|
||||||
ApiError::bad_request("Det angivna deltagar-ID:t finns inte i närvarolistan.")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let canonical_attendance = attendance_id.to_string();
|
|
||||||
entry_values.insert(
|
|
||||||
ATTENDANCE_ID_FIELD_ID.to_string(),
|
|
||||||
canonical_attendance.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let entry_label = build_entry_label(attendance_id, &person, &config, &entry_values);
|
|
||||||
|
|
||||||
for field in &config.entry_fields {
|
for field in &config.entry_fields {
|
||||||
if !field.unique {
|
if !field.unique {
|
||||||
|
|
@ -1656,46 +1731,122 @@ pub async fn update_registration_by_slug(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let attendance_id_value = entry_values
|
let is_team = config.mode == "team";
|
||||||
.get(ATTENDANCE_ID_FIELD_ID)
|
let mut lead_participant: Option<(i32, Person)> = None;
|
||||||
.map(|value| value.trim().to_string())
|
|
||||||
.filter(|value| !value.is_empty())
|
|
||||||
.ok_or_else(|| ApiError::bad_request("Ange ditt deltagar-ID från närvarolistan."))?;
|
|
||||||
|
|
||||||
let attendance_id: i32 = attendance_id_value.parse().map_err(|_| {
|
if is_team {
|
||||||
ApiError::bad_request("Deltagar-ID måste vara ett heltal från närvarolistan.")
|
entry_values.remove(ATTENDANCE_ID_FIELD_ID);
|
||||||
|
let mut seen_attendance_ids: HashSet<i32> = HashSet::new();
|
||||||
|
|
||||||
|
for (index, values) in participant_values.iter_mut().enumerate() {
|
||||||
|
let attendance_raw = values
|
||||||
|
.get(ATTENDANCE_ID_FIELD_ID)
|
||||||
|
.map(|value| value.trim().to_string())
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
.ok_or_else(|| {
|
||||||
|
ApiError::bad_request(format!(
|
||||||
|
"Spelare #{number}: ange deltagar-ID från närvarolistan.",
|
||||||
|
number = index + 1
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let attendance_id: i32 = attendance_raw.parse().map_err(|_| {
|
||||||
|
ApiError::bad_request(format!(
|
||||||
|
"Spelare #{number}: deltagar-ID måste vara ett heltal från närvarolistan.",
|
||||||
|
number = index + 1
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if !seen_attendance_ids.insert(attendance_id) {
|
||||||
|
return Err(ApiError::bad_request(format!(
|
||||||
|
"Spelare #{number}: deltagar-ID används redan av en annan spelare i laget.",
|
||||||
|
number = index + 1
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let person = sqlx::query_as::<_, Person>(
|
||||||
|
r#"
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
first_name,
|
||||||
|
last_name,
|
||||||
|
grade,
|
||||||
|
parent_name,
|
||||||
|
parent_phone_number,
|
||||||
|
checked_in,
|
||||||
|
inside,
|
||||||
|
visitor,
|
||||||
|
sleeping_spot
|
||||||
|
FROM persons
|
||||||
|
WHERE id = $1
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(attendance_id)
|
||||||
|
.fetch_optional(&mut *tx)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| {
|
||||||
|
ApiError::bad_request(format!(
|
||||||
|
"Spelare #{number}: deltagar-ID:t finns inte i närvarolistan.",
|
||||||
|
number = index + 1
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
values.insert(
|
||||||
|
ATTENDANCE_ID_FIELD_ID.to_string(),
|
||||||
|
attendance_id.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if lead_participant.is_none() {
|
||||||
|
lead_participant = Some((attendance_id, person.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let attendance_id_value = entry_values
|
||||||
|
.get(ATTENDANCE_ID_FIELD_ID)
|
||||||
|
.map(|value| value.trim().to_string())
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
.ok_or_else(|| ApiError::bad_request("Ange ditt deltagar-ID från närvarolistan."))?;
|
||||||
|
|
||||||
|
let attendance_id: i32 = attendance_id_value.parse().map_err(|_| {
|
||||||
|
ApiError::bad_request("Deltagar-ID måste vara ett heltal från närvarolistan.")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let person = sqlx::query_as::<_, Person>(
|
||||||
|
r#"
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
first_name,
|
||||||
|
last_name,
|
||||||
|
grade,
|
||||||
|
parent_name,
|
||||||
|
parent_phone_number,
|
||||||
|
checked_in,
|
||||||
|
inside,
|
||||||
|
visitor,
|
||||||
|
sleeping_spot
|
||||||
|
FROM persons
|
||||||
|
WHERE id = $1
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(attendance_id)
|
||||||
|
.fetch_optional(&mut *tx)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| {
|
||||||
|
ApiError::bad_request("Det angivna deltagar-ID:t finns inte i närvarolistan.")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
entry_values.insert(
|
||||||
|
ATTENDANCE_ID_FIELD_ID.to_string(),
|
||||||
|
attendance_id.to_string(),
|
||||||
|
);
|
||||||
|
lead_participant = Some((attendance_id, person));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (lead_attendance_id, lead_person) = lead_participant.ok_or_else(|| {
|
||||||
|
ApiError::bad_request("Minst en deltagare med deltagar-ID krävs för anmälan.")
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let person = sqlx::query_as::<_, Person>(
|
let entry_label = build_entry_label(lead_attendance_id, &lead_person, &config, &entry_values);
|
||||||
r#"
|
|
||||||
SELECT
|
|
||||||
id,
|
|
||||||
first_name,
|
|
||||||
last_name,
|
|
||||||
grade,
|
|
||||||
parent_name,
|
|
||||||
parent_phone_number,
|
|
||||||
checked_in,
|
|
||||||
inside,
|
|
||||||
visitor,
|
|
||||||
sleeping_spot
|
|
||||||
FROM persons
|
|
||||||
WHERE id = $1
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.bind(attendance_id)
|
|
||||||
.fetch_optional(&mut *tx)
|
|
||||||
.await?
|
|
||||||
.ok_or_else(|| {
|
|
||||||
ApiError::bad_request("Det angivna deltagar-ID:t finns inte i närvarolistan.")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
entry_values.insert(
|
|
||||||
ATTENDANCE_ID_FIELD_ID.to_string(),
|
|
||||||
attendance_id.to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let entry_label = build_entry_label(attendance_id, &person, &config, &entry_values);
|
|
||||||
|
|
||||||
for field in &config.entry_fields {
|
for field in &config.entry_fields {
|
||||||
if !field.unique {
|
if !field.unique {
|
||||||
|
|
|
||||||
|
|
@ -130,69 +130,107 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureAttendanceField(fields: SignupFieldForm[]): SignupFieldForm[] {
|
function sanitizeAttendanceField(field?: SignupFieldForm | null): SignupFieldForm {
|
||||||
let attendanceField: SignupFieldForm | null = null;
|
const label = field?.label?.trim() || ATTENDANCE_FIELD_LABEL;
|
||||||
const others: SignupFieldForm[] = [];
|
const placeholder = field?.placeholder?.trim() || ATTENDANCE_FIELD_PLACEHOLDER;
|
||||||
|
return {
|
||||||
|
id: ATTENDANCE_FIELD_ID,
|
||||||
|
label,
|
||||||
|
field_type: 'text',
|
||||||
|
required: true,
|
||||||
|
placeholder,
|
||||||
|
unique: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
for (const field of fields) {
|
function removeAttendanceField(fields: SignupFieldForm[]): {
|
||||||
if (isAttendanceField(field)) {
|
fields: SignupFieldForm[];
|
||||||
const label = field.label.trim() || ATTENDANCE_FIELD_LABEL;
|
attendance: SignupFieldForm | null;
|
||||||
const placeholder = field.placeholder.trim() || ATTENDANCE_FIELD_PLACEHOLDER;
|
} {
|
||||||
attendanceField = {
|
const index = fields.findIndex(isAttendanceField);
|
||||||
id: ATTENDANCE_FIELD_ID,
|
if (index === -1) {
|
||||||
label,
|
return { fields: [...fields], attendance: null };
|
||||||
field_type: 'text',
|
}
|
||||||
required: true,
|
|
||||||
placeholder,
|
const next = [...fields];
|
||||||
unique: true
|
const [field] = next.splice(index, 1);
|
||||||
};
|
return { fields: next, attendance: sanitizeAttendanceField(field) };
|
||||||
} else {
|
}
|
||||||
others.push(field);
|
|
||||||
|
function ensureAttendancePlacement(signup: SignupConfigForm) {
|
||||||
|
const entryResult = removeAttendanceField(signup.entry_fields);
|
||||||
|
const participantResult = removeAttendanceField(signup.participant_fields);
|
||||||
|
let attendance =
|
||||||
|
entryResult.attendance ?? participantResult.attendance ?? createAttendanceField();
|
||||||
|
|
||||||
|
if (signup.mode === 'team') {
|
||||||
|
const participantFields = [...participantResult.fields];
|
||||||
|
if (!participantFields.some((field) => !isAttendanceField(field))) {
|
||||||
|
participantFields.push(createSignupField('Spelarinfo'));
|
||||||
|
}
|
||||||
|
signup.entry_fields = [...entryResult.fields];
|
||||||
|
signup.participant_fields = [sanitizeAttendanceField(attendance), ...participantFields];
|
||||||
|
} else {
|
||||||
|
const entryFields = [...entryResult.fields];
|
||||||
|
if (!entryFields.some((field) => !isAttendanceField(field))) {
|
||||||
|
entryFields.push(createSignupField('Spelarnamn'));
|
||||||
|
}
|
||||||
|
signup.entry_fields = [sanitizeAttendanceField(attendance), ...entryFields];
|
||||||
|
signup.participant_fields = participantResult.fields.filter(
|
||||||
|
(field) => !isAttendanceField(field)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstEntry = signup.entry_fields.find((field) => !isAttendanceField(field));
|
||||||
|
if (firstEntry) {
|
||||||
|
const trimmed = firstEntry.placeholder.trim();
|
||||||
|
firstEntry.placeholder = trimmed || (signup.mode === 'team' ? 'Lag namn' : 'Spelarnamn');
|
||||||
|
if (signup.mode === 'team' && firstEntry.label.trim().length === 0) {
|
||||||
|
firstEntry.label = 'Lag';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!attendanceField) {
|
|
||||||
attendanceField = createAttendanceField();
|
|
||||||
}
|
|
||||||
|
|
||||||
return [attendanceField, ...others];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDefaultSignup(): SignupConfigForm {
|
function createDefaultSignup(): SignupConfigForm {
|
||||||
return {
|
const signup: SignupConfigForm = {
|
||||||
mode: 'solo',
|
mode: 'solo',
|
||||||
team_size: { min: 1, max: 1 },
|
team_size: { min: 1, max: 1 },
|
||||||
entry_fields: ensureAttendanceField([createSignupField('Lag / spelarnamn')]),
|
entry_fields: [createSignupField('Spelarnamn')],
|
||||||
participant_fields: []
|
participant_fields: []
|
||||||
};
|
};
|
||||||
|
ensureAttendancePlacement(signup);
|
||||||
|
return signup;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cloneSignupConfig(config: TournamentSignupConfig | null | undefined): SignupConfigForm {
|
function cloneSignupConfig(config: TournamentSignupConfig | null | undefined): SignupConfigForm {
|
||||||
if (!config) return createDefaultSignup();
|
if (!config) return createDefaultSignup();
|
||||||
let entry_fields = ensureAttendanceField((config.entry_fields ?? []).map(cloneSignupField));
|
|
||||||
let participant_fields = (config.participant_fields ?? []).map(cloneSignupField);
|
const signup: SignupConfigForm = {
|
||||||
if (entry_fields.length === 1) {
|
mode: config.mode === 'team' ? 'team' : 'solo',
|
||||||
entry_fields = ensureAttendanceField([
|
|
||||||
...entry_fields,
|
|
||||||
createSignupField('Lag / spelarnamn')
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (participant_fields.length === 0) {
|
|
||||||
participant_fields.push(createSignupField('Spelare'));
|
|
||||||
}
|
|
||||||
const mode = config.mode === 'team' ? 'team' : 'solo';
|
|
||||||
if (mode === 'solo') {
|
|
||||||
participant_fields = [];
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
mode,
|
|
||||||
team_size: {
|
team_size: {
|
||||||
min: config.team_size?.min ?? 1,
|
min: config.team_size?.min ?? 1,
|
||||||
max: config.team_size?.max ?? 1
|
max: config.team_size?.max ?? 1
|
||||||
},
|
},
|
||||||
entry_fields,
|
entry_fields: (config.entry_fields ?? []).map(cloneSignupField),
|
||||||
participant_fields
|
participant_fields: (config.participant_fields ?? []).map(cloneSignupField)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ensureAttendancePlacement(signup);
|
||||||
|
|
||||||
|
if (
|
||||||
|
signup.mode === 'team' &&
|
||||||
|
!signup.participant_fields.some((field) => !isAttendanceField(field))
|
||||||
|
) {
|
||||||
|
signup.participant_fields = [...signup.participant_fields, createSignupField('Spelarinfo')];
|
||||||
|
ensureAttendancePlacement(signup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (signup.mode !== 'team' && !signup.entry_fields.some((field) => !isAttendanceField(field))) {
|
||||||
|
signup.entry_fields = [...signup.entry_fields, createSignupField('Spelarnamn')];
|
||||||
|
ensureAttendancePlacement(signup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return signup;
|
||||||
}
|
}
|
||||||
|
|
||||||
function signupFieldKey(index: number, field?: SignupFieldForm) {
|
function signupFieldKey(index: number, field?: SignupFieldForm) {
|
||||||
|
|
@ -201,17 +239,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function addEntryField(form: TournamentForm) {
|
function addEntryField(form: TournamentForm) {
|
||||||
form.signup.entry_fields = ensureAttendanceField([
|
form.signup.entry_fields = [...form.signup.entry_fields, createSignupField('Nytt fält')];
|
||||||
...form.signup.entry_fields,
|
ensureAttendancePlacement(form.signup);
|
||||||
createSignupField('Nytt fält')
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeEntryField(form: TournamentForm, index: number) {
|
function removeEntryField(form: TournamentForm, index: number) {
|
||||||
const target = form.signup.entry_fields[index];
|
const target = form.signup.entry_fields[index];
|
||||||
if (!target || isAttendanceField(target)) return;
|
if (!target || isAttendanceField(target)) return;
|
||||||
const remaining = form.signup.entry_fields.filter((_, idx) => idx !== index);
|
form.signup.entry_fields = form.signup.entry_fields.filter((_, idx) => idx !== index);
|
||||||
form.signup.entry_fields = ensureAttendanceField(remaining);
|
ensureAttendancePlacement(form.signup);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addParticipantField(form: TournamentForm) {
|
function addParticipantField(form: TournamentForm) {
|
||||||
|
|
@ -219,13 +255,16 @@
|
||||||
...form.signup.participant_fields,
|
...form.signup.participant_fields,
|
||||||
createSignupField('Spelarinfo')
|
createSignupField('Spelarinfo')
|
||||||
];
|
];
|
||||||
|
ensureAttendancePlacement(form.signup);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeParticipantField(form: TournamentForm, index: number) {
|
function removeParticipantField(form: TournamentForm, index: number) {
|
||||||
if (form.signup.participant_fields.length <= 1) return;
|
const target = form.signup.participant_fields[index];
|
||||||
|
if (!target || isAttendanceField(target)) return;
|
||||||
form.signup.participant_fields = form.signup.participant_fields.filter(
|
form.signup.participant_fields = form.signup.participant_fields.filter(
|
||||||
(_, idx) => idx !== index
|
(_, idx) => idx !== index
|
||||||
);
|
);
|
||||||
|
ensureAttendancePlacement(form.signup);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSignupMode(form: TournamentForm, mode: 'solo' | 'team') {
|
function setSignupMode(form: TournamentForm, mode: 'solo' | 'team') {
|
||||||
|
|
@ -243,6 +282,7 @@
|
||||||
form.signup.participant_fields = [createSignupField('Spelare')];
|
form.signup.participant_fields = [createSignupField('Spelare')];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ensureAttendancePlacement(form.signup);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTeamSize(form: TournamentForm, key: 'min' | 'max', value: string) {
|
function setTeamSize(form: TournamentForm, key: 'min' | 'max', value: string) {
|
||||||
|
|
@ -362,8 +402,17 @@
|
||||||
max = 1;
|
max = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry_fields = ensureAttendanceField(signup.entry_fields).map(normalizeField);
|
const draft: SignupConfigForm = {
|
||||||
const participant_fields = mode === 'team' ? signup.participant_fields.map(normalizeField) : [];
|
mode,
|
||||||
|
team_size: { min, max },
|
||||||
|
entry_fields: signup.entry_fields.map((field) => ({ ...field })),
|
||||||
|
participant_fields: signup.participant_fields.map((field) => ({ ...field }))
|
||||||
|
};
|
||||||
|
|
||||||
|
ensureAttendancePlacement(draft);
|
||||||
|
|
||||||
|
const entry_fields = draft.entry_fields.map(normalizeField);
|
||||||
|
const participant_fields = mode === 'team' ? draft.participant_fields.map(normalizeField) : [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mode,
|
mode,
|
||||||
|
|
|
||||||
|
|
@ -188,34 +188,58 @@
|
||||||
signup.error = '';
|
signup.error = '';
|
||||||
signup.success = '';
|
signup.success = '';
|
||||||
|
|
||||||
const attendanceValue = (signup.entry[ATTENDANCE_FIELD_ID] ?? '').trim();
|
const isTeam = signupConfig.mode === 'team';
|
||||||
if (!attendanceValue) {
|
|
||||||
signup.error = 'Ange ditt deltagar-ID från närvarolistan.';
|
if (isTeam) {
|
||||||
return;
|
if (signup.participants.length < minParticipants) {
|
||||||
|
signup.error =
|
||||||
|
minParticipants === 1
|
||||||
|
? 'Lägg till minst en spelare.'
|
||||||
|
: `Lägg till minst ${minParticipants} spelare.`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let index = 0; index < signup.participants.length; index += 1) {
|
||||||
|
const participant = signup.participants[index];
|
||||||
|
const raw = (participant[ATTENDANCE_FIELD_ID] ?? '').trim();
|
||||||
|
if (!raw) {
|
||||||
|
signup.error = `Spelare ${index + 1}: ange deltagar-ID från närvarolistan.`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!/^\d+$/.test(raw)) {
|
||||||
|
signup.error = `Spelare ${index + 1}: deltagar-ID får endast innehålla siffror.`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const numeric = Number.parseInt(raw, 10);
|
||||||
|
if (!Number.isFinite(numeric) || numeric <= 0) {
|
||||||
|
signup.error = `Spelare ${index + 1}: ange ett giltigt deltagar-ID.`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
participant[ATTENDANCE_FIELD_ID] = String(numeric);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const attendanceValue = (signup.entry[ATTENDANCE_FIELD_ID] ?? '').trim();
|
||||||
|
if (!attendanceValue) {
|
||||||
|
signup.error = 'Ange ditt deltagar-ID från närvarolistan.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!/^\d+$/.test(attendanceValue)) {
|
||||||
|
signup.error = 'Deltagar-ID får endast innehålla siffror.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const attendanceNumeric = Number.parseInt(attendanceValue, 10);
|
||||||
|
if (!Number.isFinite(attendanceNumeric) || attendanceNumeric <= 0) {
|
||||||
|
signup.error = 'Ange ett giltigt deltagar-ID.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
signup.entry[ATTENDANCE_FIELD_ID] = String(attendanceNumeric);
|
||||||
}
|
}
|
||||||
if (!/^\d+$/.test(attendanceValue)) {
|
|
||||||
signup.error = 'Deltagar-ID får endast innehålla siffror.';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const attendanceNumeric = Number.parseInt(attendanceValue, 10);
|
|
||||||
if (!Number.isFinite(attendanceNumeric) || attendanceNumeric <= 0) {
|
|
||||||
signup.error = 'Ange ett giltigt deltagar-ID.';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
signup.entry[ATTENDANCE_FIELD_ID] = String(attendanceNumeric);
|
|
||||||
|
|
||||||
if (signup.participants.length === 0) {
|
if (signup.participants.length === 0) {
|
||||||
signup.error = 'Lägg till minst en spelare.';
|
signup.error = 'Lägg till minst en spelare.';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signupConfig.mode === 'team') {
|
|
||||||
if (signup.participants.length < minParticipants) {
|
|
||||||
signup.error = `Lägg till minst ${minParticipants} spelare.`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
signup.submitting = true;
|
signup.submitting = true;
|
||||||
try {
|
try {
|
||||||
const payload = buildSignupPayload();
|
const payload = buildSignupPayload();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue