feat: Implement video play count tracking, refactor user update API, add missing CMS delete functions, and update database transaction handling.

This commit is contained in:
2026-01-16 13:43:58 -03:00
parent 2dffbd8b71
commit 42976236b3
11 changed files with 138 additions and 39 deletions
@@ -159,6 +159,33 @@ export default function LessonPlayerPage({ params }: { params: { id: string, les
media_type={block.media_type || 'video'}
config={block.config}
onTimeUpdate={setCurrentTime}
initialPlayCount={
userGrade?.metadata?.play_counts
? (userGrade.metadata.play_counts as Record<string, number>)[block.id] || 0
: 0
}
onPlay={async () => {
if (user && lesson.max_attempts && (!userGrade || userGrade.attempts_count < lesson.max_attempts)) {
const currentPlayCounts = (userGrade?.metadata?.play_counts as Record<string, number>) || {};
const newPlayCounts = {
...currentPlayCounts,
[block.id]: (currentPlayCounts[block.id] || 0) + 1
};
try {
const res = await lmsApi.submitScore(
user.id,
params.id,
params.lessonId,
userGrade?.score || 0,
{ ...userGrade?.metadata, play_counts: newPlayCounts }
);
setUserGrade(res);
} catch (err) {
console.error("Failed to persist play count", err);
}
}
}}
/>
)}
{block.type === 'quiz' && (
+1 -1
View File
@@ -3,7 +3,7 @@
import { useState, useEffect } from "react";
import { useAuth } from "@/context/AuthContext";
import { lmsApi } from "@/lib/api";
import { User, Save, Shield, Mail, User as UserIcon, Building, Trophy, Flame } from "lucide-react";
import { Save, Shield, Mail, User as UserIcon, Building, Trophy, Flame } from "lucide-react";
export default function ProfilePage() {
const { user, logout } = useAuth();
@@ -11,11 +11,13 @@ interface MediaPlayerProps {
config?: {
maxPlays?: number;
};
initialPlayCount?: number;
onTimeUpdate?: (time: number) => void;
onPlay?: () => void;
}
export default function MediaPlayer({ id, title, url, media_type, config, onTimeUpdate }: MediaPlayerProps) {
const [playCount, setPlayCount] = useState(0);
export default function MediaPlayer({ id, title, url, media_type, config, initialPlayCount, onTimeUpdate, onPlay }: MediaPlayerProps) {
const [playCount, setPlayCount] = useState(initialPlayCount || 0);
const [hasStarted, setHasStarted] = useState(false);
const [locked, setLocked] = useState(false);
@@ -44,6 +46,7 @@ export default function MediaPlayer({ id, title, url, media_type, config, onTime
if (!hasStarted) {
setPlayCount(prev => prev + 1);
setHasStarted(true);
if (onPlay) onPlay();
}
};
+2 -2
View File
@@ -190,10 +190,10 @@ export const lmsApi = {
return apiFetch(`/enrollments/${userId}`);
},
async submitScore(userId: string, courseId: string, lessonId: string, score: number): Promise<UserGrade> {
async submitScore(userId: string, course_id: string, lessonId: string, score: number, metadata: Record<string, unknown> = {}): Promise<UserGrade> {
return apiFetch('/grades', {
method: 'POST',
body: JSON.stringify({ user_id: userId, course_id: courseId, lesson_id: lessonId, score })
body: JSON.stringify({ user_id: userId, course_id, lesson_id: lessonId, score, metadata })
});
},
+1 -1
View File
@@ -34,7 +34,7 @@ export default function UsersPage() {
const handleUpdateUser = async (userId: string, role: string, orgId: string) => {
try {
await cmsApi.updateUser(userId, role, orgId);
await cmsApi.updateUser(userId, { role, organization_id: orgId });
loadData();
} catch (error) {
console.error('Failed to update user', error);
+2 -2
View File
@@ -3,10 +3,10 @@
import { useState, useEffect } from "react";
import { useAuth } from "@/context/AuthContext";
import { cmsApi } from "@/lib/api";
import { User, Save, Shield, Mail, User as UserIcon, Building } from "lucide-react";
import { Save, Shield, Mail, User as UserIcon, Building } from "lucide-react";
export default function ProfilePage() {
const { user, token, logout } = useAuth();
const { user, logout } = useAuth();
const [fullName, setFullName] = useState(user?.full_name || "");
const [email, setEmail] = useState(user?.email || "");
const [saving, setSaving] = useState(false);
+1 -1
View File
@@ -266,7 +266,7 @@ export const cmsApi = {
// Users
getAllUsers: (): Promise<User[]> => apiFetch('/users'),
updateUser: (id: string, role: string, organization_id: string): Promise<void> => apiFetch(`/users/${id}`, { method: 'PUT', body: JSON.stringify({ role, organization_id }) }),
updateUser: (id: string, payload: { role?: string, organization_id?: string, full_name?: string }): Promise<void> => apiFetch(`/users/${id}`, { method: 'PUT', body: JSON.stringify(payload) }),
// Webhooks
getWebhooks: (): Promise<Webhook[]> => apiFetch('/webhooks'),