@memberjunction/ng-dashboards 6.2.0-edge.1 → 6.2.0-edge.2
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.
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js +3 -1
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js.map +1 -1
- package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js +1 -2
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js.map +1 -1
- package/dist/AI/components/rubric-drift-series.d.ts +32 -0
- package/dist/AI/components/rubric-drift-series.d.ts.map +1 -0
- package/dist/AI/components/rubric-drift-series.js +49 -0
- package/dist/AI/components/rubric-drift-series.js.map +1 -0
- package/dist/AI/components/rubric-drift.component.d.ts +42 -0
- package/dist/AI/components/rubric-drift.component.d.ts.map +1 -0
- package/dist/AI/components/rubric-drift.component.js +197 -0
- package/dist/AI/components/rubric-drift.component.js.map +1 -0
- package/dist/AI/components/tags/tags-resource.component.d.ts.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.js +4 -3
- package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.js +5 -2
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.d.ts +5 -0
- package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +151 -136
- package/dist/Integration/components/connections/connections.component.js.map +1 -1
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +3 -2
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.d.ts +11 -0
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.js +17 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.js.map +1 -1
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js +52 -45
- package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.d.ts +11 -0
- package/dist/QueryBrowser/query-browser-resource.component.d.ts.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.js +47 -10
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/Rubrics/catalog-grid.d.ts +4 -0
- package/dist/Rubrics/catalog-grid.d.ts.map +1 -0
- package/dist/Rubrics/catalog-grid.js +22 -0
- package/dist/Rubrics/catalog-grid.js.map +1 -0
- package/dist/Rubrics/rubrics-dashboard.component.d.ts +31 -0
- package/dist/Rubrics/rubrics-dashboard.component.d.ts.map +1 -0
- package/dist/Rubrics/rubrics-dashboard.component.js +285 -0
- package/dist/Rubrics/rubrics-dashboard.component.js.map +1 -0
- package/dist/Rubrics/rubrics-resource.component.d.ts +13 -0
- package/dist/Rubrics/rubrics-resource.component.d.ts.map +1 -0
- package/dist/Rubrics/rubrics-resource.component.js +42 -0
- package/dist/Rubrics/rubrics-resource.component.js.map +1 -0
- package/dist/Rubrics/scales-dashboard.component.d.ts +34 -0
- package/dist/Rubrics/scales-dashboard.component.d.ts.map +1 -0
- package/dist/Rubrics/scales-dashboard.component.js +204 -0
- package/dist/Rubrics/scales-dashboard.component.js.map +1 -0
- package/dist/Rubrics/scales-resource.component.d.ts +13 -0
- package/dist/Rubrics/scales-resource.component.d.ts.map +1 -0
- package/dist/Rubrics/scales-resource.component.js +42 -0
- package/dist/Rubrics/scales-resource.component.js.map +1 -0
- package/dist/Testing/components/testing-analytics.component.d.ts +15 -0
- package/dist/Testing/components/testing-analytics.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-analytics.component.js +465 -296
- package/dist/Testing/components/testing-analytics.component.js.map +1 -1
- package/dist/Testing/components/testing-review.component.d.ts +6 -0
- package/dist/Testing/components/testing-review.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-review.component.js +289 -173
- package/dist/Testing/components/testing-review.component.js.map +1 -1
- package/dist/Testing/components/widgets/test-run-detail-panel.component.d.ts +8 -1
- package/dist/Testing/components/widgets/test-run-detail-panel.component.d.ts.map +1 -1
- package/dist/Testing/components/widgets/test-run-detail-panel.component.js +68 -43
- package/dist/Testing/components/widgets/test-run-detail-panel.component.js.map +1 -1
- package/dist/module.d.ts +23 -22
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +7 -0
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -0
- package/dist/public-api.js.map +1 -1
- package/dist/rubrics-dashboards.module.d.ts +17 -0
- package/dist/rubrics-dashboards.module.d.ts.map +1 -0
- package/dist/rubrics-dashboards.module.js +76 -0
- package/dist/rubrics-dashboards.module.js.map +1 -0
- package/package.json +70 -64
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing-review.component.js","sourceRoot":"","sources":["../../../src/Testing/components/testing-review.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n OnInit,\n OnDestroy,\n Input,\n Output,\n EventEmitter,\n ChangeDetectorRef,\n ChangeDetectionStrategy\n} from '@angular/core';\nimport { Subject, Observable, combineLatest, BehaviorSubject } from 'rxjs';\nimport { takeUntil, map, shareReplay } from 'rxjs/operators';\nimport {\n TestingInstrumentationService,\n FeedbackPending,\n FeedbackStats,\n TestRunWithFeedbackSummary,\n EvaluationSummaryMetrics\n} from '../services/testing-instrumentation.service';\nimport { UUIDsEqual } from '@memberjunction/global';\n\ntype ViewMode = 'queue' | 'history';\ntype HistorySort = 'date' | 'rating' | 'test-name';\n\ninterface ReviewFormState {\n rating: number | null;\n isCorrect: boolean | null;\n comments: string;\n}\n\n@Component({\n standalone: false,\n selector: 'app-testing-review',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (HideToolbar) {\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n } @else {\n <mj-page-layout>\n <mj-page-header\n Title=\"Human Review\"\n Icon=\"fa-solid fa-clipboard-check\"\n Subtitle=\"Human-in-the-loop review for test outcomes\">\n <div meta>\n @if (PendingCount > 0) {\n <mj-stat-badge\n Icon=\"fa-solid fa-hourglass-half\"\n [Count]=\"PendingCount\"\n Label=\"pending\"\n Variant=\"warning\">\n </mj-stat-badge>\n }\n </div>\n <div actions>\n <mj-refresh-button [Loading]=\"IsRefreshing\" (Clicked)=\"Refresh()\"></mj-refresh-button>\n </div>\n </mj-page-header>\n <mj-page-body>\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n </mj-page-body>\n </mj-page-layout>\n }\n\n <ng-template #content>\n <!-- Inner page content -->\n <div class=\"review-page\">\n\n <!-- KPI Summary Row -->\n @if (Metrics) {\n <div class=\"kpi-row\">\n <div class=\"kpi-card\">\n <div class=\"kpi-icon orange\">\n <i class=\"fa-solid fa-hourglass-half\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ Metrics.humanPendingCount }}</div>\n <div class=\"kpi-label\">Pending Review</div>\n </div>\n </div>\n <div class=\"kpi-card\">\n <div class=\"kpi-icon green\">\n <i class=\"fa-solid fa-check-circle\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ Metrics.humanReviewedCount }}</div>\n <div class=\"kpi-label\">Reviewed</div>\n </div>\n </div>\n <div class=\"kpi-card\">\n <div class=\"kpi-icon gold\">\n <i class=\"fa-solid fa-star\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ FormatDecimal(Metrics.humanAvgRating, 1) }}<span class=\"kpi-unit\">/10</span></div>\n <div class=\"kpi-label\">Avg Rating</div>\n </div>\n </div>\n <div class=\"kpi-card\">\n <div class=\"kpi-icon blue\">\n <i class=\"fa-solid fa-handshake\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ FormatDecimal(Metrics.agreementRate, 0) }}<span class=\"kpi-unit\">%</span></div>\n <div class=\"kpi-label\">Agreement Rate</div>\n </div>\n </div>\n </div>\n }\n\n <!-- View Mode Toggle -->\n <div class=\"view-toggle-bar\">\n <div class=\"toggle-group\">\n <button class=\"toggle-btn\" [class.active]=\"CurrentView === 'queue'\" (click)=\"SetView('queue')\">\n <i class=\"fa-solid fa-list-check\"></i>\n Review Queue\n </button>\n <button class=\"toggle-btn\" [class.active]=\"CurrentView === 'history'\" (click)=\"SetView('history')\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n History\n </button>\n </div>\n </div>\n\n <!-- Review Queue View -->\n @if (CurrentView === 'queue') {\n <div class=\"content-card\">\n @if (PendingItems.length === 0) {\n <mj-empty-state Variant=\"success\"\n Title=\"All caught up!\"\n Message=\"No tests currently require human review.\" />\n } @else {\n <div class=\"queue-list\">\n @for (item of PendingItems; track item.testRunID) {\n <mj-accordion-panel\n Size=\"sm\"\n [FlushBody]=\"true\"\n [Expanded]=\"IsItemExpanded(item.testRunID)\"\n (ExpandedChange)=\"OnItemExpandedChange(item.testRunID, $event)\">\n <ng-template mjAccordionTitle>\n <div class=\"item-info\">\n <div class=\"item-name\">{{ item.testName }}</div>\n <div class=\"item-meta\">\n <span class=\"meta-time\">\n <i class=\"fa-solid fa-clock\"></i>\n {{ FormatRelativeTime(item.runDateTime) }}\n </span>\n <app-score-indicator [score]=\"item.automatedScore\" [showBar]=\"true\"></app-score-indicator>\n <app-test-status-badge [status]=\"$any(item.automatedStatus)\"></app-test-status-badge>\n </div>\n </div>\n </ng-template>\n <ng-template mjAccordionActions>\n <span class=\"reason-badge\" [class]=\"'reason-' + item.reason\">\n @if (item.reason === 'no-feedback') {\n <i class=\"fa-solid fa-comment-slash\"></i> No Feedback\n } @else if (item.reason === 'high-score-failed') {\n <i class=\"fa-solid fa-triangle-exclamation\"></i> Score Mismatch\n } @else {\n <i class=\"fa-solid fa-circle-question\"></i> Needs Verification\n }\n </span>\n </ng-template>\n <ng-template mjAccordionBody>\n <div class=\"review-form-panel\">\n <!-- Rating -->\n <div class=\"form-section\">\n <label class=\"form-label\">Rating</label>\n <div class=\"rating-row\">\n @for (n of RatingNumbers; track n) {\n <button class=\"rating-circle\"\n [class.selected]=\"FormState.rating != null && n <= FormState.rating\"\n [class.current]=\"FormState.rating === n\"\n (click)=\"SelectRating(n)\">\n {{ n }}\n </button>\n }\n @if (FormState.rating != null) {\n <span class=\"rating-display\">{{ FormState.rating }}/10</span>\n }\n </div>\n </div>\n\n <!-- Correctness -->\n <div class=\"form-section\">\n <label class=\"form-label\">Is the automated result correct?</label>\n <div class=\"correctness-row\">\n <button class=\"correctness-btn correct\"\n [class.active]=\"FormState.isCorrect === true\"\n (click)=\"SelectCorrectness(true)\">\n <i class=\"fa-solid fa-check\"></i> Correct\n </button>\n <button class=\"correctness-btn incorrect\"\n [class.active]=\"FormState.isCorrect === false\"\n (click)=\"SelectCorrectness(false)\">\n <i class=\"fa-solid fa-xmark\"></i> Incorrect\n </button>\n </div>\n </div>\n\n <!-- Notes -->\n <div class=\"form-section\">\n <label class=\"form-label\">Notes (optional)</label>\n <textarea class=\"notes-textarea\"\n rows=\"3\"\n placeholder=\"Add any comments about this evaluation...\"\n [value]=\"FormState.comments\"\n (input)=\"OnCommentsChange($event)\"></textarea>\n </div>\n\n <!-- Actions -->\n <div class=\"form-actions\">\n <button class=\"submit-btn\"\n [disabled]=\"!IsFormValid || IsSubmitting\"\n (click)=\"SubmitReview(item)\">\n <i class=\"fa-solid fa-paper-plane\"></i>\n {{ IsSubmitting ? 'Submitting...' : 'Submit' }}\n </button>\n <button class=\"skip-btn\" (click)=\"SkipItem()\">\n <i class=\"fa-solid fa-forward\"></i> Skip\n </button>\n </div>\n </div>\n </ng-template>\n </mj-accordion-panel>\n }\n </div>\n }\n </div>\n }\n\n <!-- History View -->\n @if (CurrentView === 'history') {\n <div class=\"content-card\">\n <!-- Filter Bar -->\n <div class=\"history-filters\">\n <div class=\"search-wrapper\">\n <i class=\"fa-solid fa-search\"></i>\n <input type=\"text\"\n placeholder=\"Search by test name...\"\n [value]=\"HistorySearchText\"\n (input)=\"OnHistorySearch($event)\" />\n </div>\n <div class=\"sort-control\">\n <label>Sort by:</label>\n <select [value]=\"HistorySortField\" (change)=\"OnHistorySortChange($event)\">\n <option value=\"date\">Date</option>\n <option value=\"rating\">Rating</option>\n <option value=\"test-name\">Test Name</option>\n </select>\n </div>\n </div>\n\n @if (FilteredHistoryItems.length === 0) {\n <mj-empty-state Icon=\"fa-solid fa-folder-open\"\n Title=\"No reviewed items\"\n Message=\"Reviewed tests will appear here once feedback is submitted.\" />\n } @else {\n <div class=\"history-list\">\n @for (item of FilteredHistoryItems; track item.id) {\n <div class=\"history-item\">\n <div class=\"history-item-main\">\n <div class=\"history-name\">{{ item.testName }}</div>\n <div class=\"history-date\">\n <i class=\"fa-solid fa-calendar\"></i>\n {{ FormatRelativeTime(item.runDateTime) }}\n </div>\n </div>\n <div class=\"history-rating\">\n <div class=\"rating-dots\">\n @for (n of RatingNumbers; track n) {\n <span class=\"rating-dot\" [class.filled]=\"item.humanRating != null && n <= item.humanRating\"></span>\n }\n </div>\n <span class=\"rating-label\">{{ item.humanRating ?? 0 }}/10</span>\n </div>\n <div class=\"history-verdict\">\n @if (item.humanIsCorrect === true) {\n <span class=\"verdict correct\">\n <i class=\"fa-solid fa-check\"></i> Correct\n </span>\n } @else if (item.humanIsCorrect === false) {\n <span class=\"verdict incorrect\">\n <i class=\"fa-solid fa-xmark\"></i> Incorrect\n </span>\n }\n </div>\n <div class=\"history-auto-score\">\n <app-score-indicator [score]=\"item.score\" [showBar]=\"true\"></app-score-indicator>\n </div>\n @if (item.humanComments) {\n <div class=\"history-comments\"\n [class.expanded]=\"ExpandedHistoryId === item.id\"\n (click)=\"ToggleHistoryComment(item.id)\">\n <p>{{ item.humanComments }}</p>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Calibration Insights -->\n <div class=\"calibration-section\">\n <h3>\n <i class=\"fa-solid fa-scale-balanced\"></i>\n Human vs Auto Agreement\n </h3>\n <div class=\"calibration-body\">\n <div class=\"gauge-display\">\n <div class=\"gauge-ring\">\n <svg viewBox=\"0 0 120 120\" class=\"gauge-svg\">\n <circle cx=\"60\" cy=\"60\" r=\"52\" fill=\"none\" class=\"gauge-track\" stroke-width=\"10\" />\n <circle cx=\"60\" cy=\"60\" r=\"52\" fill=\"none\"\n [attr.stroke]=\"AgreementColor\"\n stroke-width=\"10\"\n stroke-linecap=\"round\"\n [attr.stroke-dasharray]=\"AgreementDash\"\n stroke-dashoffset=\"0\"\n transform=\"rotate(-90 60 60)\" />\n </svg>\n <div class=\"gauge-value\">{{ FormatDecimal(AgreementRate, 0) }}%</div>\n </div>\n </div>\n <div class=\"calibration-text\">\n <p class=\"calibration-description\">\n Measures how often human reviewers agree with automated evaluation scores.\n </p>\n @if (AgreementRate < 70) {\n <mj-alert Variant=\"warning\">\n Low agreement may indicate evaluation criteria need refinement.\n </mj-alert>\n }\n </div>\n </div>\n </div>\n </div>\n </ng-template>\n `,\n styles: [`\n :host {\n display: block;\n height: 100%;\n }\n\n .review-page {\n padding: 20px;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-surface-card);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n /* Page Header */\n .page-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n background: var(--mj-bg-surface);\n padding: 20px;\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n }\n\n .header-left {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .header-left h2 {\n margin: 0;\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .header-left h2 i {\n color: var(--mj-brand-primary);\n }\n\n .pending-badge {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 14px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border: 1px solid var(--mj-status-warning);\n border-radius: 20px;\n }\n\n .badge-count {\n background: var(--mj-status-warning);\n color: var(--mj-text-inverse);\n min-width: 24px;\n height: 24px;\n padding: 0 6px;\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 700;\n }\n\n .badge-text {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-status-warning);\n }\n\n .refresh-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 18px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .refresh-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-border-strong);\n }\n\n .refresh-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n /* KPI Row */\n .kpi-row {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n margin-bottom: 20px;\n }\n\n .kpi-card {\n background: var(--mj-bg-surface);\n padding: 20px;\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .kpi-icon {\n width: 48px;\n height: 48px;\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n color: var(--mj-text-inverse);\n flex-shrink: 0;\n }\n\n .kpi-icon.orange { background: var(--mj-status-warning); }\n .kpi-icon.green { background: var(--mj-status-success); }\n .kpi-icon.gold { background: var(--mj-status-warning); }\n .kpi-icon.blue { background: var(--mj-brand-primary); }\n\n .kpi-content {\n flex: 1;\n min-width: 0;\n }\n\n .kpi-value {\n font-size: 26px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1;\n margin-bottom: 4px;\n }\n\n .kpi-unit {\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-muted);\n }\n\n .kpi-label {\n font-size: 11px;\n color: var(--mj-text-muted);\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n /* View Toggle */\n .view-toggle-bar {\n margin-bottom: 20px;\n }\n\n .toggle-group {\n display: inline-flex;\n background: var(--mj-bg-surface);\n border-radius: 10px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n }\n\n .toggle-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 24px;\n border: none;\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .toggle-btn:hover:not(.active) {\n background: var(--mj-bg-surface-card);\n }\n\n .toggle-btn.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n /* Content Card */\n .content-card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n margin-bottom: 20px;\n }\n\n /* Queue List */\n .queue-list {\n max-height: 600px;\n overflow-y: auto;\n }\n\n /* Queue items migrated to <mj-accordion-panel> — the panel owns the row\n chrome (border, hover, expanded state) and the chevron toggle, so the\n bespoke .queue-item / .queue-item-header / .expand-toggle styles were\n removed. */\n\n .item-info {\n flex: 1;\n min-width: 0;\n }\n\n .item-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin-bottom: 6px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .item-meta {\n display: flex;\n gap: 14px;\n font-size: 12px;\n color: var(--mj-text-muted);\n align-items: center;\n flex-wrap: wrap;\n }\n\n .meta-time {\n display: flex;\n align-items: center;\n gap: 5px;\n }\n\n /* .item-actions-area removed — the reason badge now lives in the accordion's\n [mjAccordionActions] slot, which provides its own flex container. */\n\n .reason-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border-radius: 16px;\n font-size: 11px;\n font-weight: 600;\n }\n\n .reason-no-feedback {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n }\n\n .reason-high-score-failed {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n }\n\n .reason-low-score-passed {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n }\n\n /* .expand-toggle removed — the bespoke chevron was replaced by\n <mj-accordion-panel>'s built-in chevron. */\n\n /* Review Form — rendered in the accordion body ([FlushBody]=\"true\"), so it\n supplies its own padding; the header/body separator is owned by the\n accordion (border-top removed to avoid a doubled line). */\n .review-form-panel {\n padding: 20px 20px 20px 36px;\n background: var(--mj-bg-surface);\n }\n\n .form-section {\n margin-bottom: 20px;\n }\n\n .form-section:last-of-type {\n margin-bottom: 16px;\n }\n\n .form-label {\n display: block;\n font-size: 12px;\n font-weight: 700;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 10px;\n }\n\n /* Rating Circles */\n .rating-row {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .rating-circle {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n border: 2px solid var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-disabled);\n font-size: 13px;\n font-weight: 700;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n }\n\n .rating-circle:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n transform: scale(1.1);\n }\n\n .rating-circle.selected {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .rating-circle.current {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n color: var(--mj-text-inverse);\n box-shadow: var(--mj-shadow-sm);\n }\n\n .rating-display {\n margin-left: 12px;\n font-size: 16px;\n font-weight: 700;\n color: var(--mj-text-primary);\n }\n\n /* Correctness Buttons */\n .correctness-row {\n display: flex;\n gap: 12px;\n }\n\n .correctness-btn {\n flex: 1;\n max-width: 200px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 12px 20px;\n border: 2px solid var(--mj-border-strong);\n border-radius: 10px;\n background: var(--mj-bg-surface-card);\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .correctness-btn.correct:hover,\n .correctness-btn.correct.active {\n border-color: var(--mj-status-success);\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n .correctness-btn.incorrect:hover,\n .correctness-btn.incorrect.active {\n border-color: var(--mj-status-error);\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n }\n\n /* Notes */\n .notes-textarea {\n width: 100%;\n max-width: 500px;\n padding: 12px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 8px;\n font-size: 14px;\n font-family: inherit;\n resize: vertical;\n min-height: 72px;\n box-sizing: border-box;\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n }\n\n .notes-textarea:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n }\n\n /* Form Actions */\n .form-actions {\n display: flex;\n gap: 12px;\n }\n\n .submit-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 11px 22px;\n border: none;\n border-radius: 8px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .submit-btn:hover:not(:disabled) {\n transform: translateY(-1px);\n box-shadow: var(--mj-shadow-md);\n }\n\n .submit-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .skip-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 11px 20px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .skip-btn:hover {\n border-color: var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n }\n\n /* History Filters */\n .history-filters {\n display: flex;\n gap: 16px;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n align-items: center;\n }\n\n .search-wrapper {\n flex: 1;\n position: relative;\n display: flex;\n align-items: center;\n }\n\n .search-wrapper > i {\n position: absolute;\n left: 12px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n }\n\n .search-wrapper input {\n width: 100%;\n padding: 10px 12px 10px 36px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 13px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n }\n\n .search-wrapper input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n }\n\n .sort-control {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n }\n\n .sort-control select {\n padding: 8px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n }\n\n /* History List */\n .history-list {\n max-height: 600px;\n overflow-y: auto;\n }\n\n .history-item {\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n display: flex;\n align-items: center;\n gap: 16px;\n flex-wrap: wrap;\n }\n\n .history-item:last-child {\n border-bottom: none;\n }\n\n .history-item-main {\n flex: 1;\n min-width: 160px;\n }\n\n .history-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin-bottom: 4px;\n }\n\n .history-date {\n font-size: 12px;\n color: var(--mj-text-disabled);\n display: flex;\n align-items: center;\n gap: 5px;\n }\n\n .history-rating {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .rating-dots {\n display: flex;\n gap: 3px;\n }\n\n .rating-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--mj-border-default);\n }\n\n .rating-dot.filled {\n background: var(--mj-status-warning);\n }\n\n .rating-label {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-muted);\n }\n\n .history-verdict {\n flex-shrink: 0;\n }\n\n .verdict {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 5px 12px;\n border-radius: 14px;\n font-size: 11px;\n font-weight: 600;\n }\n\n .verdict.correct {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n .verdict.incorrect {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n }\n\n .history-auto-score {\n flex-shrink: 0;\n }\n\n .history-comments {\n width: 100%;\n margin-top: 8px;\n padding: 10px 14px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n border-left: 3px solid var(--mj-brand-primary);\n cursor: pointer;\n }\n\n .history-comments p {\n margin: 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .history-comments.expanded p {\n white-space: normal;\n }\n\n /* Calibration Section */\n .calibration-section {\n background: var(--mj-bg-surface);\n padding: 24px;\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n }\n\n .calibration-section h3 {\n margin: 0 0 20px 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .calibration-section h3 i {\n color: var(--mj-brand-primary);\n }\n\n .calibration-body {\n display: flex;\n align-items: center;\n gap: 32px;\n }\n\n .gauge-display {\n flex-shrink: 0;\n }\n\n .gauge-ring {\n position: relative;\n width: 120px;\n height: 120px;\n }\n\n .gauge-svg {\n width: 100%;\n height: 100%;\n }\n\n .gauge-track {\n stroke: var(--mj-border-default);\n }\n\n .gauge-value {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 24px;\n font-weight: 800;\n color: var(--mj-text-primary);\n }\n\n .calibration-text {\n flex: 1;\n }\n\n .calibration-description {\n margin: 0 0 12px 0;\n font-size: 14px;\n color: var(--mj-text-muted);\n line-height: 1.6;\n }\n\n\n /* (Removed .queue-item fadeIn animation — the queue-item wrapper no longer\n exists; the accordion panel owns its own reveal.) */\n\n /* Responsive */\n @media (max-width: 1024px) {\n .kpi-row {\n grid-template-columns: repeat(2, 1fr);\n }\n }\n\n @media (max-width: 768px) {\n .review-page {\n padding: 16px;\n }\n\n .page-header {\n flex-direction: column;\n gap: 16px;\n align-items: flex-start;\n }\n\n .kpi-row {\n grid-template-columns: 1fr 1fr;\n }\n\n .kpi-card {\n padding: 16px;\n }\n\n .kpi-value {\n font-size: 22px;\n }\n\n .correctness-row {\n flex-direction: column;\n }\n\n .correctness-btn {\n max-width: 100%;\n }\n\n .history-item {\n flex-direction: column;\n align-items: flex-start;\n }\n\n .calibration-body {\n flex-direction: column;\n align-items: center;\n text-align: center;\n }\n }\n `]\n})\nexport class TestingReviewComponent implements OnInit, OnDestroy {\n @Input() InitialState: Record<string, unknown> | null = null;\n\n /** @deprecated Use {@link InitialState}. */\n @Input() set initialState(value: Record<string, unknown> | null) {\n this.InitialState = value;\n }\n /** @deprecated Use {@link InitialState}. */\n get initialState(): Record<string, unknown> | null {\n return this.InitialState;\n }\n /** When true, the inner bespoke .page-header is hidden — the parent shell owns the chrome. */\n @Input() HideToolbar = false;\n @Output() StateChange = new EventEmitter<Record<string, unknown>>();\n\n /**\n * @deprecated Use {@link StateChange}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (stateChange) keeps working. Must stay AFTER StateChange: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() stateChange = this.StateChange;\n\n private destroy$ = new Subject<void>();\n\n // View state\n CurrentView: ViewMode = 'queue';\n ExpandedItemId: string | null = null;\n ExpandedHistoryId: string | null = null;\n IsRefreshing = false;\n IsSubmitting = false;\n HistorySearchText = '';\n HistorySortField: HistorySort = 'date';\n\n // Form state for active review\n FormState: ReviewFormState = { rating: null, isCorrect: null, comments: '' };\n\n // Data\n PendingItems: FeedbackPending[] = [];\n HistoryItems: TestRunWithFeedbackSummary[] = [];\n FilteredHistoryItems: TestRunWithFeedbackSummary[] = [];\n Metrics: EvaluationSummaryMetrics | null = null;\n PendingCount = 0;\n\n // Constants\n readonly RatingNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n\n constructor(\n private instrumentationService: TestingInstrumentationService,\n private cdr: ChangeDetectorRef\n ) {}\n\n ngOnInit(): void {\n this.restoreState();\n this.setupSubscriptions();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n // ------------------------------------------------------------------\n // Computed properties\n // ------------------------------------------------------------------\n\n get IsFormValid(): boolean {\n return this.FormState.rating != null && this.FormState.isCorrect != null;\n }\n\n get AgreementRate(): number {\n return this.Metrics?.agreementRate ?? 0;\n }\n\n get AgreementColor(): string {\n const rate = this.AgreementRate;\n if (rate >= 80) return '#22c55e';\n if (rate >= 60) return '#eab308';\n return '#ef4444';\n }\n\n get AgreementDash(): string {\n const circumference = 2 * Math.PI * 52; // r=52\n const filled = (this.AgreementRate / 100) * circumference;\n return `${filled} ${circumference}`;\n }\n\n // ------------------------------------------------------------------\n // Setup\n // ------------------------------------------------------------------\n\n private restoreState(): void {\n if (!this.InitialState) return;\n const view = this.InitialState['viewMode'] as string | undefined;\n if (view === 'queue' || view === 'history') {\n this.CurrentView = view;\n }\n }\n\n private setupSubscriptions(): void {\n this.instrumentationService.pendingFeedback$\n .pipe(takeUntil(this.destroy$))\n .subscribe(items => {\n this.PendingItems = items;\n this.PendingCount = items.length;\n this.emitState();\n this.cdr.markForCheck();\n });\n\n this.instrumentationService.evaluationMetrics$\n .pipe(takeUntil(this.destroy$))\n .subscribe(metrics => {\n this.Metrics = metrics;\n this.emitState();\n this.cdr.markForCheck();\n });\n\n this.instrumentationService.testRunsWithFeedback$\n .pipe(takeUntil(this.destroy$))\n .subscribe(runs => {\n this.HistoryItems = runs.filter(r => r.hasHumanFeedback);\n this.applyHistoryFilters();\n this.cdr.markForCheck();\n });\n }\n\n // ------------------------------------------------------------------\n // View toggling\n // ------------------------------------------------------------------\n\n SetView(mode: ViewMode): void {\n this.CurrentView = mode;\n this.ExpandedItemId = null;\n this.emitState();\n this.cdr.markForCheck();\n }\n\n // ------------------------------------------------------------------\n // Queue interactions\n // ------------------------------------------------------------------\n\n IsItemExpanded(testRunID: string): boolean {\n return UUIDsEqual(this.ExpandedItemId, testRunID);\n }\n\n /**\n * Accordion ExpandedChange handler for a review queue item. Single-expand\n * (radio-like) model: expanding an item makes it the sole open one (the\n * template's `IsItemExpanded` binding collapses the prior one) and resets the\n * review form for the newly-opened item; collapsing the open item clears it.\n * OnPush — drives change detection. Preserves the original ToggleExpand side\n * effect (resetForm on open).\n */\n OnItemExpandedChange(testRunID: string, expanded: boolean): void {\n if (expanded) {\n this.ExpandedItemId = testRunID;\n this.resetForm();\n } else if (UUIDsEqual(this.ExpandedItemId, testRunID)) {\n this.ExpandedItemId = null;\n }\n this.cdr.markForCheck();\n }\n\n SelectRating(n: number): void {\n this.FormState = { ...this.FormState, rating: n };\n this.cdr.markForCheck();\n }\n\n SelectCorrectness(value: boolean): void {\n this.FormState = { ...this.FormState, isCorrect: value };\n this.cdr.markForCheck();\n }\n\n OnCommentsChange(event: Event): void {\n const target = event.target as HTMLTextAreaElement;\n this.FormState = { ...this.FormState, comments: target.value };\n }\n\n async SubmitReview(item: FeedbackPending): Promise<void> {\n if (!this.IsFormValid || this.IsSubmitting) return;\n\n this.IsSubmitting = true;\n this.cdr.markForCheck();\n\n try {\n const success = await this.instrumentationService.submitFeedback(\n item.testRunID,\n this.FormState.rating!,\n this.FormState.isCorrect!,\n this.FormState.comments\n );\n\n if (success) {\n this.ExpandedItemId = null;\n this.resetForm();\n }\n } finally {\n this.IsSubmitting = false;\n this.cdr.markForCheck();\n }\n }\n\n SkipItem(): void {\n const currentIndex = this.PendingItems.findIndex(\n i => UUIDsEqual(i.testRunID, this.ExpandedItemId)\n );\n this.ExpandedItemId = null;\n this.resetForm();\n\n // Expand the next item if available\n if (currentIndex >= 0 && currentIndex + 1 < this.PendingItems.length) {\n this.ExpandedItemId = this.PendingItems[currentIndex + 1].testRunID;\n }\n this.cdr.markForCheck();\n }\n\n // ------------------------------------------------------------------\n // History interactions\n // ------------------------------------------------------------------\n\n OnHistorySearch(event: Event): void {\n const target = event.target as HTMLInputElement;\n this.HistorySearchText = target.value;\n this.applyHistoryFilters();\n this.emitState();\n this.cdr.markForCheck();\n }\n\n OnHistorySortChange(event: Event): void {\n const target = event.target as HTMLSelectElement;\n this.HistorySortField = target.value as HistorySort;\n this.applyHistoryFilters();\n this.cdr.markForCheck();\n }\n\n ToggleHistoryComment(id: string): void {\n this.ExpandedHistoryId = this.ExpandedHistoryId === id ? null : id;\n this.cdr.markForCheck();\n }\n\n // ------------------------------------------------------------------\n // Refresh\n // ------------------------------------------------------------------\n\n Refresh(): void {\n this.IsRefreshing = true;\n this.cdr.markForCheck();\n this.instrumentationService.refresh();\n\n setTimeout(() => {\n this.IsRefreshing = false;\n this.cdr.markForCheck();\n }, 1500);\n }\n\n // ------------------------------------------------------------------\n // Formatting helpers\n // ------------------------------------------------------------------\n\n FormatDecimal(value: number | undefined | null, decimals: number): string {\n if (value == null) return '0';\n return value.toFixed(decimals);\n }\n\n FormatRelativeTime(date: Date): string {\n const now = Date.now();\n const then = date instanceof Date ? date.getTime() : new Date(date).getTime();\n const diffMs = now - then;\n const diffMin = Math.floor(diffMs / 60000);\n\n if (diffMin < 1) return 'just now';\n if (diffMin < 60) return `${diffMin}m ago`;\n\n const diffHours = Math.floor(diffMin / 60);\n if (diffHours < 24) return `${diffHours}h ago`;\n\n const diffDays = Math.floor(diffHours / 24);\n if (diffDays < 7) return `${diffDays}d ago`;\n\n const diffWeeks = Math.floor(diffDays / 7);\n if (diffWeeks < 4) return `${diffWeeks}w ago`;\n\n const diffMonths = Math.floor(diffDays / 30);\n return `${diffMonths}mo ago`;\n }\n\n // ------------------------------------------------------------------\n // Private helpers\n // ------------------------------------------------------------------\n\n private resetForm(): void {\n this.FormState = { rating: null, isCorrect: null, comments: '' };\n }\n\n private applyHistoryFilters(): void {\n let items = [...this.HistoryItems];\n\n // Search filter\n if (this.HistorySearchText) {\n const term = this.HistorySearchText.toLowerCase();\n items = items.filter(i => i.testName.toLowerCase().includes(term));\n }\n\n // Sort\n items.sort((a, b) => {\n switch (this.HistorySortField) {\n case 'date':\n return b.runDateTime.getTime() - a.runDateTime.getTime();\n case 'rating':\n return (b.humanRating ?? 0) - (a.humanRating ?? 0);\n case 'test-name':\n return a.testName.localeCompare(b.testName);\n default:\n return 0;\n }\n });\n\n this.FilteredHistoryItems = items;\n }\n\n private emitState(): void {\n this.StateChange.emit({\n viewMode: this.CurrentView,\n // Richer state for the dashboard's agent context (queue depth, reviewed\n // count, avg human rating, agreement rate, history search).\n pendingCount: this.PendingCount,\n reviewedCount: this.Metrics?.humanReviewedCount ?? 0,\n averageHumanRating: this.Metrics?.humanAvgRating ?? 0,\n agreementRate: this.Metrics?.agreementRate ?? 0,\n historySearchText: this.HistorySearchText\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"testing-review.component.js","sourceRoot":"","sources":["../../../src/Testing/components/testing-review.component.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAGT,KAAK,EACL,MAAM,EACN,YAAY,EAEZ,uBAAuB,EACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,OAAO,EAA8C,MAAM,MAAM,CAAC;AAC3E,OAAO,EAAE,SAAS,EAAoB,MAAM,gBAAgB,CAAC;AAQ7D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAyB,MAAM,4BAA4B,CAAC;;;;;;;;;;IAiBjF,wBAAyD;;;IAAzD,uGAA0C;;;;IAA3B,6CAAyB;;;IAShC,mCAKgB;;;IAHd,2CAAsB;;;IAW5B,wBAAyD;;;;IAfzD,AAJF,AADF,sCAAgB,wBAI0C,aAC5C;IACR,gHAAwB;IAQ1B,iBAAM;IAEJ,AADF,8BAAa,2BACuD;IAAtB,yMAAW,gBAAS,KAAC;IAErE,AADE,AADoE,iBAAoB,EAClF,EACS;IACjB,oCAAc;IACZ,uGAA0C;IAE9C,AADE,iBAAe,EACA;;;;IAhBX,eAOC;IAPD,kDAOC;IAGkB,eAAwB;IAAxB,6CAAwB;IAI9B,eAAyB;IAAzB,6CAAyB;;;IAS1C,4BAAgB;IAAA,YAAuB;IAAA,iBAAI;;;IAA3B,cAAuB;IAAvB,8CAAuB;;;;IAW/B,kCAA0F;IAA7B,0QAAS,4BAAkB,KAAC;IAAC,wBAAQ;IAAA,iBAAS;;;IAL/G,yBAAG;IACD,YACa;IAAA,4BAAM;IAAA,YAAa;IAAA,iBAAO;IACvC,YACA;IAAA,8HAAiB;IAGnB,iBAAI;;;IANF,cACa;IADb,yDACa;IAAM,eAAa;IAAb,gCAAa;IAChC,cACA;IADA,uFACA;IAAA,cAEC;IAFD,uCAEC;;;IARL,AADF,kCAAsE,SAChE;IAAA,mCAAmB;IAAA,iBAAK;IAC5B,qIASC;IACH,iBAAU;;;IAVR,eASC;IATD,kCASC;;;IAQC,AADF,AADF,+BAAqB,cACG,cACS;IAC3B,wBAA0C;IAC5C,iBAAM;IAEJ,AADF,+BAAyB,cACA;IAAA,YAA+B;IAAA,iBAAM;IAC5D,+BAAuB;IAAA,8BAAc;IAEzC,AADE,AADuC,iBAAM,EACvC,EACF;IAEJ,AADF,+BAAsB,eACQ;IAC1B,yBAAwC;IAC1C,iBAAM;IAEJ,AADF,gCAAyB,eACA;IAAA,aAAgC;IAAA,iBAAM;IAC7D,gCAAuB;IAAA,yBAAQ;IAEnC,AADE,AADiC,iBAAM,EACjC,EACF;IAEJ,AADF,gCAAsB,eACO;IACzB,yBAAgC;IAClC,iBAAM;IAEJ,AADF,gCAAyB,eACA;IAAA,aAA8C;IAAA,iCAAuB;IAAA,oBAAG;IAAO,AAAP,iBAAO,EAAM;IAC5G,gCAAuB;IAAA,2BAAU;IAErC,AADE,AADmC,iBAAM,EACnC,EACF;IAEJ,AADF,gCAAsB,eACO;IACzB,yBAAqC;IACvC,iBAAM;IAEJ,AADF,gCAAyB,eACA;IAAA,aAA6C;IAAA,iCAAuB;IAAA,kBAAC;IAAO,AAAP,iBAAO,EAAM;IACzG,gCAAuB;IAAA,+BAAc;IAG3C,AADE,AADE,AADuC,iBAAM,EACvC,EACF,EACF;;;IA/BuB,eAA+B;IAA/B,sDAA+B;IAS/B,eAAgC;IAAhC,uDAAgC;IAShC,eAA8C;IAA9C,4EAA8C;IAS9C,gBAA6C;IAA7C,2EAA6C;;;IAyBtE,qCAEuD;;;IAW7C,AADF,+BAAuB,cACE;IAAA,YAAmB;IAAA,iBAAM;IAE9C,AADF,+BAAuB,eACG;IACtB,wBAAiC;IACjC,YACF;IAAA,iBAAO;IAEP,AADA,0CAA0F,gCACL;IAEzF,AADE,iBAAM,EACF;;;;IATmB,eAAmB;IAAnB,sCAAmB;IAItC,eACF;IADE,+EACF;IACqB,cAA6B;IAAC,AAA9B,8CAA6B,iBAAiB;IAC5C,cAAqC;IAArC,gDAAqC;;;IAO5D,wBAAyC;IAAC,6BAC5C;;;IACE,wBAAgD;IAAC,gCACnD;;;IACE,wBAA2C;IAAC,oCAC9C;;;IAPF,gCAA6D;IAKzD,AAFA,AAFF,6IAAqC,uHAEa,uHAEzC;IAGX,iBAAO;;;IARoB,yCAAiC;IAC1D,cAMC;IAND,uGAMC;;;;IAUK,kCAGkC;IAA1B,2QAAS,0BAAe,KAAC;IAC/B,YACF;IAAA,iBAAS;;;;IAHD,AADA,+FAAoE,8CAC5B;IAE9C,cACF;IADE,sCACF;;;IAGA,gCAA6B;IAAA,YAAyB;IAAA,iBAAO;;;IAAhC,cAAyB;IAAzB,yDAAyB;;;;IAX1D,AADF,AAFF,+BAA+B,cAEH,gBACE;IAAA,sBAAM;IAAA,iBAAQ;IACxC,+BAAwB;IACtB,8KAOC;IACD,yJAAgC;IAIpC,AADE,iBAAM,EACF;IAIJ,AADF,+BAA0B,gBACE;IAAA,iDAAgC;IAAA,iBAAQ;IAEhE,AADF,gCAA6B,kBAGe;IAAlC,6OAAS,yBAAkB,IAAI,CAAC,KAAC;IACvC,yBAAiC;IAAC,0BACpC;IAAA,iBAAS;IACT,mCAE2C;IAAnC,6OAAS,yBAAkB,KAAK,CAAC,KAAC;IACxC,yBAAiC;IAAC,4BACpC;IAEJ,AADE,AADE,iBAAS,EACL,EACF;IAIJ,AADF,gCAA0B,iBACE;IAAA,iCAAgB;IAAA,iBAAQ;IAClD,qCAI6C;IAAnC,qPAAS,+BAAwB,KAAC;IAC9C,AAD+C,iBAAW,EACpD;IAIJ,AADF,gCAA0B,kBAGa;IAA7B,2RAAS,4BAAkB,KAAC;IAClC,yBAAuC;IACvC,aACF;IAAA,iBAAS;IACT,mCAA8C;IAArB,6OAAS,iBAAU,KAAC;IAC3C,yBAAmC;IAAC,uBACtC;IAEJ,AADE,AADE,iBAAS,EACL,EACF;;;IArDA,eAOC;IAPD,mCAOC;IACD,eAEC;IAFD,0DAEC;IASO,eAA6C;IAA7C,6DAA6C;IAK7C,eAA8C;IAA9C,8DAA8C;IAa9C,eAA4B;IAA5B,iDAA4B;IAO9B,eAAyC;IAAzC,qEAAyC;IAG/C,eACF;IADE,iFACF;;;;IAnFR,8CAIkE;IAAhE,4RAAkB,sDAA4C,KAAC;IAyB/D,AAXA,AAbA,yIAA8B,4HAaE,6HAWH;IA6D/B,iBAAqB;;;;IAvFnB,AADA,gCAAkB,sDACyB;;;IALjD,+BAAwB;IACtB,oJA4FC;IACH,iBAAM;;;IA7FJ,cA4FC;IA5FD,kCA4FC;;;IAnGP,+BAA0B;IAKtB,AAJF,iIAAiC,gGAIxB;IAiGX,iBAAM;;;IArGJ,cAoGC;IApGD,0DAoGC;;;IA2BC,qCAE0E;;;IAe9D,4BAAmG;;;;IAA1E,uFAAkE;;;IAO7F,iCAA8B;IAC5B,wBAAiC;IAAC,yBACpC;IAAA,iBAAO;;;IAEP,iCAAgC;IAC9B,wBAAiC;IAAC,2BACpC;IAAA,iBAAO;;;;IAOT,gCAE6C;IAAxC,2RAAS,wCAA6B,KAAC;IAC1C,yBAAG;IAAA,YAAwB;IAC7B,AAD6B,iBAAI,EAC3B;;;;IAHD,oEAAgD;IAEhD,eAAwB;IAAxB,4CAAwB;;;IAhC7B,AADF,AADF,+BAA0B,cACO,cACH;IAAA,YAAmB;IAAA,iBAAM;IACnD,+BAA0B;IACxB,wBAAoC;IACpC,YACF;IACF,AADE,iBAAM,EACF;IAEJ,AADF,+BAA4B,cACD;IACvB,gKAEC;IACH,iBAAM;IACN,iCAA2B;IAAA,aAA8B;IAC3D,AAD2D,iBAAO,EAC5D;IACN,iCAA6B;IAKzB,AAJF,+IAAoC,yHAIQ;IAK9C,iBAAM;IACN,iCAAgC;IAC9B,2CAAiF;IACnF,iBAAM;IACN,8IAA0B;IAO5B,iBAAM;;;;IAnCwB,eAAmB;IAAnB,uCAAmB;IAG3C,eACF;IADE,gFACF;IAIE,eAEC;IAFD,mCAEC;IAEwB,eAA8B;IAA9B,2DAA8B;IAGzD,eAQC;IARD,qGAQC;IAGoB,eAAoB;IAAC,AAArB,sCAAoB,iBAAiB;IAE5D,cAMC;IAND,kDAMC;;;IAtCP,+BAA0B;IACxB,uIAuCC;IACH,iBAAM;;;IAxCJ,cAuCC;IAvCD,0CAuCC;;;;IA9DH,AADF,AAFF,+BAA0B,cAEK,cACC;IAC1B,wBAAkC;IAClC,iCAG2C;IAApC,gNAAS,8BAAuB,KAAC;IAC1C,AAJE,iBAG2C,EACvC;IAEJ,AADF,+BAA0B,YACjB;IAAA,wBAAQ;IAAA,iBAAQ;IACvB,kCAA0E;IAAvC,mNAAU,kCAA2B,KAAC;IACvE,kCAAqB;IAAA,qBAAI;IAAA,iBAAS;IAClC,mCAAuB;IAAA,uBAAM;IAAA,iBAAS;IACtC,mCAA0B;IAAA,0BAAS;IAGzC,AADE,AADE,AADqC,iBAAS,EACrC,EACL,EACF;IAMJ,AAJF,mIAAyC,kGAIhC;IA4CX,iBAAM;;;IA7DO,eAA2B;IAA3B,gDAA2B;IAK1B,eAA0B;IAA1B,+CAA0B;IAQtC,eA+CC;IA/CD,oEA+CC;;;IA+BG,oCAA4B;IAC1B,iFACF;IAAA,iBAAW;;;;IA7RrB,8BAAyB;IACvB,oGAAyB;IAGzB,0GAA2B;IAiB3B,wGAAe;IA4CX,AADF,AADF,+BAA6B,cACD,iBACuE;IAA3B,0LAAS,eAAQ,OAAO,CAAC,KAAC;IAC5F,wBAAsC;IACtC,8BACF;IAAA,iBAAS;IACT,kCAAmG;IAA7B,0LAAS,eAAQ,SAAS,CAAC,KAAC;IAChG,yBAA6C;IAC7C,0BACF;IAEJ,AADE,AADE,iBAAS,EACL,EACF;IAGN,yGAA+B;IA2G/B,0GAAiC;IA0E/B,AADF,gCAAiC,UAC3B;IACF,yBAA0C;IAC1C,0CACF;IAAA,iBAAK;IAGD,AADF,AADF,gCAA8B,eACD,eACD;;IACtB,gCAA6C;IAE3C,AADA,8BAAmF,kBAO3C;IAC1C,iBAAM;;IACN,gCAAyB;IAAA,aAAsC;IAEnE,AADE,AADiE,iBAAM,EACjE,EACF;IAEJ,AADF,gCAA8B,aACO;IACjC,6FACF;IAAA,iBAAI;IACJ,8GAA0B;IAQlC,AADE,AADE,AADE,iBAAM,EACF,EACF,EACF;;;IAjSJ,cAEC;IAFD,mDAEC;IACD,cAcC;IAdD,qDAcC;IAGD,cAuCC;IAvCD,yCAuCC;IAK8B,eAAwC;IAAxC,wDAAwC;IAIxC,eAA0C;IAA1C,0DAA0C;IAQzE,eAwGC;IAxGD,0DAwGC;IAGD,cAsEC;IAtED,4DAsEC;IAciB,gBAA8B;;IAOf,eAAsC;IAAtC,6EAAsC;IAOjE,eAIC;IAJD,qDAIC;;AAqwBb,MAAM,OAAO,sBAAsB;IAmDvB;IACA;IAnDD,YAAY,GAAmC,IAAI,CAAC;IAE7D,4CAA4C;IAC5C,IAAa,YAAY,CAAC,KAAqC;QAC7D,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC5B,CAAC;IACD,4CAA4C;IAC5C,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IACD,8FAA8F;IACrF,WAAW,GAAG,KAAK,CAAC;IACnB,WAAW,GAAG,IAAI,YAAY,EAA2B,CAAC;IAEpE;;;;;;OAMG;IACO,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IAEjC,QAAQ,GAAG,IAAI,OAAO,EAAQ,CAAC;IAEvC,aAAa;IACb,WAAW,GAAa,OAAO,CAAC;IAChC,cAAc,GAAkB,IAAI,CAAC;IACrC,iBAAiB,GAAkB,IAAI,CAAC;IACxC,YAAY,GAAG,KAAK,CAAC;IACrB,YAAY,GAAG,KAAK,CAAC;IACrB,iBAAiB,GAAG,EAAE,CAAC;IACvB,gBAAgB,GAAgB,MAAM,CAAC;IAEvC,+BAA+B;IAC/B,SAAS,GAAoB,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAE7E,OAAO;IACP,YAAY,GAAsB,EAAE,CAAC;IACrC,YAAY,GAAiC,EAAE,CAAC;IAChD,oBAAoB,GAAiC,EAAE,CAAC;IACxD,OAAO,GAAoC,IAAI,CAAC;IAChD,YAAY,GAAG,CAAC,CAAC;IACjB,YAAY,GAAuB,EAAE,CAAC;IACtC,iBAAiB,GAAG,EAAE,CAAC;IAEvB,YAAY;IACH,aAAa,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IAEzD,YACU,sBAAqD,EACrD,GAAsB;QADtB,2BAAsB,GAAtB,sBAAsB,CAA+B;QACrD,QAAG,GAAH,GAAG,CAAmB;IAC7B,CAAC;IAEJ,QAAQ;QACN,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,KAAK,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC/B,CAAC;IAED,kHAAkH;IAClH,KAAK,CAAC,gBAAgB;QACpB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC;YACtD,IAAI,CAAC,QAAQ;gBAAE,OAAO;YACtB,MAAM,IAAI,GAAG,OAAO,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;YACpD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC;gBAChC,UAAU,EAAE,8BAA8B;gBAC1C,WAAW,EAAE,yHAAyH;gBACtI,UAAU,EAAE,QAAQ;gBACpB,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,YAAY,IAAI,wBAAwB,CAAC,CAAC;YACtF,IAAI,CAAC,YAAY,GAAG,kBAAkB,CAAE,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAA+B,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACvG,YAAY,EAAE,GAAG,CAAC,YAAY;gBAC9B,SAAS,EAAE,GAAG,CAAC,SAAS;gBACxB,6BAA6B,EAAE,GAAG,CAAC,6BAA6B;gBAChE,0BAA0B,EAAE,GAAG,CAAC,0BAA0B;gBAC1D,eAAe,EAAE,GAAG,CAAC,eAAe;gBACpC,eAAe,EAAE,GAAG,CAAC,eAAe;gBACpC,QAAQ,EAAE,GAAG,CAAC,QAAQ;gBACtB,kBAAkB,EAAE,GAAG,CAAC,kBAAkB;aAC3C,CAAC,CAAC,CAAC,CAAC;YACL,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;YAC5B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;YACvB,IAAI,CAAC,iBAAiB,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;YACjG,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,KAAa;QACnB,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,aAAa,CAAC,QAAQ,CAAC,gBAAgB,CAAC,eAAe,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACvF,CAAC;IAED,WAAW;QACT,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAC3B,CAAC;IAED,qEAAqE;IACrE,uBAAuB;IACvB,qEAAqE;IAErE,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS,IAAI,IAAI,CAAC;IAC3E,CAAC;IAED,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,OAAO,EAAE,aAAa,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,cAAc;QAChB,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC;QAChC,IAAI,IAAI,IAAI,EAAE;YAAE,OAAO,SAAS,CAAC;QACjC,IAAI,IAAI,IAAI,EAAE;YAAE,OAAO,SAAS,CAAC;QACjC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,aAAa;QACf,MAAM,aAAa,GAAG,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,OAAO;QAC/C,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,aAAa,GAAG,GAAG,CAAC,GAAG,aAAa,CAAC;QAC1D,OAAO,GAAG,MAAM,IAAI,aAAa,EAAE,CAAC;IACtC,CAAC;IAED,qEAAqE;IACrE,SAAS;IACT,qEAAqE;IAE7D,YAAY;QAClB,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,CAAuB,CAAC;QACjE,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YAC3C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAEO,kBAAkB;QACxB,IAAI,CAAC,sBAAsB,CAAC,gBAAgB;aACzC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;aAC9B,SAAS,CAAC,KAAK,CAAC,EAAE;YACjB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC1B,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC;YACjC,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEL,IAAI,CAAC,sBAAsB,CAAC,kBAAkB;aAC3C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;aAC9B,SAAS,CAAC,OAAO,CAAC,EAAE;YACnB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;YACvB,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEL,IAAI,CAAC,sBAAsB,CAAC,qBAAqB;aAC9C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;aAC9B,SAAS,CAAC,IAAI,CAAC,EAAE;YAChB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;YACzD,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC1B,CAAC,CAAC,CAAC;IACP,CAAC;IAED,qEAAqE;IACrE,iBAAiB;IACjB,qEAAqE;IAErE,OAAO,CAAC,IAAc;QACpB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC1B,CAAC;IAED,qEAAqE;IACrE,sBAAsB;IACtB,qEAAqE;IAErE,cAAc,CAAC,SAAiB;QAC9B,OAAO,UAAU,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;IACpD,CAAC;IAED;;;;;;;OAOG;IACH,oBAAoB,CAAC,SAAiB,EAAE,QAAiB;QACvD,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;YAChC,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,CAAC;aAAM,IAAI,UAAU,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,EAAE,CAAC;YACtD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC1B,CAAC;IAED,YAAY,CAAC,CAAS;QACpB,IAAI,CAAC,SAAS,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAClD,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC1B,CAAC;IAED,iBAAiB,CAAC,KAAc;QAC9B,IAAI,CAAC,SAAS,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QACzD,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC1B,CAAC;IAED,gBAAgB,CAAC,KAAY;QAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,MAA6B,CAAC;QACnD,IAAI,CAAC,SAAS,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,IAAqB;QACtC,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QAEnD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAExB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,cAAc,CAC9D,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,SAAS,CAAC,MAAO,EACtB,IAAI,CAAC,SAAS,CAAC,SAAU,EACzB,IAAI,CAAC,SAAS,CAAC,QAAQ,CACxB,CAAC;YAEF,IAAI,OAAO,EAAE,CAAC;gBACZ,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;gBAC3B,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC1B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,QAAQ;QACN,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAC9C,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,CAClD,CAAC;QACF,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,SAAS,EAAE,CAAC;QAEjB,oCAAoC;QACpC,IAAI,YAAY,IAAI,CAAC,IAAI,YAAY,GAAG,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;YACrE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,YAAY,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;QACtE,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC1B,CAAC;IAED,qEAAqE;IACrE,wBAAwB;IACxB,qEAAqE;IAErE,eAAe,CAAC,KAAY;QAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,MAA0B,CAAC;QAChD,IAAI,CAAC,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC;QACtC,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC1B,CAAC;IAED,mBAAmB,CAAC,KAAY;QAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,MAA2B,CAAC;QACjD,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,KAAoB,CAAC;QACpD,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC1B,CAAC;IAED,oBAAoB,CAAC,EAAU;QAC7B,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC1B,CAAC;IAED,qEAAqE;IACrE,WAAW;IACX,qEAAqE;IAErE,OAAO;QACL,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QACxB,IAAI,CAAC,sBAAsB,CAAC,OAAO,EAAE,CAAC;QAEtC,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC1B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC1B,CAAC,EAAE,IAAI,CAAC,CAAC;IACX,CAAC;IAED,qEAAqE;IACrE,sBAAsB;IACtB,qEAAqE;IAErE,aAAa,CAAC,KAAgC,EAAE,QAAgB;QAC9D,IAAI,KAAK,IAAI,IAAI;YAAE,OAAO,GAAG,CAAC;QAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC;IAED,kBAAkB,CAAC,IAAU;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,IAAI,YAAY,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;QAC9E,MAAM,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC;QAC1B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;QAE3C,IAAI,OAAO,GAAG,CAAC;YAAE,OAAO,UAAU,CAAC;QACnC,IAAI,OAAO,GAAG,EAAE;YAAE,OAAO,GAAG,OAAO,OAAO,CAAC;QAE3C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;QAC3C,IAAI,SAAS,GAAG,EAAE;YAAE,OAAO,GAAG,SAAS,OAAO,CAAC;QAE/C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC;QAC5C,IAAI,QAAQ,GAAG,CAAC;YAAE,OAAO,GAAG,QAAQ,OAAO,CAAC;QAE5C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;QAC3C,IAAI,SAAS,GAAG,CAAC;YAAE,OAAO,GAAG,SAAS,OAAO,CAAC;QAE9C,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;QAC7C,OAAO,GAAG,UAAU,QAAQ,CAAC;IAC/B,CAAC;IAED,qEAAqE;IACrE,mBAAmB;IACnB,qEAAqE;IAE7D,SAAS;QACf,IAAI,CAAC,SAAS,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IACnE,CAAC;IAEO,mBAAmB;QACzB,IAAI,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC;QAEnC,gBAAgB;QAChB,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,WAAW,EAAE,CAAC;YAClD,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QACrE,CAAC;QAED,OAAO;QACP,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAClB,QAAQ,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC9B,KAAK,MAAM;oBACT,OAAO,CAAC,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;gBAC3D,KAAK,QAAQ;oBACX,OAAO,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;gBACrD,KAAK,WAAW;oBACd,OAAO,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;gBAC9C;oBACE,OAAO,CAAC,CAAC;YACb,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC;IACpC,CAAC;IAEO,SAAS;QACf,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YACpB,QAAQ,EAAE,IAAI,CAAC,WAAW;YAC1B,wEAAwE;YACxE,4DAA4D;YAC5D,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,aAAa,EAAE,IAAI,CAAC,OAAO,EAAE,kBAAkB,IAAI,CAAC;YACpD,kBAAkB,EAAE,IAAI,CAAC,OAAO,EAAE,cAAc,IAAI,CAAC;YACrD,aAAa,EAAE,IAAI,CAAC,OAAO,EAAE,aAAa,IAAI,CAAC;YAC/C,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;SAC1C,CAAC,CAAC;IACL,CAAC;gHApXU,sBAAsB;6DAAtB,sBAAsB;YA/jC7B,AAFF,8FAAmB,0EAEV;YA0BT,0HAAsB;;YA5BtB,yCA0BC;;;iFAuiCQ,sBAAsB;cAtkClC,SAAS;6BACI,KAAK,YACP,oBAAoB,mBACb,uBAAuB,CAAC,MAAM,YACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmUT;;kBAgwBA,KAAK;;kBAGL,KAAK;;kBAQL,KAAK;;kBACL,MAAM;;kBASN,MAAM;;kFAtBI,sBAAsB","sourcesContent":["import {\n Component,\n OnInit,\n OnDestroy,\n Input,\n Output,\n EventEmitter,\n ChangeDetectorRef,\n ChangeDetectionStrategy\n} from '@angular/core';\nimport { Subject, Observable, combineLatest, BehaviorSubject } from 'rxjs';\nimport { takeUntil, map, shareReplay } from 'rxjs/operators';\nimport {\n TestingInstrumentationService,\n FeedbackPending,\n FeedbackStats,\n TestRunWithFeedbackSummary,\n EvaluationSummaryMetrics\n} from '../services/testing-instrumentation.service';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { CompositeKey, RunView } from '@memberjunction/core';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { CohortDisagreement, type DisagreementItem } from '@memberjunction/ng-testing';\n\ntype ViewMode = 'queue' | 'history';\ntype HistorySort = 'date' | 'rating' | 'test-name';\n\ninterface ReviewFormState {\n rating: number | null;\n isCorrect: boolean | null;\n comments: string;\n}\n\n@Component({\n standalone: false,\n selector: 'app-testing-review',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (HideToolbar) {\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n } @else {\n <mj-page-layout>\n <mj-page-header\n Title=\"Human Review\"\n Icon=\"fa-solid fa-clipboard-check\"\n Subtitle=\"Human-in-the-loop review for test outcomes\">\n <div meta>\n @if (PendingCount > 0) {\n <mj-stat-badge\n Icon=\"fa-solid fa-hourglass-half\"\n [Count]=\"PendingCount\"\n Label=\"pending\"\n Variant=\"warning\">\n </mj-stat-badge>\n }\n </div>\n <div actions>\n <mj-refresh-button [Loading]=\"IsRefreshing\" (Clicked)=\"Refresh()\"></mj-refresh-button>\n </div>\n </mj-page-header>\n <mj-page-body>\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n </mj-page-body>\n </mj-page-layout>\n }\n\n <ng-template #content>\n <!-- Inner page content -->\n <div class=\"review-page\">\n @if (DisagreementError) {\n <p role=\"alert\">{{ DisagreementError }}</p>\n }\n @if (Disagreement.length) {\n <section class=\"rubric-disagreement\" aria-label=\"Rubric disagreement\">\n <h3>Rubric disagreement</h3>\n @for (row of Disagreement; track $index) {\n <p>\n {{ row.name }}\n CriterionKey <code>{{ row.key }}</code>\n — human {{ row.humanMean }} / AI {{ row.aiMean }}\n @if (row.runId) {\n <button type=\"button\" mjButton variant=\"secondary\" size=\"sm\" (click)=\"OpenRun(row.runId)\">Open run</button>\n }\n </p>\n }\n </section>\n }\n\n <!-- KPI Summary Row -->\n @if (Metrics) {\n <div class=\"kpi-row\">\n <div class=\"kpi-card\">\n <div class=\"kpi-icon orange\">\n <i class=\"fa-solid fa-hourglass-half\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ Metrics.humanPendingCount }}</div>\n <div class=\"kpi-label\">Pending Review</div>\n </div>\n </div>\n <div class=\"kpi-card\">\n <div class=\"kpi-icon green\">\n <i class=\"fa-solid fa-check-circle\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ Metrics.humanReviewedCount }}</div>\n <div class=\"kpi-label\">Reviewed</div>\n </div>\n </div>\n <div class=\"kpi-card\">\n <div class=\"kpi-icon gold\">\n <i class=\"fa-solid fa-star\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ FormatDecimal(Metrics.humanAvgRating, 1) }}<span class=\"kpi-unit\">/10</span></div>\n <div class=\"kpi-label\">Avg Rating</div>\n </div>\n </div>\n <div class=\"kpi-card\">\n <div class=\"kpi-icon blue\">\n <i class=\"fa-solid fa-handshake\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ FormatDecimal(Metrics.agreementRate, 0) }}<span class=\"kpi-unit\">%</span></div>\n <div class=\"kpi-label\">Agreement Rate</div>\n </div>\n </div>\n </div>\n }\n\n <!-- View Mode Toggle -->\n <div class=\"view-toggle-bar\">\n <div class=\"toggle-group\">\n <button class=\"toggle-btn\" [class.active]=\"CurrentView === 'queue'\" (click)=\"SetView('queue')\">\n <i class=\"fa-solid fa-list-check\"></i>\n Review Queue\n </button>\n <button class=\"toggle-btn\" [class.active]=\"CurrentView === 'history'\" (click)=\"SetView('history')\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n History\n </button>\n </div>\n </div>\n\n <!-- Review Queue View -->\n @if (CurrentView === 'queue') {\n <div class=\"content-card\">\n @if (PendingItems.length === 0) {\n <mj-empty-state Variant=\"success\"\n Title=\"All caught up!\"\n Message=\"No tests currently require human review.\" />\n } @else {\n <div class=\"queue-list\">\n @for (item of PendingItems; track item.testRunID) {\n <mj-accordion-panel\n Size=\"sm\"\n [FlushBody]=\"true\"\n [Expanded]=\"IsItemExpanded(item.testRunID)\"\n (ExpandedChange)=\"OnItemExpandedChange(item.testRunID, $event)\">\n <ng-template mjAccordionTitle>\n <div class=\"item-info\">\n <div class=\"item-name\">{{ item.testName }}</div>\n <div class=\"item-meta\">\n <span class=\"meta-time\">\n <i class=\"fa-solid fa-clock\"></i>\n {{ FormatRelativeTime(item.runDateTime) }}\n </span>\n <app-score-indicator [score]=\"item.automatedScore\" [showBar]=\"true\"></app-score-indicator>\n <app-test-status-badge [status]=\"$any(item.automatedStatus)\"></app-test-status-badge>\n </div>\n </div>\n </ng-template>\n <ng-template mjAccordionActions>\n <span class=\"reason-badge\" [class]=\"'reason-' + item.reason\">\n @if (item.reason === 'no-feedback') {\n <i class=\"fa-solid fa-comment-slash\"></i> No Feedback\n } @else if (item.reason === 'high-score-failed') {\n <i class=\"fa-solid fa-triangle-exclamation\"></i> Score Mismatch\n } @else {\n <i class=\"fa-solid fa-circle-question\"></i> Needs Verification\n }\n </span>\n </ng-template>\n <ng-template mjAccordionBody>\n <div class=\"review-form-panel\">\n <!-- Rating -->\n <div class=\"form-section\">\n <label class=\"form-label\">Rating</label>\n <div class=\"rating-row\">\n @for (n of RatingNumbers; track n) {\n <button class=\"rating-circle\"\n [class.selected]=\"FormState.rating != null && n <= FormState.rating\"\n [class.current]=\"FormState.rating === n\"\n (click)=\"SelectRating(n)\">\n {{ n }}\n </button>\n }\n @if (FormState.rating != null) {\n <span class=\"rating-display\">{{ FormState.rating }}/10</span>\n }\n </div>\n </div>\n\n <!-- Correctness -->\n <div class=\"form-section\">\n <label class=\"form-label\">Is the automated result correct?</label>\n <div class=\"correctness-row\">\n <button class=\"correctness-btn correct\"\n [class.active]=\"FormState.isCorrect === true\"\n (click)=\"SelectCorrectness(true)\">\n <i class=\"fa-solid fa-check\"></i> Correct\n </button>\n <button class=\"correctness-btn incorrect\"\n [class.active]=\"FormState.isCorrect === false\"\n (click)=\"SelectCorrectness(false)\">\n <i class=\"fa-solid fa-xmark\"></i> Incorrect\n </button>\n </div>\n </div>\n\n <!-- Notes -->\n <div class=\"form-section\">\n <label class=\"form-label\">Notes (optional)</label>\n <textarea class=\"notes-textarea\"\n rows=\"3\"\n placeholder=\"Add any comments about this evaluation...\"\n [value]=\"FormState.comments\"\n (input)=\"OnCommentsChange($event)\"></textarea>\n </div>\n\n <!-- Actions -->\n <div class=\"form-actions\">\n <button class=\"submit-btn\"\n [disabled]=\"!IsFormValid || IsSubmitting\"\n (click)=\"SubmitReview(item)\">\n <i class=\"fa-solid fa-paper-plane\"></i>\n {{ IsSubmitting ? 'Submitting...' : 'Submit' }}\n </button>\n <button class=\"skip-btn\" (click)=\"SkipItem()\">\n <i class=\"fa-solid fa-forward\"></i> Skip\n </button>\n </div>\n </div>\n </ng-template>\n </mj-accordion-panel>\n }\n </div>\n }\n </div>\n }\n\n <!-- History View -->\n @if (CurrentView === 'history') {\n <div class=\"content-card\">\n <!-- Filter Bar -->\n <div class=\"history-filters\">\n <div class=\"search-wrapper\">\n <i class=\"fa-solid fa-search\"></i>\n <input type=\"text\"\n placeholder=\"Search by test name...\"\n [value]=\"HistorySearchText\"\n (input)=\"OnHistorySearch($event)\" />\n </div>\n <div class=\"sort-control\">\n <label>Sort by:</label>\n <select [value]=\"HistorySortField\" (change)=\"OnHistorySortChange($event)\">\n <option value=\"date\">Date</option>\n <option value=\"rating\">Rating</option>\n <option value=\"test-name\">Test Name</option>\n </select>\n </div>\n </div>\n\n @if (FilteredHistoryItems.length === 0) {\n <mj-empty-state Icon=\"fa-solid fa-folder-open\"\n Title=\"No reviewed items\"\n Message=\"Reviewed tests will appear here once feedback is submitted.\" />\n } @else {\n <div class=\"history-list\">\n @for (item of FilteredHistoryItems; track item.id) {\n <div class=\"history-item\">\n <div class=\"history-item-main\">\n <div class=\"history-name\">{{ item.testName }}</div>\n <div class=\"history-date\">\n <i class=\"fa-solid fa-calendar\"></i>\n {{ FormatRelativeTime(item.runDateTime) }}\n </div>\n </div>\n <div class=\"history-rating\">\n <div class=\"rating-dots\">\n @for (n of RatingNumbers; track n) {\n <span class=\"rating-dot\" [class.filled]=\"item.humanRating != null && n <= item.humanRating\"></span>\n }\n </div>\n <span class=\"rating-label\">{{ item.humanRating ?? 0 }}/10</span>\n </div>\n <div class=\"history-verdict\">\n @if (item.humanIsCorrect === true) {\n <span class=\"verdict correct\">\n <i class=\"fa-solid fa-check\"></i> Correct\n </span>\n } @else if (item.humanIsCorrect === false) {\n <span class=\"verdict incorrect\">\n <i class=\"fa-solid fa-xmark\"></i> Incorrect\n </span>\n }\n </div>\n <div class=\"history-auto-score\">\n <app-score-indicator [score]=\"item.score\" [showBar]=\"true\"></app-score-indicator>\n </div>\n @if (item.humanComments) {\n <div class=\"history-comments\"\n [class.expanded]=\"ExpandedHistoryId === item.id\"\n (click)=\"ToggleHistoryComment(item.id)\">\n <p>{{ item.humanComments }}</p>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- Calibration Insights -->\n <div class=\"calibration-section\">\n <h3>\n <i class=\"fa-solid fa-scale-balanced\"></i>\n Human vs Auto Agreement\n </h3>\n <div class=\"calibration-body\">\n <div class=\"gauge-display\">\n <div class=\"gauge-ring\">\n <svg viewBox=\"0 0 120 120\" class=\"gauge-svg\">\n <circle cx=\"60\" cy=\"60\" r=\"52\" fill=\"none\" class=\"gauge-track\" stroke-width=\"10\" />\n <circle cx=\"60\" cy=\"60\" r=\"52\" fill=\"none\"\n [attr.stroke]=\"AgreementColor\"\n stroke-width=\"10\"\n stroke-linecap=\"round\"\n [attr.stroke-dasharray]=\"AgreementDash\"\n stroke-dashoffset=\"0\"\n transform=\"rotate(-90 60 60)\" />\n </svg>\n <div class=\"gauge-value\">{{ FormatDecimal(AgreementRate, 0) }}%</div>\n </div>\n </div>\n <div class=\"calibration-text\">\n <p class=\"calibration-description\">\n Measures how often human reviewers agree with automated evaluation scores.\n </p>\n @if (AgreementRate < 70) {\n <mj-alert Variant=\"warning\">\n Low agreement may indicate evaluation criteria need refinement.\n </mj-alert>\n }\n </div>\n </div>\n </div>\n </div>\n </ng-template>\n `,\n styles: [`\n :host {\n display: block;\n height: 100%;\n }\n\n .review-page {\n padding: 20px;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-surface-card);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n /* Page Header */\n .page-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n background: var(--mj-bg-surface);\n padding: 20px;\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n }\n\n .header-left {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .header-left h2 {\n margin: 0;\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .header-left h2 i {\n color: var(--mj-brand-primary);\n }\n\n .pending-badge {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 14px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n border: 1px solid var(--mj-status-warning);\n border-radius: 20px;\n }\n\n .badge-count {\n background: var(--mj-status-warning);\n color: var(--mj-text-inverse);\n min-width: 24px;\n height: 24px;\n padding: 0 6px;\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n font-weight: 700;\n }\n\n .badge-text {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-status-warning);\n }\n\n .refresh-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 18px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .refresh-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-border-strong);\n }\n\n .refresh-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n /* KPI Row */\n .kpi-row {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n margin-bottom: 20px;\n }\n\n .kpi-card {\n background: var(--mj-bg-surface);\n padding: 20px;\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .kpi-icon {\n width: 48px;\n height: 48px;\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n color: var(--mj-text-inverse);\n flex-shrink: 0;\n }\n\n .kpi-icon.orange { background: var(--mj-status-warning); }\n .kpi-icon.green { background: var(--mj-status-success); }\n .kpi-icon.gold { background: var(--mj-status-warning); }\n .kpi-icon.blue { background: var(--mj-brand-primary); }\n\n .kpi-content {\n flex: 1;\n min-width: 0;\n }\n\n .kpi-value {\n font-size: 26px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1;\n margin-bottom: 4px;\n }\n\n .kpi-unit {\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-muted);\n }\n\n .kpi-label {\n font-size: 11px;\n color: var(--mj-text-muted);\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n /* View Toggle */\n .view-toggle-bar {\n margin-bottom: 20px;\n }\n\n .toggle-group {\n display: inline-flex;\n background: var(--mj-bg-surface);\n border-radius: 10px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n }\n\n .toggle-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 12px 24px;\n border: none;\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .toggle-btn:hover:not(.active) {\n background: var(--mj-bg-surface-card);\n }\n\n .toggle-btn.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n /* Content Card */\n .content-card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n margin-bottom: 20px;\n }\n\n /* Queue List */\n .queue-list {\n max-height: 600px;\n overflow-y: auto;\n }\n\n /* Queue items migrated to <mj-accordion-panel> — the panel owns the row\n chrome (border, hover, expanded state) and the chevron toggle, so the\n bespoke .queue-item / .queue-item-header / .expand-toggle styles were\n removed. */\n\n .item-info {\n flex: 1;\n min-width: 0;\n }\n\n .item-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin-bottom: 6px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .item-meta {\n display: flex;\n gap: 14px;\n font-size: 12px;\n color: var(--mj-text-muted);\n align-items: center;\n flex-wrap: wrap;\n }\n\n .meta-time {\n display: flex;\n align-items: center;\n gap: 5px;\n }\n\n /* .item-actions-area removed — the reason badge now lives in the accordion's\n [mjAccordionActions] slot, which provides its own flex container. */\n\n .reason-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border-radius: 16px;\n font-size: 11px;\n font-weight: 600;\n }\n\n .reason-no-feedback {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n }\n\n .reason-high-score-failed {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n }\n\n .reason-low-score-passed {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n }\n\n /* .expand-toggle removed — the bespoke chevron was replaced by\n <mj-accordion-panel>'s built-in chevron. */\n\n /* Review Form — rendered in the accordion body ([FlushBody]=\"true\"), so it\n supplies its own padding; the header/body separator is owned by the\n accordion (border-top removed to avoid a doubled line). */\n .review-form-panel {\n padding: 20px 20px 20px 36px;\n background: var(--mj-bg-surface);\n }\n\n .form-section {\n margin-bottom: 20px;\n }\n\n .form-section:last-of-type {\n margin-bottom: 16px;\n }\n\n .form-label {\n display: block;\n font-size: 12px;\n font-weight: 700;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 10px;\n }\n\n /* Rating Circles */\n .rating-row {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .rating-circle {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n border: 2px solid var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-disabled);\n font-size: 13px;\n font-weight: 700;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n }\n\n .rating-circle:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n transform: scale(1.1);\n }\n\n .rating-circle.selected {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .rating-circle.current {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n color: var(--mj-text-inverse);\n box-shadow: var(--mj-shadow-sm);\n }\n\n .rating-display {\n margin-left: 12px;\n font-size: 16px;\n font-weight: 700;\n color: var(--mj-text-primary);\n }\n\n /* Correctness Buttons */\n .correctness-row {\n display: flex;\n gap: 12px;\n }\n\n .correctness-btn {\n flex: 1;\n max-width: 200px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 12px 20px;\n border: 2px solid var(--mj-border-strong);\n border-radius: 10px;\n background: var(--mj-bg-surface-card);\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .correctness-btn.correct:hover,\n .correctness-btn.correct.active {\n border-color: var(--mj-status-success);\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n .correctness-btn.incorrect:hover,\n .correctness-btn.incorrect.active {\n border-color: var(--mj-status-error);\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n }\n\n /* Notes */\n .notes-textarea {\n width: 100%;\n max-width: 500px;\n padding: 12px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 8px;\n font-size: 14px;\n font-family: inherit;\n resize: vertical;\n min-height: 72px;\n box-sizing: border-box;\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n }\n\n .notes-textarea:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n }\n\n /* Form Actions */\n .form-actions {\n display: flex;\n gap: 12px;\n }\n\n .submit-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 11px 22px;\n border: none;\n border-radius: 8px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 14px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .submit-btn:hover:not(:disabled) {\n transform: translateY(-1px);\n box-shadow: var(--mj-shadow-md);\n }\n\n .submit-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .skip-btn {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 11px 20px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .skip-btn:hover {\n border-color: var(--mj-border-strong);\n background: var(--mj-bg-surface-card);\n }\n\n /* History Filters */\n .history-filters {\n display: flex;\n gap: 16px;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n align-items: center;\n }\n\n .search-wrapper {\n flex: 1;\n position: relative;\n display: flex;\n align-items: center;\n }\n\n .search-wrapper > i {\n position: absolute;\n left: 12px;\n color: var(--mj-text-disabled);\n font-size: 13px;\n }\n\n .search-wrapper input {\n width: 100%;\n padding: 10px 12px 10px 36px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 13px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n }\n\n .search-wrapper input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n }\n\n .sort-control {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n }\n\n .sort-control select {\n padding: 8px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n }\n\n /* History List */\n .history-list {\n max-height: 600px;\n overflow-y: auto;\n }\n\n .history-item {\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n display: flex;\n align-items: center;\n gap: 16px;\n flex-wrap: wrap;\n }\n\n .history-item:last-child {\n border-bottom: none;\n }\n\n .history-item-main {\n flex: 1;\n min-width: 160px;\n }\n\n .history-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin-bottom: 4px;\n }\n\n .history-date {\n font-size: 12px;\n color: var(--mj-text-disabled);\n display: flex;\n align-items: center;\n gap: 5px;\n }\n\n .history-rating {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .rating-dots {\n display: flex;\n gap: 3px;\n }\n\n .rating-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: var(--mj-border-default);\n }\n\n .rating-dot.filled {\n background: var(--mj-status-warning);\n }\n\n .rating-label {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-muted);\n }\n\n .history-verdict {\n flex-shrink: 0;\n }\n\n .verdict {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 5px 12px;\n border-radius: 14px;\n font-size: 11px;\n font-weight: 600;\n }\n\n .verdict.correct {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n .verdict.incorrect {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n }\n\n .history-auto-score {\n flex-shrink: 0;\n }\n\n .history-comments {\n width: 100%;\n margin-top: 8px;\n padding: 10px 14px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n border-left: 3px solid var(--mj-brand-primary);\n cursor: pointer;\n }\n\n .history-comments p {\n margin: 0;\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .history-comments.expanded p {\n white-space: normal;\n }\n\n /* Calibration Section */\n .calibration-section {\n background: var(--mj-bg-surface);\n padding: 24px;\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n }\n\n .calibration-section h3 {\n margin: 0 0 20px 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .calibration-section h3 i {\n color: var(--mj-brand-primary);\n }\n\n .calibration-body {\n display: flex;\n align-items: center;\n gap: 32px;\n }\n\n .gauge-display {\n flex-shrink: 0;\n }\n\n .gauge-ring {\n position: relative;\n width: 120px;\n height: 120px;\n }\n\n .gauge-svg {\n width: 100%;\n height: 100%;\n }\n\n .gauge-track {\n stroke: var(--mj-border-default);\n }\n\n .gauge-value {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 24px;\n font-weight: 800;\n color: var(--mj-text-primary);\n }\n\n .calibration-text {\n flex: 1;\n }\n\n .calibration-description {\n margin: 0 0 12px 0;\n font-size: 14px;\n color: var(--mj-text-muted);\n line-height: 1.6;\n }\n\n\n /* (Removed .queue-item fadeIn animation — the queue-item wrapper no longer\n exists; the accordion panel owns its own reveal.) */\n\n /* Responsive */\n @media (max-width: 1024px) {\n .kpi-row {\n grid-template-columns: repeat(2, 1fr);\n }\n }\n\n @media (max-width: 768px) {\n .review-page {\n padding: 16px;\n }\n\n .page-header {\n flex-direction: column;\n gap: 16px;\n align-items: flex-start;\n }\n\n .kpi-row {\n grid-template-columns: 1fr 1fr;\n }\n\n .kpi-card {\n padding: 16px;\n }\n\n .kpi-value {\n font-size: 22px;\n }\n\n .correctness-row {\n flex-direction: column;\n }\n\n .correctness-btn {\n max-width: 100%;\n }\n\n .history-item {\n flex-direction: column;\n align-items: flex-start;\n }\n\n .calibration-body {\n flex-direction: column;\n align-items: center;\n text-align: center;\n }\n }\n `]\n})\nexport class TestingReviewComponent implements OnInit, OnDestroy {\n @Input() InitialState: Record<string, unknown> | null = null;\n\n /** @deprecated Use {@link InitialState}. */\n @Input() set initialState(value: Record<string, unknown> | null) {\n this.InitialState = value;\n }\n /** @deprecated Use {@link InitialState}. */\n get initialState(): Record<string, unknown> | null {\n return this.InitialState;\n }\n /** When true, the inner bespoke .page-header is hidden — the parent shell owns the chrome. */\n @Input() HideToolbar = false;\n @Output() StateChange = new EventEmitter<Record<string, unknown>>();\n\n /**\n * @deprecated Use {@link StateChange}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (stateChange) keeps working. Must stay AFTER StateChange: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() stateChange = this.StateChange;\n\n private destroy$ = new Subject<void>();\n\n // View state\n CurrentView: ViewMode = 'queue';\n ExpandedItemId: string | null = null;\n ExpandedHistoryId: string | null = null;\n IsRefreshing = false;\n IsSubmitting = false;\n HistorySearchText = '';\n HistorySortField: HistorySort = 'date';\n\n // Form state for active review\n FormState: ReviewFormState = { rating: null, isCorrect: null, comments: '' };\n\n // Data\n PendingItems: FeedbackPending[] = [];\n HistoryItems: TestRunWithFeedbackSummary[] = [];\n FilteredHistoryItems: TestRunWithFeedbackSummary[] = [];\n Metrics: EvaluationSummaryMetrics | null = null;\n PendingCount = 0;\n Disagreement: DisagreementItem[] = [];\n DisagreementError = '';\n\n // Constants\n readonly RatingNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n\n constructor(\n private instrumentationService: TestingInstrumentationService,\n private cdr: ChangeDetectorRef\n ) {}\n\n ngOnInit(): void {\n this.restoreState();\n this.setupSubscriptions();\n void this.LoadDisagreement();\n }\n\n /** Stored cohort means, largest gap first. The view computes the means; this screen does not average a sample. */\n async LoadDisagreement(): Promise<void> {\n try {\n const provider = this.instrumentationService.Provider;\n if (!provider) return;\n const view = RunView.FromMetadataProvider(provider);\n const scores = await view.RunView({\n EntityName: 'MJ: Rubric Evaluation Scores',\n ExtraFilter: `EvaluationStatus = 'Submitted' AND CriterionCohortHumanMeanScore IS NOT NULL AND CriterionCohortAIMeanScore IS NOT NULL`,\n ResultType: 'simple',\n MaxRows: 1000,\n });\n if (!scores.Success) throw new Error(scores.ErrorMessage || 'Could not load scores.');\n this.Disagreement = CohortDisagreement(((scores.Results ?? []) as Record<string, unknown>[]).map(row => ({\n CriterionKey: row.CriterionKey,\n Criterion: row.Criterion,\n CriterionCohortHumanMeanScore: row.CriterionCohortHumanMeanScore,\n CriterionCohortAIMeanScore: row.CriterionCohortAIMeanScore,\n SubjectRecordID: row.SubjectRecordID,\n ContextRecordID: row.ContextRecordID,\n RubricID: row.RubricID,\n RubricMajorVersion: row.RubricMajorVersion,\n })));\n this.DisagreementError = '';\n this.cdr.markForCheck();\n } catch (error) {\n this.Disagreement = [];\n this.DisagreementError = error instanceof Error ? error.message : 'Could not load disagreement.';\n this.cdr.markForCheck();\n }\n }\n\n OpenRun(runId: string): void {\n if (!runId) return;\n SharedService.Instance.OpenEntityRecord('MJ: Test Runs', CompositeKey.FromID(runId));\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n // ------------------------------------------------------------------\n // Computed properties\n // ------------------------------------------------------------------\n\n get IsFormValid(): boolean {\n return this.FormState.rating != null && this.FormState.isCorrect != null;\n }\n\n get AgreementRate(): number {\n return this.Metrics?.agreementRate ?? 0;\n }\n\n get AgreementColor(): string {\n const rate = this.AgreementRate;\n if (rate >= 80) return '#22c55e';\n if (rate >= 60) return '#eab308';\n return '#ef4444';\n }\n\n get AgreementDash(): string {\n const circumference = 2 * Math.PI * 52; // r=52\n const filled = (this.AgreementRate / 100) * circumference;\n return `${filled} ${circumference}`;\n }\n\n // ------------------------------------------------------------------\n // Setup\n // ------------------------------------------------------------------\n\n private restoreState(): void {\n if (!this.InitialState) return;\n const view = this.InitialState['viewMode'] as string | undefined;\n if (view === 'queue' || view === 'history') {\n this.CurrentView = view;\n }\n }\n\n private setupSubscriptions(): void {\n this.instrumentationService.pendingFeedback$\n .pipe(takeUntil(this.destroy$))\n .subscribe(items => {\n this.PendingItems = items;\n this.PendingCount = items.length;\n this.emitState();\n this.cdr.markForCheck();\n });\n\n this.instrumentationService.evaluationMetrics$\n .pipe(takeUntil(this.destroy$))\n .subscribe(metrics => {\n this.Metrics = metrics;\n this.emitState();\n this.cdr.markForCheck();\n });\n\n this.instrumentationService.testRunsWithFeedback$\n .pipe(takeUntil(this.destroy$))\n .subscribe(runs => {\n this.HistoryItems = runs.filter(r => r.hasHumanFeedback);\n this.applyHistoryFilters();\n this.cdr.markForCheck();\n });\n }\n\n // ------------------------------------------------------------------\n // View toggling\n // ------------------------------------------------------------------\n\n SetView(mode: ViewMode): void {\n this.CurrentView = mode;\n this.ExpandedItemId = null;\n this.emitState();\n this.cdr.markForCheck();\n }\n\n // ------------------------------------------------------------------\n // Queue interactions\n // ------------------------------------------------------------------\n\n IsItemExpanded(testRunID: string): boolean {\n return UUIDsEqual(this.ExpandedItemId, testRunID);\n }\n\n /**\n * Accordion ExpandedChange handler for a review queue item. Single-expand\n * (radio-like) model: expanding an item makes it the sole open one (the\n * template's `IsItemExpanded` binding collapses the prior one) and resets the\n * review form for the newly-opened item; collapsing the open item clears it.\n * OnPush — drives change detection. Preserves the original ToggleExpand side\n * effect (resetForm on open).\n */\n OnItemExpandedChange(testRunID: string, expanded: boolean): void {\n if (expanded) {\n this.ExpandedItemId = testRunID;\n this.resetForm();\n } else if (UUIDsEqual(this.ExpandedItemId, testRunID)) {\n this.ExpandedItemId = null;\n }\n this.cdr.markForCheck();\n }\n\n SelectRating(n: number): void {\n this.FormState = { ...this.FormState, rating: n };\n this.cdr.markForCheck();\n }\n\n SelectCorrectness(value: boolean): void {\n this.FormState = { ...this.FormState, isCorrect: value };\n this.cdr.markForCheck();\n }\n\n OnCommentsChange(event: Event): void {\n const target = event.target as HTMLTextAreaElement;\n this.FormState = { ...this.FormState, comments: target.value };\n }\n\n async SubmitReview(item: FeedbackPending): Promise<void> {\n if (!this.IsFormValid || this.IsSubmitting) return;\n\n this.IsSubmitting = true;\n this.cdr.markForCheck();\n\n try {\n const success = await this.instrumentationService.submitFeedback(\n item.testRunID,\n this.FormState.rating!,\n this.FormState.isCorrect!,\n this.FormState.comments\n );\n\n if (success) {\n this.ExpandedItemId = null;\n this.resetForm();\n }\n } finally {\n this.IsSubmitting = false;\n this.cdr.markForCheck();\n }\n }\n\n SkipItem(): void {\n const currentIndex = this.PendingItems.findIndex(\n i => UUIDsEqual(i.testRunID, this.ExpandedItemId)\n );\n this.ExpandedItemId = null;\n this.resetForm();\n\n // Expand the next item if available\n if (currentIndex >= 0 && currentIndex + 1 < this.PendingItems.length) {\n this.ExpandedItemId = this.PendingItems[currentIndex + 1].testRunID;\n }\n this.cdr.markForCheck();\n }\n\n // ------------------------------------------------------------------\n // History interactions\n // ------------------------------------------------------------------\n\n OnHistorySearch(event: Event): void {\n const target = event.target as HTMLInputElement;\n this.HistorySearchText = target.value;\n this.applyHistoryFilters();\n this.emitState();\n this.cdr.markForCheck();\n }\n\n OnHistorySortChange(event: Event): void {\n const target = event.target as HTMLSelectElement;\n this.HistorySortField = target.value as HistorySort;\n this.applyHistoryFilters();\n this.cdr.markForCheck();\n }\n\n ToggleHistoryComment(id: string): void {\n this.ExpandedHistoryId = this.ExpandedHistoryId === id ? null : id;\n this.cdr.markForCheck();\n }\n\n // ------------------------------------------------------------------\n // Refresh\n // ------------------------------------------------------------------\n\n Refresh(): void {\n this.IsRefreshing = true;\n this.cdr.markForCheck();\n this.instrumentationService.refresh();\n\n setTimeout(() => {\n this.IsRefreshing = false;\n this.cdr.markForCheck();\n }, 1500);\n }\n\n // ------------------------------------------------------------------\n // Formatting helpers\n // ------------------------------------------------------------------\n\n FormatDecimal(value: number | undefined | null, decimals: number): string {\n if (value == null) return '0';\n return value.toFixed(decimals);\n }\n\n FormatRelativeTime(date: Date): string {\n const now = Date.now();\n const then = date instanceof Date ? date.getTime() : new Date(date).getTime();\n const diffMs = now - then;\n const diffMin = Math.floor(diffMs / 60000);\n\n if (diffMin < 1) return 'just now';\n if (diffMin < 60) return `${diffMin}m ago`;\n\n const diffHours = Math.floor(diffMin / 60);\n if (diffHours < 24) return `${diffHours}h ago`;\n\n const diffDays = Math.floor(diffHours / 24);\n if (diffDays < 7) return `${diffDays}d ago`;\n\n const diffWeeks = Math.floor(diffDays / 7);\n if (diffWeeks < 4) return `${diffWeeks}w ago`;\n\n const diffMonths = Math.floor(diffDays / 30);\n return `${diffMonths}mo ago`;\n }\n\n // ------------------------------------------------------------------\n // Private helpers\n // ------------------------------------------------------------------\n\n private resetForm(): void {\n this.FormState = { rating: null, isCorrect: null, comments: '' };\n }\n\n private applyHistoryFilters(): void {\n let items = [...this.HistoryItems];\n\n // Search filter\n if (this.HistorySearchText) {\n const term = this.HistorySearchText.toLowerCase();\n items = items.filter(i => i.testName.toLowerCase().includes(term));\n }\n\n // Sort\n items.sort((a, b) => {\n switch (this.HistorySortField) {\n case 'date':\n return b.runDateTime.getTime() - a.runDateTime.getTime();\n case 'rating':\n return (b.humanRating ?? 0) - (a.humanRating ?? 0);\n case 'test-name':\n return a.testName.localeCompare(b.testName);\n default:\n return 0;\n }\n });\n\n this.FilteredHistoryItems = items;\n }\n\n private emitState(): void {\n this.StateChange.emit({\n viewMode: this.CurrentView,\n // Richer state for the dashboard's agent context (queue depth, reviewed\n // count, avg human rating, agreement rate, history search).\n pendingCount: this.PendingCount,\n reviewedCount: this.Metrics?.humanReviewedCount ?? 0,\n averageHumanRating: this.Metrics?.humanAvgRating ?? 0,\n agreementRate: this.Metrics?.agreementRate ?? 0,\n historySearchText: this.HistorySearchText\n });\n }\n}\n"]}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { EventEmitter } from '@angular/core';
|
|
2
|
+
import type { IMetadataProvider } from '@memberjunction/core';
|
|
2
3
|
import { TestRunSummary } from '../../services/testing-instrumentation.service';
|
|
3
4
|
import { OracleResult } from './oracle-breakdown-table.component';
|
|
5
|
+
import { RubricRunView } from '@memberjunction/ng-testing';
|
|
4
6
|
import * as i0 from "@angular/core";
|
|
5
7
|
export declare class TestRunDetailPanelComponent {
|
|
8
|
+
private readonly session;
|
|
9
|
+
/** The signed-in session provider. An input wins; otherwise the testing service's provider is used. */
|
|
10
|
+
Provider: IMetadataProvider | null;
|
|
11
|
+
get SessionProvider(): IMetadataProvider | null;
|
|
6
12
|
TestRun: TestRunSummary;
|
|
7
13
|
/** @deprecated Use {@link TestRun}. */
|
|
8
14
|
set testRun(value: TestRunSummary);
|
|
@@ -11,6 +17,7 @@ export declare class TestRunDetailPanelComponent {
|
|
|
11
17
|
OracleResults: OracleResult[];
|
|
12
18
|
/** @deprecated Use {@link OracleResults}. */
|
|
13
19
|
set oracleResults(value: OracleResult[]);
|
|
20
|
+
get RubricView(): RubricRunView | null;
|
|
14
21
|
/** @deprecated Use {@link OracleResults}. */
|
|
15
22
|
get oracleResults(): OracleResult[];
|
|
16
23
|
ResultDetails: any;
|
|
@@ -105,6 +112,6 @@ export declare class TestRunDetailPanelComponent {
|
|
|
105
112
|
/** @deprecated Use {@link OnSubmitFeedback}. */
|
|
106
113
|
onSubmitFeedback(): Promise<void>;
|
|
107
114
|
static ɵfac: i0.ɵɵFactoryDeclaration<TestRunDetailPanelComponent, never>;
|
|
108
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<TestRunDetailPanelComponent, "app-test-run-detail-panel", never, { "TestRun": { "alias": "TestRun"; "required": false; }; "testRun": { "alias": "testRun"; "required": false; }; "OracleResults": { "alias": "OracleResults"; "required": false; }; "oracleResults": { "alias": "oracleResults"; "required": false; }; "ResultDetails": { "alias": "ResultDetails"; "required": false; }; "resultDetails": { "alias": "resultDetails"; "required": false; }; "Closeable": { "alias": "Closeable"; "required": false; }; "closeable": { "alias": "closeable"; "required": false; }; }, { "Close": "Close"; "close": "close"; "ViewTarget": "ViewTarget"; "viewTarget": "viewTarget"; "SubmitFeedback": "SubmitFeedback"; "submitFeedback": "submitFeedback"; }, never, never, false, never>;
|
|
115
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<TestRunDetailPanelComponent, "app-test-run-detail-panel", never, { "Provider": { "alias": "Provider"; "required": false; }; "TestRun": { "alias": "TestRun"; "required": false; }; "testRun": { "alias": "testRun"; "required": false; }; "OracleResults": { "alias": "OracleResults"; "required": false; }; "oracleResults": { "alias": "oracleResults"; "required": false; }; "ResultDetails": { "alias": "ResultDetails"; "required": false; }; "resultDetails": { "alias": "resultDetails"; "required": false; }; "Closeable": { "alias": "Closeable"; "required": false; }; "closeable": { "alias": "closeable"; "required": false; }; }, { "Close": "Close"; "close": "close"; "ViewTarget": "ViewTarget"; "viewTarget": "viewTarget"; "SubmitFeedback": "SubmitFeedback"; "submitFeedback": "submitFeedback"; }, never, never, false, never>;
|
|
109
116
|
}
|
|
110
117
|
//# sourceMappingURL=test-run-detail-panel.component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-detail-panel.component.d.ts","sourceRoot":"","sources":["../../../../src/Testing/components/widgets/test-run-detail-panel.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,
|
|
1
|
+
{"version":3,"file":"test-run-detail-panel.component.d.ts","sourceRoot":"","sources":["../../../../src/Testing/components/widgets/test-run-detail-panel.component.ts"],"names":[],"mappings":"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"}
|