feat(planning): make optimization rules composable
Evaluate explicit, ordered optimization configurations through one shared planner model. Add six editable presets, durable one-time legacy migration, and consistent completed and cancelled plan provenance. Include functional regressions and the agreed trace-removal and benchmark follow-up specs. Performance acceptance remains in #78 under unchanged gates. Refs: #70, #71, #72, #73, #74, #75, #76, #77, #78
This commit is contained in:
1 parent
209bf05f36
commit
6811142299
45 files changed
+8795
-3075
No files matched your search
@@ -183,6 +183,8 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
} @else if ($any(editBuffer).type === RuleType.OPTIMIZATION) {
|
||||
<ng-container [ngTemplateOutlet]="optimizationEditor" />
|
||||
} @else {
|
||||
<div class="editor-row">
|
||||
<mat-form-field>
|
||||
@@ -239,6 +241,9 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (validationError()) {
|
||||
<p role="alert">{{ validationError() }}</p>
|
||||
}
|
||||
<div class="editor-actions">
|
||||
<button mat-button (click)="cancelEdit()">
|
||||
{{ tr().rules.cancel }}
|
||||
@@ -338,6 +343,8 @@
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
} @else if ($any(editBuffer).type === RuleType.OPTIMIZATION) {
|
||||
<ng-container [ngTemplateOutlet]="optimizationEditor" />
|
||||
} @else {
|
||||
<div class="editor-row">
|
||||
<mat-form-field>
|
||||
@@ -394,6 +401,9 @@
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (validationError()) {
|
||||
<p role="alert">{{ validationError() }}</p>
|
||||
}
|
||||
<div class="editor-actions">
|
||||
<button mat-button (click)="cancelEdit()">{{ tr().rules.cancel }}</button>
|
||||
<button mat-flat-button color="primary" (click)="saveEdit()">
|
||||
@@ -411,6 +421,89 @@
|
||||
<button mat-stroked-button (click)="startAddNew(RuleType.TIME_WINDOW)">
|
||||
<mat-icon>add</mat-icon> {{ tr().rules.newTimeWindowRule }}
|
||||
</button>
|
||||
<button mat-stroked-button (click)="startAddNew(RuleType.OPTIMIZATION)">
|
||||
<mat-icon aria-hidden="true">add</mat-icon> {{ tr().rules.optimization.newRule }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<ng-template #optimizationEditor>
|
||||
@if (optimizationConfiguration; as configuration) {
|
||||
<div class="editor-row">
|
||||
<mat-form-field>
|
||||
<mat-label>{{ tr().rules.optimization.elementsLabel }}</mat-label>
|
||||
<mat-select
|
||||
[ngModel]="configuration.elements"
|
||||
(ngModelChange)="changeOptimizationElements($event)"
|
||||
>
|
||||
@for (elements of optimizationElements; track elements) {
|
||||
<mat-option [value]="elements">{{
|
||||
tr().rules.optimization.elements[elements]
|
||||
}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ tr().rules.optimization.measureLabel }}</mat-label>
|
||||
<mat-select
|
||||
[ngModel]="configuration.measure"
|
||||
(ngModelChange)="changeOptimizationMeasure($event)"
|
||||
>
|
||||
@for (row of optimizationMeasures; track row.measure) {
|
||||
<mat-option [value]="row.measure">{{
|
||||
tr().rules.optimization.measures[row.measure]
|
||||
}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@if (optimizationConditions.length) {
|
||||
<mat-form-field class="full-width">
|
||||
<mat-label>{{ tr().rules.optimization.conditionsLabel }}</mat-label>
|
||||
<mat-select multiple [(ngModel)]="configuration.conditions">
|
||||
@for (condition of optimizationConditions; track condition) {
|
||||
<mat-option [value]="condition">{{
|
||||
tr().rules.optimization.conditions[condition]
|
||||
}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
}
|
||||
<div class="editor-row">
|
||||
<mat-form-field>
|
||||
<mat-label>{{ tr().rules.optimization.aggregationLabel }}</mat-label>
|
||||
<mat-select [(ngModel)]="configuration.aggregation">
|
||||
@for (aggregation of optimizationAggregations; track aggregation) {
|
||||
<mat-option [value]="aggregation">{{
|
||||
tr().rules.optimization.aggregations[aggregation]
|
||||
}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ tr().rules.optimization.directionLabel }}</mat-label>
|
||||
<mat-select [(ngModel)]="configuration.direction">
|
||||
@for (direction of optimizationDirections; track direction) {
|
||||
<mat-option [value]="direction">{{
|
||||
tr().rules.optimization.directions[direction]
|
||||
}}</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
@if (configuration.measure === 'work-sequence-deficit') {
|
||||
<mat-form-field>
|
||||
<mat-label>{{ tr().rules.optimization.thresholdLabel }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="number"
|
||||
min="1"
|
||||
step="1"
|
||||
required
|
||||
[(ngModel)]="configuration.thresholdDays"
|
||||
/>
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</ng-template>
|
||||
Reference in new issue
Block a user