mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
add: more commits
Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
@@ -1,10 +1,18 @@
|
||||
import { Trophy, Clock } from "lucide-react";
|
||||
import { Trophy, Clock, CheckCircle2 } from "lucide-react";
|
||||
import { useDateFormat } from "@/hooks/useDateFormat";
|
||||
|
||||
/**
|
||||
* Props:
|
||||
* course — { title, ... } the completed course
|
||||
* course — { title, pending_certificate, certificate, ... } the completed course
|
||||
*/
|
||||
const CourseCompleteBlock = ({ course }) => {
|
||||
const { fmtDate } = useDateFormat();
|
||||
|
||||
const certificate = course?.certificate ?? null;
|
||||
const pendingCert = course?.pending_certificate ?? null;
|
||||
const isIssued = !!certificate;
|
||||
const isPending = !isIssued && !!pendingCert;
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto">
|
||||
<div className="rounded-xl border bg-card p-6 space-y-6 text-center sm:p-10">
|
||||
@@ -25,18 +33,33 @@ const CourseCompleteBlock = ({ course }) => {
|
||||
You've passed all required units and the final assessment for this course.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-start gap-2.5 rounded-lg border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-left">
|
||||
<Clock className="size-4 text-amber-500 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-amber-700 dark:text-amber-400">Certificate Pending</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Certificates are issued automatically every hour. Yours will be ready within the next hour — check your notifications.
|
||||
</p>
|
||||
|
||||
{isIssued ? (
|
||||
<div className="flex items-start gap-2.5 rounded-lg border border-green-500/30 bg-green-500/5 px-4 py-3 text-left">
|
||||
<CheckCircle2 className="size-4 text-green-500 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-green-700 dark:text-green-400">Certificate Issued</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Issued on {fmtDate(certificate.issued_at)}. View and download it from your Certificates page.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-start gap-2.5 rounded-lg border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-left">
|
||||
<Clock className="size-4 text-amber-500 shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-amber-700 dark:text-amber-400">Certificate Pending</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{isPending
|
||||
? `Certificates are issued automatically every hour. Yours will be ready by ${fmtDate(pendingCert.issue_at)} — check your notifications.`
|
||||
: "Certificates are issued automatically every hour. Yours will be ready within the next hour — check your notifications."}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CourseCompleteBlock;
|
||||
export default CourseCompleteBlock;
|
||||
|
||||
@@ -179,18 +179,13 @@ const ReadCourse = ({ title = "Read Course", courses = [], groupId, taskListId,
|
||||
<div>
|
||||
<h1
|
||||
onClick={(e) => {
|
||||
if (!taskId || !groupId || !taskListId) return;
|
||||
if (!info?.course_id) return;
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/group/${groupId}/view/${taskListId}/task/${taskId}/requirement`,
|
||||
{ state: { course: { id: course.id, reference_id: course.reference_id, title: course.title } } }
|
||||
);
|
||||
navigate(`/course/${info.course_id}/unit`, {
|
||||
state: taskId ? { taskCtx: { has_task: true, groupId, taskListId, taskId } } : {},
|
||||
});
|
||||
}}
|
||||
className={`text-base font-semibold leading-snug line-clamp-2 transition-colors ${
|
||||
taskId
|
||||
? 'text-blue-600 dark:text-blue-400 hover:underline cursor-pointer'
|
||||
: 'group-hover:text-blue-700 dark:group-hover:text-blue-400'
|
||||
}`}
|
||||
className="text-base font-semibold leading-snug line-clamp-2 transition-colors text-blue-600 dark:text-blue-400 hover:underline cursor-pointer"
|
||||
>
|
||||
{course.title}
|
||||
</h1>
|
||||
@@ -242,18 +237,12 @@ const ReadCourse = ({ title = "Read Course", courses = [], groupId, taskListId,
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (!info?.course_id) return;
|
||||
if (taskId && groupId && taskListId) {
|
||||
// Task context — read inside ViewRequirement
|
||||
navigate(
|
||||
`/group/${groupId}/view/${taskListId}/task/${taskId}/requirement`,
|
||||
{ state: { course: { id: selected.id, reference_id: selected.reference_id, title: selected.title } } }
|
||||
);
|
||||
} else {
|
||||
// No task context — fall back to standalone course reader
|
||||
navigate(`/course/${info.course_id}/unit`, {
|
||||
state: allRead ? { seekFirstIncomplete: true } : {},
|
||||
});
|
||||
}
|
||||
navigate(`/course/${info.course_id}/unit`, {
|
||||
state: {
|
||||
...(allRead ? { seekFirstIncomplete: true } : {}),
|
||||
...(taskId ? { taskCtx: { has_task: true, groupId, taskListId, taskId } } : {}),
|
||||
},
|
||||
});
|
||||
}}
|
||||
disabled={done || !info?.course_id}
|
||||
>
|
||||
|
||||
@@ -147,10 +147,16 @@ const ReadLesson = ({ title = "Read Lessons", lessons = [], groupId, taskListId,
|
||||
return (
|
||||
<div
|
||||
key={lesson.id}
|
||||
onClick={() => !isFetching && navigate(
|
||||
`/group/${groupId}/view/${taskListId}/task/${taskId}/requirement`,
|
||||
{ state: { lesson } },
|
||||
)}
|
||||
onClick={() => {
|
||||
if (isFetching || !info?.unit?.course?.course_id) return;
|
||||
navigate(`/course/${info.unit.course.course_id}/unit`, {
|
||||
state: {
|
||||
lessonId: info.lesson_id,
|
||||
unitId: info.unit.unit_id,
|
||||
...(taskId ? { taskCtx: { has_task: true, groupId, taskListId, taskId } } : {}),
|
||||
},
|
||||
});
|
||||
}}
|
||||
className={`bg-card rounded-2xl border p-4 flex flex-col gap-3 transition-all w-96 shrink-0 ${
|
||||
isFetching
|
||||
? 'opacity-60 cursor-wait'
|
||||
|
||||
@@ -194,18 +194,13 @@ const ReadUnit = ({ title = "Read Units", units = [], groupId, taskListId, taskI
|
||||
<div>
|
||||
<h1
|
||||
onClick={(e) => {
|
||||
if (!taskId || !groupId || !taskListId) return;
|
||||
if (!info?.course?.course_id) return;
|
||||
e.stopPropagation();
|
||||
navigate(
|
||||
`/group/${groupId}/view/${taskListId}/task/${taskId}/requirement`,
|
||||
{ state: { unit } }
|
||||
);
|
||||
navigate(`/course/${info.course.course_id}/unit`, {
|
||||
state: taskId ? { taskCtx: { has_task: true, groupId, taskListId, taskId } } : {},
|
||||
});
|
||||
}}
|
||||
className={`text-base font-semibold leading-snug line-clamp-2 transition-colors ${
|
||||
taskId
|
||||
? 'text-blue-600 dark:text-blue-400 hover:underline cursor-pointer'
|
||||
: 'group-hover:text-blue-700 dark:group-hover:text-blue-400'
|
||||
}`}
|
||||
className="text-base font-semibold leading-snug line-clamp-2 transition-colors text-blue-600 dark:text-blue-400 hover:underline cursor-pointer"
|
||||
>
|
||||
{unit.title}
|
||||
</h1>
|
||||
@@ -251,10 +246,13 @@ const ReadUnit = ({ title = "Read Units", units = [], groupId, taskListId, taskI
|
||||
<>
|
||||
<Button variant="outline" onClick={() => setSelected(null)}>Cancel</Button>
|
||||
<Button
|
||||
onClick={() => navigate(
|
||||
`/group/${groupId}/view/${taskListId}/task/${taskId}/requirement`,
|
||||
{ state: { unit: selected } },
|
||||
)}
|
||||
onClick={() => {
|
||||
const info = details[selected?.reference_id];
|
||||
if (!info?.course?.course_id) return;
|
||||
navigate(`/course/${info.course.course_id}/unit`, {
|
||||
state: taskId ? { taskCtx: { has_task: true, groupId, taskListId, taskId } } : {},
|
||||
});
|
||||
}}
|
||||
disabled={
|
||||
getProgress(selected ?? {}) >= 100 ||
|
||||
locked[selected?.reference_id] ||
|
||||
|
||||
Reference in New Issue
Block a user