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
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<div class="skills-summary-dialog">
<div class="dialog-header">
<h2 mat-dialog-title>Skills Summary</h2>
<button mat-icon-button (click)="onClose()" class="close-button">
<mat-icon>close</mat-icon>
</button>
</div>

<div mat-dialog-content class="dialog-content">
<!-- Completed Units Section -->
<div class="section" *ngIf="getCompletedUnits().length > 0">
<h3 class="section-title">
<mat-icon class="section-icon completed">check_circle</mat-icon>
Skills Acquired ({{ getCompletedUnits().length }} completed units)
</h3>
<div class="units-list">
<div *ngFor="let unit of getCompletedUnits()" class="unit-item completed">
<div class="unit-header">
<strong>{{ unit.code }} - {{ unit.name }}</strong>
<mat-icon class="status-icon">check_circle</mat-icon>
</div>
<div class="unit-description" *ngIf="unit.description">
{{ unit.description }}
</div>
<div class="no-description" *ngIf="!unit.description">
<em>No skills description available for this unit.</em>
</div>
</div>
</div>
</div>

<!-- In Progress Units Section -->
<div class="section" *ngIf="getInProgressUnits().length > 0">
<h3 class="section-title">
<mat-icon class="section-icon in-progress">schedule</mat-icon>
Skills in Development ({{ getInProgressUnits().length }} units in progress)
</h3>
<div class="units-list">
<div *ngFor="let unit of getInProgressUnits()" class="unit-item in-progress">
<div class="unit-header">
<strong>{{ unit.code }} - {{ unit.name }}</strong>
<mat-icon class="status-icon">schedule</mat-icon>
</div>
<div class="unit-description" *ngIf="unit.description">
{{ unit.description }}
</div>
<div class="no-description" *ngIf="!unit.description">
<em>No skills description available for this unit.</em>
</div>
</div>
</div>
</div>

<!-- No Units Message -->
<div class="no-units" *ngIf="getAllPlacedUnits().length === 0">
<mat-icon class="empty-icon">lightbulb_outline</mat-icon>
<h3>No Units Selected</h3>
<p>Add units to your course plan to see a summary of skills you'll learn.</p>
</div>
</div>

<div mat-dialog-actions class="dialog-actions">
<button mat-raised-button color="primary" (click)="onClose()">Close</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
.skills-summary-dialog {
max-width: 800px;
width: 100%;
max-height: 80vh;
}

.dialog-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px 0;
border-bottom: 1px solid #e0e0e0;
margin-bottom: 20px;

h2 {
margin: 0;
color: #333;
font-weight: 500;
}

.close-button {
color: #666;
}
}

.dialog-content {
padding: 0 24px;
max-height: 60vh;
overflow-y: auto;
}

.section {
margin-bottom: 30px;

.section-title {
display: flex;
align-items: center;
margin-bottom: 15px;
color: #333;
font-size: 18px;
font-weight: 500;

.section-icon {
margin-right: 8px;

&.completed {
color: #4caf50;
}

&.in-progress {
color: #ff9800;
}
}
}
}

.units-list {
display: flex;
flex-direction: column;
gap: 16px;
}

.unit-item {
padding: 16px;
border-radius: 8px;
border: 1px solid #e0e0e0;
background-color: #fafafa;

&.completed {
border-left: 4px solid #4caf50;
background-color: #f3f9f3;
}

&.in-progress {
border-left: 4px solid #ff9800;
background-color: #fff8f0;
}

.unit-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;

strong {
color: #333;
font-size: 16px;
}

.status-icon {
font-size: 20px;

&.completed {
color: #4caf50;
}

&.in-progress {
color: #ff9800;
}
}
}

.unit-description {
color: #555;
line-height: 1.6;
font-size: 14px;
}

.no-description {
color: #999;
font-style: italic;
font-size: 14px;
}
}

.no-units {
text-align: center;
padding: 40px 20px;
color: #666;

.empty-icon {
font-size: 48px;
color: #ccc;
margin-bottom: 16px;
}

h3 {
margin: 0 0 12px 0;
color: #555;
}

p {
margin: 0;
color: #777;
}
}

.dialog-actions {
padding: 16px 24px;
border-top: 1px solid #e0e0e0;
display: flex;
justify-content: flex-end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {Component, Inject} from '@angular/core';
import {CommonModule} from '@angular/common';
import {MatDialogModule, MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import {MatButtonModule} from '@angular/material/button';
import {MatIconModule} from '@angular/material/icon';
import {CourseUnit} from '../../models/course-map.models';

interface SkillsSummaryData {
units: CourseUnit[];
}

@Component({
selector: 'skills-summary-dialog',
templateUrl: './skills-summary-dialog.component.html',
styleUrls: ['./skills-summary-dialog.component.scss'],
standalone: true,
imports: [CommonModule, MatDialogModule, MatButtonModule, MatIconModule],
})
export class SkillsSummaryDialogComponent {
constructor(
public dialogRef: MatDialogRef<SkillsSummaryDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: SkillsSummaryData,
) {}

onClose(): void {
this.dialogRef.close();
}

getCompletedUnits(): CourseUnit[] {
return this.data.units.filter(unit => {
const unitWithCompletion = unit as CourseUnit & {isCompleted?: boolean};
return unitWithCompletion.isCompleted;
});
}

getInProgressUnits(): CourseUnit[] {
return this.data.units.filter(unit => {
const unitWithCompletion = unit as CourseUnit & {isCompleted?: boolean};
return !unitWithCompletion.isCompleted;
});
}

getAllPlacedUnits(): CourseUnit[] {
return this.data.units.filter(unit => unit !== null);
}
}
20 changes: 18 additions & 2 deletions src/app/courseflow/common/unit-card/unit-card.component.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
<div class="unit" cdkDrag [cdkDragData]="dragData">
<div
class="unit"
[cdkDragDisabled]="isCompleted"
cdkDrag
[cdkDragData]="dragData"
[class.completed]="isCompleted"
[class.drag-disabled]="isCompleted"
>
<strong>{{ unit.code }}</strong> - {{ unit.name }}
<mat-icon *ngIf="isCompleted" class="completion-indicator">check_circle</mat-icon>

<button *ngIf="showMenu" mat-icon-button class="unit-menu-button" [matMenuTriggerFor]="unitMenu">
<mat-icon>more_vert</mat-icon>
</button>

<mat-menu #unitMenu="matMenu">
<button mat-menu-item (click)="onRemoveUnit()">
<button mat-menu-item (click)="onToggleCompletion()">
<mat-icon>{{ isCompleted ? 'radio_button_checked' : 'radio_button_unchecked' }}</mat-icon>
<span>{{ isCompleted ? 'Mark as Incomplete' : 'Mark as Complete' }}</span>
</button>
<button mat-menu-item (click)="onShowSkillsSummary()">
<mat-icon>lightbulb</mat-icon>
<span>Skills Summary</span>
</button>
<button mat-menu-item (click)="onRemoveUnit()" [disabled]="isCompleted">
<mat-icon>delete</mat-icon>
<span>Remove</span>
</button>
Expand Down
29 changes: 29 additions & 0 deletions src/app/courseflow/common/unit-card/unit-card.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,25 @@
background-color: #f2f2f2;
transform: translateY(-2px);
}

&.completed {
background-color: #e8f5e8;
border-color: #4caf50;

&:hover {
background-color: #d4edda;
}
}

&.drag-disabled {
cursor: not-allowed;
opacity: 0.8;

&:hover {
transform: none;
background-color: #e8f5e8;
}
}
}

.unit-menu-button {
Expand All @@ -46,3 +65,13 @@
height: 18px;
line-height: 18px;
}

.completion-indicator {
position: absolute;
top: 5px;
left: 5px;
color: #4caf50;
font-size: 16px;
width: 16px;
height: 16px;
}
21 changes: 21 additions & 0 deletions src/app/courseflow/common/unit-card/unit-card.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,29 @@ export class UnitCardComponent {
@Input() dragData!: any;
@Input() showMenu = false;
@Output() removeUnit = new EventEmitter<void>();
@Output() toggleCompletion = new EventEmitter<void>();
@Output() showSkillsSummary = new EventEmitter<CourseUnit>();

// Track completion status locally if not available on the unit
get isCompleted(): boolean {
// Check if unit has a completion property, otherwise use local storage or default to false
const unitWithCompletion = this.unit as CourseUnit & {isCompleted?: boolean};
return unitWithCompletion.isCompleted || false;
}

onRemoveUnit(): void {
// Don't allow removal of completed units
if (this.isCompleted) {
return;
}
this.removeUnit.emit();
}

onToggleCompletion(): void {
this.toggleCompletion.emit();
}

onShowSkillsSummary(): void {
this.showSkillsSummary.emit(this.unit);
}
}
12 changes: 12 additions & 0 deletions src/app/courseflow/services/course-map-state.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,4 +386,16 @@ export class CourseMapStateService {
requiredUnits: units,
});
}

toggleUnitCompletion(unit: CourseUnit): void {
// For now, we'll store completion status on the unit object itself
// In a real implementation, this might be stored in a service or backend
const unitWithCompletion = unit as CourseUnit & {isCompleted?: boolean};
unitWithCompletion.isCompleted = !unitWithCompletion.isCompleted;

// Trigger a state update to ensure components re-render
this.updateState({
...this.currentState,
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ <h3 class="study-periods-title">Study Periods</h3>
[yearIndex]="yIndex"
[stateService]="stateService"
(dropEvent)="handleDrop($event)"
(showSkillsSummary)="onShowSkillsSummary($event)"
>
</course-year-editor>

Expand Down
Loading