@@ -59,6 +59,9 @@ export class ResultsBuilder {
5959 private static successStatus = [ 'Ready for migration' , 'Complete' , 'Successfully migrated' ] ;
6060 private static errorStatus = [ 'Failed' , 'Needs manual intervention' ] ;
6161
62+ /** Set at report generation start; used to show "Manual deployment needed" when deployment failed */
63+ private static deploymentFailed = false ;
64+
6265 public static async generateReport (
6366 results : MigratedObject [ ] ,
6467 relatedObjectMigrationResult : RelatedObjectAssesmentInfo ,
@@ -67,8 +70,11 @@ export class ResultsBuilder {
6770 messages : Messages < string > ,
6871 actionItems : string [ ] ,
6972 objectsToProcess : string [ ] ,
70- migrateOnly : string
73+ migrateOnly : string ,
74+ deploymentFailed = false
7175 ) : Promise < void > {
76+ ResultsBuilder . deploymentFailed = deploymentFailed ;
77+
7278 fs . mkdirSync ( resultsDir , { recursive : true } ) ;
7379 Logger . info ( messages . getMessage ( 'generatingComponentReports' ) ) ;
7480 for ( const result of results ) {
@@ -382,7 +388,9 @@ export class ResultsBuilder {
382388 total : result . length ,
383389 filterGroups : [
384390 ...this . getStatusFilterGroup (
385- result . flatMap ( ( item ) => item . experienceSiteAssessmentPageInfos . map ( ( page ) => page . status ) )
391+ result . flatMap ( ( item ) =>
392+ item . experienceSiteAssessmentPageInfos . map ( ( page ) => this . resolveDisplayStatus ( page . status , messages ) )
393+ )
386394 ) ,
387395 ] ,
388396 headerGroups : [
@@ -421,7 +429,7 @@ export class ResultsBuilder {
421429 ] ,
422430 } ,
423431 ] ,
424- rows : this . getRowsForExperienceSites ( result ) ,
432+ rows : this . getRowsForExperienceSites ( result , messages ) ,
425433 props : JSON . stringify ( {
426434 recordName : 'Pages' ,
427435 rowBased : true ,
@@ -453,7 +461,9 @@ export class ResultsBuilder {
453461 } ,
454462 assessmentDate : new Date ( ) . toLocaleString ( ) ,
455463 total : result . length ,
456- filterGroups : [ ...this . getStatusFilterGroup ( result . map ( ( item ) => item . status ) ) ] ,
464+ filterGroups : [
465+ ...this . getStatusFilterGroup ( result . map ( ( item ) => this . resolveDisplayStatus ( item . status , messages ) ) ) ,
466+ ] ,
457467 headerGroups : [
458468 {
459469 header : [
@@ -499,14 +509,14 @@ export class ResultsBuilder {
499509 createRowDataParam ( 'path' , item . name , false , 1 , 1 , true , item . path ) ,
500510 createRowDataParam (
501511 'status' ,
502- item . status ,
512+ this . resolveDisplayStatus ( item . status , messages ) ,
503513 false ,
504514 1 ,
505515 1 ,
506516 false ,
507517 undefined ,
508518 undefined ,
509- item . status === 'Successfully migrated' ? 'text-success' : 'text-error'
519+ this . resolveStatusCssClass ( item . status )
510520 ) ,
511521 createRowDataParam (
512522 'diff' ,
@@ -658,7 +668,11 @@ export class ResultsBuilder {
658668 } ,
659669 assessmentDate : new Date ( ) . toLocaleString ( ) ,
660670 total : result . length ,
661- filterGroups : [ ...this . getStatusFilterGroup ( result . flatMap ( ( item ) => this . getStatusFromErrors ( item . errors ) ) ) ] ,
671+ filterGroups : [
672+ ...this . getStatusFilterGroup (
673+ result . flatMap ( ( item ) => this . resolveDisplayStatus ( this . getStatusFromErrors ( item . errors ) , messages ) )
674+ ) ,
675+ ] ,
662676 headerGroups : [
663677 {
664678 header : [
@@ -695,21 +709,27 @@ export class ResultsBuilder {
695709 ] ,
696710 } ,
697711 ] ,
698- rows : this . getLwcRowsForReport ( result ) ,
712+ rows : this . getLwcRowsForReport ( result , messages ) ,
699713 } ;
700714
701715 const reportTemplate = fs . readFileSync ( reportTemplateFilePath , 'utf8' ) ;
702716 const html = TemplateParser . generate ( reportTemplate , data , messages ) ;
703717 fs . writeFileSync ( path . join ( resultsDir , lwcFileName ) , html ) ;
704718 }
705719
706- private static getLwcRowsForReport ( lwcAssessmentInfos : LWCAssessmentInfo [ ] ) : ReportRowParam [ ] {
720+ private static getLwcRowsForReport (
721+ lwcAssessmentInfos : LWCAssessmentInfo [ ] ,
722+ messages : Messages < string >
723+ ) : ReportRowParam [ ] {
707724 const rows : ReportRowParam [ ] = [ ] ;
708725
709726 for ( const lwcAssessmentInfo of lwcAssessmentInfos ) {
710727 let showCommon = true ;
711728 const rid = `${ this . rowClass } ${ this . rowId ++ } ` ;
712729 const commonRowSpan = Math . max ( 1 , lwcAssessmentInfo . changeInfos . length ) ;
730+ const actualStatus = this . getStatusFromErrors ( lwcAssessmentInfo . errors ) ;
731+ const displayStatus = this . resolveDisplayStatus ( actualStatus , messages ) ;
732+ const statusCssClass = this . resolveStatusCssClass ( actualStatus ) ;
713733 for ( const fileChangeInfo of lwcAssessmentInfo . changeInfos ) {
714734 rows . push ( {
715735 rowId : rid ,
@@ -719,14 +739,14 @@ export class ResultsBuilder {
719739 createRowDataParam ( 'name' , lwcAssessmentInfo . name , true , commonRowSpan , 1 , false ) ,
720740 createRowDataParam (
721741 'status' ,
722- this . getStatusFromErrors ( lwcAssessmentInfo . errors ) ,
742+ displayStatus ,
723743 false ,
724744 commonRowSpan ,
725745 1 ,
726746 false ,
727747 undefined ,
728748 undefined ,
729- this . getStatusCssClass ( lwcAssessmentInfo . errors )
749+ statusCssClass
730750 ) ,
731751 ]
732752 : [ ] ) ,
@@ -1021,57 +1041,66 @@ export class ResultsBuilder {
10211041 ] ;
10221042 }
10231043
1024- private static getDifferentStatusDataForFlexipage ( data : FlexiPageAssessmentInfo [ ] ) : SummaryItemDetailParam [ ] {
1044+ private static buildStatusSummary ( counts : {
1045+ completed : number ;
1046+ manualDeploymentNeeded : number ;
1047+ skipped : number ;
1048+ failed : number ;
1049+ } ) : SummaryItemDetailParam [ ] {
1050+ const result : SummaryItemDetailParam [ ] = [
1051+ { name : 'Successfully migrated' , count : counts . completed , cssClass : 'text-success' } ,
1052+ ] ;
1053+ if ( counts . manualDeploymentNeeded > 0 ) {
1054+ result . push ( {
1055+ name : 'Manual deployment needed' ,
1056+ count : counts . manualDeploymentNeeded ,
1057+ cssClass : 'text-error' ,
1058+ } ) ;
1059+ }
1060+ result . push ( { name : 'Skipped' , count : counts . skipped , cssClass : 'text-error' } ) ;
1061+ result . push ( { name : 'Failed' , count : counts . failed , cssClass : 'text-error' } ) ;
1062+ return result ;
1063+ }
1064+
1065+ private static countStatusesFromItems ( statuses : string [ ] ) : {
1066+ completed : number ;
1067+ manualDeploymentNeeded : number ;
1068+ skipped : number ;
1069+ failed : number ;
1070+ } {
10251071 let completed = 0 ;
1072+ let manualDeploymentNeeded = 0 ;
10261073 let skipped = 0 ;
10271074 let failed = 0 ;
1028- data . forEach ( ( item ) => {
1029- if ( item . status === 'Successfully migrated' ) completed ++ ;
1030- else if ( item . status === 'Skipped' ) skipped ++ ;
1031- else failed ++ ;
1032- } ) ;
1075+ for ( const status of statuses ) {
1076+ if ( this . isManualDeploymentNeeded ( status ) ) {
1077+ manualDeploymentNeeded ++ ;
1078+ } else if ( status === 'Successfully migrated' ) {
1079+ completed ++ ;
1080+ } else if ( status === 'Skipped' ) {
1081+ skipped ++ ;
1082+ } else {
1083+ failed ++ ;
1084+ }
1085+ }
1086+ return { completed, manualDeploymentNeeded, skipped, failed } ;
1087+ }
10331088
1034- return [
1035- { name : 'Successfully migrated' , count : completed , cssClass : 'text-success' } ,
1036- { name : 'Skipped' , count : skipped , cssClass : 'text-error' } ,
1037- { name : 'Failed' , count : failed , cssClass : 'text-error' } ,
1038- ] ;
1089+ private static getDifferentStatusDataForFlexipage ( data : FlexiPageAssessmentInfo [ ] ) : SummaryItemDetailParam [ ] {
1090+ return this . buildStatusSummary ( this . countStatusesFromItems ( data . map ( ( item ) => item . status ) ) ) ;
10391091 }
10401092
10411093 private static getDifferentStatusDataForLwc ( data : LWCAssessmentInfo [ ] ) : SummaryItemDetailParam [ ] {
1042- let completed = 0 ;
1043- let failed = 0 ;
1044- data . forEach ( ( item ) => {
1045- if ( this . getStatusFromErrors ( item . errors ) === 'Successfully migrated' ) completed ++ ;
1046- else failed ++ ;
1047- } ) ;
1048-
1049- return [
1050- { name : 'Successfully migrated' , count : completed , cssClass : 'text-success' } ,
1051- { name : 'Skipped' , count : 0 , cssClass : 'text-error' } ,
1052- { name : 'Failed' , count : failed , cssClass : 'text-error' } ,
1053- ] ;
1094+ return this . buildStatusSummary (
1095+ this . countStatusesFromItems ( data . map ( ( item ) => this . getStatusFromErrors ( item . errors ) ) )
1096+ ) ;
10541097 }
10551098
10561099 private static getDifferentStatusDataForExperienceSites (
10571100 data : ExperienceSiteAssessmentInfo [ ]
10581101 ) : SummaryItemDetailParam [ ] {
1059- let completed = 0 ;
1060- let skipped = 0 ;
1061- let failed = 0 ;
1062- data
1063- . flatMap ( ( item ) => item . experienceSiteAssessmentPageInfos )
1064- . forEach ( ( item ) => {
1065- if ( item . status === 'Successfully migrated' ) completed ++ ;
1066- else if ( item . status === 'Skipped' ) skipped ++ ;
1067- else failed ++ ;
1068- } ) ;
1069-
1070- return [
1071- { name : 'Successfully migrated' , count : completed , cssClass : 'text-success' } ,
1072- { name : 'Skipped' , count : skipped , cssClass : 'text-error' } ,
1073- { name : 'Failed' , count : failed , cssClass : 'text-error' } ,
1074- ] ;
1102+ const statuses = data . flatMap ( ( item ) => item . experienceSiteAssessmentPageInfos . map ( ( page ) => page . status ) ) ;
1103+ return this . buildStatusSummary ( this . countStatusesFromItems ( statuses ) ) ;
10751104 }
10761105
10771106 private static getStatusFilterGroup ( statuses : string [ ] ) : FilterGroupParam [ ] {
@@ -1085,13 +1114,26 @@ export class ResultsBuilder {
10851114 return 'Successfully migrated' ;
10861115 }
10871116
1088- private static getStatusCssClass ( errors : string [ ] , neutralSuccess = false ) : string {
1089- if ( errors && errors . length > 0 ) return 'text-error' ;
1090- if ( neutralSuccess ) return '' ;
1091- return 'text-success' ;
1117+ /** True when deployment failed but the component was successfully processed locally. */
1118+ private static isManualDeploymentNeeded ( status : string ) : boolean {
1119+ return this . deploymentFailed && status === 'Successfully migrated' ;
1120+ }
1121+
1122+ /** Returns display status — "Manual deployment needed" when deployment failed, original status otherwise. */
1123+ private static resolveDisplayStatus ( status : string , messages : Messages < string > ) : string {
1124+ return this . isManualDeploymentNeeded ( status ) ? messages . getMessage ( 'manualDeploymentNeeded' ) : status ;
1125+ }
1126+
1127+ /** Returns CSS class — error for manual deployment needed, success/error otherwise. */
1128+ private static resolveStatusCssClass ( status : string ) : string {
1129+ if ( this . isManualDeploymentNeeded ( status ) ) return 'text-error' ;
1130+ return status === 'Successfully migrated' ? 'text-success' : 'text-error' ;
10921131 }
10931132
1094- private static getRowsForExperienceSites ( result : ExperienceSiteAssessmentInfo [ ] ) : ReportRowParam [ ] {
1133+ private static getRowsForExperienceSites (
1134+ result : ExperienceSiteAssessmentInfo [ ] ,
1135+ messages : Messages < string >
1136+ ) : ReportRowParam [ ] {
10951137 const rows : ReportRowParam [ ] = [ ] ;
10961138
10971139 result . forEach ( ( item ) => {
@@ -1100,7 +1142,7 @@ export class ResultsBuilder {
11001142 item . experienceSiteAssessmentPageInfos . forEach ( ( page ) => {
11011143 rows . push ( {
11021144 rowId : rId ,
1103- data : this . getRowDataForExperienceSites ( page , item , showBundleName ) ,
1145+ data : this . getRowDataForExperienceSites ( page , item , showBundleName , messages ) ,
11041146 } ) ;
11051147 showBundleName = false ;
11061148 } ) ;
@@ -1112,8 +1154,11 @@ export class ResultsBuilder {
11121154 private static getRowDataForExperienceSites (
11131155 page : ExperienceSiteAssessmentPageInfo ,
11141156 item : ExperienceSiteAssessmentInfo ,
1115- showBundleName : boolean
1157+ showBundleName : boolean ,
1158+ messages : Messages < string >
11161159 ) : ReportDataParam [ ] {
1160+ const displayStatus = this . resolveDisplayStatus ( page . status , messages ) ;
1161+ const statusCssClass = this . resolveStatusCssClass ( page . status ) ;
11171162 return [
11181163 createRowDataParam (
11191164 'name' ,
@@ -1128,17 +1173,7 @@ export class ResultsBuilder {
11281173 ) ,
11291174 createRowDataParam ( 'pageName' , page . name , false , 1 , 1 , false , undefined , undefined ) ,
11301175 createRowDataParam ( 'path' , page . name + this . experienceSiteFileSuffix , false , 1 , 1 , true , page . path ) ,
1131- createRowDataParam (
1132- 'status' ,
1133- page . status ,
1134- false ,
1135- 1 ,
1136- 1 ,
1137- false ,
1138- undefined ,
1139- undefined ,
1140- page . status === 'Successfully migrated' ? 'text-success' : 'text-error'
1141- ) ,
1176+ createRowDataParam ( 'status' , displayStatus , false , 1 , 1 , false , undefined , undefined , statusCssClass ) ,
11421177 createRowDataParam (
11431178 'diff' ,
11441179 page . name + 'diff' ,
0 commit comments