Refactor app state management: replace globalStore.isStudent with isOrgStudent across multiple components for improved clarity and consistency. Update related logic to utilize isSelfPacedLikeCourse for self-paced course checks.
This commit is contained in:
@@ -31,8 +31,9 @@
|
||||
import { snackbar } from '$features/ui/snackbar/store';
|
||||
import { ContentType } from '@cio/utils/constants/content';
|
||||
import { getOrderedNavigableContent } from '$features/course/utils/content';
|
||||
import { globalStore } from '$lib/utils/store/app';
|
||||
import { isOrgStudent } from '$lib/utils/store/app';
|
||||
import { openCourseCompletionModal } from '$features/course/store/course-completion-modal';
|
||||
import { isSelfPacedLikeCourse } from '$features/course/utils/compliance-utils';
|
||||
import { toExerciseQuestionModel } from './question-type-utils';
|
||||
import { getExerciseQuestionLabels } from './question-labels';
|
||||
import axios from 'axios';
|
||||
@@ -154,7 +155,7 @@
|
||||
|
||||
const allContentItems = getOrderedNavigableContent(courseApi.course);
|
||||
const allComplete =
|
||||
$globalStore.isStudent && allContentItems.length > 0 && allContentItems.every((item) => item.isComplete);
|
||||
$isOrgStudent && allContentItems.length > 0 && allContentItems.every((item) => item.isComplete);
|
||||
|
||||
if (allComplete && courseApi.course?.id) {
|
||||
openCourseCompletionModal(courseApi.course.id);
|
||||
@@ -573,7 +574,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if courseApi.course?.type === 'SELF_PACED'}
|
||||
{:else if isSelfPacedLikeCourse(courseApi.course?.type)}
|
||||
<Badge
|
||||
variant="success"
|
||||
title={$t('course.navItem.lessons.exercises.all_exercises.view_mode.status_submitted')}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
import { Chip } from '@cio/ui/custom/chip';
|
||||
import { lessonApi, exerciseApi, courseApi, contentApi } from '$features/course/api';
|
||||
import { globalStore } from '$lib/utils/store/app';
|
||||
import { isOrgStudent } from '$lib/utils/store/app';
|
||||
import formatDate from '$lib/utils/functions/formatDate';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
import type { CourseContentItem } from '$features/course/utils/types';
|
||||
@@ -25,6 +25,7 @@
|
||||
import ContentRow from './content-row.svelte';
|
||||
import ContentActions from './content-actions.svelte';
|
||||
import { deleteContent, saveContent, toggleLock } from './content-action-helpers';
|
||||
import { isSelfPacedLikeCourse } from '$features/course/utils/compliance-utils';
|
||||
|
||||
const flipDurationMs = 300;
|
||||
|
||||
@@ -43,9 +44,9 @@
|
||||
let courseId = $derived(courseApi.course?.id || '');
|
||||
const profileId = $derived($profile?.id || '');
|
||||
const contentData = $derived(getCourseContent(courseApi.course));
|
||||
const isStudentView = $derived($globalStore.isStudent === true);
|
||||
const isStudentView = $derived($isOrgStudent === true);
|
||||
const isLiveCourse = $derived(courseApi.course?.type === 'LIVE_CLASS');
|
||||
const isSelfPacedCourse = $derived(courseApi.course?.type === 'SELF_PACED');
|
||||
const isSelfPacedCourse = $derived(isSelfPacedLikeCourse(courseApi.course?.type));
|
||||
const metaChipClass =
|
||||
'inline-flex items-center gap-1 rounded-full bg-gray-100 px-2 py-1 text-[10px] text-gray-700 dark:bg-neutral-700 dark:text-gray-200';
|
||||
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@
|
||||
import { CircleCheckIcon } from '$features/ui/icons';
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
import * as Tooltip from '@cio/ui/base/tooltip';
|
||||
import { globalStore } from '$lib/utils/store/app';
|
||||
import { isOrgStudent } from '$lib/utils/store/app';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
import { courseApi, lessonApi } from '$features/course/api';
|
||||
import { getOrderedNavigableContent, getContentRoute } from '$features/course/utils/content';
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
const currentLessonItem = $derived(lessonId ? lessonItems.find((l) => l.id === lessonId) : null);
|
||||
const isLessonLocked = $derived(
|
||||
$globalStore.isStudent && currentLessonItem && !(currentLessonItem.isUnlocked ?? false)
|
||||
$isOrgStudent && currentLessonItem && !(currentLessonItem.isUnlocked ?? false)
|
||||
);
|
||||
const isMarkCompleteDisabled = $derived(isMarkingComplete || isLessonLocked || isLessonComplete);
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
updateCourseContentCompletion(currentLessonId, isComplete);
|
||||
|
||||
const allComplete =
|
||||
$globalStore.isStudent &&
|
||||
$isOrgStudent &&
|
||||
isComplete &&
|
||||
navigableContentItems.length > 0 &&
|
||||
navigableContentItems.every((item) => item.isComplete);
|
||||
|
||||
+4
-3
@@ -14,7 +14,7 @@
|
||||
import { HoverableItem, AttachmentIcon } from '@cio/ui/custom/moving-icons';
|
||||
import formatDate from '$lib/utils/functions/formatDate';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
import { globalStore } from '$lib/utils/store/app';
|
||||
import { isOrgStudent } from '$lib/utils/store/app';
|
||||
import { contentApi, courseApi, exerciseApi, lessonApi } from '$features/course/api';
|
||||
import { snackbar } from '$features/ui/snackbar/store';
|
||||
import { profile } from '$lib/utils/store/user';
|
||||
@@ -27,6 +27,7 @@
|
||||
import ContentActions from './content-actions.svelte';
|
||||
import SectionHeader from './section-header.svelte';
|
||||
import { deleteContent, saveContent, toggleLock } from './content-action-helpers';
|
||||
import { isSelfPacedLikeCourse } from '$features/course/utils/compliance-utils';
|
||||
|
||||
type CrudParam = {
|
||||
sectionId?: string;
|
||||
@@ -52,9 +53,9 @@
|
||||
const courseId = $derived(courseApi.course?.id || '');
|
||||
const profileId = $derived($profile?.id || '');
|
||||
const contentData = $derived(getCourseContent(courseApi.course));
|
||||
const isStudentView = $derived($globalStore.isStudent === true);
|
||||
const isStudentView = $derived($isOrgStudent === true);
|
||||
const isLiveCourse = $derived(courseApi.course?.type === 'LIVE_CLASS');
|
||||
const isSelfPacedCourse = $derived(courseApi.course?.type === 'SELF_PACED');
|
||||
const isSelfPacedCourse = $derived(isSelfPacedLikeCourse(courseApi.course?.type));
|
||||
const metaChipClass =
|
||||
'inline-flex items-center gap-1 rounded-full bg-gray-100 px-2 py-1 text-[10px] text-gray-700 dark:bg-neutral-700 dark:text-gray-200';
|
||||
|
||||
|
||||
+3
-3
@@ -7,7 +7,7 @@
|
||||
import { InputField } from '@cio/ui/custom/input-field';
|
||||
import * as Page from '@cio/ui/base/page';
|
||||
import MODES from '$lib/utils/constants/mode';
|
||||
import { globalStore } from '$lib/utils/store/app';
|
||||
import { isOrgStudent } from '$lib/utils/store/app';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
import { RoleBasedSecurity } from '$features/ui';
|
||||
import DeleteLessonConfirmation from '$features/course/components/lesson/delete-lesson-confirmation.svelte';
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
<div class="flex w-full items-center justify-between gap-2">
|
||||
<div class="min-w-0 flex-1">
|
||||
{#if mode === MODES.edit && !$globalStore.isStudent}
|
||||
{#if mode === MODES.edit && !$isOrgStudent}
|
||||
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
||||
<InputField
|
||||
className="max-w-xl"
|
||||
@@ -55,7 +55,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if mode === MODES.edit && !$globalStore.isStudent}
|
||||
{#if mode === MODES.edit && !$isOrgStudent}
|
||||
<RoleBasedSecurity allowedRoles={[1, 2]}>
|
||||
<div class="flex items-center gap-2">
|
||||
<IconButton onclick={onToggleLock} aria-label={lockLabel} title={lockLabel}>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import { Skeleton } from '@cio/ui/base/skeleton';
|
||||
import { profile } from '$lib/utils/store/user';
|
||||
import { orgs } from '$lib/utils/store/org';
|
||||
import { globalStore } from '$lib/utils/store/app';
|
||||
import { isOrgStudent } from '$lib/utils/store/app';
|
||||
import { isFreePlan } from '$lib/utils/store/org';
|
||||
|
||||
import OrgLogo from '$features/ui/sidebar/org-sidebar/org-logo.svelte';
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
<Sidebar.Content>
|
||||
{#if isCourseReady}
|
||||
<Navigation path={currentPath} {id} isStudent={$globalStore.isStudent} />
|
||||
<Navigation path={currentPath} {id} isStudent={$isOrgStudent ?? undefined} />
|
||||
{:else}
|
||||
<Sidebar.Group class="pt-0!">
|
||||
<div class="flex h-8 items-center gap-2 rounded-md px-2">
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import { profile } from '$lib/utils/store/user';
|
||||
import { ROLE } from '@cio/utils/constants';
|
||||
import { globalStore } from '$lib/utils/store/app';
|
||||
import { isOrgStudent } from '$lib/utils/store/app';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
import { courseApi, attendanceApi } from '$features/course/api';
|
||||
import { getLectureNo } from '$features/course/utils/functions';
|
||||
@@ -23,7 +23,7 @@
|
||||
let { courseId }: Props = $props();
|
||||
|
||||
const students: CourseMembers = $derived(
|
||||
$globalStore.isStudent
|
||||
$isOrgStudent
|
||||
? courseApi.group.people.filter((person) => !!person.profile && person.profile.id === $profile.id)
|
||||
: courseApi.group.people.filter((person) => !!person.profile && Number(person.roleId) === ROLE.STUDENT)
|
||||
);
|
||||
@@ -71,7 +71,7 @@
|
||||
}
|
||||
|
||||
async function handleAttendanceChange(e: any, student: CourseMember, lesson: CourseContentItem) {
|
||||
if ($globalStore.isStudent) return;
|
||||
if ($isOrgStudent) return;
|
||||
|
||||
const isPresent = e.target.checked;
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
<Table.Cell class="text-center">
|
||||
<div class="flex justify-center">
|
||||
<Checkbox
|
||||
disabled={$globalStore.isStudent}
|
||||
disabled={$isOrgStudent}
|
||||
checked={getAttendanceStatus(student.id, lesson.id)}
|
||||
onCheckedChange={(checked) => {
|
||||
const e = { target: { checked } };
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
import { ZExerciseUpdate } from '@cio/utils/validation/exercise';
|
||||
import { mapZodErrorsToTranslations } from '$lib/utils/validation';
|
||||
import { transformQuestionsToApiFormat } from '$features/course/components/exercise/functions';
|
||||
import { globalStore } from '$lib/utils/store/app';
|
||||
import { isOrgStudent } from '$lib/utils/store/app';
|
||||
import { t } from '$lib/utils/functions/translations';
|
||||
import { snackbar } from '$features/ui/snackbar/store';
|
||||
import { exerciseApi } from '$features/course/api';
|
||||
@@ -37,6 +37,7 @@
|
||||
import UpdateDescription from '$features/course/components/exercise/update-description.svelte';
|
||||
import { ContentNavigationActions } from '$features/course/components/lesson';
|
||||
import { RefreshPageData, RoleBasedSecurity } from '$features/ui';
|
||||
import { isSelfPacedLikeCourse } from '$features/course/utils/compliance-utils';
|
||||
import { getOrderedNavigableContent } from '$features/course/utils/content';
|
||||
import { Empty } from '@cio/ui/custom/empty';
|
||||
import VideoIcon from '@lucide/svelte/icons/video';
|
||||
@@ -94,7 +95,7 @@
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
if ($globalStore.isStudent || !courseApi.course?.id) return;
|
||||
if ($isOrgStudent || !courseApi.course?.id) return;
|
||||
|
||||
// Transform questionnaire to API format for validation
|
||||
// Include all non-deleted options (even empty ones) so Zod can catch validation errors
|
||||
@@ -231,7 +232,7 @@
|
||||
);
|
||||
const isExerciseUnlocked = $derived(exerciseContentItem?.isUnlocked ?? false);
|
||||
|
||||
const isSelfPacedCourse = $derived(courseApi.course?.type === 'SELF_PACED');
|
||||
const isSelfPacedCourse = $derived(isSelfPacedLikeCourse(courseApi.course?.type));
|
||||
const activeQuestionTypeIds = $derived(
|
||||
($questionnaire.questions ?? []).filter((q) => !q.deletedAt).map((q) => getQuestionTypeId(q))
|
||||
);
|
||||
@@ -244,7 +245,7 @@
|
||||
isSelfPacedCourse && isExerciseFullyAutoGradable(activeQuestionTypeIds)
|
||||
);
|
||||
const teacherAutoGradeBadge = $derived.by(() => {
|
||||
if ($globalStore.isStudent || !isSelfPacedCourse || activeQuestionTypeIds.length === 0) return null;
|
||||
if ($isOrgStudent || !isSelfPacedCourse || activeQuestionTypeIds.length === 0) return null;
|
||||
return isExerciseFullyAutoGradable(activeQuestionTypeIds) ? ('auto' as const) : ('manual' as const);
|
||||
});
|
||||
</script>
|
||||
@@ -268,7 +269,7 @@
|
||||
</Page.HeaderContent>
|
||||
<Page.Action>
|
||||
<div class="flex items-center gap-2">
|
||||
{#if $globalStore.isStudent && courseApi.course?.id && exerciseId}
|
||||
{#if $isOrgStudent && courseApi.course?.id && exerciseId}
|
||||
<ContentNavigationActions courseId={courseApi.course.id} {exerciseId} />
|
||||
{/if}
|
||||
|
||||
@@ -332,7 +333,7 @@
|
||||
<Page.Body>
|
||||
{#snippet child()}
|
||||
<div class="overflow-x-hidden">
|
||||
{#if $globalStore.isStudent}
|
||||
{#if $isOrgStudent}
|
||||
{#if isExerciseUnlocked}
|
||||
<ViewMode {preview} {exerciseId} isFetchingExercise={isFetching} {mySubmissions} />
|
||||
{:else}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
import MODES from '$lib/utils/constants/mode';
|
||||
import { profile } from '$lib/utils/store/user';
|
||||
import { globalStore } from '$lib/utils/store/app';
|
||||
import { isOrgStudent } from '$lib/utils/store/app';
|
||||
import { currentOrg } from '$lib/utils/store/org';
|
||||
import { snackbar } from '$features/ui/snackbar/store';
|
||||
import { RefreshPageData, UnsavedChanges } from '$features/ui';
|
||||
@@ -365,7 +365,7 @@
|
||||
</Page.HeaderContent>
|
||||
<Page.Action>
|
||||
<div class="flex items-center gap-2">
|
||||
{#if mode === MODES.view && $globalStore.isStudent}
|
||||
{#if mode === MODES.view && $isOrgStudent}
|
||||
<ContentNavigationActions {lessonId} {courseId} />
|
||||
{/if}
|
||||
|
||||
@@ -406,7 +406,7 @@
|
||||
<Page.Body>
|
||||
{#snippet child()}
|
||||
<div class="overflow-x-hidden lg:w-full xl:w-11/12">
|
||||
{#if $globalStore.isStudent && lessonApi.lesson && !isLessonUnlocked}
|
||||
{#if $isOrgStudent && lessonApi.lesson && !isLessonUnlocked}
|
||||
<Empty
|
||||
title={$t('course.navItem.lessons.content_locked_title')}
|
||||
description={$t('course.navItem.lessons.content_locked_description')}
|
||||
@@ -488,7 +488,7 @@
|
||||
variant="page"
|
||||
class="text-center"
|
||||
>
|
||||
{#if !$globalStore.isStudent}
|
||||
{#if !$isOrgStudent}
|
||||
<Button onclick={toggleMode}>
|
||||
{$t('course.navItem.lessons.materials.get_started')}
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user