From c1e1e88c54c98a32ee127421ca938da6412ebc39 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 25 Jul 2025 06:55:33 +0200 Subject: [PATCH 1/9] hotfix: Improve tournament name visibility and efficiency --- convex/_generated/api.d.ts | 2 -- .../mutations/createTournamentCompetitor.ts | 10 ++++++ .../_helpers/checkUserTournamentForcedName.ts | 34 ------------------- .../checkUserTournamentRelationship.ts | 10 +++--- convex/_model/users/_helpers/redactUser.ts | 7 +--- .../UserProfileForm/UserProfileForm.tsx | 9 +++-- .../UserProfileForm/UserProfileForm.utils.ts | 18 ++++++++-- 7 files changed, 40 insertions(+), 50 deletions(-) delete mode 100644 convex/_model/users/_helpers/checkUserTournamentForcedName.ts diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts index 26751c9a..2409c14c 100644 --- a/convex/_generated/api.d.ts +++ b/convex/_generated/api.d.ts @@ -132,7 +132,6 @@ import type * as _model_tournaments_queries_getTournaments from "../_model/tourn import type * as _model_tournaments_queries_getTournamentsByStatus from "../_model/tournaments/queries/getTournamentsByStatus.js"; import type * as _model_tournaments_queries_getTournamentsByUser from "../_model/tournaments/queries/getTournamentsByUser.js"; import type * as _model_users__helpers_checkUserAuth from "../_model/users/_helpers/checkUserAuth.js"; -import type * as _model_users__helpers_checkUserTournamentForcedName from "../_model/users/_helpers/checkUserTournamentForcedName.js"; import type * as _model_users__helpers_checkUserTournamentRelationship from "../_model/users/_helpers/checkUserTournamentRelationship.js"; import type * as _model_users__helpers_getShallowUser from "../_model/users/_helpers/getShallowUser.js"; import type * as _model_users__helpers_redactUser from "../_model/users/_helpers/redactUser.js"; @@ -321,7 +320,6 @@ declare const fullApi: ApiFromModules<{ "_model/tournaments/queries/getTournamentsByStatus": typeof _model_tournaments_queries_getTournamentsByStatus; "_model/tournaments/queries/getTournamentsByUser": typeof _model_tournaments_queries_getTournamentsByUser; "_model/users/_helpers/checkUserAuth": typeof _model_users__helpers_checkUserAuth; - "_model/users/_helpers/checkUserTournamentForcedName": typeof _model_users__helpers_checkUserTournamentForcedName; "_model/users/_helpers/checkUserTournamentRelationship": typeof _model_users__helpers_checkUserTournamentRelationship; "_model/users/_helpers/getShallowUser": typeof _model_users__helpers_getShallowUser; "_model/users/_helpers/redactUser": typeof _model_users__helpers_redactUser; diff --git a/convex/_model/tournamentCompetitors/mutations/createTournamentCompetitor.ts b/convex/_model/tournamentCompetitors/mutations/createTournamentCompetitor.ts index 6f41797b..a26e345a 100644 --- a/convex/_model/tournamentCompetitors/mutations/createTournamentCompetitor.ts +++ b/convex/_model/tournamentCompetitors/mutations/createTournamentCompetitor.ts @@ -51,6 +51,16 @@ export const createTournamentCompetitor = async ( } // ---- PRIMARY ACTIONS ---- + + if (tournament.requireRealNames) { + for (const { userId } of args.players) { + const user = await ctx.db.get(userId); + if (user?.nameVisibility && ['hidden', 'friends'].includes(user.nameVisibility)) { + await ctx.db.patch(userId, { nameVisibility: 'tournaments' }); + } + } + } + return await ctx.db.insert('tournamentCompetitors', { ...args, active: false, diff --git a/convex/_model/users/_helpers/checkUserTournamentForcedName.ts b/convex/_model/users/_helpers/checkUserTournamentForcedName.ts deleted file mode 100644 index 423be595..00000000 --- a/convex/_model/users/_helpers/checkUserTournamentForcedName.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Id } from '../../../_generated/dataModel'; -import { QueryCtx } from '../../../_generated/server'; -import { getTournamentUserIds } from '../../../_model/tournaments'; - -export const checkUserTournamentForcedName = async ( - ctx: QueryCtx, - userIdA?: Id<'users'> | null, - userIdB?: Id<'users'> | null, -): Promise => { - if (!userIdA || !userIdB) { - return false; - } - - const tournaments = await ctx.db.query('tournaments').collect(); - - // Check each tournament for a relationship, return true if one is found - // Check each tournament for a relationship, return true if one is found - for (const { _id, organizerUserIds, requireRealNames } of tournaments) { - const playerUserIds = await getTournamentUserIds(ctx, _id); - - // Merge all organizer IDs and player IDs into one set - const allTournamentUserIds = new Set([ - ...organizerUserIds, - ...playerUserIds, - ]); - - // If the set contains both user IDs, they were at the same tournament - if (allTournamentUserIds.has(userIdA) && allTournamentUserIds.has(userIdB) && requireRealNames) { - return true; - } - } - - return false; -}; diff --git a/convex/_model/users/_helpers/checkUserTournamentRelationship.ts b/convex/_model/users/_helpers/checkUserTournamentRelationship.ts index 716a4d78..3d83fe3c 100644 --- a/convex/_model/users/_helpers/checkUserTournamentRelationship.ts +++ b/convex/_model/users/_helpers/checkUserTournamentRelationship.ts @@ -14,8 +14,7 @@ export const checkUserTournamentRelationship = async ( const tournaments = await ctx.db.query('tournaments').collect(); // Check each tournament for a relationship, return true if one is found - return tournaments.some(async ({ _id, organizerUserIds }) => { - + for (const { _id, organizerUserIds } of tournaments) { const playerUserIds = await getTournamentUserIds(ctx, _id); // Merge all organizer IDs and player IDs into one set @@ -25,6 +24,9 @@ export const checkUserTournamentRelationship = async ( ]); // If the set contains both user IDs, they were at the same tournament - return allTournamentUserIds.has(userIdA) && allTournamentUserIds.has(userIdB); - }); + if (allTournamentUserIds.has(userIdA) && allTournamentUserIds.has(userIdB)) { + return true; + } + } + return false; }; diff --git a/convex/_model/users/_helpers/redactUser.ts b/convex/_model/users/_helpers/redactUser.ts index 961820b9..d3e001ef 100644 --- a/convex/_model/users/_helpers/redactUser.ts +++ b/convex/_model/users/_helpers/redactUser.ts @@ -3,7 +3,6 @@ import { getAuthUserId } from '@convex-dev/auth/server'; import { Doc } from '../../../_generated/dataModel'; import { QueryCtx } from '../../../_generated/server'; import { getStorageUrl } from '../../common/_helpers/getStorageUrl'; -import { checkUserTournamentForcedName } from './checkUserTournamentForcedName'; import { checkUserTournamentRelationship } from './checkUserTournamentRelationship'; /** @@ -52,15 +51,11 @@ export const redactUser = async ( // If user is querying someone they are in a tournament with const hasTournamentRelationship = await checkUserTournamentRelationship(ctx, userId, user._id); - // If user is querying someone they are in a tournament with which requires real names - const requiredByTournament = await checkUserTournamentForcedName(ctx, userId, user._id); - // Add name information if allowed if ( (user?.nameVisibility === 'public') || (user?.nameVisibility === 'friends' && hasFriendRelationship) || - (user?.nameVisibility === 'tournaments' && (hasFriendRelationship || hasTournamentRelationship)) || - requiredByTournament + (user?.nameVisibility === 'tournaments' && (hasFriendRelationship || hasTournamentRelationship)) ) { limitedUser.givenName = user.givenName; limitedUser.familyName = user.familyName; diff --git a/src/pages/SettingsPage/components/UserProfileForm/UserProfileForm.tsx b/src/pages/SettingsPage/components/UserProfileForm/UserProfileForm.tsx index 1a99a702..c4163639 100644 --- a/src/pages/SettingsPage/components/UserProfileForm/UserProfileForm.tsx +++ b/src/pages/SettingsPage/components/UserProfileForm/UserProfileForm.tsx @@ -8,13 +8,14 @@ import { Form, FormField } from '~/components/generic/Form'; import { InputSelect } from '~/components/generic/InputSelect'; import { InputText } from '~/components/generic/InputText'; import { Separator } from '~/components/generic/Separator'; +import { useGetTournamentsByUser } from '~/services/tournaments'; import { useUpdateUser } from '~/services/users'; import { userProfileNameVisibilityOptions } from '~/types/UserProfileNameVisibility'; import { getCountryOptions } from '~/utils/common/getCountryOptions'; import { + createSchema, defaultValues, UserProfileFormData, - userProfileFormSchema, } from './UserProfileForm.utils'; import styles from './UserProfileForm.module.scss'; @@ -22,12 +23,16 @@ import styles from './UserProfileForm.module.scss'; export const UserProfileForm = (): JSX.Element => { const user = useAuth(); + const { data: tournaments } = useGetTournamentsByUser(user ? { + userId: user._id, + } : 'skip'); + const { mutation: updateUser } = useUpdateUser({ successMessage: 'Profile updated!', }); const form = useForm({ - resolver: zodResolver(userProfileFormSchema), + resolver: zodResolver(createSchema(tournaments ?? [])), defaultValues, values: { ...defaultValues, ...(user ?? {}) }, }); diff --git a/src/pages/SettingsPage/components/UserProfileForm/UserProfileForm.utils.ts b/src/pages/SettingsPage/components/UserProfileForm/UserProfileForm.utils.ts index fb23f5dd..72d25801 100644 --- a/src/pages/SettingsPage/components/UserProfileForm/UserProfileForm.utils.ts +++ b/src/pages/SettingsPage/components/UserProfileForm/UserProfileForm.utils.ts @@ -1,8 +1,11 @@ import { z } from 'zod'; +import { Tournament } from '~/api'; import { UserProfileNameVisibility, userProfileNameVisibilitySchema } from '~/types/UserProfileNameVisibility'; -export const userProfileFormSchema = z.object({ +export const createSchema = ( + userTournaments: Tournament[], +) => z.object({ username: z.string().min(3, { message: 'Username must be at least 3 characters.', }), @@ -10,9 +13,20 @@ export const userProfileFormSchema = z.object({ familyName: z.string().min(2), nameVisibility: userProfileNameVisibilitySchema, countryCode: z.optional(z.string()), +}).superRefine((data, ctx) => { + const requireRealNames = userTournaments.some((tournament) => ( + tournament.requireRealNames && (tournament.status === 'published' || tournament.status === 'active') + )); + if (['hidden', 'friends'].includes(data.nameVisibility) && requireRealNames) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: 'You are currently registered in a tournament which requires real names.', + path: ['nameVisibility'], + }); + } }); -export type UserProfileFormData = z.infer ; +export type UserProfileFormData = z.infer>; export const defaultValues = { username: '', From 9c4fd6da461d475c0af9c04071e858bb935bf9b8 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 25 Jul 2025 08:51:08 +0200 Subject: [PATCH 2/9] hotfix: Improve add-match label --- .../components/ActiveTournament/ActiveTournament.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/DashboardPage/components/ActiveTournament/ActiveTournament.tsx b/src/pages/DashboardPage/components/ActiveTournament/ActiveTournament.tsx index 1c600420..f653bb89 100644 --- a/src/pages/DashboardPage/components/ActiveTournament/ActiveTournament.tsx +++ b/src/pages/DashboardPage/components/ActiveTournament/ActiveTournament.tsx @@ -90,7 +90,7 @@ export const ActiveTournament = ({ From 8fa4bf155b0fc855831734983eb103d1987a76ad Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 25 Jul 2025 14:27:27 +0200 Subject: [PATCH 3/9] hotfix: Fix battle plan display in match result details --- .../FowV4MatchResultDetails/FowV4MatchResultDetails.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/FowV4MatchResultDetails/FowV4MatchResultDetails.tsx b/src/components/FowV4MatchResultDetails/FowV4MatchResultDetails.tsx index 24a443aa..3769a946 100644 --- a/src/components/FowV4MatchResultDetails/FowV4MatchResultDetails.tsx +++ b/src/components/FowV4MatchResultDetails/FowV4MatchResultDetails.tsx @@ -27,8 +27,8 @@ export const FowV4MatchResultDetails = ({ const score = calculateMatchScore(matchResult.details); const missionName = matchResult.details.missionName ?? getMission(matchResult.details.missionId)?.displayName; const battlePlans: [string, string] = [ - fowV4BattlePlanOptions.find((option) => option.value === matchResult.details.player0BattlePlan)?.label ?? '', - fowV4BattlePlanOptions.find((option) => option.value === matchResult.details.player0BattlePlan)?.label ?? '', + fowV4BattlePlanOptions.find((option) => option.value === matchResult.details.player0BattlePlan)?.label ?? 'Hidden', + fowV4BattlePlanOptions.find((option) => option.value === matchResult.details.player1BattlePlan)?.label ?? 'Hidden', ]; const unitsLost: [number, number] = [ matchResult.details.player0UnitsLost, From d495934b1316ffbd624fef9d251955ae52201e77 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Fri, 25 Jul 2025 14:27:42 +0200 Subject: [PATCH 4/9] hotfix: Allow selection of any mission --- convex/static/fowV4/missionPacks.ts | 4 ++++ src/api.ts | 1 + .../components/GameConfigFields.tsx | 12 ++++++++---- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/convex/static/fowV4/missionPacks.ts b/convex/static/fowV4/missionPacks.ts index d0d64bb0..657010cc 100644 --- a/convex/static/fowV4/missionPacks.ts +++ b/convex/static/fowV4/missionPacks.ts @@ -402,6 +402,10 @@ export const getFowV4MissionMatrixesByMissionPackId = (id: string): FowV4Mission missionPacks.find((missionPack) => missionPack.id === id)?.matrixes ); +export const getFowV4MissionsByMissionPackId = (id: string): FowV4Mission[] | undefined => ( + missionPacks.find((missionPack) => missionPack.id === id)?.missions +); + export const getFowV4MissionMatrixOptionsByMissionPackId = ( id?: string, ): { value: string; label: string; }[] => { diff --git a/src/api.ts b/src/api.ts index 2193ecae..d06c9418 100644 --- a/src/api.ts +++ b/src/api.ts @@ -154,5 +154,6 @@ export type { FowV4MissionMatrixId } from '../convex/static/fowV4/missionPacks'; export { getFowV4MissionMatrixesByMissionPackId, getFowV4MissionMatrixOptionsByMissionPackId, + getFowV4MissionsByMissionPackId, } from '../convex/static/fowV4/missionPacks'; export type { FowV4MissionMatrix } from '../convex/static/fowV4/missionPacks.types'; diff --git a/src/components/FowV4MatchResultForm/components/GameConfigFields.tsx b/src/components/FowV4MatchResultForm/components/GameConfigFields.tsx index f9f9f835..a8a7de70 100644 --- a/src/components/FowV4MatchResultForm/components/GameConfigFields.tsx +++ b/src/components/FowV4MatchResultForm/components/GameConfigFields.tsx @@ -5,7 +5,7 @@ import { fowV4EraOptions, fowV4LessonsFromTheFrontVersionOptions, fowV4MissionPackOptions, - getFowV4MissionMatrixOptionsByMissionPackId, + getFowV4MissionsByMissionPackId, } from '~/api'; import { Animate } from '~/components/generic/Animate'; import { FormField } from '~/components/generic/Form'; @@ -35,7 +35,11 @@ export const GameConfigFields = ({ const missionPackId = watch(`${formPath}.missionPackId`); - const matrixOptions = getFowV4MissionMatrixOptionsByMissionPackId(missionPackId); + const missionOptions = (getFowV4MissionsByMissionPackId(missionPackId) ?? []).map((mission) => ({ + label: mission.displayName, + value: mission.id, + })); + return (
@@ -63,8 +67,8 @@ export const GameConfigFields = ({ - - + + From 3f93d77384d345126794e5290d9a13540c6135e5 Mon Sep 17 00:00:00 2001 From: Ian Paschal Date: Sat, 26 Jul 2025 08:39:12 +0200 Subject: [PATCH 5/9] ETC improvements day 2 (#127) * feat: Add tournament pairing detail page * feat: Add score to match results * feat: Improve match result editing and creation for tournaments * fix: Improve dashboard styling * feat: Improve match result editing and creation for tournaments (pt2) --- .../fowV4/calculateFowV4MatchResultScore.ts | 3 +- .../_helpers/deepenMatchResult.ts | 6 ++ .../_helpers/deepenTournamentPairing.ts | 2 +- .../FowV4MatchResultDetails.types.ts | 2 +- .../FowV4MatchResultForm.tsx | 28 +++---- .../components/CommonFields.hooks.ts | 8 +- .../components/TournamentPlayersFields.tsx | 14 ++-- src/components/IdentityBadge/index.ts | 5 +- .../MatchResultContextMenu.tsx | 9 ++- .../MatchResultCreateDialog.hooks.tsx | 3 +- .../MatchResultCreateDialog.tsx | 12 ++- .../MatchResultEditDialog.tsx | 2 +- src/pages/DashboardPage/DashboardPage.tsx | 2 +- .../ActiveTournament.module.scss | 8 +- .../ActiveTournament/ActiveTournament.tsx | 32 ++++---- .../TournamentPairingsCard.tsx | 4 +- .../TournamentPairingsCard.utils.tsx | 21 ++++- .../TournamentPairingDetailPage.module.scss | 22 ++++++ .../TournamentPairingDetailPage.tsx | 76 +++++++++++++++++++ .../TournamentPairingDetailPage/index.ts | 1 + src/routes.tsx | 6 ++ src/settings.ts | 1 + 22 files changed, 206 insertions(+), 61 deletions(-) create mode 100644 src/pages/TournamentPairingDetailPage/TournamentPairingDetailPage.module.scss create mode 100644 src/pages/TournamentPairingDetailPage/TournamentPairingDetailPage.tsx create mode 100644 src/pages/TournamentPairingDetailPage/index.ts diff --git a/convex/_model/fowV4/calculateFowV4MatchResultScore.ts b/convex/_model/fowV4/calculateFowV4MatchResultScore.ts index 21d695ba..74c2517c 100644 --- a/convex/_model/fowV4/calculateFowV4MatchResultScore.ts +++ b/convex/_model/fowV4/calculateFowV4MatchResultScore.ts @@ -1,5 +1,4 @@ import { Doc } from '../../_generated/dataModel'; -import { DeepMatchResult } from '../matchResults'; /** * Calculate the Victory Points (i.e. score) for a given match result. @@ -10,7 +9,7 @@ import { DeepMatchResult } from '../matchResults'; * @param matchResult - The match result to score * @returns - A tuple with the scores for player 0 and 1 respectively */ -export const calculateFowV4MatchResultScore = (matchResult: Doc<'matchResults'> | DeepMatchResult): [number, number] => { +export const calculateFowV4MatchResultScore = (matchResult: Doc<'matchResults'>): [number, number] => { // TODO: Add some guards in case matchResult is not FowV4 diff --git a/convex/_model/matchResults/_helpers/deepenMatchResult.ts b/convex/_model/matchResults/_helpers/deepenMatchResult.ts index 55cd026f..b9d7625f 100644 --- a/convex/_model/matchResults/_helpers/deepenMatchResult.ts +++ b/convex/_model/matchResults/_helpers/deepenMatchResult.ts @@ -1,5 +1,6 @@ import { Doc } from '../../../_generated/dataModel'; import { QueryCtx } from '../../../_generated/server'; +import { calculateFowV4MatchResultScore } from '../../fowV4/calculateFowV4MatchResultScore'; import { getMission } from '../../fowV4/getMission'; import { getUser } from '../../users/queries/getUser'; import { checkMatchResultBattlePlanVisibility } from './checkMatchResultBattlePlanVisibility'; @@ -38,6 +39,9 @@ export const deepenMatchResult = async ( const mission = getMission(matchResult.details.missionId); const battlePlansVisible = await checkMatchResultBattlePlanVisibility(ctx, matchResult); + // TODO: This is FowV4 specific, needs to be made generic! + const [player0Score, player1Score] = calculateFowV4MatchResultScore(matchResult); + return { ...matchResult, ...(player0User ? { player0User } : {}), @@ -47,6 +51,8 @@ export const deepenMatchResult = async ( player0BattlePlan: battlePlansVisible ? matchResult.details.player0BattlePlan : undefined, player1BattlePlan: battlePlansVisible ? matchResult.details.player1BattlePlan : undefined, missionName: mission?.displayName, + player0Score, + player1Score, }, likedByUserIds: likes.map((like) => like.userId), commentCount: comments.length, diff --git a/convex/_model/tournamentPairings/_helpers/deepenTournamentPairing.ts b/convex/_model/tournamentPairings/_helpers/deepenTournamentPairing.ts index 1ed4b351..00650d54 100644 --- a/convex/_model/tournamentPairings/_helpers/deepenTournamentPairing.ts +++ b/convex/_model/tournamentPairings/_helpers/deepenTournamentPairing.ts @@ -14,7 +14,7 @@ import { getTournamentShallow } from '../../tournaments'; * This method's return type is, by nature, the definition of a deep TournamentPairing. * * @param ctx - Convex query context - * @param tournament - Raw TournamentPairing document + * @param tournamentPairing - Raw TournamentPairing document * @returns A deep TournamentPairing */ export const deepenTournamentPairing = async ( diff --git a/src/components/FowV4MatchResultDetails/FowV4MatchResultDetails.types.ts b/src/components/FowV4MatchResultDetails/FowV4MatchResultDetails.types.ts index 15c3b11c..4ecf00f2 100644 --- a/src/components/FowV4MatchResultDetails/FowV4MatchResultDetails.types.ts +++ b/src/components/FowV4MatchResultDetails/FowV4MatchResultDetails.types.ts @@ -3,7 +3,7 @@ import { MatchResult, User } from '~/api'; export type FowV4MatchResultDetailsData = Pick & { player0User?: User; player1User?: User; - details: Omit & { + details: Omit & { missionName?: string; } }; diff --git a/src/components/FowV4MatchResultForm/FowV4MatchResultForm.tsx b/src/components/FowV4MatchResultForm/FowV4MatchResultForm.tsx index 4c872870..04a0de8f 100644 --- a/src/components/FowV4MatchResultForm/FowV4MatchResultForm.tsx +++ b/src/components/FowV4MatchResultForm/FowV4MatchResultForm.tsx @@ -3,7 +3,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import clsx from 'clsx'; import { - MatchResultId, + MatchResult, TournamentPairingId, UserId, } from '~/api'; @@ -16,11 +16,7 @@ import { SelectValue } from '~/components/generic/InputSelect/InputSelect.types' import { Label } from '~/components/generic/Label'; import { Separator } from '~/components/generic/Separator'; import { useAsyncState } from '~/hooks/useAsyncState'; -import { - useCreateMatchResult, - useGetMatchResult, - useUpdateMatchResult, -} from '~/services/matchResults'; +import { useCreateMatchResult, useUpdateMatchResult } from '~/services/matchResults'; import { useGetActiveTournamentPairingsByUser } from '~/services/tournamentPairings'; import { getTournamentPairingDisplayName } from '~/utils/common/getTournamentPairingDisplayName'; import { CommonFields } from './components/CommonFields'; @@ -40,7 +36,7 @@ const confirmMatchResultDialogId = 'confirm-match-result'; export interface FowV4MatchResultFormProps { id: string; className?: string; - matchResultId?: MatchResultId; + matchResult?: MatchResult; tournamentPairingId?: TournamentPairingId; onSuccess?: () => void; } @@ -48,17 +44,12 @@ export interface FowV4MatchResultFormProps { export const FowV4MatchResultForm = ({ id, className, - matchResultId, + matchResult, tournamentPairingId: forcedTournamentPairingId, onSuccess, }: FowV4MatchResultFormProps): JSX.Element => { const user = useAuth(); - const { - data: matchResult, - loading: matchResultLoading, - } = useGetMatchResult(matchResultId ? { id: matchResultId } : 'skip'); - const [ tournamentPairingId, setTournamentPairingId, @@ -89,9 +80,10 @@ export const FowV4MatchResultForm = ({ const form = useForm({ resolver: zodResolver(fowV4MatchResultFormSchema), - defaultValues, - // React-Hook-Form is stupid and doesn't allow applying a partial record to the form values - values: { ...matchResult as FowV4MatchResultFormData }, + defaultValues: { + ...defaultValues, + ...(matchResult ? fowV4MatchResultFormSchema.parse(matchResult) : {}), + }, mode: 'onSubmit', }); @@ -143,13 +135,13 @@ export const FowV4MatchResultForm = ({ const disableSubmit = createMatchResultLoading || updateMatchResultLoading; - if (tournamentPairingsLoading || matchResultLoading) { + if (tournamentPairingsLoading) { return
Loading...
; } return (
- {!matchResultId && ( + {!matchResult && !forcedTournamentPairingId && ( <>