feat: persist learner progress and comments
All checks were successful
CI / Validate frontend and API (push) Successful in 47s
CI / Build and publish Docker images (push) Successful in 39s

This commit is contained in:
Cauê Faleiros
2026-08-31 16:33:08 -03:00
parent 7842a757cd
commit 2dfca33758
10 changed files with 225 additions and 491 deletions

View File

@@ -1,9 +1,6 @@
import localforage from 'localforage';
import { Attachment, AttachmentType, Comment, Course, Lesson } from '../types';
import { apiRequest } from './api';
const COMMENTS_KEY = 'compor_hub_comments_v1';
export const initDB = async (): Promise<void> => undefined;
type ApiAsset = {
@@ -148,19 +145,53 @@ export const deleteCourse = async (id: string): Promise<void> => {
await apiRequest(`/manage/courses/${id}`, { method: 'DELETE' });
};
// Comments and learner progress move to PostgreSQL in the next milestone.
type ApiComment = {
id: string;
courseId: string;
lessonId: string | null;
user: string;
text: string;
createdAt: string;
reply?: string | null;
replyCreatedAt?: string | null;
};
const toComment = (comment: ApiComment): Comment => ({
id: comment.id,
courseId: comment.courseId,
lessonId: comment.lessonId || undefined,
user: comment.user,
text: comment.text,
date: new Intl.DateTimeFormat('pt-BR').format(new Date(comment.createdAt)),
reply: comment.reply || undefined,
replyDate: comment.replyCreatedAt
? new Intl.DateTimeFormat('pt-BR').format(new Date(comment.replyCreatedAt))
: undefined,
});
export const getComments = async (courseId: string): Promise<Comment[]> => {
const allComments = (await localforage.getItem<Comment[]>(COMMENTS_KEY)) || [];
return allComments.filter((comment) => comment.courseId === courseId);
const response = await apiRequest<{ data: ApiComment[] }>(`/courses/${courseId}/comments`);
return response.data.map(toComment);
};
export const saveComment = async (comment: Comment): Promise<void> => {
const allComments = (await localforage.getItem<Comment[]>(COMMENTS_KEY)) || [];
const index = allComments.findIndex((item) => item.id === comment.id);
if (index >= 0) allComments[index] = comment;
else allComments.unshift(comment);
await localforage.setItem(COMMENTS_KEY, allComments);
export const saveComment = async (comment: Pick<Comment, 'courseId' | 'lessonId' | 'text'>): Promise<Comment> => {
const response = await apiRequest<{ data: ApiComment }>(`/courses/${comment.courseId}/comments`, {
method: 'POST',
body: JSON.stringify({ lessonId: comment.lessonId || null, text: comment.text }),
});
return toComment(response.data);
};
export const getCompletedLessonIds = async (courseId: string): Promise<string[]> => {
const response = await apiRequest<{ data: Array<{ lessonId: string; completedAt: string | null }> }>(`/courses/${courseId}/progress`);
return response.data.filter((progress) => progress.completedAt).map((progress) => progress.lessonId);
};
export const saveLessonCompletion = async (lessonId: string, completed: boolean): Promise<void> => {
await apiRequest(`/lessons/${lessonId}/progress`, {
method: 'PUT',
body: JSON.stringify({ completed }),
});
};
export const saveProgress = async (_courseId: string, _progress: number): Promise<void> => undefined;
export const incrementViews = async (_courseId: string): Promise<void> => undefined;