mirror of
https://github.com/opelly27/WinStudentGoalTracker.git
synced 2026-05-20 02:57:36 +00:00
Edit button locate, delete, group header
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<app-modal-shell [title]="title()" (closed)="onCancel()">
|
||||
@if (!awaitingSecondConfirm()) {
|
||||
<p class="confirm-message">{{ message() }}</p>
|
||||
} @else {
|
||||
<p class="confirm-message second-confirm-message">Are you absolutely sure? This action is permanent and cannot be undone.</p>
|
||||
}
|
||||
<div class="modal-actions">
|
||||
<button class="btn-secondary" (click)="onCancel()">{{ cancelLabel() }}</button>
|
||||
@if (!awaitingSecondConfirm()) {
|
||||
<button [class]="destructive() ? 'btn-danger' : 'btn-primary'" (click)="onConfirm()">
|
||||
{{ confirmLabel() }}
|
||||
</button>
|
||||
} @else {
|
||||
<button class="btn-danger-confirm" (click)="onConfirm()">
|
||||
Yes, delete permanently
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</app-modal-shell>
|
||||
@@ -0,0 +1,42 @@
|
||||
.confirm-message {
|
||||
font-size: 14px;
|
||||
line-height: 1.55;
|
||||
color: var(--text-secondary);
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
|
||||
.second-confirm-message {
|
||||
color: #DC2626;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:host ::ng-deep .btn-danger {
|
||||
padding: 8px 20px;
|
||||
border-radius: var(--radius-md);
|
||||
border: none;
|
||||
background: #DC2626;
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #B91C1C;
|
||||
}
|
||||
}
|
||||
|
||||
:host ::ng-deep .btn-danger-confirm {
|
||||
padding: 8px 20px;
|
||||
border-radius: var(--radius-md);
|
||||
border: 2px solid #DC2626;
|
||||
background: #FEF2F2;
|
||||
color: #DC2626;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #DC2626;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Component, input, output, signal } from '@angular/core';
|
||||
import { ModalShell } from '../modal-shell/modal-shell';
|
||||
|
||||
@Component({
|
||||
selector: 'app-confirm-modal',
|
||||
imports: [ModalShell],
|
||||
templateUrl: './confirm-modal.html',
|
||||
styleUrl: './confirm-modal.scss',
|
||||
})
|
||||
export class ConfirmModal {
|
||||
|
||||
// ************************** Declarations *************************
|
||||
|
||||
readonly title = input('Confirm');
|
||||
readonly message = input.required<string>();
|
||||
readonly confirmLabel = input('Delete');
|
||||
readonly cancelLabel = input('Cancel');
|
||||
readonly destructive = input(false);
|
||||
readonly doubleConfirm = input(false);
|
||||
|
||||
readonly confirmed = output<void>();
|
||||
readonly closed = output<void>();
|
||||
|
||||
// ************************** Properties ***************************
|
||||
|
||||
protected readonly awaitingSecondConfirm = signal(false);
|
||||
|
||||
// ************************ Event Handlers *************************
|
||||
|
||||
// *****************************************************************
|
||||
// When doubleConfirm is enabled, the first click transitions to a
|
||||
// second confirmation state. The second click emits confirmed.
|
||||
// *****************************************************************
|
||||
onConfirm() {
|
||||
if (this.doubleConfirm() && !this.awaitingSecondConfirm()) {
|
||||
this.awaitingSecondConfirm.set(true);
|
||||
return;
|
||||
}
|
||||
this.confirmed.emit();
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.awaitingSecondConfirm.set(false);
|
||||
this.closed.emit();
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,58 @@
|
||||
[event]="showEditEventModal() === 'new' ? null : $any(showEditEventModal())" (saved)="onEventSaved()"
|
||||
(closed)="showEditEventModal.set(null)" />
|
||||
}
|
||||
@if (showDeleteConfirm()) {
|
||||
<app-confirm-modal
|
||||
title="Delete Goal"
|
||||
[message]="'Delete \u0022' + selectedGoal()!.category + '\u0022 goal and all its benchmarks and progress events? This cannot be undone.'"
|
||||
confirmLabel="Delete"
|
||||
[destructive]="true"
|
||||
[doubleConfirm]="true"
|
||||
(confirmed)="onDeleteConfirmed()"
|
||||
(closed)="showDeleteConfirm.set(false)" />
|
||||
}
|
||||
@if (showDeleteBenchmarkConfirm() && deletingBenchmark()) {
|
||||
<app-confirm-modal
|
||||
title="Delete Benchmark"
|
||||
[message]="'Delete \u0022' + (deletingBenchmark()!.shortName || deletingBenchmark()!.benchmark) + '\u0022? This cannot be undone.'"
|
||||
confirmLabel="Delete"
|
||||
[destructive]="true"
|
||||
(confirmed)="onDeleteBenchmarkConfirmed()"
|
||||
(closed)="showDeleteBenchmarkConfirm.set(false)" />
|
||||
}
|
||||
@if (showDeleteEventConfirm() && deletingEvent()) {
|
||||
<app-confirm-modal
|
||||
title="Delete Progress Event"
|
||||
message="Delete this progress event? This cannot be undone."
|
||||
confirmLabel="Delete"
|
||||
[destructive]="true"
|
||||
(confirmed)="onDeleteEventConfirmed()"
|
||||
(closed)="showDeleteEventConfirm.set(false)" />
|
||||
}
|
||||
@if (showDeleteStudentConfirm()) {
|
||||
<app-confirm-modal
|
||||
title="Delete Student"
|
||||
[message]="'Delete \u0022' + student()!.identifier + '\u0022 and all their goals, benchmarks, and progress events? This cannot be undone.'"
|
||||
confirmLabel="Delete"
|
||||
[destructive]="true"
|
||||
[doubleConfirm]="true"
|
||||
(confirmed)="onDeleteStudentConfirmed()"
|
||||
(closed)="showDeleteStudentConfirm.set(false)" />
|
||||
}
|
||||
|
||||
<!-- Student Header -->
|
||||
<div class="student-header">
|
||||
<div class="student-info">
|
||||
<h1 class="student-name">{{ student()!.identifier }}</h1>
|
||||
<span class="student-iep">IEP {{ formatDate(student()!.nextIepDate) }}</span>
|
||||
<button class="delete-student-btn" (click)="onDeleteStudent()" aria-label="Delete student" title="Delete student">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="3 6 5 6 21 6"/>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||
<line x1="10" y1="11" x2="10" y2="17"/>
|
||||
<line x1="14" y1="11" x2="14" y2="17"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="goal-tabs">
|
||||
<button class="goal-tab add-goal" (click)="onAddGoal()">+ Goal</button>
|
||||
@@ -47,11 +93,19 @@
|
||||
<!-- Goal Card -->
|
||||
<div class="goal-card">
|
||||
<div class="goal-card-header">
|
||||
<span class="goal-badge">{{ selectedGoal()!.category }} Goal</span>
|
||||
<app-edit-icon (click)="onEditGoal()" ariaLabel="Edit goal" />
|
||||
<span class="goal-badge">{{ selectedGoal()!.category }} Goal</span>
|
||||
@if (selectedGoal()!.targetCompletionDate) {
|
||||
<span class="goal-due">Due {{ formatDate(selectedGoal()!.targetCompletionDate) }}</span>
|
||||
}
|
||||
<button class="delete-goal-btn" (click)="onDeleteGoal()" aria-label="Delete goal" title="Delete goal">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="3 6 5 6 21 6"/>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||
<line x1="10" y1="11" x2="10" y2="17"/>
|
||||
<line x1="14" y1="11" x2="14" y2="17"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<p class="goal-description">{{ selectedGoal()!.description }}</p>
|
||||
</div>
|
||||
@@ -75,8 +129,16 @@
|
||||
@for (b of goalBenchmarks(); track b.benchmarkId) {
|
||||
<div class="benchmark-card">
|
||||
<div class="benchmark-header">
|
||||
<span class="benchmark-name">{{ b.shortName || b.benchmark }}</span>
|
||||
<app-edit-icon size="13" (click)="onEditBenchmark(b)" ariaLabel="Edit benchmark" />
|
||||
<span class="benchmark-name">{{ b.shortName || b.benchmark }}</span>
|
||||
<button class="delete-benchmark-btn" (click)="onDeleteBenchmark(b)" aria-label="Delete benchmark" title="Delete benchmark">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="3 6 5 6 21 6"/>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||
<line x1="10" y1="11" x2="10" y2="17"/>
|
||||
<line x1="14" y1="11" x2="14" y2="17"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<p class="benchmark-desc">{{ b.benchmark }}</p>
|
||||
</div>
|
||||
@@ -94,8 +156,16 @@
|
||||
<div class="timeline-dot"></div>
|
||||
<div class="event-card">
|
||||
<div class="event-header">
|
||||
<span class="event-date">{{ formatDate(ev.createdAt) }}</span>
|
||||
<app-edit-icon size="13" (click)="onEditEvent(ev)" ariaLabel="Edit event" color="#bbb" />
|
||||
<span class="event-date">{{ formatDate(ev.createdAt) }}</span>
|
||||
<button class="delete-event-btn" (click)="onDeleteEvent(ev)" aria-label="Delete event" title="Delete event">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="3 6 5 6 21 6"/>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
|
||||
<line x1="10" y1="11" x2="10" y2="17"/>
|
||||
<line x1="14" y1="11" x2="14" y2="17"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<p class="event-content">{{ ev.content }}</p>
|
||||
@if (getBenchmarksForEvent(ev.progressEventId).length > 0) {
|
||||
|
||||
@@ -43,6 +43,26 @@
|
||||
color: var(--text-faint);
|
||||
}
|
||||
|
||||
.delete-student-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
align-self: center;
|
||||
padding: 4px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--text-faint);
|
||||
cursor: pointer;
|
||||
border-radius: var(--radius-sm);
|
||||
transition: color var(--transition-fast), background var(--transition-fast);
|
||||
|
||||
&:hover {
|
||||
color: #DC2626;
|
||||
background: #FEE2E2;
|
||||
}
|
||||
}
|
||||
|
||||
.goal-tabs {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
@@ -117,6 +137,24 @@
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.delete-goal-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--text-faint);
|
||||
cursor: pointer;
|
||||
border-radius: var(--radius-sm);
|
||||
transition: color var(--transition-fast), background var(--transition-fast);
|
||||
|
||||
&:hover {
|
||||
color: #DC2626;
|
||||
background: #FEE2E2;
|
||||
}
|
||||
}
|
||||
|
||||
.goal-description {
|
||||
font-size: 15px;
|
||||
line-height: 1.55;
|
||||
@@ -193,6 +231,25 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.delete-benchmark-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
padding: 4px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--text-faint);
|
||||
cursor: pointer;
|
||||
border-radius: var(--radius-sm);
|
||||
transition: color var(--transition-fast), background var(--transition-fast);
|
||||
|
||||
&:hover {
|
||||
color: #DC2626;
|
||||
background: #FEE2E2;
|
||||
}
|
||||
}
|
||||
|
||||
/* ─── Progress Timeline ─── */
|
||||
.timeline {
|
||||
position: relative;
|
||||
@@ -251,6 +308,25 @@
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.delete-event-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: auto;
|
||||
padding: 4px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--text-faint);
|
||||
cursor: pointer;
|
||||
border-radius: var(--radius-sm);
|
||||
transition: color var(--transition-fast), background var(--transition-fast);
|
||||
|
||||
&:hover {
|
||||
color: #DC2626;
|
||||
background: #FEE2E2;
|
||||
}
|
||||
}
|
||||
|
||||
.event-benchmarks {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -9,13 +9,14 @@ import { GoalModal } from '../goal-modal/goal-modal';
|
||||
import { EditBenchmarkModal } from '../edit-benchmark-modal/edit-benchmark-modal';
|
||||
import { EditEventModal } from '../edit-event-modal/edit-event-modal';
|
||||
import { EditIcon } from '../edit-icon/edit-icon';
|
||||
import { ConfirmModal } from '../confirm-modal/confirm-modal';
|
||||
import { formatDate } from '../../../shared/utils/format-date';
|
||||
|
||||
type TabView = 'benchmarks' | 'progress';
|
||||
|
||||
@Component({
|
||||
selector: 'app-workspace',
|
||||
imports: [GoalModal, EditBenchmarkModal, EditEventModal, EditIcon],
|
||||
imports: [GoalModal, EditBenchmarkModal, EditEventModal, EditIcon, ConfirmModal],
|
||||
templateUrl: './workspace.html',
|
||||
styleUrl: './workspace.scss',
|
||||
})
|
||||
@@ -79,6 +80,12 @@ export class Workspace {
|
||||
protected readonly showGoalModal = signal<StudentGoalItem | 'add' | null>(null);
|
||||
protected readonly showEditBenchmarkModal = signal<BenchmarkDto | 'new' | null>(null);
|
||||
protected readonly showEditEventModal = signal<ProgressEventWithGoalDto | null | 'new'>(null);
|
||||
protected readonly showDeleteConfirm = signal(false);
|
||||
protected readonly showDeleteBenchmarkConfirm = signal(false);
|
||||
protected readonly deletingBenchmark = signal<BenchmarkDto | null>(null);
|
||||
protected readonly showDeleteEventConfirm = signal(false);
|
||||
protected readonly deletingEvent = signal<ProgressEventWithGoalDto | null>(null);
|
||||
protected readonly showDeleteStudentConfirm = signal(false);
|
||||
|
||||
// ************************** Properties ***************************
|
||||
|
||||
@@ -135,6 +142,28 @@ export class Workspace {
|
||||
this.showGoalModal.set('add');
|
||||
}
|
||||
|
||||
onDeleteGoal() {
|
||||
if (!this.selectedGoal()) return;
|
||||
this.showDeleteConfirm.set(true);
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Called when the user confirms deletion in the confirm modal.
|
||||
// Deletes the selected goal and all its child entities.
|
||||
// *****************************************************************
|
||||
async onDeleteConfirmed() {
|
||||
this.showDeleteConfirm.set(false);
|
||||
const goal = this.selectedGoal();
|
||||
if (!goal) return;
|
||||
|
||||
const result = await this.studentService.deleteGoal(this.studentId()!, goal.goalId);
|
||||
if (!result.success) return;
|
||||
|
||||
this.selectedGoalId.set(null);
|
||||
this.studentService.notifyDataChanged();
|
||||
await this.refetchProfile();
|
||||
}
|
||||
|
||||
onGoalCreated(goal: StudentGoalItem) {
|
||||
this.showGoalModal.set(null);
|
||||
this.studentService.notifyDataChanged();
|
||||
@@ -156,6 +185,27 @@ export class Workspace {
|
||||
this.showEditBenchmarkModal.set('new');
|
||||
}
|
||||
|
||||
onDeleteBenchmark(b: BenchmarkDto) {
|
||||
this.deletingBenchmark.set(b);
|
||||
this.showDeleteBenchmarkConfirm.set(true);
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Called when the user confirms deletion in the confirm modal.
|
||||
// Deletes the benchmark and its event associations.
|
||||
// *****************************************************************
|
||||
async onDeleteBenchmarkConfirmed() {
|
||||
this.showDeleteBenchmarkConfirm.set(false);
|
||||
const b = this.deletingBenchmark();
|
||||
if (!b) return;
|
||||
|
||||
this.deletingBenchmark.set(null);
|
||||
const result = await this.studentService.deleteBenchmark(this.studentId()!, b.benchmarkId);
|
||||
if (!result.success) return;
|
||||
|
||||
await this.refetchProfile();
|
||||
}
|
||||
|
||||
onNewEvent() {
|
||||
this.showEditEventModal.set('new');
|
||||
}
|
||||
@@ -169,6 +219,27 @@ export class Workspace {
|
||||
this.refetchProfile();
|
||||
}
|
||||
|
||||
onDeleteEvent(ev: ProgressEventWithGoalDto) {
|
||||
this.deletingEvent.set(ev);
|
||||
this.showDeleteEventConfirm.set(true);
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Called when the user confirms deletion in the confirm modal.
|
||||
// Deletes the progress event and its benchmark associations.
|
||||
// *****************************************************************
|
||||
async onDeleteEventConfirmed() {
|
||||
this.showDeleteEventConfirm.set(false);
|
||||
const ev = this.deletingEvent();
|
||||
if (!ev) return;
|
||||
|
||||
this.deletingEvent.set(null);
|
||||
const result = await this.studentService.deleteProgressEvent(this.studentId()!, ev.progressEventId);
|
||||
if (!result.success) return;
|
||||
|
||||
await this.refetchProfile();
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Returns the benchmark IDs associated with a given progress event,
|
||||
// read from the cached profile data.
|
||||
@@ -186,6 +257,26 @@ export class Workspace {
|
||||
|
||||
formatDate = formatDate;
|
||||
|
||||
onDeleteStudent() {
|
||||
this.showDeleteStudentConfirm.set(true);
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Called when the user confirms student deletion. Deletes the
|
||||
// student and navigates back to the home page.
|
||||
// *****************************************************************
|
||||
async onDeleteStudentConfirmed() {
|
||||
this.showDeleteStudentConfirm.set(false);
|
||||
const id = this.studentId();
|
||||
if (!id) return;
|
||||
|
||||
const result = await this.studentService.deleteStudent(id);
|
||||
if (!result.success) return;
|
||||
|
||||
this.studentService.notifyDataChanged();
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
|
||||
// ********************** Support Procedures ***********************
|
||||
|
||||
private async loadStudentData(studentId: string) {
|
||||
|
||||
@@ -102,15 +102,11 @@
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--text-faint);
|
||||
padding: 12px 12px 4px;
|
||||
margin-top: 4px;
|
||||
border-top: 1px solid var(--border-color);
|
||||
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
margin-top: 0;
|
||||
}
|
||||
color: #4338CA;
|
||||
background: #EEF2FF;
|
||||
padding: 6px 12px;
|
||||
margin-top: 6px;
|
||||
border-left: 3px solid #818CF8;
|
||||
}
|
||||
|
||||
.student-item {
|
||||
|
||||
@@ -145,6 +145,23 @@ export class StudentService {
|
||||
}
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Deletes a goal and all its child entities (benchmarks, progress
|
||||
// events, event-benchmark links).
|
||||
// *****************************************************************
|
||||
async deleteGoal(studentId: string, goalId: string): Promise<ApiResult> {
|
||||
try {
|
||||
const result = await firstValueFrom(
|
||||
this.http.delete<ResponseResult<void>>(`${this.base}/api/Student/${studentId}/goals/${goalId}`)
|
||||
);
|
||||
return result.success
|
||||
? ApiResult.empty()
|
||||
: ApiResult.fail(result.message);
|
||||
} catch (error) {
|
||||
return ApiResult.fail(describeHttpError(error as HttpErrorResponse));
|
||||
}
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Creates a new progress event, optionally with benchmark
|
||||
// associations. Returns the new progress event ID on success.
|
||||
@@ -178,6 +195,22 @@ export class StudentService {
|
||||
}
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Deletes a progress event and its benchmark associations.
|
||||
// *****************************************************************
|
||||
async deleteProgressEvent(studentId: string, progressEventId: string): Promise<ApiResult> {
|
||||
try {
|
||||
const result = await firstValueFrom(
|
||||
this.http.delete<ResponseResult<void>>(`${this.base}/api/Student/${studentId}/progress-events/${progressEventId}`)
|
||||
);
|
||||
return result.success
|
||||
? ApiResult.empty()
|
||||
: ApiResult.fail(result.message);
|
||||
} catch (error) {
|
||||
return ApiResult.fail(describeHttpError(error as HttpErrorResponse));
|
||||
}
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Returns a full progress report for a student within a date
|
||||
// range, including goals, events, and benchmark associations.
|
||||
@@ -221,6 +254,22 @@ export class StudentService {
|
||||
}
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Deletes a student and all associated data.
|
||||
// *****************************************************************
|
||||
async deleteStudent(studentId: string): Promise<ApiResult> {
|
||||
try {
|
||||
const result = await firstValueFrom(
|
||||
this.http.delete<ResponseResult<void>>(`${this.base}/api/Student/${studentId}`)
|
||||
);
|
||||
return result.success
|
||||
? ApiResult.empty()
|
||||
: ApiResult.fail(result.message);
|
||||
} catch (error) {
|
||||
return ApiResult.fail(describeHttpError(error as HttpErrorResponse));
|
||||
}
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Returns benchmarks for a given student.
|
||||
// *****************************************************************
|
||||
@@ -269,6 +318,22 @@ export class StudentService {
|
||||
}
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Deletes a benchmark and its progress-event associations.
|
||||
// *****************************************************************
|
||||
async deleteBenchmark(studentId: string, benchmarkId: string): Promise<ApiResult> {
|
||||
try {
|
||||
const result = await firstValueFrom(
|
||||
this.http.delete<ResponseResult<void>>(`${this.base}/api/Student/${studentId}/benchmarks/${benchmarkId}`)
|
||||
);
|
||||
return result.success
|
||||
? ApiResult.empty()
|
||||
: ApiResult.fail(result.message);
|
||||
} catch (error) {
|
||||
return ApiResult.fail(describeHttpError(error as HttpErrorResponse));
|
||||
}
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Requests an AI-generated benchmark recommendation for a goal.
|
||||
// *****************************************************************
|
||||
|
||||
Reference in New Issue
Block a user