feat: implement core course platform UI and logic
- Add project configuration and git ignore rules - Implement App router with protected routes - Develop course player, grid, and card components - Add state management for course progress and navigation - Initialize database services for course and comment data
This commit is contained in:
33
services/ai.ts
Normal file
33
services/ai.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { GoogleGenAI } from "@google/genai";
|
||||
|
||||
export const generateStudentReply = async (studentComment: string, courseContext: string): Promise<string> => {
|
||||
try {
|
||||
const apiKey = process.env.API_KEY;
|
||||
|
||||
if (!apiKey) {
|
||||
console.warn("EduClean: API Key is missing. AI features are disabled.");
|
||||
return "Peço desculpas, mas não consigo gerar uma resposta no momento. Por favor, contate o professor diretamente.";
|
||||
}
|
||||
|
||||
const ai = new GoogleGenAI({ apiKey });
|
||||
|
||||
const prompt = `
|
||||
Você é um assistente de professor útil, encorajador e claro.
|
||||
Contexto: O aluno está fazendo uma pergunta sobre o curso: "${courseContext}".
|
||||
Pergunta do Aluno: "${studentComment}"
|
||||
|
||||
Tarefa: Escreva uma resposta concisa, didática e amigável (máximo de 2 frases) em Português do Brasil.
|
||||
Responda diretamente ao ponto do aluno.
|
||||
`;
|
||||
|
||||
const response = await ai.models.generateContent({
|
||||
model: 'gemini-3-flash-preview',
|
||||
contents: prompt,
|
||||
});
|
||||
|
||||
return response.text || "";
|
||||
} catch (error) {
|
||||
console.error("Gemini API Error:", error);
|
||||
return "Desculpe, não consegui gerar uma resposta automática agora. Por favor, tente novamente.";
|
||||
}
|
||||
};
|
||||
81
services/db.ts
Normal file
81
services/db.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import localforage from 'localforage';
|
||||
import { Course, Comment, Section } from '../types';
|
||||
import { INITIAL_COURSES, generateMockSections } from '../constants';
|
||||
|
||||
const COURSES_KEY = 'compor_hub_courses_v1';
|
||||
const COMMENTS_KEY = 'compor_hub_comments_v1';
|
||||
const PROGRESS_KEY = 'compor_hub_progress_v1';
|
||||
|
||||
// Initialize DB with seed courses
|
||||
export const initDB = async (): Promise<void> => {
|
||||
const existingCourses = await localforage.getItem<Course[]>(COURSES_KEY);
|
||||
if (!existingCourses || existingCourses.length === 0) {
|
||||
await localforage.setItem(COURSES_KEY, INITIAL_COURSES);
|
||||
}
|
||||
};
|
||||
|
||||
export const getCourses = async (): Promise<Course[]> => {
|
||||
const courses = await localforage.getItem<Course[]>(COURSES_KEY);
|
||||
if (!courses || courses.length === 0) {
|
||||
await initDB();
|
||||
return INITIAL_COURSES;
|
||||
}
|
||||
return courses;
|
||||
};
|
||||
|
||||
export const getCourseById = async (id: string): Promise<Course | null> => {
|
||||
const courses = await getCourses();
|
||||
return courses.find(c => c.id === id) || null;
|
||||
};
|
||||
|
||||
export const saveCourse = async (course: Course): Promise<void> => {
|
||||
const courses = await getCourses();
|
||||
const index = courses.findIndex(c => c.id === course.id);
|
||||
if (index >= 0) {
|
||||
courses[index] = course;
|
||||
} else {
|
||||
courses.unshift(course);
|
||||
}
|
||||
await localforage.setItem(COURSES_KEY, courses);
|
||||
};
|
||||
|
||||
export const deleteCourse = async (id: string): Promise<void> => {
|
||||
const courses = await getCourses();
|
||||
const filtered = courses.filter(c => c.id !== id);
|
||||
await localforage.setItem(COURSES_KEY, filtered);
|
||||
};
|
||||
|
||||
export const getComments = async (courseId: string): Promise<Comment[]> => {
|
||||
const allComments = (await localforage.getItem<Comment[]>(COMMENTS_KEY)) || [];
|
||||
return allComments.filter(c => c.courseId === courseId);
|
||||
};
|
||||
|
||||
export const saveComment = async (comment: Comment): Promise<void> => {
|
||||
const allComments = (await localforage.getItem<Comment[]>(COMMENTS_KEY)) || [];
|
||||
const index = allComments.findIndex(c => c.id === comment.id);
|
||||
|
||||
if (index >= 0) {
|
||||
allComments[index] = comment;
|
||||
} else {
|
||||
allComments.unshift(comment);
|
||||
}
|
||||
await localforage.setItem(COMMENTS_KEY, allComments);
|
||||
};
|
||||
|
||||
export const saveProgress = async (courseId: string, progress: number): Promise<void> => {
|
||||
const courses = await getCourses();
|
||||
const course = courses.find(c => c.id === courseId);
|
||||
if (course) {
|
||||
course.progress = progress;
|
||||
await saveCourse(course);
|
||||
}
|
||||
};
|
||||
|
||||
export const incrementViews = async (courseId: string): Promise<void> => {
|
||||
const courses = await getCourses();
|
||||
const course = courses.find(c => c.id === courseId);
|
||||
if (course) {
|
||||
course.views = (course.views || 0) + 1;
|
||||
await saveCourse(course);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user