@@ -4,6 +4,10 @@ import {
44 upsertAssignmentsOnPerson ,
55} from '../../lib/domain/persons' ;
66import { mapIn , updateIn } from '../../lib/utils/utils' ;
7+ import {
8+ getGroupifierActivityConfig ,
9+ setGroupifierActivityConfig ,
10+ } from '../../lib/wcif/extensions/groupifier' ;
711import { validateWcif } from '../../lib/wcif/validation' ;
812import {
913 type AddPersonAssignmentsPayload ,
@@ -14,7 +18,7 @@ import {
1418 type UpsertPersonAssignmentsPayload ,
1519} from '../actions' ;
1620import { type AppState } from '../initialState' ;
17- import type { Assignment , Person } from '@wca/helpers' ;
21+ import type { Activity , Assignment , Competition , Person } from '@wca/helpers' ;
1822
1923const determineErrors = ( state : AppState ) : AppState => {
2024 if ( ! state . wcif ) return state ;
@@ -24,6 +28,77 @@ const determineErrors = (state: AppState): AppState => {
2428 } ;
2529} ;
2630
31+ /**
32+ * Removes a person from featuredCompetitors for all activities where they don't have
33+ * a competitor assignment. This ensures featuredCompetitors stays in sync with actual
34+ * competitor assignments.
35+ *
36+ * @param wcif - The WCIF object to clean up
37+ * @param registrantId - The registrant ID of the person to clean up
38+ * @returns Updated WCIF with cleaned featuredCompetitors
39+ */
40+ const fixFeaturedCompetitors = ( wcif : Competition , registrantId : number ) : Competition => {
41+ const person = wcif . persons . find ( ( p ) => p . registrantId === registrantId ) ;
42+ if ( ! person ?. wcaUserId ) {
43+ return wcif ;
44+ }
45+
46+ const wcaUserId = person . wcaUserId ;
47+
48+ // Get all activity IDs where this person has a competitor assignment
49+ const competitorActivityIds = new Set (
50+ person . assignments ?. filter ( ( a ) => a . assignmentCode === 'competitor' ) . map ( ( a ) => a . activityId ) ||
51+ [ ]
52+ ) ;
53+
54+ const updateFeaturedCompetitors = ( activity : Activity ) : Activity => {
55+ const config = getGroupifierActivityConfig ( activity ) ;
56+ if ( ! config ?. featuredCompetitorWcaUserIds ) {
57+ return activity ;
58+ }
59+
60+ // Check if person should be removed from this activity's featuredCompetitors
61+ const isFeatured = config . featuredCompetitorWcaUserIds . includes ( wcaUserId ) ;
62+ const hasCompetitorAssignment = competitorActivityIds . has ( activity . id ) ;
63+
64+ // Remove from featured if they're listed but don't have a competitor assignment
65+ if ( isFeatured && ! hasCompetitorAssignment ) {
66+ const updatedIds = config . featuredCompetitorWcaUserIds . filter ( ( id ) => id !== wcaUserId ) ;
67+
68+ return setGroupifierActivityConfig ( activity , {
69+ ...config ,
70+ featuredCompetitorWcaUserIds : updatedIds ,
71+ } ) ;
72+ }
73+
74+ return activity ;
75+ } ;
76+
77+ const newSchedule = updateIn ( wcif . schedule , 'venues' , ( venues ) =>
78+ venues . map ( ( venue ) =>
79+ updateIn ( venue , 'rooms' , ( rooms ) =>
80+ rooms . map ( ( room ) =>
81+ updateIn ( room , 'activities' , ( activities ) =>
82+ activities . map ( ( activity ) => {
83+ return {
84+ ...updateFeaturedCompetitors ( activity ) ,
85+ childActivities : activity . childActivities . map ( ( childActivity ) =>
86+ updateFeaturedCompetitors ( childActivity )
87+ ) ,
88+ } ;
89+ } )
90+ )
91+ )
92+ )
93+ )
94+ ) ;
95+
96+ return {
97+ ...wcif ,
98+ schedule : newSchedule ,
99+ } ;
100+ } ;
101+
27102export const addPersonAssignments = (
28103 state : AppState ,
29104 action : AddPersonAssignmentsPayload
@@ -44,36 +119,44 @@ export const addPersonAssignments = (
44119export const removePersonAssignments = (
45120 state : AppState ,
46121 action : RemovePersonAssignmentsPayload
47- ) : AppState =>
48- determineErrors ( {
122+ ) : AppState => {
123+ if ( ! state . wcif ) return state ;
124+
125+ let updatedWcif : Competition = mapIn ( state . wcif , 'persons' , ( p ) =>
126+ p . registrantId === action . registrantId ? removeAssignmentsFromPerson ( p , action . activityId ) : p
127+ ) ;
128+
129+ updatedWcif = fixFeaturedCompetitors ( updatedWcif , action . registrantId ) ;
130+
131+ return determineErrors ( {
49132 ...state ,
50133 needToSave : true ,
51134 changedKeys : new Set ( [ ...state . changedKeys , 'persons' ] ) ,
52- wcif :
53- state . wcif &&
54- mapIn ( state . wcif , 'persons' , ( person ) =>
55- person . registrantId === action . registrantId
56- ? removeAssignmentsFromPerson ( person , action . activityId )
57- : person
58- ) ,
135+ wcif : updatedWcif ,
59136 } ) ;
137+ } ;
60138
61139export const upsertPersonAssignments = (
62140 state : AppState ,
63141 action : UpsertPersonAssignmentsPayload
64- ) : AppState =>
65- determineErrors ( {
142+ ) : AppState => {
143+ if ( ! state . wcif ) return determineErrors ( state ) ;
144+
145+ let updatedWcif : Competition = mapIn ( state . wcif , 'persons' , ( person ) =>
146+ person . registrantId === action . registrantId
147+ ? upsertAssignmentsOnPerson ( person , action . assignments )
148+ : person
149+ ) ;
150+
151+ updatedWcif = fixFeaturedCompetitors ( updatedWcif , action . registrantId ) ;
152+
153+ return determineErrors ( {
66154 ...state ,
67155 needToSave : true ,
68156 changedKeys : new Set ( [ ...state . changedKeys , 'persons' ] ) ,
69- wcif :
70- state . wcif &&
71- mapIn ( state . wcif , 'persons' , ( person ) =>
72- person . registrantId === action . registrantId
73- ? upsertAssignmentsOnPerson ( person , action . assignments )
74- : person
75- ) ,
157+ wcif : updatedWcif ,
76158 } ) ;
159+ } ;
77160
78161/**
79162 * @param {* } state
@@ -112,70 +195,99 @@ export const bulkAddPersonAssignments = (
112195export const bulkRemovePersonAssignments = (
113196 state : AppState ,
114197 action : BulkRemovePersonAssignmentsPayload
115- ) : AppState =>
116- determineErrors ( {
198+ ) : AppState => {
199+ if ( ! state . wcif ) return state ;
200+
201+ // Track registrant IDs that had assignments removed
202+ const affectedRegistrantIds = new Set < number > ( ) ;
203+
204+ let updatedWcif : Competition = mapIn ( state . wcif , 'persons' , ( person ) => {
205+ if ( person . assignments ?. length === 0 || ! person . assignments ) {
206+ return person ;
207+ }
208+
209+ // Find arguments to keep assignment: that is, return true
210+ return updateIn ( person , 'assignments' , ( assignments ) =>
211+ assignments ?. filter ( ( personAssignment : Assignment ) => {
212+ const filtersApplicable = action . assignments . filter ( ( a ) => {
213+ const filterByRegistrantId = a . registrantId
214+ ? a . registrantId === person . registrantId
215+ : null ;
216+ const filterByActivityId = a . activityId
217+ ? a . activityId === personAssignment . activityId
218+ : null ;
219+ const filterByAssignmentCode = a . assignmentCode
220+ ? a . assignmentCode === personAssignment . assignmentCode
221+ : null ;
222+
223+ // return true if any filter is applicable
224+ // We are looking for at least 1 false. If so, return no applicable filters
225+ return ! (
226+ filterByRegistrantId === false ||
227+ filterByActivityId === false ||
228+ filterByAssignmentCode === false
229+ ) ; // note do actually want these values to be "false" and not "null"
230+ } ) ;
231+
232+ const isBeingRemoved = filtersApplicable . length > 0 ;
233+
234+ if ( isBeingRemoved ) {
235+ affectedRegistrantIds . add ( person . registrantId ) ;
236+ }
237+
238+ // At least 1 filter is filtering them out
239+ return ! isBeingRemoved ;
240+ } )
241+ ) ;
242+ } ) ;
243+
244+ // Clean up featuredCompetitors for all affected persons
245+ for ( const registrantId of affectedRegistrantIds ) {
246+ updatedWcif = fixFeaturedCompetitors ( updatedWcif , registrantId ) ;
247+ }
248+
249+ return determineErrors ( {
117250 ...state ,
118251 needToSave : true ,
119252 changedKeys : new Set ( [ ...state . changedKeys , 'persons' ] ) ,
120- wcif :
121- state . wcif &&
122- mapIn ( state . wcif , 'persons' , ( person ) => {
123- if ( person . assignments ?. length === 0 || ! person . assignments ) {
124- return person ;
125- }
126-
127- // Find arguments to keep assignment: that is, return true
128- return updateIn ( person , 'assignments' , ( assignments ) =>
129- assignments ?. filter ( ( personAssignment : Assignment ) => {
130- const filtersApplicable = action . assignments . filter ( ( a ) => {
131- const filterByRegistrantId = a . registrantId
132- ? a . registrantId === person . registrantId
133- : null ;
134- const filterByActivityId = a . activityId
135- ? a . activityId === personAssignment . activityId
136- : null ;
137- const filterByAssignmentCode = a . assignmentCode
138- ? a . assignmentCode === personAssignment . assignmentCode
139- : null ;
140-
141- // return true if any filter is applicable
142- // We are looking for at least 1 false. If so, return no applicable filters
143- return ! (
144- filterByRegistrantId === false ||
145- filterByActivityId === false ||
146- filterByAssignmentCode === false
147- ) ; // note do actually want these values to be "false" and not "null"
148- } ) ;
149-
150- // At least 1 filter is filtering them out
151- return filtersApplicable . length === 0 ;
152- } )
153- ) ;
154- } ) ,
253+ wcif : updatedWcif ,
155254 } ) ;
255+ } ;
156256
157257export const bulkUpsertPersonAssignments = (
158258 state : AppState ,
159259 action : BulkUpsertPersonAssignmentsPayload
160- ) : AppState =>
161- determineErrors ( {
260+ ) : AppState => {
261+ if ( ! state . wcif ) return determineErrors ( state ) ;
262+
263+ // Track registrant IDs that had assignments updated
264+ const affectedRegistrantIds = new Set < number > ( ) ;
265+
266+ let updatedWcif : Competition = mapIn ( state . wcif , 'persons' , ( person : Person ) => {
267+ const personAssignments = action . assignments
268+ . filter ( ( a ) => a . registrantId === person . registrantId )
269+ . map ( ( a ) => ( {
270+ ...a . assignment ,
271+ activityId : a . assignment . activityId ,
272+ } ) ) ;
273+
274+ if ( personAssignments . length > 0 ) {
275+ affectedRegistrantIds . add ( person . registrantId ) ;
276+ return upsertAssignmentsOnPerson ( person , personAssignments ) ;
277+ }
278+
279+ return person ;
280+ } ) ;
281+
282+ // Clean up featuredCompetitors for all affected persons
283+ for ( const registrantId of affectedRegistrantIds ) {
284+ updatedWcif = fixFeaturedCompetitors ( updatedWcif , registrantId ) ;
285+ }
286+
287+ return determineErrors ( {
162288 ...state ,
163289 needToSave : true ,
164290 changedKeys : new Set ( [ ...state . changedKeys , 'persons' ] ) ,
165- wcif :
166- state . wcif &&
167- mapIn ( state . wcif , 'persons' , ( person : Person ) => {
168- const personAssignments = action . assignments
169- . filter ( ( a ) => a . registrantId === person . registrantId )
170- . map ( ( a ) => ( {
171- ...a . assignment ,
172- activityId : a . assignment . activityId ,
173- } ) ) ;
174-
175- if ( personAssignments . length > 0 ) {
176- return upsertAssignmentsOnPerson ( person , personAssignments ) ;
177- }
178-
179- return person ;
180- } ) ,
291+ wcif : updatedWcif ,
181292 } ) ;
293+ } ;
0 commit comments