@silvestv/migration-planificator 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/LICENSE +96 -0
  2. package/README.fr.md +359 -0
  3. package/README.md +360 -0
  4. package/SECURITY.md +187 -0
  5. package/dist/client.bundle.js +357 -0
  6. package/dist/src/core/app-analyzer.js +134 -0
  7. package/dist/src/core/ast/matchers/html/html-attribute-matcher.js +86 -0
  8. package/dist/src/core/ast/matchers/html/html-component-matcher.js +40 -0
  9. package/dist/src/core/ast/matchers/html/html-element-matcher.js +54 -0
  10. package/dist/src/core/ast/matchers/html/html-parser.js +58 -0
  11. package/dist/src/core/ast/matchers/html/html-pipe-matcher.js +95 -0
  12. package/dist/src/core/ast/matchers/html/html-text-matcher.js +53 -0
  13. package/dist/src/core/ast/matchers/html/index.js +118 -0
  14. package/dist/src/core/ast/matchers/index.js +377 -0
  15. package/dist/src/core/ast/matchers/ts/collection-matcher.js +51 -0
  16. package/dist/src/core/ast/matchers/ts/context-matcher.js +275 -0
  17. package/dist/src/core/ast/matchers/ts/decorator-matcher.js +465 -0
  18. package/dist/src/core/ast/matchers/ts/expression-matcher.js +237 -0
  19. package/dist/src/core/ast/matchers/ts/file-matcher.js +97 -0
  20. package/dist/src/core/ast/matchers/ts/hierarchy-matcher.js +172 -0
  21. package/dist/src/core/ast/matchers/ts/import-matcher.js +39 -0
  22. package/dist/src/core/ast/matchers/ts/index.js +53 -0
  23. package/dist/src/core/ast/matchers/ts/node-matcher.js +156 -0
  24. package/dist/src/core/ast/matchers/ts/symbol-matcher.js +281 -0
  25. package/dist/src/core/ast/matchers/ts/type-matcher.js +207 -0
  26. package/dist/src/core/ast/matchers/utils/matcher-helpers.js +37 -0
  27. package/dist/src/core/ast/scanner-ast.js +444 -0
  28. package/dist/src/core/project-detector.js +196 -0
  29. package/dist/src/core/project-strategy/index.js +9 -0
  30. package/dist/src/core/project-strategy/nx-strategy.js +130 -0
  31. package/dist/src/core/project-strategy/project-strategy.interface.js +2 -0
  32. package/dist/src/core/project-strategy/standalone-strategy.js +74 -0
  33. package/dist/src/core/project-strategy/strategy-factory.js +15 -0
  34. package/dist/src/core/rules-loader.js +89 -0
  35. package/dist/src/core/scan-reporter.js +316 -0
  36. package/dist/src/core/scanner-delta.js +339 -0
  37. package/dist/src/core/scanner-orchestrator.js +266 -0
  38. package/dist/src/core/scanner-regex.js +298 -0
  39. package/dist/src/core/workload/calculator.js +82 -0
  40. package/dist/src/core/workload/constants.js +15 -0
  41. package/dist/src/core/workload/grouping.js +18 -0
  42. package/dist/src/core/workload/hierarchy-calculator.js +127 -0
  43. package/dist/src/core/workload/index.js +11 -0
  44. package/dist/src/core/workload/metadata.js +20 -0
  45. package/dist/src/core/workload/special-workload.js +101 -0
  46. package/dist/src/core/workload/target-resolver.js +34 -0
  47. package/dist/src/data/angular-migration-rules.json +2337 -0
  48. package/dist/src/data/markdown/angular-migration-17-18.md +408 -0
  49. package/dist/src/data/markdown/angular-migration-18-19.md +600 -0
  50. package/dist/src/data/markdown/angular-migration-19-20.md +521 -0
  51. package/dist/src/data/rules/rearchitecture/rearchitecture-rules.json +66 -0
  52. package/dist/src/data/rules/to18/rules-18-obligatoire.json +374 -0
  53. package/dist/src/data/rules/to18/rules-18-optionnelle.json +188 -0
  54. package/dist/src/data/rules/to18/rules-18-recommande.json +218 -0
  55. package/dist/src/data/rules/to19/rules-19-obligatoire.json +348 -0
  56. package/dist/src/data/rules/to19/rules-19-optionnelle.json +223 -0
  57. package/dist/src/data/rules/to19/rules-19-recommande.json +200 -0
  58. package/dist/src/data/rules/to20/rules-20-obligatoire.json +556 -0
  59. package/dist/src/data/rules/to20/rules-20-optionnelle.json +190 -0
  60. package/dist/src/data/rules/to20/rules-20-recommande.json +151 -0
  61. package/dist/src/index.js +161 -0
  62. package/dist/src/models/chip-config.js +45 -0
  63. package/dist/src/models/interfaces/app-details.interface.js +2 -0
  64. package/dist/src/models/interfaces/ast-interfaces.js +5 -0
  65. package/dist/src/models/interfaces/ast-pattern.interface.js +2 -0
  66. package/dist/src/models/interfaces/client-interfaces.js +6 -0
  67. package/dist/src/models/interfaces/detection-stats.interface.js +2 -0
  68. package/dist/src/models/interfaces/html-match.interface.js +2 -0
  69. package/dist/src/models/interfaces/html-report-data.interface.js +2 -0
  70. package/dist/src/models/interfaces/lib-details.interface.js +2 -0
  71. package/dist/src/models/interfaces/migration-rules.interface.js +2 -0
  72. package/dist/src/models/interfaces/parsed-args.interface.js +2 -0
  73. package/dist/src/models/interfaces/project-info.interface.js +2 -0
  74. package/dist/src/models/interfaces/project-overview-data.interface.js +2 -0
  75. package/dist/src/models/interfaces/rule-match.interface.js +2 -0
  76. package/dist/src/models/interfaces/rule.interface.js +2 -0
  77. package/dist/src/models/interfaces/rules-by-priority.interface.js +2 -0
  78. package/dist/src/models/interfaces/scanner-comparison.interface.js +2 -0
  79. package/dist/src/models/interfaces/special-workload.interface.js +2 -0
  80. package/dist/src/models/interfaces/workload-report.interface.js +2 -0
  81. package/dist/src/models/types/build-block-blob.type.js +2 -0
  82. package/dist/src/models/types/migration-version.type.js +2 -0
  83. package/dist/src/models/types/project-type.type.js +2 -0
  84. package/dist/src/models/types/risk-level.type.js +2 -0
  85. package/dist/src/models/types/rule-category.type.js +2 -0
  86. package/dist/src/models/types/rule-priority.type.js +2 -0
  87. package/dist/src/models/types/rule-workload-type.type.js +2 -0
  88. package/dist/src/templates/landing/applications-analyzed.template.js +18 -0
  89. package/dist/src/templates/landing/card-app-info.template.js +63 -0
  90. package/dist/src/templates/landing/card-lib-info.template.js +67 -0
  91. package/dist/src/templates/landing/libs-analyzed.template.js +22 -0
  92. package/dist/src/templates/landing/nx-summary.template.js +115 -0
  93. package/dist/src/templates/landing/project-overview.template.js +27 -0
  94. package/dist/src/templates/page/index-page.template.js +95 -0
  95. package/dist/src/templates/page/main.template.js +83 -0
  96. package/dist/src/templates/page/migration-guide.template.js +175 -0
  97. package/dist/src/templates/page/workload-report.template.js +53 -0
  98. package/dist/src/templates/workload/dashboard.template.js +184 -0
  99. package/dist/src/templates/workload/filters-panel.template.js +215 -0
  100. package/dist/src/templates/workload/guide-rule-card.template.js +107 -0
  101. package/dist/src/templates/workload/hierarchy-nx.template.js +104 -0
  102. package/dist/src/templates/workload/hierarchy-shared.js +163 -0
  103. package/dist/src/templates/workload/hierarchy-standalone.template.js +36 -0
  104. package/dist/src/templates/workload/hierarchy.template.js +35 -0
  105. package/dist/src/templates/workload/rule-modal.template.js +280 -0
  106. package/dist/src/utils/core/args-parser.js +123 -0
  107. package/dist/src/utils/core/array-helpers.js +18 -0
  108. package/dist/src/utils/core/ast-helpers.js +99 -0
  109. package/dist/src/utils/core/file-helpers.js +109 -0
  110. package/dist/src/utils/core/html-helpers.js +36 -0
  111. package/dist/src/utils/core/index.js +28 -0
  112. package/dist/src/utils/core/logger.js +38 -0
  113. package/dist/src/utils/core/rule-helpers.js +15 -0
  114. package/dist/src/utils/core/workload-formatter.js +6 -0
  115. package/dist/src/utils/shared/array-helpers.js +25 -0
  116. package/dist/src/utils/shared/date-helpers.js +109 -0
  117. package/dist/src/utils/shared/html-helpers.js +37 -0
  118. package/dist/src/utils/shared/index.js +25 -0
  119. package/dist/src/utils/shared/rule-helpers.js +20 -0
  120. package/dist/src/utils/shared/time-formatters.js +76 -0
  121. package/dist/styles.css +2 -0
  122. package/package.json +107 -0
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ /**
3
+ * Template pour cards de règles dans le Migration Guide
4
+ * Réutilise les sections de rule-modal.template.ts (principe DRY)
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.renderGuideRuleCard = renderGuideRuleCard;
8
+ exports.renderGuideRuleCards = renderGuideRuleCards;
9
+ const shared_1 = require("../../utils/shared");
10
+ const rule_modal_template_1 = require("./rule-modal.template");
11
+ /**
12
+ * Retourne la couleur du badge selon le niveau de risque
13
+ */
14
+ function getRiskColor(riskLevel) {
15
+ const colorMap = {
16
+ critical: 'red',
17
+ high: 'orange',
18
+ medium: 'yellow',
19
+ low: 'green'
20
+ };
21
+ return colorMap[riskLevel] || 'gray';
22
+ }
23
+ /**
24
+ * Génère une card de règle pour le guide
25
+ */
26
+ function renderGuideRuleCard(rule, matches) {
27
+ const cardId = `guide-card-${rule.key}`;
28
+ const riskColor = getRiskColor(rule.risk_level);
29
+ return `
30
+ <div id="${cardId}"
31
+ class="bg-white border-2 border-gray-200 rounded-xl shadow-lg mb-6 overflow-hidden"
32
+ data-rule-card
33
+ data-rule-key="${rule.key}"
34
+ data-migration="${getMigrationFromRuleKey(rule.key)}">
35
+
36
+ <!-- Header avec checkbox et badges -->
37
+ <div class="bg-gradient-to-r from-${riskColor}-50 to-${riskColor}-100 p-4 border-b border-gray-200">
38
+ <div class="flex items-start justify-between">
39
+ <div class="flex items-start space-x-3 flex-1">
40
+ <!-- Checkbox completion -->
41
+ <input
42
+ type="checkbox"
43
+ data-rule-checkbox="${rule.key}"
44
+ class="mt-1 w-5 h-5 text-green-600 rounded focus:ring-2 focus:ring-green-500 cursor-pointer"
45
+ title="Marquer comme complété"
46
+ />
47
+
48
+ <div class="flex-1">
49
+ <h3 class="text-xl font-bold text-gray-800 mb-2">${(0, shared_1.escapeHtml)(rule.summary)}</h3>
50
+ <div class="flex items-center flex-wrap gap-2">
51
+ ${(0, shared_1.renderBadge)(rule.risk_level.toUpperCase(), riskColor)}
52
+ ${(0, shared_1.renderBadge)(rule.category, 'blue')}
53
+ ${rule.auto_fixable ? (0, shared_1.renderBadge)('Auto-fixable', 'green') : ''}
54
+ </div>
55
+ </div>
56
+ </div>
57
+
58
+ <!-- Bouton collapse -->
59
+ <button
60
+ data-collapse-trigger="${cardId}"
61
+ class="ml-4 text-2xl font-bold text-gray-600 hover:text-gray-800 transition-colors"
62
+ title="Réduire/Développer"
63
+ >
64
+ <span data-collapse-icon>▶</span>
65
+ </button>
66
+ </div>
67
+ </div>
68
+
69
+ <!-- Contenu de la card (collapsible) - REPLIÉ PAR DÉFAUT -->
70
+ <div data-collapse-content class="hidden p-6 space-y-6">
71
+ ${(0, rule_modal_template_1.renderDescriptionSection)(rule)}
72
+ ${(0, rule_modal_template_1.renderCodeExamplesSection)(rule)}
73
+ ${(0, rule_modal_template_1.renderMigrationCommandSection)(rule)}
74
+ ${(0, rule_modal_template_1.renderDocumentationSection)(rule)}
75
+ ${(0, rule_modal_template_1.renderAffectedFilesSection)(matches)}
76
+ ${(0, rule_modal_template_1.renderMetadataSection)(rule)}
77
+ </div>
78
+ </div>
79
+ `;
80
+ }
81
+ /**
82
+ * Extrait la migration (to18, to19, to20) depuis la clé de règle
83
+ * Ex: "angular_18_environment" -> "to18"
84
+ */
85
+ function getMigrationFromRuleKey(ruleKey) {
86
+ if (ruleKey.includes('_18_'))
87
+ return 'to18';
88
+ if (ruleKey.includes('_19_'))
89
+ return 'to19';
90
+ if (ruleKey.includes('_20_'))
91
+ return 'to20';
92
+ return 'unknown';
93
+ }
94
+ /**
95
+ * Génère toutes les cards pour un ensemble de règles
96
+ */
97
+ function renderGuideRuleCards(rules, matches) {
98
+ // Grouper matches par ruleKey
99
+ const matchesByRule = new Map();
100
+ matches.forEach(match => {
101
+ if (!matchesByRule.has(match.ruleKey)) {
102
+ matchesByRule.set(match.ruleKey, []);
103
+ }
104
+ matchesByRule.get(match.ruleKey).push(match);
105
+ });
106
+ return rules.map(rule => renderGuideRuleCard(rule, matchesByRule.get(rule.key) || [])).join('');
107
+ }
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ /**
3
+ * Template spécifique pour vue hiérarchique Nx Monorepo
4
+ * Structure: Monorepo > (Routines | Apps | Libs) > Migrations > Priorities > Rules
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.renderNxMonorepoView = renderNxMonorepoView;
8
+ const core_1 = require("../../utils/core");
9
+ const hierarchy_shared_1 = require("./hierarchy-shared");
10
+ /**
11
+ * Vue pour Monorepo Nx : Affiche Monorepo > (Routines | Apps | Libs)
12
+ */
13
+ function renderNxMonorepoView(workload) {
14
+ // Séparer monorepo (routines) et apps/libs
15
+ const monorepoTarget = workload.targets.find(t => t.targetType === 'monorepo');
16
+ const apps = workload.targets.filter(t => t.targetType === 'app');
17
+ const libs = workload.targets.filter(t => t.targetType === 'lib');
18
+ // Calculer le temps total global (monorepo + apps + libs)
19
+ const globalTotalTime = (0, core_1.calculateTotalTime)(workload.targets);
20
+ const monorepoName = workload.projectInfo.name;
21
+ const sectionId = `monorepo-${monorepoName.replace(/[^a-z0-9]/gi, '-')}`;
22
+ return `
23
+ <div id="${sectionId}" class="mb-6 border-l-4 border-purple-500 pl-4">
24
+ <div class="flex items-center justify-between bg-purple-50 p-4 rounded-lg cursor-pointer" data-collapse-trigger="${sectionId}">
25
+ <div class="flex items-center space-x-3">
26
+ <span data-collapse-icon class="text-xl">▼</span>
27
+ <h3 class="text-xl font-bold text-purple-900">🏗️ ${(0, core_1.escapeHtml)(monorepoName)}</h3>
28
+ ${(0, core_1.renderBadge)('monorepo', 'purple')}
29
+ </div>
30
+ <span class="text-lg font-semibold text-purple-600" data-monorepo-total-time="${monorepoName}">${globalTotalTime}</span>
31
+ </div>
32
+ <div data-collapse-content class="mt-4 space-y-6">
33
+ ${monorepoTarget ? renderRoutinesSection(monorepoTarget, monorepoName) : ''}
34
+ ${apps.length > 0 ? renderTargetGroupSection('Applications', apps, 'blue', '📱', monorepoName) : ''}
35
+ ${libs.length > 0 ? renderTargetGroupSection('Librairies', libs, 'green', '📚', monorepoName) : ''}
36
+ </div>
37
+ </div>
38
+ `;
39
+ }
40
+ /**
41
+ * Section Routines Globales (monorepo)
42
+ */
43
+ function renderRoutinesSection(monorepoTarget, monorepoName) {
44
+ const sectionId = `routines-section`;
45
+ return `
46
+ <div id="${sectionId}" class="ml-6 border-l-4 border-teal-400 pl-4">
47
+ <div class="flex items-center justify-between bg-teal-50 p-3 rounded-lg cursor-pointer" data-collapse-trigger="${sectionId}">
48
+ <div class="flex items-center space-x-2">
49
+ <span data-collapse-icon class="text-lg">▼</span>
50
+ <h4 class="text-lg font-bold text-teal-800">🔧 Routines Globales</h4>
51
+ </div>
52
+ <span class="font-semibold text-teal-600" data-target-time="${monorepoTarget.targetName}">${(0, core_1.formatTime)(monorepoTarget.totalTime.minutes, monorepoTarget.totalTime.hours, monorepoTarget.totalTime.days)}</span>
53
+ </div>
54
+ <div data-collapse-content class="mt-3 space-y-3">
55
+ ${monorepoTarget.migrations.map(migration => (0, hierarchy_shared_1.renderMigration)(monorepoTarget.targetName, migration)).join('')}
56
+ </div>
57
+ </div>
58
+ `;
59
+ }
60
+ /**
61
+ * Section générique pour Apps ou Libs (DRY)
62
+ */
63
+ function renderTargetGroupSection(title, targets, color, icon, monorepoName) {
64
+ const sectionId = `${title.toLowerCase()}-section`;
65
+ const totalTime = (0, core_1.calculateTotalTime)(targets);
66
+ const groupId = `${monorepoName}-${title.toLowerCase()}`;
67
+ return `
68
+ <div id="${sectionId}" class="ml-6 border-l-4 border-${color}-400 pl-4">
69
+ <div class="flex items-center justify-between bg-${color}-50 p-3 rounded-lg cursor-pointer" data-collapse-trigger="${sectionId}">
70
+ <div class="flex items-center space-x-2">
71
+ <span data-collapse-icon class="text-lg">▼</span>
72
+ <h4 class="text-lg font-bold text-${color}-800">${icon} ${title}</h4>
73
+ <span class="text-sm text-gray-600">(${targets.length})</span>
74
+ </div>
75
+ <span class="font-semibold text-${color}-600" data-group-time="${groupId}">${totalTime}</span>
76
+ </div>
77
+ <div data-collapse-content class="mt-3 space-y-4">
78
+ ${targets.map(target => renderSubTarget(target)).join('')}
79
+ </div>
80
+ </div>
81
+ `;
82
+ }
83
+ /**
84
+ * Render Sub-Target (app ou lib dans monorepo)
85
+ */
86
+ function renderSubTarget(target) {
87
+ const sectionId = `subtarget-${target.targetName.replace(/[^a-z0-9]/gi, '-')}`;
88
+ const color = target.targetType === 'app' ? 'blue' : 'green';
89
+ return `
90
+ <div id="${sectionId}" class="ml-6 border-l-2 border-gray-300 pl-4">
91
+ <div class="flex items-center justify-between bg-gray-50 p-3 rounded-lg cursor-pointer" data-collapse-trigger="${sectionId}">
92
+ <div class="flex items-center space-x-2">
93
+ <span data-collapse-icon class="text-base">▼</span>
94
+ <h5 class="text-base font-semibold text-gray-800">${(0, core_1.escapeHtml)(target.targetName)}</h5>
95
+ ${(0, core_1.renderBadge)(target.targetType, color)}
96
+ </div>
97
+ <span class="font-semibold text-gray-600" data-target-time="${target.targetName}">${(0, core_1.formatTime)(target.totalTime.minutes, target.totalTime.hours, target.totalTime.days)}</span>
98
+ </div>
99
+ <div data-collapse-content class="mt-3 space-y-3">
100
+ ${target.migrations.map(migration => (0, hierarchy_shared_1.renderMigration)(target.targetName, migration)).join('')}
101
+ </div>
102
+ </div>
103
+ `;
104
+ }
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ /**
3
+ * Composants partagés pour templates hiérarchiques (Nx & Standalone)
4
+ * Contient les renderers pour: Migration, Priority, Rule
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.renderMigration = renderMigration;
8
+ exports.renderPriority = renderPriority;
9
+ exports.renderRule = renderRule;
10
+ const core_1 = require("../../utils/core");
11
+ /**
12
+ * Render Migration (niveau 2 : to18, to19, to20)
13
+ */
14
+ function renderMigration(targetName, migration) {
15
+ const migrationId = `${targetName}-${migration.migration}`;
16
+ const sectionId = `section-${migrationId}`;
17
+ const colorMap = { to18: 'blue', to19: 'indigo', to20: 'purple' };
18
+ const color = colorMap[migration.migration] || 'gray';
19
+ return `
20
+ <div id="${sectionId}" class="ml-8 border-l-4 border-${color}-400 pl-4" data-migration="${migration.migration}">
21
+ <div class="flex items-center justify-between bg-${color}-50 p-3 rounded-lg cursor-pointer" data-collapse-trigger="${sectionId}">
22
+ <div class="flex items-center space-x-2">
23
+ <span data-collapse-icon class="text-lg">▼</span>
24
+ <span class="font-bold text-${color}-800">${migration.migration.toUpperCase()}</span>
25
+ </div>
26
+ <span class="font-semibold text-${color}-600" data-migration-time="${migrationId}">${(0, core_1.formatTime)(migration.totalTime.minutes, migration.totalTime.hours, migration.totalTime.days)}</span>
27
+ </div>
28
+ <div data-collapse-content class="mt-3 space-y-3">
29
+ ${migration.priorities.map(priority => renderPriority(migrationId, priority)).join('')}
30
+ </div>
31
+ </div>
32
+ `;
33
+ }
34
+ /**
35
+ * Render Priority (niveau 3 : obligatoire, recommandé, optionnel)
36
+ */
37
+ function renderPriority(migrationId, priority) {
38
+ const priorityId = `${migrationId}-${priority.priority}`;
39
+ const sectionId = `section-${priorityId}`;
40
+ const colorMap = { obligatoire: 'red', recommande: 'yellow', optionnelle: 'green' };
41
+ const color = colorMap[priority.priority] || 'gray';
42
+ return `
43
+ <div id="${sectionId}" class="ml-8 border-l-4 border-${color}-400 pl-4">
44
+ <div class="flex items-center justify-between bg-${color}-50 p-3 rounded-lg cursor-pointer" data-collapse-trigger="${sectionId}">
45
+ <div class="flex items-center space-x-2">
46
+ <span data-collapse-icon class="text-lg">▼</span>
47
+ ${(0, core_1.renderBadge)(priority.priority.charAt(0).toUpperCase() + priority.priority.slice(1), color)}
48
+ <span class="text-sm text-gray-600">(${priority.rules.length} règles)</span>
49
+ </div>
50
+ <span class="font-semibold text-${color}-600" data-priority-time="${priorityId}">${(0, core_1.formatTime)(priority.totalTime.minutes, priority.totalTime.hours, priority.totalTime.days)}</span>
51
+ </div>
52
+ <div data-collapse-content class="mt-3 space-y-2">
53
+ ${priority.rules.map(rule => renderRule(rule)).join('')}
54
+ </div>
55
+ </div>
56
+ `;
57
+ }
58
+ /**
59
+ * Render Rule (niveau 4 : règle individuelle avec édition temps)
60
+ * Note: Les attributs data-risk-level, data-category, data-rule-type seront injectés dynamiquement
61
+ * côté client car RuleWorkload ne contient pas ces métadonnées (uniquement Rule les a)
62
+ */
63
+ function renderRule(rule) {
64
+ const typeColor = rule.type === 'routine' ? 'teal' : rule.type === 'special' ? 'pink' : rule.type === 'combined' ? 'purple' : 'blue';
65
+ const hasSpecialWorkload = rule.type === 'special' || rule.type === 'combined';
66
+ // Calcul des temps
67
+ const baseOccurrences = rule.baseOccurrences ?? rule.occurrences;
68
+ const baseMinutes = rule.minutesPerOccurrence * baseOccurrences;
69
+ const specialMinutes = hasSpecialWorkload && rule.specialWorkloadFiles && rule.specialTimePerFile
70
+ ? rule.specialWorkloadFiles * rule.specialTimePerFile
71
+ : 0;
72
+ return `
73
+ <div
74
+ class="ml-8 bg-white border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow"
75
+ data-rule-card
76
+ data-rule-key="${rule.ruleKey}"
77
+ data-title="${(0, core_1.escapeHtml)(rule.ruleSummary)}"
78
+ data-rule-type="${rule.type || 'standard'}"
79
+ >
80
+ <div class="flex justify-between items-start">
81
+ <div class="flex-1">
82
+ <!-- Chips container (rempli dynamiquement côté client) -->
83
+ <div class="flex items-center gap-2 mb-2" data-chips-container="${rule.ruleKey}"></div>
84
+
85
+ <h5 class="font-semibold text-gray-800 mb-2">${(0, core_1.escapeHtml)(rule.ruleSummary)}</h5>
86
+ <div class="flex items-center space-x-2 text-sm mb-2">
87
+ ${(0, core_1.renderBadge)(rule.type || 'standard', typeColor)}
88
+ </div>
89
+ <div class="text-sm text-gray-700">
90
+ ${rule.type === 'combined' ? `
91
+ <span data-calc-display="${rule.ruleKey}">
92
+ ${baseOccurrences} occ ×
93
+ <input
94
+ type="number"
95
+ data-time-input="${rule.ruleKey}"
96
+ data-original-time="${rule.minutesPerOccurrence}"
97
+ value="${rule.minutesPerOccurrence}"
98
+ min="1"
99
+ class="w-14 px-2 py-1 mx-1 border border-gray-300 rounded text-center"
100
+ />min +
101
+ ${rule.specialWorkloadFiles} fichiers ×
102
+ <input
103
+ type="number"
104
+ data-special-time-input="${rule.ruleKey}"
105
+ data-original-special-time="${rule.specialTimePerFile}"
106
+ value="${rule.specialTimePerFile}"
107
+ min="1"
108
+ class="w-14 px-2 py-1 mx-1 border border-gray-300 rounded text-center"
109
+ />min =
110
+ <span class="font-semibold text-gray-900" data-rule-time="${rule.ruleKey}">${(0, core_1.formatTime)(rule.totalTime.minutes, rule.totalTime.hours, rule.totalTime.days)}</span>
111
+ </span>
112
+ ` : rule.type === 'special' ? `
113
+ <span data-calc-display="${rule.ruleKey}">
114
+ ${rule.specialWorkloadFiles} fichiers ×
115
+ <input
116
+ type="number"
117
+ data-special-time-input="${rule.ruleKey}"
118
+ data-original-special-time="${rule.specialTimePerFile}"
119
+ value="${rule.specialTimePerFile}"
120
+ min="1"
121
+ class="w-14 px-2 py-1 mx-1 border border-gray-300 rounded text-center"
122
+ />min =
123
+ <span class="font-semibold text-gray-900" data-rule-time="${rule.ruleKey}">${(0, core_1.formatTime)(rule.totalTime.minutes, rule.totalTime.hours, rule.totalTime.days)}</span>
124
+ </span>
125
+ ` : rule.type === 'routine' ? `
126
+ <span data-calc-display="${rule.ruleKey}">
127
+ ${rule.occurrences} ×
128
+ <input
129
+ type="number"
130
+ data-time-input="${rule.ruleKey}"
131
+ data-original-time="${rule.minutesPerOccurrence}"
132
+ value="${rule.minutesPerOccurrence}"
133
+ min="1"
134
+ class="w-14 px-2 py-1 mx-1 border border-gray-300 rounded text-center"
135
+ />min =
136
+ <span class="font-semibold text-gray-900" data-rule-time="${rule.ruleKey}">${(0, core_1.formatTime)(rule.totalTime.minutes, rule.totalTime.hours, rule.totalTime.days)}</span>
137
+ </span>
138
+ ` : `
139
+ <span data-calc-display="${rule.ruleKey}">
140
+ ${rule.occurrences} occ ×
141
+ <input
142
+ type="number"
143
+ data-time-input="${rule.ruleKey}"
144
+ data-original-time="${rule.minutesPerOccurrence}"
145
+ value="${rule.minutesPerOccurrence}"
146
+ min="1"
147
+ class="w-14 px-2 py-1 mx-1 border border-gray-300 rounded text-center"
148
+ />min =
149
+ <span class="font-semibold text-gray-900" data-rule-time="${rule.ruleKey}">${(0, core_1.formatTime)(rule.totalTime.minutes, rule.totalTime.hours, rule.totalTime.days)}</span>
150
+ </span>
151
+ `}
152
+ </div>
153
+ </div>
154
+ <button
155
+ class="px-3 py-1 text-sm bg-blue-500 text-white rounded hover:bg-blue-600"
156
+ data-modal-trigger="modal-${rule.ruleKey}"
157
+ >
158
+ Détails
159
+ </button>
160
+ </div>
161
+ </div>
162
+ `;
163
+ }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /**
3
+ * Template spécifique pour vue hiérarchique Angular Standalone
4
+ * Structure: Target > Migrations > Priorities > Rules
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.renderStandaloneView = renderStandaloneView;
8
+ const core_1 = require("../../utils/core");
9
+ const hierarchy_shared_1 = require("./hierarchy-shared");
10
+ /**
11
+ * Vue pour projet Standalone : Affiche directement les migrations
12
+ */
13
+ function renderStandaloneView(workload) {
14
+ return workload.targets.map(target => renderStandaloneTarget(target)).join('');
15
+ }
16
+ /**
17
+ * Render Target pour Standalone (structure simple)
18
+ */
19
+ function renderStandaloneTarget(target) {
20
+ const sectionId = `target-${target.targetName.replace(/[^a-z0-9]/gi, '-')}`;
21
+ return `
22
+ <div id="${sectionId}" class="mb-6 border-l-4 border-blue-500 pl-4">
23
+ <div class="flex items-center justify-between bg-blue-50 p-4 rounded-lg cursor-pointer" data-collapse-trigger="${sectionId}">
24
+ <div class="flex items-center space-x-3">
25
+ <span data-collapse-icon class="text-xl">▼</span>
26
+ <h3 class="text-xl font-bold text-blue-900">${(0, core_1.escapeHtml)(target.targetName)}</h3>
27
+ ${(0, core_1.renderBadge)(target.targetType, 'blue')}
28
+ </div>
29
+ <span class="text-lg font-semibold text-blue-600" data-target-time="${target.targetName}">${(0, core_1.formatTime)(target.totalTime.minutes, target.totalTime.hours, target.totalTime.days)}</span>
30
+ </div>
31
+ <div data-collapse-content class="mt-4 space-y-4">
32
+ ${target.migrations.map(migration => (0, hierarchy_shared_1.renderMigration)(target.targetName, migration)).join('')}
33
+ </div>
34
+ </div>
35
+ `;
36
+ }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ /**
3
+ * Template vue hiérarchique (Entry Point)
4
+ * Délègue vers Nx ou Standalone selon le type de projet
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.renderHierarchy = renderHierarchy;
8
+ const project_strategy_1 = require("../../core/project-strategy");
9
+ const hierarchy_nx_template_1 = require("./hierarchy-nx.template");
10
+ const hierarchy_standalone_template_1 = require("./hierarchy-standalone.template");
11
+ /**
12
+ * Point d'entrée : Render hiérarchie complète
13
+ * Détecte automatiquement le type de projet et délègue
14
+ */
15
+ function renderHierarchy(workload) {
16
+ const strategy = (0, project_strategy_1.createProjectStrategy)(workload.projectInfo);
17
+ const isMultiTarget = strategy.isMultiTarget(workload.projectInfo);
18
+ return `
19
+ <div class="bg-white rounded-2xl shadow-xl p-8">
20
+ <div class="flex justify-between items-center mb-6">
21
+ <h2 class="text-3xl font-bold text-gray-800">🗂️ Vue Hiérarchique</h2>
22
+ <div class="space-x-2">
23
+ <button onclick="expandAll()" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
24
+ Tout Déplier
25
+ </button>
26
+ <button onclick="collapseAll()" class="px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600">
27
+ Tout Replier
28
+ </button>
29
+ </div>
30
+ </div>
31
+
32
+ ${isMultiTarget ? (0, hierarchy_nx_template_1.renderNxMonorepoView)(workload) : (0, hierarchy_standalone_template_1.renderStandaloneView)(workload)}
33
+ </div>
34
+ `;
35
+ }