@@ -20,6 +20,9 @@ const CippAddEditUser = (props) => {
2020 const [ selectedTemplate , setSelectedTemplate ] = useState ( null )
2121 const [ displayNameManuallySet , setDisplayNameManuallySet ] = useState ( false )
2222 const [ usernameManuallySet , setUsernameManuallySet ] = useState ( false )
23+ // Tracks the template already applied to the form so we can tell a first
24+ // apply (fill empty fields) apart from a switch (replace/clear stale values)
25+ const appliedTemplateKeyRef = useRef ( null )
2326 const router = useRouter ( )
2427 const { userId } = router . query
2528
@@ -261,6 +264,7 @@ const CippAddEditUser = (props) => {
261264 // Only clear selected template if it's not the default template
262265 if ( selectedTemplate && ! selectedTemplate . defaultForTenant ) {
263266 setSelectedTemplate ( null )
267+ appliedTemplateKeyRef . current = null
264268 }
265269 }
266270 } , [
@@ -290,100 +294,123 @@ const CippAddEditUser = (props) => {
290294
291295 // Auto-populate fields when template selected
292296 useEffect ( ( ) => {
293- if ( formType === 'add' && watchedFields . userTemplate ?. addedFields ) {
294- const template = watchedFields . userTemplate . addedFields
295- setSelectedTemplate ( template )
297+ if ( formType !== 'add' || ! watchedFields . userTemplate ?. addedFields ) return
298+ const template = watchedFields . userTemplate . addedFields
299+ const templateKey = watchedFields . userTemplate . value ?? template . GUID ?? template . templateName
296300
297- // Reset manual edit flags when template changes
298- setDisplayNameManuallySet ( false )
299- setUsernameManuallySet ( false )
301+ // Distinguish the first apply from a switch. On a switch we replace
302+ // template-driven fields (and clear ones the new template doesn't define)
303+ // so stale values from the previous template don't linger. On the first
304+ // apply we only fill fields that have a template value, so we don't clobber
305+ // input the user already entered or copied from another user.
306+ const isSwitch =
307+ appliedTemplateKeyRef . current !== null && appliedTemplateKeyRef . current !== templateKey
308+ appliedTemplateKeyRef . current = templateKey
300309
301- // Only set fields if they don't already have values (don't override user input)
302- const setFieldIfEmpty = ( fieldName , value ) => {
303- if ( value ) {
304- formControl . setValue ( fieldName , value )
305- }
306- }
310+ setSelectedTemplate ( template )
307311
308- // Populate form fields from template
309- if ( template . primDomain ) {
310- // If primDomain is an object, use it as-is; if it's a string, convert to object
311- const primDomainValue =
312- typeof template . primDomain === 'string'
313- ? { label : template . primDomain , value : template . primDomain }
314- : template . primDomain
315- formControl . setValue ( 'primDomain' , primDomainValue )
316- }
317- if ( template . usageLocation ) {
318- // Handle both object and string formats
319- const usageLocationCode =
320- typeof template . usageLocation === 'string'
321- ? template . usageLocation
322- : template . usageLocation ?. value
323- const country = countryList . find ( ( c ) => c . Code === usageLocationCode )
324- if ( country ) {
325- setFieldIfEmpty ( 'usageLocation' , {
326- label : country . Name ,
327- value : country . Code ,
328- } )
329- }
330- }
331- setFieldIfEmpty ( 'jobTitle' , template . jobTitle )
332- setFieldIfEmpty ( 'streetAddress' , template . streetAddress )
333- setFieldIfEmpty ( 'city' , template . city )
334- setFieldIfEmpty ( 'state' , template . state )
335- setFieldIfEmpty ( 'postalCode' , template . postalCode )
336- setFieldIfEmpty ( 'country' , template . country )
337- setFieldIfEmpty ( 'companyName' , template . companyName )
338- setFieldIfEmpty ( 'department' , template . department )
339- setFieldIfEmpty ( 'mobilePhone' , template . mobilePhone )
340- const templateBusinessPhone = Array . isArray ( template . businessPhones )
341- ? template . businessPhones [ 0 ]
342- : template . businessPhones
343- if ( templateBusinessPhone ) {
344- formControl . setValue ( 'businessPhones' , [ templateBusinessPhone ] )
345- }
312+ // Reset manual edit flags when template changes
313+ setDisplayNameManuallySet ( false )
314+ setUsernameManuallySet ( false )
346315
347- // Handle licenses - need to match the format expected by CippFormLicenseSelector
348- if ( template . licenses && Array . isArray ( template . licenses ) ) {
349- setFieldIfEmpty ( 'licenses' , template . licenses )
316+ // Apply a template value to a field. When the template has a value we set
317+ // it; when it doesn't and this is a switch we clear the field (emptyValue)
318+ // so the previous template's value doesn't linger.
319+ const applyField = ( fieldName , value , emptyValue = '' ) => {
320+ const hasValue = Array . isArray ( value )
321+ ? value . length > 0
322+ : value !== undefined && value !== null && value !== ''
323+ if ( hasValue ) {
324+ formControl . setValue ( fieldName , value , { shouldDirty : true } )
325+ } else if ( isSwitch ) {
326+ formControl . setValue ( fieldName , emptyValue , { shouldDirty : true } )
350327 }
328+ }
351329
352- // Handle groups from template
353- const templateGroups = template . addToGroups || template . groupMemberships
354- if ( templateGroups ) {
355- const rawGroups = Array . isArray ( templateGroups ) ? templateGroups : [ templateGroups ]
356- const groups = rawGroups . map ( ( g ) => {
357- if ( g . label && g . value ) return g
358- const groupType = g . groupTypes ?. includes ( 'Unified' )
359- ? 'Microsoft 365'
360- : g . mailEnabled && ! g . groupTypes ?. includes ( 'Unified' )
361- ? g . securityEnabled
362- ? 'Mail-Enabled Security'
363- : 'Distribution list'
364- : 'Security'
365- return {
366- label : g . displayName ,
367- value : g . id ,
368- addedFields : { groupType } ,
369- }
370- } )
371- if ( groups . length > 0 ) {
372- const currentGroups = watchedFields . AddToGroups
373- if ( ! currentGroups || ( Array . isArray ( currentGroups ) && currentGroups . length === 0 ) ) {
374- formControl . setValue ( 'AddToGroups' , groups , { shouldDirty : true } )
375- }
376- }
330+ // Primary domain - accept both object and string formats
331+ const primDomainValue = template . primDomain
332+ ? typeof template . primDomain === 'string'
333+ ? { label : template . primDomain , value : template . primDomain }
334+ : template . primDomain
335+ : null
336+ applyField ( 'primDomain' , primDomainValue , null )
337+
338+ // Usage location - accept both object and string formats
339+ const usageLocationCode =
340+ typeof template . usageLocation === 'string'
341+ ? template . usageLocation
342+ : template . usageLocation ?. value
343+ const country = usageLocationCode
344+ ? countryList . find ( ( c ) => c . Code === usageLocationCode )
345+ : null
346+ applyField (
347+ 'usageLocation' ,
348+ country ? { label : country . Name , value : country . Code } : null ,
349+ null
350+ )
351+
352+ applyField ( 'jobTitle' , template . jobTitle )
353+ applyField ( 'streetAddress' , template . streetAddress )
354+ applyField ( 'city' , template . city )
355+ applyField ( 'state' , template . state )
356+ applyField ( 'postalCode' , template . postalCode )
357+ applyField ( 'country' , template . country )
358+ applyField ( 'companyName' , template . companyName )
359+ applyField ( 'department' , template . department )
360+ applyField ( 'mobilePhone' , template . mobilePhone )
361+
362+ const templateBusinessPhone = Array . isArray ( template . businessPhones )
363+ ? template . businessPhones [ 0 ]
364+ : template . businessPhones
365+ applyField ( 'businessPhones' , templateBusinessPhone ? [ templateBusinessPhone ] : [ ] , [ ] )
366+
367+ // Licenses - match the format expected by CippFormLicenseSelector
368+ applyField (
369+ 'licenses' ,
370+ Array . isArray ( template . licenses ) ? template . licenses : [ ] ,
371+ [ ]
372+ )
373+
374+ // Groups from template
375+ const templateGroups = template . addToGroups || template . groupMemberships
376+ const rawGroups = templateGroups
377+ ? Array . isArray ( templateGroups )
378+ ? templateGroups
379+ : [ templateGroups ]
380+ : [ ]
381+ const groups = rawGroups . map ( ( g ) => {
382+ if ( g . label && g . value ) return g
383+ const groupType = g . groupTypes ?. includes ( 'Unified' )
384+ ? 'Microsoft 365'
385+ : g . mailEnabled && ! g . groupTypes ?. includes ( 'Unified' )
386+ ? g . securityEnabled
387+ ? 'Mail-Enabled Security'
388+ : 'Distribution list'
389+ : 'Security'
390+ return {
391+ label : g . displayName ,
392+ value : g . id ,
393+ addedFields : { groupType } ,
377394 }
395+ } )
396+ applyField ( 'AddToGroups' , groups , [ ] )
378397
379- // Populate custom user attributes from template
380- if ( template . defaultAttributes ) {
381- Object . entries ( template . defaultAttributes ) . forEach ( ( [ key , attr ] ) => {
382- if ( attr ?. Value ) {
383- setFieldIfEmpty ( `defaultAttributes.${ key } .Value` , attr . Value )
384- }
398+ // Custom user attributes. On a switch, clear every known attribute field
399+ // first so attributes the new template doesn't define don't linger, then
400+ // apply the template's values.
401+ if ( isSwitch ) {
402+ userSettingsDefaults ?. userAttributes
403+ ?. filter ( ( attribute ) => attribute . value !== 'sponsor' )
404+ . forEach ( ( attribute ) => {
405+ formControl . setValue ( `defaultAttributes.${ attribute . label } .Value` , '' , {
406+ shouldDirty : true ,
407+ } )
385408 } )
386- }
409+ }
410+ if ( template . defaultAttributes ) {
411+ Object . entries ( template . defaultAttributes ) . forEach ( ( [ key , attr ] ) => {
412+ applyField ( `defaultAttributes.${ key } .Value` , attr ?. Value )
413+ } )
387414 }
388415 } , [ watchedFields . userTemplate , formType ] )
389416
0 commit comments