Files
banu-front/docs/backend-vs-ui-gaps.md
T
sajjadtalkhabi 9660e3fe8f
Deploy banu-front / deploy (push) Successful in 1m28s
fix: linter
2026-05-28 15:52:48 +03:30

14 KiB

Backend (Postman) vs Frontend — fields & endpoints to decide on

Source of truth: the Postman collection covering Terms, Courses, Sessions, Exams, Homeworks, Media (2026-05). This doc lists every place where backend and FE disagree on shape, plus FE-side concepts the backend doc has no slot for. Each item needs a product/design call before we wire it up.

Conventions in the rest of this doc:

  • B → FE means the backend exposes a field/endpoint that the FE doesn't surface yet.
  • FE → B means the FE shows/sends a field the backend doc doesn't accept.
  • shape diff means both sides handle the concept but in different shapes (enum values, nesting, naming).

Terms

Endpoints

Backend FE today Status
GET /terms?per_page&active_only getTermsList aligned
GET /terms/:id showTerm aligned
POST /terms addNewTerm aligned
PATCH /terms/:id updateTerm aligned
DELETE /terms/:id deleteTerm aligned
cloneTerm (POST /admin/terms/:id/clone) FE-only; backend has nothing. Decide: drop the clone button, or ask backend to add it.
changeStatusTerm (POST /admin/terms/:id/status) Use PATCH /terms/:id with is_active — dedicated status endpoint dropped.
listUserTerm, addUserTerm, removeUserTerm, changeLeaveStatus FE-only. Term-students subtab + leave toggle. Backend exposes nothing equivalent. Keep mock-only until backend adds.
listCourseTerm, addCourseTerm, removeCourseTerm FE-only. Can be replaced by GET /courses?term_id= for the list; the attach/detach side has no backend equivalent (course term_id is set at create-time).

Fields

Backend → FE FE has it as Notes
starts_at, ends_at (ISO datetime) startDate, endDate Naming diff; we send starts_at/ends_at in the mock already — confirm the FE form should rename or keep an adapter.
cover_url (read) + cover_media_id (write) coverUrl / coverMediaId aligned in shape, just camelCase.
created_at createdAt aligned.
is_active isActive aligned.
description description aligned.

FE → B (need decision):

  • studentsCount, coursesCount — computed UI counts. Backend response doesn't include them. Either compute client-side or ask backend for ?include=counts.
  • image (UI alias of cover) — FE keeps this for a fallback. Backend response only has cover_url.

Courses

Endpoints

Backend FE today Status
GET /courses?term_id&per_page getCoursesList aligned (drop /admin/ prefix in endpoints.js).
GET /courses/:id showCourse aligned. Backend response includes nested term and teacher — FE already reads course.term / course.teacher, good.
POST /courses addNewCourse aligned. Backend currently requires term_id (422 example), but per user instruction the standalone (template-tab) flow must allow term_id: null. Decide: ask backend to make term_id nullable, or refuse to submit until a term is picked.
PATCH /courses/:id updateCourse aligned. Used now for status toggle too (drops dedicated /status endpoint).
DELETE /courses/:id deleteCourse aligned.
changeStatusCourse (/admin/courses/:id/status) Dropped. Use PATCH /courses/:id { is_active }.
listCourseStudents, addCourseStudent, removeCourseStudent FE-only. CourseDetailsModal "students" tab + AddCourseStudentModal. Backend doesn't expose course-students; either add a sub-resource or remove the UI. Kept mock-only for now.
listCourseSessions, attachCourseSession, detachCourseSession FE-only. CourseDetailsModal "sessions" tab can be served by GET /sessions?course_id=. The attach/detach side (M:N) has no backend equivalent — backend uses session.course_id (1:N). Kept mock-only for now; recommend swapping the list tab to the regular sessions query.

Fields

Backend ↔ FE Notes
term_idtermId aligned. Nullable in CourseFormPage, required in AddOfferedCourseModal — schema reflects this.
teacher_idteacherId aligned.
capacitycapacity aligned.
is_activeisActive aligned.
cover_media_id (write) / cover_url (read) ⇄ coverMediaId / coverUrl aligned.
descriptiondescription aligned.
Nested term, teacher on show response FE already reads.

FE → B (need decision):

  • sessionsCount — number-of-sessions field on the create form. Backend has nothing. Either compute server-side from related sessions, or drop the field.
  • prerequisites (array of { courseId, course }) — backend has no prerequisite relation. Drop or ask backend for it.
  • contentType (video/voice/text) + contentMediaId — single course-level content file. Backend treats files only as session media. Decide whether course-level content should move to "intro session" or stay a course concept.
  • image (UI alias of cover_url) — kept as a fallback alongside coverUrl.

Sessions

Endpoints

Backend FE today Status
GET /sessions?course_id&per_page getSessionsList aligned.
GET /sessions/:id showSession aligned.
POST /sessions addNewSession aligned.
PATCH /sessions/:id updateSession aligned. Used for status toggle now.
DELETE /sessions/:id deleteSession aligned.
changeStatusSession (/admin/sessions/:id/toggle-status) Dropped. Use PATCH with is_active.
getSessionsAttendance (/admin/sessions/:sessionId/attendances) FE-only. SessionAttendanceModal depends on this. Kept mock-only.

Fields — biggest gap of all five resources

Backend FE today Notes
type enum: online | offline | content sessionType enum: in_person, online, video, audio, text, slide, pdf Shape diff. FE has 7 values; backend has 3. There's an existing SESSION_TYPE_TO_SPEC mapper in services/mock/fixtures/admin-sessions.js. Decide whether the FE keeps the richer 7-value enum (and we map down to backend's 3) or collapses.
starts_at, location, link All three live inside form.sessionConfig.* plus also derived to top-level startsAt / location / link in the mock Structural diff. Backend wants flat fields; FE form nests them under sessionConfig keyed by sessionType. The mock derives top-level from sessionConfig.* for show payloads. Decide whether the FE form should flatten the schema to match backend or keep the conditional-by-type config UI.
media_ids[] (write) / media[] (read with collection_name, file_name, mime_type, file_size, url, download_url) materials[] with { fileId, isRequired, type, title, order } Shape diff. Backend's media rows are typed by upload-purpose (video/voice/pdf/slide/attachment); FE has its own type enum. Decide which shape the FE keeps.
durationMinutes, order, sessionConfig.minWatchedPercent, sessionConfig.minReadPercent, sessionConfig.mustCompleteBeforeNext, sessionConfig.platform FE → B, all UI-only fields. Backend has nothing equivalent. Drop, move into a metadata JSON, or ask backend to add.
usedInTerms UI-only count, no backend.
image (vs media) UI-only thumbnail; backend doesn't separate.

Exams

Endpoints

Backend FE today Status
GET /exams/:id (with questions+options) showExam aligned.
POST /exams addNewExam aligned.
PATCH /exams/:id updateExam aligned.
DELETE /exams/:id deleteExam aligned.
POST /exams/:examId/questions Backend → FE. New endpoint. Today the FE submits the whole question list inside the exam create/update payload. Decide whether to keep "all-in-one" submission (and ask backend to accept it) or switch to add-questions-after-create.
POST /questions/:questionId/options Backend → FE. Same as above — backend lets you add options one at a time. FE today bundles all options with the question.
POST /exams/:examId/submit — (student-side feature) Backend → FE. Student-side; not in current admin UI.
getExamsList (/admin/exams) FE-only. ExamsListPage depends on it. Kept mock-only; ask backend to add a list endpoint.
getExamParticipants, showExamParticipant FE-only. ExamParticipantsModal + ExamParticipantDetailsModal depend on these. Kept mock-only.

Fields

Backend ↔ FE Notes
session_idsessionId aligned.
titletitle aligned.
descriptiondescription aligned.
pass_scorepassingScore aligned (naming diff).
is_active Backend → FE. Exam form has no active toggle. Decide whether to add it.
Backend question shape: { question_text, position, options: [{ option_text, is_correct }] } FE: { title, score, correctAnswerId, answers: [{ id, title }] } Shape diff. Backend hides is_correct from public reads (only on add). FE concept of score (per-question weighting) has no backend slot. Decide: keep FE scoring (ask backend to store) or drop.

FE → B (need decision):

  • durationMinutes — no backend slot.
  • randomize — no backend slot.
  • endDate / startDate — exam validity window, no backend slot.
  • usedInTerms — derived UI count.

Homeworks (FE calls them "assignments")

Endpoints

Backend FE today Status
POST /homeworks addNewAssignment aligned (URL renamed to /homeworks; FE key name kept).
PATCH /homeworks/:id updateAssignment aligned.
DELETE /homeworks/:id deleteAssignment aligned.
POST /homeworks/:homeworkId/submit (student) Backend → FE. Student submit, not in admin UI yet.
PATCH /homework-submissions/:submissionId/review reviewAssignmentSubmission aligned (URL renamed).
getAssignmentsList FE-only. AssignmentsListPage depends on it. Kept mock-only.
showAssignment, getAssignmentSubmissions, showAssignmentSubmission FE-only. Detail + submissions list — kept mock-only.

Fields

Backend ↔ FE Notes
session_idsessionId aligned.
title, description aligned.
deadline (single datetime) FE: startDate + endDate + computed durationDays Shape diff. Backend has one deadline; FE has a window. Decide: drop start/end and use single deadline, or ask backend to add a window.
is_active Backend → FE. FE form has no active toggle.
Submission status: accepted | denied FE: pending | approved | rejected | needs_revision Shape diff. Backend has two states; FE has four. The FE pending/needs_revision have no backend slot.
Submission media_id (single) FE attachments[] (multiple) Shape diff. Backend allows one file per submission; FE expects many.
Submission teacher_feedbackreviewerNote naming diff.
Submission reviewed_at (read) Backend provides; FE doesn't surface.

FE → B (need decision):

  • priority enum (mandatory \| optional) — no backend slot.
  • submissionsCount — derived count, no backend slot.

Media

Endpoints

Backend FE today Status
POST /media (multipart with purpose + file) uploadMedia aligned URL & method.
GET /media/:id/download Backend → FE. Add as downloadMedia.
DELETE /media/:id Backend → FE. Add as deleteMedia.

Fields

  • Backend response: { id, collection_name, file_name, mime_type, file_size, url, download_url }. FE today reads payload.id, payload.url, sometimes payload.uploadId (a now-stale field). Cleanup needed: drop uploadId, use id everywhere.
  • Backend purpose enum: avatar | cover | video | voice | pdf | slide | attachment | homework_file. FE upload calls currently hardcode purpose: 'cover' or purpose: 'content'. content is not in the backend enum. Decide which of the backend purposes each FE uploader should send (e.g., session video → video, course PDF → pdf, homework upload → homework_file).
  • Pending media TTL: backend deletes unreferenced pending uploads after 24h. FE doesn't track this; if a user uploads a cover, abandons the form, and comes back next day, the cover_media_id reference will 404 on submit. Document the failure mode.

Cross-cutting

Snake_case vs camelCase

Backend wire format is snake_case throughout. FE today reads camelCase (e.g., the mock returns coverUrl, isActive). When the real backend lands, the FE will either need:

  • a HTTP-layer transformer (camelize on response, snake_case on request), or
  • camelCase field aliases on the backend serializer.

Decide before swapping the mock for the real API. Affects every screen.

/admin/ URL prefix

Backend doc has none — endpoints live at /terms, /courses, etc. The FE previously had /admin/courses, /admin/sessions, /admin/exams, /admin/assignments. Aligned to backend (prefix dropped). Auth context (admin role) is implicit in the token, not the URL.

Sub-features that depend on missing backend endpoints

UI screens that work today against the mock but have no backend equivalent in the current doc (kept mock-only with TODOs in endpoints.js):

  • Term: clone term, term status toggle, students-in-term subtab, leave toggle, courses-in-term subtab (could swap to GET /courses?term_id=)
  • Course: status toggle (swapped to PATCH), students-in-course subtab, attach/detach sessions (M:N), AddCourseStudentModal, AddSessionToCourseModal
  • Session: status toggle (swapped to PATCH), attendance roster
  • Exam: list page, participants list, participant detail
  • Assignment/Homework: list page, detail show, submissions list, submission detail

For each of these we need to either (a) ask backend to expose the endpoint, or (b) drop the UI surface.