|
4 | 4 | import { error, success } from "$src/modules/utils/toast.svelte"; |
5 | 5 | import * as Tooltip from "$src/lib/components/ui/tooltip"; |
6 | 6 | import type { AuditReport, AuditReportData } from "$src/types/report"; |
7 | | - import type { DocumentState, IdsDocument, Specification } from "$src/types/ids"; |
| 7 | + import type { DocumentState, Facet, IdsDocument, Specification } from "$src/types/ids"; |
8 | 8 |
|
9 | 9 | let activeDocument = $derived( |
10 | 10 | IDS.Module.activeDocument ? (IDS.Module.documents[IDS.Module.activeDocument] as IdsDocument) : null |
|
122 | 122 | return spec.requirements[reqIndex]; |
123 | 123 | } |
124 | 124 |
|
| 125 | + type RequirementGroup = { |
| 126 | + facetType: string; |
| 127 | + items: { facet: Facet; reqIndex: number }[]; |
| 128 | + }; |
| 129 | +
|
| 130 | + function getRequirementGroups(spec: Specification | undefined | null): RequirementGroup[] { |
| 131 | + if (!spec?.requirements) return []; |
| 132 | +
|
| 133 | + const groups: RequirementGroup[] = []; |
| 134 | + let reqIndex = 0; |
| 135 | +
|
| 136 | + for (const [facetType, facets] of Object.entries(spec.requirements)) { |
| 137 | + if (!Array.isArray(facets) || facets.length === 0) continue; |
| 138 | + groups.push({ |
| 139 | + facetType, |
| 140 | + items: facets.map((facet) => ({ |
| 141 | + facet, |
| 142 | + reqIndex: reqIndex++ |
| 143 | + })) |
| 144 | + }); |
| 145 | + } |
| 146 | +
|
| 147 | + return groups; |
| 148 | + } |
| 149 | +
|
125 | 150 | function toggleRequirementDetails(specIndex: number, reqIndex: number) { |
126 | 151 | const key = `${specIndex}-${reqIndex}`; |
127 | 152 | if (expandedRequirements.has(key)) { |
|
230 | 255 | </div> |
231 | 256 | {#each activeDocument.specifications.specification as spec, index} |
232 | 257 | {@const usage = getDocumentSpecificationUsage(spec)} |
| 258 | + {@const requirementGroups = getRequirementGroups(spec)} |
233 | 259 | <div class="specification-card {auditReport ? 'with-audit' : ''} {auditReport && getSpecificationStatus(index, auditReport.data) !== null ? (getSpecificationStatus(index, auditReport.data) === 'skipped' ? 'spec-skipped' : (getSpecificationStatus(index, auditReport.data) ? 'spec-pass' : 'spec-fail')) : ''}"> |
234 | 260 | <div |
235 | 261 | class="spec-card-header" |
|
428 | 454 | </div> |
429 | 455 |
|
430 | 456 | <!-- Requirements Section --> |
431 | | - {#if Array.isArray(spec.requirements) && spec.requirements.length > 0} |
| 457 | + {#if requirementGroups.length > 0} |
432 | 458 | <div class="facet-section"> |
433 | 459 | <h3>Requirements</h3> |
434 | 460 |
|
435 | 461 | <div class="facets-list"> |
436 | | - {#each Object.entries(spec.requirements || {}) as [facetType, facets]} |
437 | | - {#if Array.isArray(facets) && facets.length > 0} |
438 | | - <div class="facet-group"> |
439 | | - {#each facets as facet, facetIndex} |
440 | | - {@const reqAuditData = auditReport ? getRequirementStatus(index, facetIndex, auditReport.data) : null} |
441 | | - {@const specStatus = auditReport ? getSpecificationStatus(index, auditReport.data) : null} |
442 | | - <div class="facet-item {auditReport && reqAuditData && specStatus !== 'skipped' ? (reqAuditData.status ? 'audit-pass' : 'audit-fail') : ''}"> |
443 | | - <button class="facet-header" onclick={() => {if (auditReport && reqAuditData && specStatus !== 'skipped') toggleRequirementDetails(index, facetIndex)}}> |
444 | | - <span class="facet-bullet">•</span> |
445 | | - <span class="facet-text">{@html IDS.stringifyFacet("requirements", facet, facetType, spec)}</span> |
446 | | - {#if auditReport && reqAuditData && specStatus !== 'skipped'} |
447 | | - {#if reqAuditData.total_applicable > 0} |
448 | | - <div class="audit-details-toggle"> |
449 | | - {reqAuditData.status ? 'PASS' : 'FAIL'} ({reqAuditData.total_pass}/{reqAuditData.total_applicable}) |
450 | | - <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class:rotated={isRequirementDetailsExpanded(index, facetIndex)}> |
451 | | - <polyline points="6,9 12,15 18,9"></polyline> |
452 | | - </svg> |
453 | | - </div> |
454 | | - {:else} |
455 | | - <span class="audit-status-badge"> |
456 | | - {reqAuditData.status ? 'PASS' : 'FAIL'} |
457 | | - </span> |
458 | | - {/if} |
| 462 | + {#each requirementGroups as group} |
| 463 | + <div class="facet-group"> |
| 464 | + {#each group.items as item} |
| 465 | + {@const reqAuditData = auditReport ? getRequirementStatus(index, item.reqIndex, auditReport.data) : null} |
| 466 | + {@const specStatus = auditReport ? getSpecificationStatus(index, auditReport.data) : null} |
| 467 | + <div class="facet-item {auditReport && reqAuditData && specStatus !== 'skipped' ? (reqAuditData.status ? 'audit-pass' : 'audit-fail') : ''}"> |
| 468 | + <button class="facet-header" onclick={() => {if (auditReport && reqAuditData && specStatus !== 'skipped') toggleRequirementDetails(index, item.reqIndex)}}> |
| 469 | + <span class="facet-bullet">•</span> |
| 470 | + <span class="facet-text">{@html IDS.stringifyFacet("requirements", item.facet, group.facetType, spec)}</span> |
| 471 | + {#if auditReport && reqAuditData && specStatus !== 'skipped'} |
| 472 | + {#if reqAuditData.total_applicable > 0} |
| 473 | + <div class="audit-details-toggle"> |
| 474 | + {reqAuditData.status ? 'PASS' : 'FAIL'} ({reqAuditData.total_pass}/{reqAuditData.total_applicable}) |
| 475 | + <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class:rotated={isRequirementDetailsExpanded(index, item.reqIndex)}> |
| 476 | + <polyline points="6,9 12,15 18,9"></polyline> |
| 477 | + </svg> |
| 478 | + </div> |
| 479 | + {:else} |
| 480 | + <span class="audit-status-badge"> |
| 481 | + {reqAuditData.status ? 'PASS' : 'FAIL'} |
| 482 | + </span> |
459 | 483 | {/if} |
460 | | - </button> |
461 | | - {#if reqAuditData && isRequirementDetailsExpanded(index, facetIndex)} |
| 484 | + {/if} |
| 485 | + </button> |
| 486 | + {#if reqAuditData && isRequirementDetailsExpanded(index, item.reqIndex)} |
462 | 487 | <div class="facet-expansion"> |
463 | 488 | <div class="entity-tables"> |
464 | 489 | {#if reqAuditData.passed_entities && reqAuditData.passed_entities.length > 0} |
|
623 | 648 | {/if} |
624 | 649 | </div> |
625 | 650 | </div> |
626 | | - {/if} |
627 | | - </div> |
628 | | - {/each} |
629 | | - </div> |
630 | | - {/if} |
| 651 | + {/if} |
| 652 | + </div> |
| 653 | + {/each} |
| 654 | + </div> |
631 | 655 | {/each} |
632 | 656 | </div> |
633 | 657 | </div> |
|
0 commit comments