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
6 changes: 6 additions & 0 deletions src/app/api/models/doubtfire-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,9 @@ export * from '../services/teaching-period-break.service';
export * from '../services/learning-outcome.service';
export * from '../services/group-set.service';
export * from '../services/task-similarity.service';

// Courseflow models and services
export * from './requirement';
export * from './requirement-set';
export * from '../services/requirement.service';
export * from '../services/requirement-set.service';
9 changes: 7 additions & 2 deletions src/app/api/models/requirement-set.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import {Unit} from './doubtfire-model';
import {Requirement} from './requirement';

export interface RequirementSet {
id?: string;
id?: number;
requirementSetGroupId: number;
description: string;
unitId: number;
unitId?: number;
requirementId: number;
unit?: Unit; // Optional populated unit data
requirement?: Requirement; // Optional populated requirement data
}

11 changes: 11 additions & 0 deletions src/app/api/models/requirement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface Requirement {
id?: number;
courseId: number;
unitId?: number;
type: 'course' | 'unit';
category: string;
description: string;
minimum?: number;
maximum?: number;
requirementSetGroupId: number;
}
67 changes: 23 additions & 44 deletions src/app/api/services/requirement-set.service.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,37 @@
import {Observable} from 'rxjs';
import {HttpClient, HttpParams} from '@angular/common/http';
import {HttpClient} from '@angular/common/http';
import {Injectable} from '@angular/core';
import API_URL from 'src/app/config/constants/apiURL';
import {RequirementSet} from '../models/requirement-set';

@Injectable()
export class RequirementSet {
@Injectable({
providedIn: 'root',
})
export class RequirementSetService {
constructor(private http: HttpClient) {}

private baseUrl: string = `${API_URL}/requirementset`;

// Get requirement-sets
getRequirementSets(): Observable<RequirementSet> {
const url = `${this.baseUrl}`;
return this.http.get<RequirementSet>(url);
}

getRequirementSetById(): Observable<RequirementSet> {
const url = `${this.baseUrl}/:id:`;
return this.http.get<RequirementSet>(url);
}

addNewRequirementSet(
requirementSetGroupId: number,
name: string,
description: string,
unitId: string,
requirementId: number): Observable<RequirementSet> {
const params = new HttpParams();

params.set('requirementSetId', requirementSetGroupId.toString());
params.set('name', name);
params.set('description', description);
params.set('unitId', unitId);
params.set('requirementId', requirementId.toString());
const url = `${this.baseUrl}`;
return this.http.post<RequirementSet>(url, {params});
/**
* Get all requirement sets
*/
getAllRequirementSets(): Observable<RequirementSet[]> {
return this.http.get<RequirementSet[]>(this.baseUrl);
}

updateRequirementSet(
requirementSetGroupId: number,
name: string,
description: string,
code: string,): Observable<RequirementSet> {
const params = new HttpParams();

params.set('requirementSetId', requirementSetGroupId.toString());
params.set('description', description);
const url = `${this.baseUrl}`;
return this.http.put<RequirementSet>(url, {params});
/**
* Get requirement sets by group ID
*/
getRequirementSetsByGroupId(groupId: number): Observable<RequirementSet[]> {
const url = `${this.baseUrl}/requirementSetGroupId/${groupId}`;
return this.http.get<RequirementSet[]>(url);
}

deleteRequirementSet(requirementSetGroupId: number): Observable<RequirementSet> {
const url = `${this.baseUrl}/requirementSetId/${requirementSetGroupId}`;
return this.http.delete<RequirementSet>(url);
/**
* Get a specific requirement set by ID
*/
getRequirementSetById(id: number): Observable<RequirementSet> {
const url = `${this.baseUrl}/${id}`;
return this.http.get<RequirementSet>(url);
}

}
38 changes: 38 additions & 0 deletions src/app/api/services/requirement.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {Observable} from 'rxjs';
import {HttpClient} from '@angular/common/http';
import {Injectable} from '@angular/core';
import API_URL from 'src/app/config/constants/apiURL';
import {Requirement} from '../models/requirement';

@Injectable({
providedIn: 'root',
})
export class RequirementService {
constructor(private http: HttpClient) {}

private baseUrl: string = `${API_URL}/requirement`;

/**
* Get all requirements for a course
*/
getRequirementsByCourseId(courseId: number): Observable<Requirement[]> {
const url = `${this.baseUrl}/courseId/${courseId}`;
return this.http.get<Requirement[]>(url);
}

/**
* Get requirements for a specific unit
*/
getRequirementsByUnitId(unitId: number): Observable<Requirement[]> {
const url = `${this.baseUrl}/unitId/${unitId}`;
return this.http.get<Requirement[]>(url);
}

/**
* Get all requirements
*/
getAllRequirements(): Observable<Requirement[]> {
const url = `${this.baseUrl}`;
return this.http.get<Requirement[]>(url);
}
}
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;
}
Loading