@@ -946,7 +946,44 @@ export class RetrospectiveBoardPageComponent implements OnInit, OnDestroy {
946946 return ;
947947 }
948948
949- // Show confirmation modal
949+ // Special handling for GROUPING phase - offer AI assistance
950+ if ( phase === RetroPhase . GROUPING && this . currentBoard ?. currentPhase === RetroPhase . BRAINSTORMING ) {
951+ this . modal . confirm ( {
952+ nzTitle : `Switch to ${ this . getPhaseTitle ( phase ) } Phase?` ,
953+ nzContent : `
954+ <div style="margin-bottom: 12px;">
955+ <strong>Switching to Grouping phase will:</strong>
956+ <ul style="margin-top: 8px; padding-left: 20px;">
957+ <li>Disable adding new notes</li>
958+ <li>Allow moving notes between columns</li>
959+ <li>Keep author information hidden</li>
960+ </ul>
961+ </div>
962+ <div style="margin-top: 16px; padding: 12px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 8px; color: white;">
963+ <div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
964+ <span style="font-size: 18px;">💡</span>
965+ <strong>AI-Powered Grouping Available!</strong>
966+ </div>
967+ <p style="margin: 0; font-size: 13px; opacity: 0.95;">
968+ Would you like AI to automatically analyze and group similar notes with tags?
969+ </p>
970+ </div>
971+ ` ,
972+ nzOkText : '✨ Use AI Grouping' ,
973+ nzOkType : 'primary' ,
974+ nzCancelText : 'Switch Without AI' ,
975+ nzOnOk : ( ) => {
976+ this . retrospectiveService . updatePhase ( phase ) ;
977+ setTimeout ( ( ) => this . performAIGrouping ( ) , 500 ) ;
978+ } ,
979+ nzOnCancel : ( ) => {
980+ this . retrospectiveService . updatePhase ( phase ) ;
981+ }
982+ } ) ;
983+ return ;
984+ }
985+
986+ // Show confirmation modal for other phases
950987 this . modal . confirm ( {
951988 nzTitle : `Switch to ${ this . getPhaseTitle ( phase ) } Phase?` ,
952989 nzContent : this . getPhaseChangeWarning ( phase ) ,
@@ -1170,4 +1207,161 @@ export class RetrospectiveBoardPageComponent implements OnInit, OnDestroy {
11701207 this . isPhaseModalVisible = false ;
11711208 this . selectedPhase = this . currentBoard ?. currentPhase || RetroPhase . BRAINSTORMING ;
11721209 }
1210+
1211+ // AI Grouping functionality
1212+ performAIGrouping ( ) {
1213+ if ( ! this . currentBoard ) return ;
1214+
1215+ const notificationKey = `ai-grouping-${ Date . now ( ) } ` ;
1216+
1217+ // Show loading notification
1218+ this . modal . info ( {
1219+ nzTitle : '🤖 AI Grouping in Progress' ,
1220+ nzContent : `
1221+ <div style="text-align: center; padding: 20px;">
1222+ <div style="font-size: 48px; margin-bottom: 16px;">
1223+ <span style="display: inline-block; animation: spin 2s linear infinite;">🔄</span>
1224+ </div>
1225+ <p style="font-size: 14px; color: #6b7280;">
1226+ Analyzing ${ this . currentBoard . stickyNotes . length } notes across ${ this . currentBoard . columns . length } columns...
1227+ </p>
1228+ <p style="font-size: 13px; color: #9ca3af; margin-top: 8px;">
1229+ This may take a few moments
1230+ </p>
1231+ <style>
1232+ @keyframes spin {
1233+ from { transform: rotate(0deg); }
1234+ to { transform: rotate(360deg); }
1235+ }
1236+ </style>
1237+ </div>
1238+ ` ,
1239+ nzOkText : 'Working...' ,
1240+ nzOkDisabled : true ,
1241+ nzClosable : false ,
1242+ nzMaskClosable : false
1243+ } ) ;
1244+
1245+ // Simulate AI processing (in production, this would call an AI service)
1246+ setTimeout ( ( ) => {
1247+ const groupedNotes = this . analyzeAndGroupNotes ( ) ;
1248+
1249+ // Apply the grouping
1250+ groupedNotes . forEach ( noteUpdate => {
1251+ this . retrospectiveService . updateStickyNote ( noteUpdate . noteId , {
1252+ tags : noteUpdate . tags ,
1253+ groupId : noteUpdate . groupId
1254+ } ) ;
1255+ } ) ;
1256+
1257+ // Close loading modal and show success
1258+ this . modal . closeAll ( ) ;
1259+
1260+ setTimeout ( ( ) => {
1261+ this . modal . success ( {
1262+ nzTitle : '✨ AI Grouping Complete!' ,
1263+ nzContent : `
1264+ <div style="padding: 12px;">
1265+ <p style="margin-bottom: 12px;">Successfully analyzed and grouped ${ this . currentBoard ?. stickyNotes . length } notes.</p>
1266+ <div style="background: #f3f4f6; padding: 12px; border-radius: 8px; margin-top: 12px;">
1267+ <strong style="display: block; margin-bottom: 8px;">Groups Identified:</strong>
1268+ <ul style="margin: 0; padding-left: 20px; font-size: 13px;">
1269+ ${ this . getUniqueGroups ( ) . map ( group => `<li>${ group } </li>` ) . join ( '' ) }
1270+ </ul>
1271+ </div>
1272+ <p style="margin-top: 12px; font-size: 13px; color: #6b7280;">
1273+ Notes have been tagged and can now be easily grouped by dragging them together.
1274+ </p>
1275+ </div>
1276+ ` ,
1277+ nzOkText : 'Great!' ,
1278+ nzWidth : 500
1279+ } ) ;
1280+ } , 100 ) ;
1281+ } , 2500 ) ; // Simulate AI processing time
1282+ }
1283+
1284+ private analyzeAndGroupNotes ( ) : Array < { noteId : string ; tags : string [ ] ; groupId : string } > {
1285+ if ( ! this . currentBoard ) return [ ] ;
1286+
1287+ const noteUpdates : Array < { noteId : string ; tags : string [ ] ; groupId : string } > = [ ] ;
1288+
1289+ // Group notes by column first
1290+ const notesByColumn : { [ columnId : string ] : StickyNote [ ] } = { } ;
1291+ this . currentBoard . stickyNotes . forEach ( note => {
1292+ if ( ! notesByColumn [ note . columnId ] ) {
1293+ notesByColumn [ note . columnId ] = [ ] ;
1294+ }
1295+ notesByColumn [ note . columnId ] . push ( note ) ;
1296+ } ) ;
1297+
1298+ // Analyze each column separately
1299+ Object . entries ( notesByColumn ) . forEach ( ( [ columnId , notes ] ) => {
1300+ const columnGroups = this . identifyGroupsInColumn ( notes , columnId ) ;
1301+ noteUpdates . push ( ...columnGroups ) ;
1302+ } ) ;
1303+
1304+ return noteUpdates ;
1305+ }
1306+
1307+ private identifyGroupsInColumn ( notes : StickyNote [ ] , columnId : string ) : Array < { noteId : string ; tags : string [ ] ; groupId : string } > {
1308+ // Simple keyword-based grouping (in production, use NLP/AI service)
1309+ const keywords = {
1310+ 'Communication' : [ 'communication' , 'talk' , 'discuss' , 'meeting' , 'sync' , 'share' , 'update' , 'inform' ] ,
1311+ 'Process' : [ 'process' , 'workflow' , 'procedure' , 'system' , 'method' , 'approach' , 'way' ] ,
1312+ 'Technical' : [ 'code' , 'bug' , 'technical' , 'deploy' , 'build' , 'test' , 'review' , 'refactor' , 'architecture' ] ,
1313+ 'Team' : [ 'team' , 'collaboration' , 'together' , 'help' , 'support' , 'pair' , 'cooperation' ] ,
1314+ 'Documentation' : [ 'document' , 'docs' , 'documentation' , 'wiki' , 'readme' , 'guide' , 'manual' ] ,
1315+ 'Time' : [ 'time' , 'deadline' , 'schedule' , 'late' , 'early' , 'duration' , 'speed' , 'fast' , 'slow' ] ,
1316+ 'Quality' : [ 'quality' , 'improvement' , 'better' , 'improve' , 'enhance' , 'optimize' , 'excellent' ] ,
1317+ 'Planning' : [ 'plan' , 'planning' , 'estimate' , 'forecast' , 'strategy' , 'goal' , 'objective' ] ,
1318+ 'Tools' : [ 'tool' , 'platform' , 'software' , 'application' , 'service' , 'framework' , 'library' ] ,
1319+ 'Blocker' : [ 'blocker' , 'blocked' , 'issue' , 'problem' , 'obstacle' , 'challenge' , 'difficulty' ]
1320+ } ;
1321+
1322+ const noteUpdates : Array < { noteId : string ; tags : string [ ] ; groupId : string } > = [ ] ;
1323+
1324+ notes . forEach ( note => {
1325+ const content = note . content . toLowerCase ( ) ;
1326+ const matchedTags : string [ ] = [ ] ;
1327+
1328+ // Find matching keywords
1329+ Object . entries ( keywords ) . forEach ( ( [ category , words ] ) => {
1330+ const hasMatch = words . some ( word => content . includes ( word . toLowerCase ( ) ) ) ;
1331+ if ( hasMatch ) {
1332+ matchedTags . push ( category ) ;
1333+ }
1334+ } ) ;
1335+
1336+ // If no tags matched, assign a default tag
1337+ if ( matchedTags . length === 0 ) {
1338+ matchedTags . push ( 'General' ) ;
1339+ }
1340+
1341+ // Use the primary tag as the group ID
1342+ const primaryTag = matchedTags [ 0 ] ;
1343+ const groupId = `${ columnId } -${ primaryTag . toLowerCase ( ) . replace ( / \s + / g, '-' ) } ` ;
1344+
1345+ noteUpdates . push ( {
1346+ noteId : note . id ,
1347+ tags : matchedTags . slice ( 0 , 3 ) , // Limit to 3 tags per note
1348+ groupId : groupId
1349+ } ) ;
1350+ } ) ;
1351+
1352+ return noteUpdates ;
1353+ }
1354+
1355+ private getUniqueGroups ( ) : string [ ] {
1356+ if ( ! this . currentBoard ) return [ ] ;
1357+
1358+ const groups = new Set < string > ( ) ;
1359+ this . currentBoard . stickyNotes . forEach ( note => {
1360+ if ( note . tags && note . tags . length > 0 ) {
1361+ note . tags . forEach ( tag => groups . add ( tag ) ) ;
1362+ }
1363+ } ) ;
1364+
1365+ return Array . from ( groups ) . sort ( ) ;
1366+ }
11731367}
0 commit comments