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:
Rotimi Best
2026-04-12 18:07:33 +01:00
parent 31cc45a277
commit 8ea0ab0a76
16 changed files with 57 additions and 64 deletions
@@ -1,5 +1,6 @@
import { BaseApi, classroomio } from '$lib/utils/services/api'; import { BaseApi, classroomio } from '$lib/utils/services/api';
import { currentOrg, isOrgStudent, orgs } from '$lib/utils/store/org'; import { isOrgStudent } from '$lib/utils/store/app';
import { currentOrg, orgs } from '$lib/utils/store/org';
import { defaultProfileState, defaultUserState, profile, user } from '$lib/utils/store/user'; import { defaultProfileState, defaultUserState, profile, user } from '$lib/utils/store/user';
import type { AccountResponse } from './types'; import type { AccountResponse } from './types';
@@ -31,8 +31,9 @@
import { snackbar } from '$features/ui/snackbar/store'; import { snackbar } from '$features/ui/snackbar/store';
import { ContentType } from '@cio/utils/constants/content'; import { ContentType } from '@cio/utils/constants/content';
import { getOrderedNavigableContent } from '$features/course/utils/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 { openCourseCompletionModal } from '$features/course/store/course-completion-modal';
import { isSelfPacedLikeCourse } from '$features/course/utils/compliance-utils';
import { toExerciseQuestionModel } from './question-type-utils'; import { toExerciseQuestionModel } from './question-type-utils';
import { getExerciseQuestionLabels } from './question-labels'; import { getExerciseQuestionLabels } from './question-labels';
import axios from 'axios'; import axios from 'axios';
@@ -154,7 +155,7 @@
const allContentItems = getOrderedNavigableContent(courseApi.course); const allContentItems = getOrderedNavigableContent(courseApi.course);
const allComplete = 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) { if (allComplete && courseApi.course?.id) {
openCourseCompletionModal(courseApi.course.id); openCourseCompletionModal(courseApi.course.id);
@@ -573,7 +574,7 @@
</div> </div>
</div> </div>
</div> </div>
{:else if courseApi.course?.type === 'SELF_PACED'} {:else if isSelfPacedLikeCourse(courseApi.course?.type)}
<Badge <Badge
variant="success" variant="success"
title={$t('course.navItem.lessons.exercises.all_exercises.view_mode.status_submitted')} title={$t('course.navItem.lessons.exercises.all_exercises.view_mode.status_submitted')}
@@ -13,7 +13,7 @@
import { Button } from '@cio/ui/base/button'; import { Button } from '@cio/ui/base/button';
import { Chip } from '@cio/ui/custom/chip'; import { Chip } from '@cio/ui/custom/chip';
import { lessonApi, exerciseApi, courseApi, contentApi } from '$features/course/api'; 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 formatDate from '$lib/utils/functions/formatDate';
import { t } from '$lib/utils/functions/translations'; import { t } from '$lib/utils/functions/translations';
import type { CourseContentItem } from '$features/course/utils/types'; import type { CourseContentItem } from '$features/course/utils/types';
@@ -25,6 +25,7 @@
import ContentRow from './content-row.svelte'; import ContentRow from './content-row.svelte';
import ContentActions from './content-actions.svelte'; import ContentActions from './content-actions.svelte';
import { deleteContent, saveContent, toggleLock } from './content-action-helpers'; import { deleteContent, saveContent, toggleLock } from './content-action-helpers';
import { isSelfPacedLikeCourse } from '$features/course/utils/compliance-utils';
const flipDurationMs = 300; const flipDurationMs = 300;
@@ -43,9 +44,9 @@
let courseId = $derived(courseApi.course?.id || ''); let courseId = $derived(courseApi.course?.id || '');
const profileId = $derived($profile?.id || ''); const profileId = $derived($profile?.id || '');
const contentData = $derived(getCourseContent(courseApi.course)); 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 isLiveCourse = $derived(courseApi.course?.type === 'LIVE_CLASS');
const isSelfPacedCourse = $derived(courseApi.course?.type === 'SELF_PACED'); const isSelfPacedCourse = $derived(isSelfPacedLikeCourse(courseApi.course?.type));
const metaChipClass = 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'; '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';
@@ -6,7 +6,7 @@
import { CircleCheckIcon } from '$features/ui/icons'; import { CircleCheckIcon } from '$features/ui/icons';
import { Button } from '@cio/ui/base/button'; import { Button } from '@cio/ui/base/button';
import * as Tooltip from '@cio/ui/base/tooltip'; 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 { t } from '$lib/utils/functions/translations';
import { courseApi, lessonApi } from '$features/course/api'; import { courseApi, lessonApi } from '$features/course/api';
import { getOrderedNavigableContent, getContentRoute } from '$features/course/utils/content'; import { getOrderedNavigableContent, getContentRoute } from '$features/course/utils/content';
@@ -64,7 +64,7 @@
const currentLessonItem = $derived(lessonId ? lessonItems.find((l) => l.id === lessonId) : null); const currentLessonItem = $derived(lessonId ? lessonItems.find((l) => l.id === lessonId) : null);
const isLessonLocked = $derived( const isLessonLocked = $derived(
$globalStore.isStudent && currentLessonItem && !(currentLessonItem.isUnlocked ?? false) $isOrgStudent && currentLessonItem && !(currentLessonItem.isUnlocked ?? false)
); );
const isMarkCompleteDisabled = $derived(isMarkingComplete || isLessonLocked || isLessonComplete); const isMarkCompleteDisabled = $derived(isMarkingComplete || isLessonLocked || isLessonComplete);
@@ -83,7 +83,7 @@
updateCourseContentCompletion(currentLessonId, isComplete); updateCourseContentCompletion(currentLessonId, isComplete);
const allComplete = const allComplete =
$globalStore.isStudent && $isOrgStudent &&
isComplete && isComplete &&
navigableContentItems.length > 0 && navigableContentItems.length > 0 &&
navigableContentItems.every((item) => item.isComplete); navigableContentItems.every((item) => item.isComplete);
@@ -14,7 +14,7 @@
import { HoverableItem, AttachmentIcon } from '@cio/ui/custom/moving-icons'; import { HoverableItem, AttachmentIcon } from '@cio/ui/custom/moving-icons';
import formatDate from '$lib/utils/functions/formatDate'; import formatDate from '$lib/utils/functions/formatDate';
import { t } from '$lib/utils/functions/translations'; 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 { contentApi, courseApi, exerciseApi, lessonApi } from '$features/course/api';
import { snackbar } from '$features/ui/snackbar/store'; import { snackbar } from '$features/ui/snackbar/store';
import { profile } from '$lib/utils/store/user'; import { profile } from '$lib/utils/store/user';
@@ -27,6 +27,7 @@
import ContentActions from './content-actions.svelte'; import ContentActions from './content-actions.svelte';
import SectionHeader from './section-header.svelte'; import SectionHeader from './section-header.svelte';
import { deleteContent, saveContent, toggleLock } from './content-action-helpers'; import { deleteContent, saveContent, toggleLock } from './content-action-helpers';
import { isSelfPacedLikeCourse } from '$features/course/utils/compliance-utils';
type CrudParam = { type CrudParam = {
sectionId?: string; sectionId?: string;
@@ -52,9 +53,9 @@
const courseId = $derived(courseApi.course?.id || ''); const courseId = $derived(courseApi.course?.id || '');
const profileId = $derived($profile?.id || ''); const profileId = $derived($profile?.id || '');
const contentData = $derived(getCourseContent(courseApi.course)); 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 isLiveCourse = $derived(courseApi.course?.type === 'LIVE_CLASS');
const isSelfPacedCourse = $derived(courseApi.course?.type === 'SELF_PACED'); const isSelfPacedCourse = $derived(isSelfPacedLikeCourse(courseApi.course?.type));
const metaChipClass = 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'; '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';
@@ -7,7 +7,7 @@
import { InputField } from '@cio/ui/custom/input-field'; import { InputField } from '@cio/ui/custom/input-field';
import * as Page from '@cio/ui/base/page'; import * as Page from '@cio/ui/base/page';
import MODES from '$lib/utils/constants/mode'; 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 { t } from '$lib/utils/functions/translations';
import { RoleBasedSecurity } from '$features/ui'; import { RoleBasedSecurity } from '$features/ui';
import DeleteLessonConfirmation from '$features/course/components/lesson/delete-lesson-confirmation.svelte'; 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="flex w-full items-center justify-between gap-2">
<div class="min-w-0 flex-1"> <div class="min-w-0 flex-1">
{#if mode === MODES.edit && !$globalStore.isStudent} {#if mode === MODES.edit && !$isOrgStudent}
<RoleBasedSecurity allowedRoles={[1, 2]}> <RoleBasedSecurity allowedRoles={[1, 2]}>
<InputField <InputField
className="max-w-xl" className="max-w-xl"
@@ -55,7 +55,7 @@
{/if} {/if}
</div> </div>
{#if mode === MODES.edit && !$globalStore.isStudent} {#if mode === MODES.edit && !$isOrgStudent}
<RoleBasedSecurity allowedRoles={[1, 2]}> <RoleBasedSecurity allowedRoles={[1, 2]}>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<IconButton onclick={onToggleLock} aria-label={lockLabel} title={lockLabel}> <IconButton onclick={onToggleLock} aria-label={lockLabel} title={lockLabel}>
@@ -4,7 +4,7 @@
import { Skeleton } from '@cio/ui/base/skeleton'; import { Skeleton } from '@cio/ui/base/skeleton';
import { profile } from '$lib/utils/store/user'; import { profile } from '$lib/utils/store/user';
import { orgs } from '$lib/utils/store/org'; 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 { isFreePlan } from '$lib/utils/store/org';
import OrgLogo from '$features/ui/sidebar/org-sidebar/org-logo.svelte'; import OrgLogo from '$features/ui/sidebar/org-sidebar/org-logo.svelte';
@@ -41,7 +41,7 @@
<Sidebar.Content> <Sidebar.Content>
{#if isCourseReady} {#if isCourseReady}
<Navigation path={currentPath} {id} isStudent={$globalStore.isStudent} /> <Navigation path={currentPath} {id} isStudent={$isOrgStudent ?? undefined} />
{:else} {:else}
<Sidebar.Group class="pt-0!"> <Sidebar.Group class="pt-0!">
<div class="flex h-8 items-center gap-2 rounded-md px-2"> <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 { profile } from '$lib/utils/store/user';
import { ROLE } from '@cio/utils/constants'; 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 { t } from '$lib/utils/functions/translations';
import { courseApi, attendanceApi } from '$features/course/api'; import { courseApi, attendanceApi } from '$features/course/api';
import { getLectureNo } from '$features/course/utils/functions'; import { getLectureNo } from '$features/course/utils/functions';
@@ -23,7 +23,7 @@
let { courseId }: Props = $props(); let { courseId }: Props = $props();
const students: CourseMembers = $derived( 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 && person.profile.id === $profile.id)
: courseApi.group.people.filter((person) => !!person.profile && Number(person.roleId) === ROLE.STUDENT) : 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) { async function handleAttendanceChange(e: any, student: CourseMember, lesson: CourseContentItem) {
if ($globalStore.isStudent) return; if ($isOrgStudent) return;
const isPresent = e.target.checked; const isPresent = e.target.checked;
@@ -167,7 +167,7 @@
<Table.Cell class="text-center"> <Table.Cell class="text-center">
<div class="flex justify-center"> <div class="flex justify-center">
<Checkbox <Checkbox
disabled={$globalStore.isStudent} disabled={$isOrgStudent}
checked={getAttendanceStatus(student.id, lesson.id)} checked={getAttendanceStatus(student.id, lesson.id)}
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
const e = { target: { checked } }; const e = { target: { checked } };
@@ -24,7 +24,7 @@
import { ZExerciseUpdate } from '@cio/utils/validation/exercise'; import { ZExerciseUpdate } from '@cio/utils/validation/exercise';
import { mapZodErrorsToTranslations } from '$lib/utils/validation'; import { mapZodErrorsToTranslations } from '$lib/utils/validation';
import { transformQuestionsToApiFormat } from '$features/course/components/exercise/functions'; 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 { t } from '$lib/utils/functions/translations';
import { snackbar } from '$features/ui/snackbar/store'; import { snackbar } from '$features/ui/snackbar/store';
import { exerciseApi } from '$features/course/api'; import { exerciseApi } from '$features/course/api';
@@ -37,6 +37,7 @@
import UpdateDescription from '$features/course/components/exercise/update-description.svelte'; import UpdateDescription from '$features/course/components/exercise/update-description.svelte';
import { ContentNavigationActions } from '$features/course/components/lesson'; import { ContentNavigationActions } from '$features/course/components/lesson';
import { RefreshPageData, RoleBasedSecurity } from '$features/ui'; import { RefreshPageData, RoleBasedSecurity } from '$features/ui';
import { isSelfPacedLikeCourse } from '$features/course/utils/compliance-utils';
import { getOrderedNavigableContent } from '$features/course/utils/content'; import { getOrderedNavigableContent } from '$features/course/utils/content';
import { Empty } from '@cio/ui/custom/empty'; import { Empty } from '@cio/ui/custom/empty';
import VideoIcon from '@lucide/svelte/icons/video'; import VideoIcon from '@lucide/svelte/icons/video';
@@ -94,7 +95,7 @@
} }
async function handleSave() { async function handleSave() {
if ($globalStore.isStudent || !courseApi.course?.id) return; if ($isOrgStudent || !courseApi.course?.id) return;
// Transform questionnaire to API format for validation // Transform questionnaire to API format for validation
// Include all non-deleted options (even empty ones) so Zod can catch validation errors // 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 isExerciseUnlocked = $derived(exerciseContentItem?.isUnlocked ?? false);
const isSelfPacedCourse = $derived(courseApi.course?.type === 'SELF_PACED'); const isSelfPacedCourse = $derived(isSelfPacedLikeCourse(courseApi.course?.type));
const activeQuestionTypeIds = $derived( const activeQuestionTypeIds = $derived(
($questionnaire.questions ?? []).filter((q) => !q.deletedAt).map((q) => getQuestionTypeId(q)) ($questionnaire.questions ?? []).filter((q) => !q.deletedAt).map((q) => getQuestionTypeId(q))
); );
@@ -244,7 +245,7 @@
isSelfPacedCourse && isExerciseFullyAutoGradable(activeQuestionTypeIds) isSelfPacedCourse && isExerciseFullyAutoGradable(activeQuestionTypeIds)
); );
const teacherAutoGradeBadge = $derived.by(() => { 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); return isExerciseFullyAutoGradable(activeQuestionTypeIds) ? ('auto' as const) : ('manual' as const);
}); });
</script> </script>
@@ -268,7 +269,7 @@
</Page.HeaderContent> </Page.HeaderContent>
<Page.Action> <Page.Action>
<div class="flex items-center gap-2"> <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} /> <ContentNavigationActions courseId={courseApi.course.id} {exerciseId} />
{/if} {/if}
@@ -332,7 +333,7 @@
<Page.Body> <Page.Body>
{#snippet child()} {#snippet child()}
<div class="overflow-x-hidden"> <div class="overflow-x-hidden">
{#if $globalStore.isStudent} {#if $isOrgStudent}
{#if isExerciseUnlocked} {#if isExerciseUnlocked}
<ViewMode {preview} {exerciseId} isFetchingExercise={isFetching} {mySubmissions} /> <ViewMode {preview} {exerciseId} isFetchingExercise={isFetching} {mySubmissions} />
{:else} {:else}
@@ -15,7 +15,7 @@
import MODES from '$lib/utils/constants/mode'; import MODES from '$lib/utils/constants/mode';
import { profile } from '$lib/utils/store/user'; 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 { currentOrg } from '$lib/utils/store/org';
import { snackbar } from '$features/ui/snackbar/store'; import { snackbar } from '$features/ui/snackbar/store';
import { RefreshPageData, UnsavedChanges } from '$features/ui'; import { RefreshPageData, UnsavedChanges } from '$features/ui';
@@ -365,7 +365,7 @@
</Page.HeaderContent> </Page.HeaderContent>
<Page.Action> <Page.Action>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
{#if mode === MODES.view && $globalStore.isStudent} {#if mode === MODES.view && $isOrgStudent}
<ContentNavigationActions {lessonId} {courseId} /> <ContentNavigationActions {lessonId} {courseId} />
{/if} {/if}
@@ -406,7 +406,7 @@
<Page.Body> <Page.Body>
{#snippet child()} {#snippet child()}
<div class="overflow-x-hidden lg:w-full xl:w-11/12"> <div class="overflow-x-hidden lg:w-full xl:w-11/12">
{#if $globalStore.isStudent && lessonApi.lesson && !isLessonUnlocked} {#if $isOrgStudent && lessonApi.lesson && !isLessonUnlocked}
<Empty <Empty
title={$t('course.navItem.lessons.content_locked_title')} title={$t('course.navItem.lessons.content_locked_title')}
description={$t('course.navItem.lessons.content_locked_description')} description={$t('course.navItem.lessons.content_locked_description')}
@@ -488,7 +488,7 @@
variant="page" variant="page"
class="text-center" class="text-center"
> >
{#if !$globalStore.isStudent} {#if !$isOrgStudent}
<Button onclick={toggleMode}> <Button onclick={toggleMode}>
{$t('course.navItem.lessons.materials.get_started')} {$t('course.navItem.lessons.materials.get_started')}
</Button> </Button>
+14 -14
View File
@@ -1,34 +1,34 @@
import { currentOrg, currentOrgPath } from './org';
import { derived, writable } from 'svelte/store'; import { derived, writable } from 'svelte/store';
import { PUBLIC_IS_SELFHOSTED } from '$env/static/public'; import { PUBLIC_IS_SELFHOSTED } from '$env/static/public';
import { currentOrgPath } from './org'; import { ROLE } from '@cio/utils/constants';
export const globalStore = writable<{ export const globalStore = writable<{
isDark: boolean; isDark: boolean;
isOrgSite: boolean; isOrgSite: boolean;
orgSiteName: string; orgSiteName: string;
isStudent?: boolean;
}>({ }>({
isDark: false, isDark: false,
isOrgSite: false, isOrgSite: false,
orgSiteName: '', orgSiteName: ''
isStudent: undefined });
export const isOrgStudent = derived(currentOrg, ($currentOrg) => {
if ($currentOrg.roleId === 0) return null;
return $currentOrg.roleId === ROLE.STUDENT;
}); });
/** /**
* Single source of truth: is this a student experience? * Cloud: orgSite is always student (a teacher on an org subdomain sees the student view).
* Cloud: orgSite is always student. Self-hosted: check role. * Self-hosted: derived from isOrgStudent (role-based).
*
* why are we not using isOrgStudent from org store?
* because this is derived from the currentOrg which returns null if no org OR boolean if org is present. Once org data is set, we then update globalStore.isStudent to the same value.
*
* why are we not using only globalStore.isStudent for this?
* because in the cloud instance, when a teacher logs into the lms aka org site, they need to be treated as a student so they see what the student sees.
*/ */
export const isStudentExperience = derived(globalStore, ($gs) => { export const isStudentExperience = derived([globalStore, isOrgStudent], ([$gs, $isStudent]) => {
const isCloud = PUBLIC_IS_SELFHOSTED !== 'true'; const isCloud = PUBLIC_IS_SELFHOSTED !== 'true';
if (isCloud) return $gs.isOrgSite; if (isCloud) return $gs.isOrgSite;
return $gs.isStudent ?? false;
return $isStudent ?? false;
}); });
/** /**
@@ -43,12 +43,6 @@ export const isOrgAdmin = derived(currentOrg, ($currentOrg) => {
return $currentOrg.roleId === ROLE.ADMIN; return $currentOrg.roleId === ROLE.ADMIN;
}); });
export const isOrgStudent = derived(currentOrg, ($currentOrg) => {
if ($currentOrg.roleId === 0) return null;
return $currentOrg.roleId === ROLE.STUDENT;
});
const getActivePlan = (org: AccountOrg) => { const getActivePlan = (org: AccountOrg) => {
return org.plans.find((p) => p.isActive); return org.plans.find((p) => p.isActive);
}; };
@@ -6,7 +6,7 @@
import { setTheme } from '$lib/utils/functions/theme'; import { setTheme } from '$lib/utils/functions/theme';
import { setupCloudAnalytics } from '$lib/utils/functions/appSetup'; import { setupCloudAnalytics } from '$lib/utils/functions/appSetup';
import { globalStore } from '$lib/utils/store/app'; import { globalStore } from '$lib/utils/store/app';
import { currentOrg, isOrgStudent } from '$lib/utils/store/org'; import { currentOrg } from '$lib/utils/store/org';
import { appInitApi } from '$features/app/init.svelte'; import { appInitApi } from '$features/app/init.svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { authClient } from '$lib/utils/services/auth/client'; import { authClient } from '$lib/utils/services/auth/client';
@@ -98,13 +98,6 @@
} }
}); });
$effect(() => {
if ($isOrgStudent === null) return;
if ($isOrgStudent !== $globalStore.isStudent) {
globalStore.update((s) => ({ ...s, isStudent: !!$isOrgStudent }));
}
});
</script> </script>
<UpgradeModal /> <UpgradeModal />
@@ -12,7 +12,7 @@
clearQuestionnaireValidation, clearQuestionnaireValidation,
reset reset
} from '$features/course/components/exercise/store'; } from '$features/course/components/exercise/store';
import { globalStore } from '$lib/utils/store/app'; import { isOrgStudent } from '$lib/utils/store/app';
import { t } from '$lib/utils/functions/translations'; import { t } from '$lib/utils/functions/translations';
import type { Question } from '$features/course/types'; import type { Question } from '$features/course/types';
import { import {
@@ -24,7 +24,7 @@
let { data = $bindable() } = $props(); let { data = $bindable() } = $props();
const path = `/courses/${data.courseId}/lessons`; const path = `/courses/${data.courseId}/lessons`;
const isLockedForStudent = $derived($globalStore.isStudent && data.exercise?.isUnlocked === false); const isLockedForStudent = $derived($isOrgStudent && data.exercise?.isUnlocked === false);
$effect(() => { $effect(() => {
if (!data.exercise || isLockedForStudent) return; if (!data.exercise || isLockedForStudent) return;
@@ -41,6 +41,7 @@
currentOrg.set(data.org); currentOrg.set(data.org);
setTheme(data.org.theme || 'blue'); setTheme(data.org.theme || 'blue');
}); });
</script> </script>
{#if data.org?.isRestricted || $currentOrg.isRestricted} {#if data.org?.isRestricted || $currentOrg.isRestricted}
+2 -2
View File
@@ -10,8 +10,7 @@
importThemeComponent, importThemeComponent,
normalizeLandingPageSettings normalizeLandingPageSettings
} from '$features/org/utils/landing-page'; } from '$features/org/utils/landing-page';
import { basePath } from '$lib/utils/store/app'; import { basePath, globalStore } from '$lib/utils/store/app';
import { globalStore } from '$lib/utils/store/app';
import { currentOrg } from '$lib/utils/store/org'; import { currentOrg } from '$lib/utils/store/org';
import { setTheme } from '$lib/utils/functions/theme'; import { setTheme } from '$lib/utils/functions/theme';
import { t } from '$lib/utils/functions/translations'; import { t } from '$lib/utils/functions/translations';
@@ -82,6 +81,7 @@
const mod = await importThemeComponent(settings.theme); const mod = await importThemeComponent(settings.theme);
ThemeComponent = mod.default; ThemeComponent = mod.default;
}); });
</script> </script>
<svelte:head> <svelte:head>