Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
},
"defaultProject": "DSOMM",
"cli": {
"defaultCollection": "@angular-eslint/schematics"
"defaultCollection": "@angular-eslint/schematics",
"analytics": false
}
}
6 changes: 6 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ import { ReportComponent } from './pages/report/report.component';
import { ReportConfigModalComponent } from './component/report-config-modal/report-config-modal.component';
import { TeamSelectorComponent } from './component/team-selector/team-selector.component';
import { ColResizeDirective } from './directive/col-resize.directive';
import { AddEvidenceModalComponent } from './component/add-evidence-modal/add-evidence-modal.component';
import { EvidencePanelComponent } from './component/evidence-panel/evidence-panel.component';
import { ViewEvidenceModalComponent } from './component/view-evidence-modal/view-evidence-modal.component';

@NgModule({
declarations: [
Expand Down Expand Up @@ -65,6 +68,9 @@ import { ColResizeDirective } from './directive/col-resize.directive';
ReportConfigModalComponent,
TeamSelectorComponent,
ColResizeDirective,
AddEvidenceModalComponent,
EvidencePanelComponent,
ViewEvidenceModalComponent,
],
imports: [
BrowserModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,26 +285,6 @@ <h3><b>Usefulness</b></h3>
>
</mat-expansion-panel>

<!-- <mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
<b>Teams Evidence</b>
</mat-panel-title>
</mat-expansion-panel-header>
<mat-accordion multi="true">
<mat-expansion-panel
id="teamsEvidence"
*ngFor="let item of this.currentActivity.teamsEvidence | keyvalue">
<mat-expansion-panel-header>
<mat-panel-title>
<b [innerHTML]="item.key"></b>
</mat-panel-title>
</mat-expansion-panel-header>
<p [innerHTML]="item.value"></p>
</mat-expansion-panel>
</mat-accordion>
</mat-expansion-panel> -->

<mat-expansion-panel
[expanded]="true"
#implementationPanel
Expand Down Expand Up @@ -370,6 +350,9 @@ <h4 class="tool-name" [innerHTML]="implement['name']"></h4>
<p>No teams have started implementing this activity yet.</p>
</ng-template>
</mat-expansion-panel>

<app-evidence-panel [activityUuid]="currentActivity.uuid || ''" [expanded]="isNarrowScreen">
</app-evidence-panel>
</mat-accordion>

<div
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
.config-content {
max-height: 70vh;
overflow-y: auto;
padding: 0 40px;
background-color: var(--background-primary);
}

.config-section {
padding: 16px 0;
}

.config-section h3 {
margin: 0 0 4px 0;
font-size: 1.1em;
font-weight: 500;
}

.config-hint {
margin: 0 0 12px 0;
font-size: 0.85em;
color: var(--text-secondary);
}

.required {
color: #c62828;
font-weight: bold;
}

.select-all-actions {
display: flex;
gap: 8px;
margin-bottom: 8px;
}

.checkbox-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 8px;
}

.full-width {
width: 100%;
}

.form-row {
display: flex;
gap: 16px;
flex-wrap: wrap;
}

.form-row mat-form-field {
flex: 1;
min-width: 180px;
}

.attachment-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
}

.attachment-type {
flex: 0 0 120px;
}

.attachment-link {
flex: 1;
}

.field-error {
font-size: 0.8em;
margin-top: -12px;
margin-bottom: 16px;
display: block;
}

mat-divider {
margin: 4px 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<h2 mat-dialog-title align="center">Add Evidence</h2>

<mat-dialog-content class="config-content">
<app-team-selector
[allTeams]="allTeams"
[(selectedTeams)]="selectedTeams"
[teamGroups]="teamGroups"
(selectedTeamsChange)="onSelectedTeamsChange($event)"
[type]="'add-evidence-config'">
</app-team-selector>
<mat-error *ngIf="teamsError" class="field-error" style="padding: 0 0 8px 0"
>At least one team must be selected.</mat-error
>

<mat-divider></mat-divider>

<!-- Title -->
<div class="config-section">
<h3>Evidence Details</h3>
<mat-form-field appearance="outline" class="full-width">
<mat-label>Title</mat-label>
<input matInput [(ngModel)]="title" placeholder="e.g. Implemented SAST scanning" required />
</mat-form-field>
<mat-error *ngIf="titleError" class="field-error">Title is required.</mat-error>

<mat-form-field appearance="outline" class="full-width">
<mat-label>Description</mat-label>
<textarea
matInput
[(ngModel)]="description"
rows="4"
placeholder="Describe the evidence in detail..."></textarea>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Reviewer</mat-label>
<input matInput [(ngModel)]="reviewer" placeholder="e.g. Jane Doe" />
</mat-form-field>
</div>

<mat-divider></mat-divider>
<!-- Attachments -->
<div class="config-section">
<h3>Attachments</h3>
<p class="config-hint">Add links to supporting documents, images, or URLs.</p>
<div *ngFor="let att of attachments; let i = index" class="attachment-row">
<mat-form-field appearance="outline" class="attachment-type">
<mat-label>Type</mat-label>
<mat-select [(ngModel)]="att.type">
<mat-option *ngFor="let aType of attachmentTypes" [value]="aType">
{{ aType }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline" class="attachment-link">
<mat-label>URL</mat-label>
<input matInput [(ngModel)]="att.externalLink" placeholder="https://..." />
</mat-form-field>
<button mat-icon-button color="warn" (click)="removeAttachment(i)" matTooltip="Remove">
<mat-icon>delete</mat-icon>
</button>
</div>
<button mat-stroked-button color="primary" (click)="addAttachment()">
<mat-icon>add</mat-icon> Add Attachment
</button>
</div>
</mat-dialog-content>

<mat-dialog-actions align="end">
<button mat-button (click)="onCancel()">Cancel</button>
<button mat-raised-button color="primary" (click)="onSave()">Save Evidence</button>
</mat-dialog-actions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { EvidenceEntry, EvidenceStore } from '../../model/evidence-store';
import { TeamGroups } from '../../model/types';

export interface AddEvidenceModalData {
activityUuid: string;
allTeams: string[];
teamGroups: TeamGroups;
}

@Component({
selector: 'app-add-evidence-modal',
templateUrl: './add-evidence-modal.component.html',
styleUrls: ['./add-evidence-modal.component.css'],
})
export class AddEvidenceModalComponent {
activityUuid: string;
allTeams: string[];
teamGroups: TeamGroups;

// Form fields
selectedTeams: string[] = [];
title: string = '';
description: string = '';
progress: string = '';
evidenceRecorded: string = EvidenceStore.todayDateString();
reviewer: string = '';
attachments: { type: string; externalLink: string }[] = [];

// Validation
teamsError: boolean = false;
titleError: boolean = false;

attachmentTypes: string[] = ['document', 'image', 'link'];

constructor(
public dialogRef: MatDialogRef<AddEvidenceModalComponent>,
@Inject(MAT_DIALOG_DATA) public data: AddEvidenceModalData
) {
this.activityUuid = data.activityUuid;
this.allTeams = data.allTeams;
this.teamGroups = data.teamGroups || {};
}

onSelectedTeamsChange(teams: string[]): void {
this.selectedTeams = teams;
this.teamsError = this.selectedTeams.length === 0;
}

addAttachment(): void {
this.attachments.push({ type: 'link', externalLink: '' });
}

removeAttachment(index: number): void {
this.attachments.splice(index, 1);
}

onSave(): void {
this.teamsError = this.selectedTeams.length === 0;
this.titleError = !this.title.trim();

if (this.teamsError || this.titleError) {
return;
}

// Filter out empty attachments
const validAttachments = this.attachments.filter(a => a.externalLink.trim());

const entry: EvidenceEntry = {
id: EvidenceStore.generateId(),
teams: [...this.selectedTeams],
title: this.title.trim(),
description: this.description.trim(),
evidenceRecorded: this.evidenceRecorded,
reviewer: this.reviewer.trim() || undefined,
attachment: validAttachments.length > 0 ? validAttachments : undefined,
};

this.dialogRef.close({ activityUuid: this.activityUuid, entry });
}

onCancel(): void {
this.dialogRef.close(null);
}
}
Loading
Loading