@@ -3285,6 +3285,76 @@ const formatSetupAssistantSavedAt = (value) => {
32853285 return formatted || raw ;
32863286} ;
32873287
3288+ const toSetupAssistantDisplayText = ( value , fallback = '-' ) => {
3289+ const text = String ( value ?? '' ) . trim ( ) ;
3290+ return text || fallback ;
3291+ } ;
3292+
3293+ const renderSetupAssistantSwalSummaryHtml = ( {
3294+ metaRows = [ ] ,
3295+ detailRows = [ ] ,
3296+ warningLines = [ ] ,
3297+ failureLines = [ ] ,
3298+ footerText = ''
3299+ } = { } ) => {
3300+ const rowDivider = 'border-bottom:1px solid rgba(148, 170, 196, 0.18);' ;
3301+ const metaHtml = metaRows . length
3302+ ? `
3303+ <div style="display:flex;flex-wrap:wrap;gap:6px 8px;margin-bottom:10px;">
3304+ ${ metaRows . map ( ( row ) => `
3305+ <span style="display:inline-flex;align-items:center;padding:3px 8px;border-radius:999px;border:1px solid rgba(148,170,196,0.3);background:rgba(18,28,42,0.72);font-size:12px;line-height:1.2;">
3306+ <strong style="margin-right:5px;">${ escapeHtml ( row . label ) } :</strong>${ escapeHtml ( row . value ) }
3307+ </span>
3308+ ` ) . join ( '' ) }
3309+ </div>
3310+ `
3311+ : '' ;
3312+ const detailHtml = detailRows . length
3313+ ? `
3314+ <div style="border:1px solid rgba(148,170,196,0.28);border-radius:10px;background:rgba(9,16,24,0.74);overflow:hidden;">
3315+ ${ detailRows . map ( ( row , index ) => `
3316+ <div style="display:flex;align-items:flex-start;justify-content:space-between;gap:14px;padding:7px 10px;${ index < detailRows . length - 1 ? rowDivider : '' } ">
3317+ <span style="font-weight:600;color:#d9e5f6;">${ escapeHtml ( row . label ) } </span>
3318+ <span style="text-align:right;color:#eaf2ff;">${ escapeHtml ( row . value ) } </span>
3319+ </div>
3320+ ` ) . join ( '' ) }
3321+ </div>
3322+ `
3323+ : '' ;
3324+ const warningHtml = warningLines . length
3325+ ? `
3326+ <div style="margin-top:10px;padding:8px 10px;border-radius:9px;border:1px solid rgba(255,193,94,0.42);background:rgba(255,193,94,0.1);text-align:left;">
3327+ <div style="font-weight:700;margin-bottom:4px;color:#ffd494;">Warnings</div>
3328+ <ul style="margin:0 0 0 18px;padding:0;line-height:1.35;">
3329+ ${ warningLines . map ( ( line ) => `<li style="margin:2px 0;">${ escapeHtml ( line ) } </li>` ) . join ( '' ) }
3330+ </ul>
3331+ </div>
3332+ `
3333+ : '' ;
3334+ const failureHtml = failureLines . length
3335+ ? `
3336+ <div style="margin-top:10px;padding:8px 10px;border-radius:9px;border:1px solid rgba(255,116,116,0.48);background:rgba(255,116,116,0.1);text-align:left;">
3337+ <div style="font-weight:700;margin-bottom:4px;color:#ffb4b4;">Failed tasks</div>
3338+ <ul style="margin:0 0 0 18px;padding:0;line-height:1.35;">
3339+ ${ failureLines . map ( ( line ) => `<li style="margin:2px 0;">${ escapeHtml ( line ) } </li>` ) . join ( '' ) }
3340+ </ul>
3341+ </div>
3342+ `
3343+ : '' ;
3344+ const footerHtml = footerText
3345+ ? `<div style="margin-top:10px;font-weight:600;color:#d7e6fb;">${ escapeHtml ( footerText ) } </div>`
3346+ : '' ;
3347+ return `
3348+ <div style="max-width:560px;margin:0 auto;text-align:left;line-height:1.35;">
3349+ ${ metaHtml }
3350+ ${ detailHtml }
3351+ ${ warningHtml }
3352+ ${ failureHtml }
3353+ ${ footerHtml }
3354+ </div>
3355+ ` ;
3356+ } ;
3357+
32883358const buildSetupAssistantVerificationReport = ( importOutcomes , templateOutcomes , ruleOutcomes , validationWarnings = [ ] ) => {
32893359 const checks = [ ] ;
32903360 const register = ( label , ok , detail = '' ) => {
@@ -3550,24 +3620,28 @@ const confirmSetupAssistantApply = async (impactSummary, reviewValidation) => (
35503620 const prefsTotal = Number ( impactSummary ?. prefs ?. totalChanges ) || 0 ;
35513621 const rulesTotal = Number ( impactSummary ?. rules ?. creatable ) || 0 ;
35523622 const hasDeletes = Number ( totals . deletes ) > 0 ;
3553- const lines = [
3554- `Route: ${ setupAssistantState . route } ` ,
3555- `Mode: ${ setupAssistantState . mode } ` ,
3556- `Wizard detail: ${ normalizeSetupAssistantExperienceMode ( setupAssistantState . experienceMode ) } ` ,
3557- `Safety mode: ${ normalizeSetupAssistantSafetyMode ( setupAssistantState . applySafetyMode ) } ` ,
3558- `Imports: ${ totals . totalOps } ops (create ${ totals . creates } , update ${ totals . updates } , delete ${ totals . deletes } )` ,
3559- `Starter folders: ${ templateTotals . creatable } create (selected ${ templateTotals . selected } , skip existing ${ templateTotals . skippedExisting } )` ,
3560- `Template auto-assign: ${ templateTotals . autoAssignMatched } matched / ${ templateTotals . autoAssignUnmatched } unmatched` ,
3561- `Settings: ${ prefsTotal } changes` ,
3562- `Starter rules: ${ rulesTotal } ` ,
3563- `Dry run: ${ setupAssistantState . dryRunOnly ? 'ON' : 'OFF' } `
3564- ] ;
3565- if ( reviewValidation ?. warnings ?. length ) {
3566- lines . push ( `Warnings: ${ reviewValidation . warnings . length } ` ) ;
3567- }
3623+ const html = renderSetupAssistantSwalSummaryHtml ( {
3624+ metaRows : [
3625+ { label : 'Route' , value : toSetupAssistantDisplayText ( setupAssistantState . route ) } ,
3626+ { label : 'Mode' , value : toSetupAssistantDisplayText ( setupAssistantState . mode ) } ,
3627+ { label : 'Wizard detail' , value : normalizeSetupAssistantExperienceMode ( setupAssistantState . experienceMode ) } ,
3628+ { label : 'Safety mode' , value : normalizeSetupAssistantSafetyMode ( setupAssistantState . applySafetyMode ) } ,
3629+ { label : 'Dry run' , value : setupAssistantState . dryRunOnly ? 'ON' : 'OFF' }
3630+ ] ,
3631+ detailRows : [
3632+ { label : 'Imports' , value : `${ totals . totalOps } ops (create ${ totals . creates } , update ${ totals . updates } , delete ${ totals . deletes } )` } ,
3633+ { label : 'Starter folders' , value : `${ templateTotals . creatable } create (selected ${ templateTotals . selected } , skip existing ${ templateTotals . skippedExisting } )` } ,
3634+ { label : 'Template auto-assign' , value : `${ templateTotals . autoAssignMatched } matched / ${ templateTotals . autoAssignUnmatched } unmatched` } ,
3635+ { label : 'Settings' , value : `${ prefsTotal } changes` } ,
3636+ { label : 'Starter rules' , value : `${ rulesTotal } ` }
3637+ ] ,
3638+ warningLines : Array . isArray ( reviewValidation ?. warnings ) ? reviewValidation . warnings . slice ( 0 , 5 ) : [ ] ,
3639+ footerText : 'Proceed to apply these changes?'
3640+ } ) ;
35683641 swal ( {
35693642 title : setupAssistantState . dryRunOnly ? 'Run setup dry run?' : 'Apply setup assistant changes?' ,
3570- text : lines . join ( '\n' ) ,
3643+ text : html ,
3644+ html : true ,
35713645 type : hasDeletes && setupAssistantState . dryRunOnly !== true ? 'warning' : 'info' ,
35723646 showCancelButton : true ,
35733647 confirmButtonText : setupAssistantState . dryRunOnly ? 'Run dry run' : 'Apply now' ,
@@ -3923,9 +3997,31 @@ const applySetupAssistantPlan = async () => {
39233997
39243998 if ( applyFailures . length > 0 ) {
39253999 const retryNow = await new Promise ( ( resolve ) => {
4000+ const failureSummaryHtml = renderSetupAssistantSwalSummaryHtml ( {
4001+ metaRows : [
4002+ { label : 'Mode' , value : toSetupAssistantDisplayText ( setupAssistantState . mode ) } ,
4003+ { label : 'Route' , value : toSetupAssistantDisplayText ( setupAssistantState . route ) } ,
4004+ { label : 'Wizard detail' , value : normalizeSetupAssistantExperienceMode ( setupAssistantState . experienceMode ) } ,
4005+ { label : 'Safety mode' , value : safetyMode }
4006+ ] ,
4007+ detailRows : [
4008+ { label : 'Docker imports' , value : `${ importOutcomes . docker } ` } ,
4009+ { label : 'VM imports' , value : `${ importOutcomes . vm } ` } ,
4010+ { label : 'Docker starter folders' , value : `${ templateOutcomes . docker . created } created, ${ templateOutcomes . docker . skippedExisting } skipped, ${ Number ( templateOutcomes . docker . assignment ?. matched ) || 0 } auto-assigned` } ,
4011+ { label : 'VM starter folders' , value : `${ templateOutcomes . vm . created } created, ${ templateOutcomes . vm . skippedExisting } skipped, ${ Number ( templateOutcomes . vm . assignment ?. matched ) || 0 } auto-assigned` } ,
4012+ { label : 'Docker starter rules' , value : `${ ruleOutcomes . docker . created } added` } ,
4013+ { label : 'VM starter rules' , value : `${ ruleOutcomes . vm . created } added` } ,
4014+ { label : 'Verification' , value : `${ verification . passed } /${ verification . total } checks passed` } ,
4015+ { label : 'Retryable failures' , value : `${ applyFailures . length } ` }
4016+ ] ,
4017+ warningLines : validationWarnings . slice ( 0 , 5 ) ,
4018+ failureLines : applyFailures . slice ( 0 , 8 ) . map ( ( entry ) => `${ String ( entry . phase || '' ) . toUpperCase ( ) } ${ String ( entry . type || '' ) . toUpperCase ( ) } : ${ String ( entry . message || '' ) . trim ( ) } ` ) ,
4019+ footerText : 'Retry failed tasks now?'
4020+ } ) ;
39264021 swal ( {
39274022 title : 'Setup applied with partial failures' ,
3928- text : `${ summaryLines . join ( '\n' ) } \n\nRetry failed tasks now?` ,
4023+ text : failureSummaryHtml ,
4024+ html : true ,
39294025 type : 'warning' ,
39304026 showCancelButton : true ,
39314027 confirmButtonText : 'Retry failures' ,
0 commit comments