mirror of
https://github.com/opelly27/WinStudentGoalTracker.git
synced 2026-05-20 07:37:38 +00:00
latest
This commit is contained in:
@@ -246,6 +246,49 @@ public class StudentController : BaseController
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("goals/{idGoal:guid}/progress-events")]
|
||||||
|
[Authorize(Roles = $"{UserRoles.Teacher},{UserRoles.Paraeducator},{UserRoles.ProgramAdmin}")]
|
||||||
|
[ProducesResponseType(typeof(ResponseResult<IEnumerable<ProgressEventResponse>>), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(typeof(ResponseResult<IEnumerable<ProgressEventResponse>>), StatusCodes.Status404NotFound)]
|
||||||
|
public async Task<ActionResult<ResponseResult<IEnumerable<ProgressEventResponse>>>> GetProgressEventsForGoal(Guid idGoal)
|
||||||
|
{
|
||||||
|
var (userId, email, programId, role, error) = GetProgramUserFromClaims();
|
||||||
|
if (error is not null)
|
||||||
|
{
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var studentId = await _studentRepository.GetStudentIdForGoalAsync(idGoal);
|
||||||
|
if (!studentId.HasValue)
|
||||||
|
{
|
||||||
|
return NotFound(new ResponseResult<IEnumerable<ProgressEventResponse>>
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = "Goal not found."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var students = await _studentRepository.GetMyStudentsAsync(userId, programId, role);
|
||||||
|
|
||||||
|
if (!students.Select(s => s.StudentId).Contains(studentId.Value))
|
||||||
|
{
|
||||||
|
return NotFound(new ResponseResult<IEnumerable<ProgressEventResponse>>
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = "Goal not found."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var progressEvents = await _studentRepository.GetProgressEventsForGoalAsync(idGoal);
|
||||||
|
|
||||||
|
return Ok(new ResponseResult<IEnumerable<ProgressEventResponse>>
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Message = "Progress events retrieved successfully.",
|
||||||
|
Data = progressEvents
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Authorize(Roles = $"{UserRoles.Teacher}")]
|
[Authorize(Roles = $"{UserRoles.Teacher}")]
|
||||||
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status201Created)]
|
[ProducesResponseType(typeof(ResponseResult<StudentResponse>), StatusCodes.Status201Created)]
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace WinStudentGoalTracker.DataAccess;
|
||||||
|
|
||||||
|
public class dbGoalStudentRow
|
||||||
|
{
|
||||||
|
public Guid StudentId { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace WinStudentGoalTracker.DataAccess;
|
||||||
|
|
||||||
|
public class dbProgressEventRow
|
||||||
|
{
|
||||||
|
public required Guid ProgressEventId { get; set; }
|
||||||
|
public string? Content { get; set; }
|
||||||
|
public DateTime? CreatedAt { get; set; }
|
||||||
|
public string? CreatedByName { get; set; }
|
||||||
|
}
|
||||||
@@ -101,6 +101,37 @@ public class StudentRepository
|
|||||||
return row is not null;
|
return row is not null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<Guid?> GetStudentIdForGoalAsync(Guid idGoal)
|
||||||
|
{
|
||||||
|
using var db = Connection;
|
||||||
|
var row = await db.QuerySingleOrDefaultAsync<dbGoalStudentRow>(
|
||||||
|
"sp_Goal_GetById",
|
||||||
|
new { p_id_goal = idGoal.ToString() },
|
||||||
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
|
return row?.StudentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<ProgressEventResponse>> GetProgressEventsForGoalAsync(Guid idGoal)
|
||||||
|
{
|
||||||
|
using var db = Connection;
|
||||||
|
var rows = await db.QueryAsync<dbProgressEventRow>(
|
||||||
|
"sp_ProgressEvent_GetByGoalId",
|
||||||
|
new
|
||||||
|
{
|
||||||
|
p_id_goal = idGoal.ToString()
|
||||||
|
},
|
||||||
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
|
return rows.Select(r => new ProgressEventResponse
|
||||||
|
{
|
||||||
|
ProgressEventId = r.ProgressEventId,
|
||||||
|
Content = r.Content,
|
||||||
|
CreatedAt = r.CreatedAt,
|
||||||
|
CreatedByName = r.CreatedByName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<StudentGoalItem?> InsertGoalAsync(Guid idStudent, Guid userId, CreateGoalDto dto)
|
public async Task<StudentGoalItem?> InsertGoalAsync(Guid idStudent, Guid userId, CreateGoalDto dto)
|
||||||
{
|
{
|
||||||
var newGoalId = Guid.NewGuid();
|
var newGoalId = Guid.NewGuid();
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace WinStudentGoalTracker.Models;
|
||||||
|
|
||||||
|
public class ProgressEventResponse
|
||||||
|
{
|
||||||
|
public Guid ProgressEventId { get; set; }
|
||||||
|
public string? Content { get; set; }
|
||||||
|
public DateTime? CreatedAt { get; set; }
|
||||||
|
public string? CreatedByName { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
DELIMITER ;;
|
||||||
|
CREATE DEFINER=`root`@`%` PROCEDURE `sp_ProgressEvent_GetByGoalId`(IN p_id_goal CHAR(36))
|
||||||
|
BEGIN
|
||||||
|
SELECT
|
||||||
|
vc.`progressEventId`,
|
||||||
|
vc.`content`,
|
||||||
|
vc.`createdAt`,
|
||||||
|
vc.`createdByName`
|
||||||
|
FROM `v_progress_event_card` vc
|
||||||
|
WHERE vc.`goalId` = p_id_goal
|
||||||
|
ORDER BY vc.`createdAt` DESC;
|
||||||
|
END;;
|
||||||
|
DELIMITER ;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`%` SQL SECURITY DEFINER VIEW `winstudentgoaltracker`.`v_progress_event_card` AS
|
||||||
|
select `pe`.`id_progress_event` AS `progressEventId`,`pe`.`id_goal` AS `goalId`,`g`.`id_student` AS `studentId`,`pe`.`content` AS `content`,`pe`.`created_at` AS `createdAt`,`u`.`name` AS `createdByName`
|
||||||
|
from ((`winstudentgoaltracker`.`progress_event` `pe`
|
||||||
|
join `winstudentgoaltracker`.`goal` `g` on((`g`.`id_goal` = `pe`.`id_goal`)))
|
||||||
|
left join `winstudentgoaltracker`.`user` `u` on((`u`.`id_user` = `pe`.`id_user_created`)));
|
||||||
@@ -4,7 +4,6 @@ import { Subject } from 'rxjs';
|
|||||||
import { debounceTime } from 'rxjs/operators';
|
import { debounceTime } from 'rxjs/operators';
|
||||||
import { ProgressItem } from '../progress-item/progress-item';
|
import { ProgressItem } from '../progress-item/progress-item';
|
||||||
import { ProgressEventDto } from '../../../shared/classes/progress-event.dto';
|
import { ProgressEventDto } from '../../../shared/classes/progress-event.dto';
|
||||||
import { DummyStudentService } from '../../../shared/services/dummy-student.service';
|
|
||||||
import { StudentService } from '../../../shared/services/student.service';
|
import { StudentService } from '../../../shared/services/student.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -30,7 +29,6 @@ export class ProgressList implements OnDestroy {
|
|||||||
|
|
||||||
// ************************** Declarations *************************
|
// ************************** Declarations *************************
|
||||||
|
|
||||||
private readonly dummyService = inject(DummyStudentService);
|
|
||||||
private readonly studentService = inject(StudentService);
|
private readonly studentService = inject(StudentService);
|
||||||
private readonly route = inject(ActivatedRoute);
|
private readonly route = inject(ActivatedRoute);
|
||||||
private readonly router = inject(Router);
|
private readonly router = inject(Router);
|
||||||
@@ -97,12 +95,11 @@ export class ProgressList implements OnDestroy {
|
|||||||
// ********************** Support Procedures ***********************
|
// ********************** Support Procedures ***********************
|
||||||
|
|
||||||
// *****************************************************************
|
// *****************************************************************
|
||||||
// Loads progress events for the given goal from the dummy service,
|
// Loads progress events for the given goal from the API, sorted
|
||||||
// sorted newest-first by createdAt.
|
// newest-first by createdAt.
|
||||||
// TODO: Replace DummyStudentService with StudentService
|
|
||||||
// *****************************************************************
|
// *****************************************************************
|
||||||
private loadEvents() {
|
private loadEvents() {
|
||||||
this.dummyService.getProgressEventsForGoal(this.goalId).then(result => {
|
this.studentService.getProgressEventsForGoal(this.goalId).then(result => {
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
this.errorMessage.set(result.message);
|
this.errorMessage.set(result.message);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { CreateStudentDto } from '../classes/create-student.dto';
|
|||||||
import { CreateGoalDto } from '../classes/create-goal.dto';
|
import { CreateGoalDto } from '../classes/create-goal.dto';
|
||||||
import { StudentCardDto } from '../classes/student-card.dto';
|
import { StudentCardDto } from '../classes/student-card.dto';
|
||||||
import { StudentGoalSummary, StudentGoalItem } from '../classes/student-goal';
|
import { StudentGoalSummary, StudentGoalItem } from '../classes/student-goal';
|
||||||
|
import { ProgressEventDto } from '../classes/progress-event.dto';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -103,6 +104,22 @@ export class StudentService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// *****************************************************************
|
||||||
|
// Returns progress events for a given student goal.
|
||||||
|
// *****************************************************************
|
||||||
|
async getProgressEventsForGoal(goalId: string): Promise<ApiResult<ProgressEventDto[]>> {
|
||||||
|
try {
|
||||||
|
const result = await firstValueFrom(
|
||||||
|
this.http.get<ResponseResult<ProgressEventDto[]>>(`${this.base}/api/Student/goals/${goalId}/progress-events`)
|
||||||
|
);
|
||||||
|
return result.success
|
||||||
|
? ApiResult.ok(result.data ?? [])
|
||||||
|
: ApiResult.fail(result.message);
|
||||||
|
} catch (error) {
|
||||||
|
return ApiResult.fail(describeHttpError(error as HttpErrorResponse));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ************************ Event Handlers *************************
|
// ************************ Event Handlers *************************
|
||||||
|
|
||||||
// ********************** Support Procedures ***********************
|
// ********************** Support Procedures ***********************
|
||||||
|
|||||||
Reference in New Issue
Block a user