mirror of
https://github.com/opelly27/WinStudentGoalTracker.git
synced 2026-05-20 05:17:41 +00:00
parent id fix
This commit is contained in:
@@ -163,6 +163,30 @@ public class StudentController : BaseController
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dto.GoalParentId.HasValue)
|
||||||
|
{
|
||||||
|
var summary = await _studentRepository.GetGoalSummaryAsync(idStudent);
|
||||||
|
var parentGoal = summary?.Goals.FirstOrDefault(g => g.GoalId == dto.GoalParentId.Value);
|
||||||
|
|
||||||
|
if (parentGoal is null)
|
||||||
|
{
|
||||||
|
return BadRequest(new ResponseResult<StudentGoalItem>
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = "Parent goal not found."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentGoal.GoalParentId.HasValue)
|
||||||
|
{
|
||||||
|
return BadRequest(new ResponseResult<StudentGoalItem>
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Message = "The selected parent goal already has a parent."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var created = await _studentRepository.InsertGoalAsync(idStudent, userId, dto);
|
var created = await _studentRepository.InsertGoalAsync(idStudent, userId, dto);
|
||||||
if (created is null)
|
if (created is null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5,4 +5,5 @@ public class CreateGoalDto
|
|||||||
public string? Title { get; set; }
|
public string? Title { get; set; }
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
public string? Category { get; set; }
|
public string? Category { get; set; }
|
||||||
|
public Guid? GoalParentId { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ public class StudentRepository
|
|||||||
new
|
new
|
||||||
{
|
{
|
||||||
p_id_goal = newGoalId.ToString(),
|
p_id_goal = newGoalId.ToString(),
|
||||||
|
p_id_goal_parent = dto.GoalParentId?.ToString(),
|
||||||
p_id_student = idStudent.ToString(),
|
p_id_student = idStudent.ToString(),
|
||||||
p_id_user = userId.ToString(),
|
p_id_user = userId.ToString(),
|
||||||
p_title = dto.Title,
|
p_title = dto.Title,
|
||||||
|
|||||||
+12
@@ -32,6 +32,18 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if (parentGoalOptions().length > 0) {
|
||||||
|
<div class="field">
|
||||||
|
<label for="goalParentId">Parent Goal <span class="optional">(optional)</span></label>
|
||||||
|
<select id="goalParentId" [(ngModel)]="form.goalParentId" name="goalParentId">
|
||||||
|
<option [ngValue]="null">None</option>
|
||||||
|
@for (goal of parentGoalOptions(); track goal.goalId) {
|
||||||
|
<option [ngValue]="goal.goalId">{{ goal.title }}</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label for="description">Description</label>
|
<label for="description">Description</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
|||||||
+9
-2
@@ -68,8 +68,14 @@
|
|||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.optional {
|
||||||
|
font-weight: 400;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
.field input,
|
.field input,
|
||||||
.field textarea {
|
.field textarea,
|
||||||
|
.field select {
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
@@ -80,7 +86,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.field input:focus,
|
.field input:focus,
|
||||||
.field textarea:focus {
|
.field textarea:focus,
|
||||||
|
.field select:focus {
|
||||||
border-color: #4f46e5;
|
border-color: #4f46e5;
|
||||||
box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.15);
|
box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.15);
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -1,4 +1,4 @@
|
|||||||
import { Component, inject, input, output, signal } from '@angular/core';
|
import { Component, computed, inject, input, output, signal } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { CreateGoalDto } from '../../../shared/classes/create-goal.dto';
|
import { CreateGoalDto } from '../../../shared/classes/create-goal.dto';
|
||||||
import { StudentGoalItem } from '../../../shared/classes/student-goal';
|
import { StudentGoalItem } from '../../../shared/classes/student-goal';
|
||||||
@@ -19,16 +19,22 @@ export class AddGoalModal {
|
|||||||
private readonly studentService = inject(StudentService);
|
private readonly studentService = inject(StudentService);
|
||||||
|
|
||||||
readonly studentId = input.required<string>();
|
readonly studentId = input.required<string>();
|
||||||
|
readonly existingGoals = input.required<StudentGoalItem[]>();
|
||||||
readonly goalCreated = output<StudentGoalItem>();
|
readonly goalCreated = output<StudentGoalItem>();
|
||||||
readonly cancelled = output<void>();
|
readonly cancelled = output<void>();
|
||||||
|
|
||||||
protected readonly isSubmitting = signal(false);
|
protected readonly isSubmitting = signal(false);
|
||||||
protected readonly errorMessage = signal<string | null>(null);
|
protected readonly errorMessage = signal<string | null>(null);
|
||||||
|
|
||||||
|
protected readonly parentGoalOptions = computed(() =>
|
||||||
|
this.existingGoals().filter(g => g.goalParentId === null)
|
||||||
|
);
|
||||||
|
|
||||||
protected form: CreateGoalDto = {
|
protected form: CreateGoalDto = {
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
category: '',
|
category: '',
|
||||||
|
goalParentId: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
// ************************** Properties ***************************
|
// ************************** Properties ***************************
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
@if (showAddModal()) {
|
@if (showAddModal()) {
|
||||||
<app-add-goal-modal
|
<app-add-goal-modal
|
||||||
[studentId]="studentId"
|
[studentId]="studentId"
|
||||||
|
[existingGoals]="goals()"
|
||||||
(goalCreated)="onGoalCreated($event)"
|
(goalCreated)="onGoalCreated($event)"
|
||||||
(cancelled)="onModalCancelled()"
|
(cancelled)="onModalCancelled()"
|
||||||
/>
|
/>
|
||||||
@@ -19,8 +20,12 @@
|
|||||||
<p class="error">{{ errorMessage() }}</p>
|
<p class="error">{{ errorMessage() }}</p>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="card-grid">
|
@if (goals().length === 0 && !errorMessage()) {
|
||||||
|
<p class="empty-state">No goals yet. Click <strong>+ Add a Goal</strong> to get started.</p>
|
||||||
|
} @else {
|
||||||
|
<div class="card-grid">
|
||||||
@for (goal of goals(); track goal.goalId) {
|
@for (goal of goals(); track goal.goalId) {
|
||||||
<app-goal-card [goal]="goal" />
|
<app-goal-card [goal]="goal" />
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,6 +52,13 @@
|
|||||||
margin: 0 0 1rem;
|
margin: 0 0 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
color: #888;
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
margin: 2rem auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.card-grid {
|
.card-grid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|||||||
@@ -2,4 +2,5 @@ export interface CreateGoalDto {
|
|||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
category: string;
|
category: string;
|
||||||
|
goalParentId: string | null;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user