package classrooms import ( "boostai-backend/internal/handlers/api/shared" "boostai-backend/internal/http/params" "boostai-backend/internal/http/respond" authmw "boostai-backend/internal/middleware" "boostai-backend/internal/sqlc" "strings" "time" "github.com/gofiber/fiber/v2" ) type Handler struct { queries *sqlc.Queries } type ClassroomResponse struct { ID int64 `json:"id"` TeacherID int64 `json:"teacher_id"` Name string `json:"name"` Code *string `json:"code,omitempty"` Description *string `json:"description,omitempty"` CreatedAt *time.Time `json:"created_at,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"` } type StudentResponse struct { ID int64 `json:"id"` Email string `json:"email"` Role string `json:"role"` FullName string `json:"full_name"` IsActive bool `json:"is_active"` CreatedAt *time.Time `json:"created_at,omitempty"` UpdatedAt *time.Time `json:"updated_at,omitempty"` } type createClassroomRequest struct { TeacherID int64 `json:"teacher_id"` Name string `json:"name"` Code *string `json:"code"` Description *string `json:"description"` } type addStudentToClassroomRequest struct { StudentID int64 `json:"student_id"` } func NewHandler(queries *sqlc.Queries) *Handler { return &Handler{queries: queries} } func (h *Handler) ListClassroomsByTeacher(c *fiber.Ctx) error { teacherID, err := params.Int64PathParam(c, "teacherId") if err != nil { return err } ctx, cancel := shared.WithTimeout() defer cancel() classrooms, err := h.queries.ListClassroomsByTeacher(ctx, teacherID) if err != nil { return respond.DatabaseError(c, err) } items := make([]ClassroomResponse, 0, len(classrooms)) for _, classroom := range classrooms { items = append(items, mapClassroom(classroom)) } return c.JSON(shared.ListResponse[ClassroomResponse]{Data: items}) } func (h *Handler) ListStudentsForClassroom(c *fiber.Ctx) error { classroomID, err := params.Int64PathParam(c, "classroomId") if err != nil { return err } ctx, cancel := shared.WithTimeout() defer cancel() students, err := h.queries.ListStudentsForClassroom(ctx, classroomID) if err != nil { return respond.DatabaseError(c, err) } items := make([]StudentResponse, 0, len(students)) for _, student := range students { items = append(items, mapStudent(student)) } return c.JSON(shared.ListResponse[StudentResponse]{Data: items}) } func (h *Handler) CreateClassroom(c *fiber.Ctx) error { var req createClassroomRequest if err := c.BodyParser(&req); err != nil { return respond.Error(c, fiber.StatusBadRequest, "invalid_request", "Unable to parse request body") } teacherID := authmw.CurrentUserID(c) if teacherID == 0 || strings.TrimSpace(req.Name) == "" { return respond.Error(c, fiber.StatusBadRequest, "invalid_request", "teacher authentication and name are required") } ctx, cancel := shared.WithTimeout() defer cancel() classroom, err := h.queries.CreateClassroom(ctx, sqlc.CreateClassroomParams{ TeacherID: teacherID, Name: strings.TrimSpace(req.Name), Code: shared.NullableText(req.Code), Description: shared.NullableText(req.Description), }) if err != nil { return respond.DatabaseError(c, err) } return c.Status(fiber.StatusCreated).JSON(mapClassroom(classroom)) } func (h *Handler) AddStudentToClassroom(c *fiber.Ctx) error { classroomID, err := params.Int64PathParam(c, "classroomId") if err != nil { return err } var req addStudentToClassroomRequest if err := c.BodyParser(&req); err != nil { return respond.Error(c, fiber.StatusBadRequest, "invalid_request", "Unable to parse request body") } if req.StudentID == 0 { return respond.Error(c, fiber.StatusBadRequest, "invalid_request", "student_id is required") } ctx, cancel := shared.WithTimeout() defer cancel() err = h.queries.AddStudentToClassroom(ctx, sqlc.AddStudentToClassroomParams{ ClassroomID: classroomID, StudentID: req.StudentID, }) if err != nil { return respond.DatabaseError(c, err) } return c.Status(fiber.StatusCreated).JSON(fiber.Map{ "status": "ok", "classroom_id": classroomID, "student_id": req.StudentID, }) } func mapClassroom(classroom sqlc.Classroom) ClassroomResponse { return ClassroomResponse{ ID: classroom.ID, TeacherID: classroom.TeacherID, Name: classroom.Name, Code: shared.TextPointer(classroom.Code), Description: shared.TextPointer(classroom.Description), CreatedAt: shared.TimePointer(classroom.CreatedAt), UpdatedAt: shared.TimePointer(classroom.UpdatedAt), } } func mapStudent(user sqlc.User) StudentResponse { return StudentResponse{ ID: user.ID, Email: user.Email, Role: string(user.Role), FullName: user.FullName, IsActive: user.IsActive, CreatedAt: shared.TimePointer(user.CreatedAt), UpdatedAt: shared.TimePointer(user.UpdatedAt), } }