({
learnApiBaseUrl: '',
auth0Domain: '',
- features: {
- courseStatusIndicator: false,
- },
+ features: [],
});
+export const isFeatureEnabled = (
+ feature: EFeatureFlag,
+ features: EFeatureFlag[]
+) => features.includes(feature);
+
export default ConfigContext;
diff --git a/packages/gatsby-theme-learning/src/components/page-course-status.tsx b/packages/gatsby-theme-learning/src/components/page-course-status.tsx
deleted file mode 100644
index 137ca639f8..0000000000
--- a/packages/gatsby-theme-learning/src/components/page-course-status.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import React, { useContext } from 'react';
-import { useAuth0 } from '@auth0/auth0-react';
-import {
- getCourseStatusByCourseId,
- useFetchCourses,
-} from '../hooks/use-course-status';
-import ConfigContext from './config-context';
-
-type CourseStatusProps = {
- error?: string;
- status?: string;
-};
-
-const CourseStatus = (props: CourseStatusProps) => {
- if (props.error) {
- return unavailable;
- }
- if (props.status) {
- return {props.status};
- }
- return null;
-};
-
-type PageCourseStatusProps = {
- courseId: number;
-};
-
-const PageCourseStatus = (props: PageCourseStatusProps) => {
- const { isAuthenticated } = useAuth0();
- const { data, isLoading, error } = useFetchCourses();
- const {
- features: { courseStatusIndicator },
- } = useContext(ConfigContext);
-
- // courseStatusIndicator feature flag
- if (!courseStatusIndicator) {
- return null;
- }
-
- const courseStatus = data?.result?.enrolledCourses
- ? getCourseStatusByCourseId(data.result.enrolledCourses, props.courseId)
- : undefined;
- return (
- <>
- {props.courseId && isAuthenticated && (
-
- Course status:{' '}
- {isLoading ? (
- '...'
- ) : (
-
- )}
-
- )}
- >
- );
-};
-
-export default PageCourseStatus;
diff --git a/packages/gatsby-theme-learning/src/components/sidebar-course-status.tsx b/packages/gatsby-theme-learning/src/components/sidebar-course-status.tsx
new file mode 100644
index 0000000000..0cf3d1b088
--- /dev/null
+++ b/packages/gatsby-theme-learning/src/components/sidebar-course-status.tsx
@@ -0,0 +1,54 @@
+import React, { useContext } from 'react';
+import { CircleIcon, VerifiedIcon } from '@commercetools-uikit/icons';
+import {
+ getCourseStatusByCourseId,
+ useFetchCourses,
+} from '../hooks/use-course-status';
+import ConfigContext, {
+ isFeatureEnabled,
+ EFeatureFlag,
+} from './config-context';
+import styled from '@emotion/styled';
+import { designSystem } from '@commercetools-docs/ui-kit';
+
+const UnknownStateSpacer = styled.div`
+ width: ${designSystem.dimensions.spacings.l};
+ margin-right: 5px;
+`;
+
+type StatusIndicatorProps = {
+ status?: string;
+};
+
+export const StatusIndicator = (props: StatusIndicatorProps) => {
+ console.log(props.status);
+ switch (props.status) {
+ case 'completed':
+ return ;
+ case 'inProgress':
+ return ;
+ default:
+ return ;
+ }
+};
+
+type SidebarCourseStatusProps = {
+ courseId: number;
+};
+
+const SidebarCourseStatus = (props: SidebarCourseStatusProps) => {
+ const { data } = useFetchCourses();
+ const { features } = useContext(ConfigContext);
+
+ // CourseStatus feature flag
+ if (!isFeatureEnabled(EFeatureFlag.CourseStatus, features)) {
+ return null;
+ }
+
+ const courseStatus = data?.result?.enrolledCourses
+ ? getCourseStatusByCourseId(data.result.enrolledCourses, props.courseId)
+ : undefined;
+ return <>{props.courseId && }>;
+};
+
+export default SidebarCourseStatus;
diff --git a/packages/gatsby-theme-learning/src/components/sidebar-topic-status.tsx b/packages/gatsby-theme-learning/src/components/sidebar-topic-status.tsx
new file mode 100644
index 0000000000..af1ac2f161
--- /dev/null
+++ b/packages/gatsby-theme-learning/src/components/sidebar-topic-status.tsx
@@ -0,0 +1,55 @@
+import React, { useContext } from 'react';
+import { CheckActiveIcon, CircleIcon } from '@commercetools-uikit/icons';
+import {
+ useFetchCourseDetails,
+ getTopicStatusByPageTitle,
+} from '../hooks/use-course-details';
+import ConfigContext, {
+ isFeatureEnabled,
+ EFeatureFlag,
+} from './config-context';
+import { designSystem } from '@commercetools-docs/ui-kit';
+import styled from '@emotion/styled';
+
+const UnknownStateSpacer = styled.div`
+ width: ${designSystem.dimensions.spacings.m};
+ margin-right: 5px;
+`;
+
+type StatusIndicatorProps = {
+ status?: string;
+};
+
+export const StatusIndicator = (props: StatusIndicatorProps) => {
+ switch (props.status) {
+ case 'completed':
+ return ;
+ case 'notCompleted':
+ return ;
+ default:
+ return ;
+ }
+};
+
+type PageTopicStatusProps = {
+ courseId: number;
+ pageTitle: string;
+};
+
+const SidebarTopicStatus = (props: PageTopicStatusProps) => {
+ const { data } = useFetchCourseDetails(props.courseId);
+ const { features } = useContext(ConfigContext);
+
+ // CourseStatus feature flag
+ if (!isFeatureEnabled(EFeatureFlag.CourseStatus, features)) {
+ return null;
+ }
+
+ const topicStatus = data?.result?.topics
+ ? getTopicStatusByPageTitle(data.result.topics, props.pageTitle)
+ : undefined;
+
+ return props.courseId && ;
+};
+
+export default SidebarTopicStatus;
diff --git a/packages/gatsby-theme-learning/src/hooks/use-course-details.ts b/packages/gatsby-theme-learning/src/hooks/use-course-details.ts
new file mode 100644
index 0000000000..98b7c022ab
--- /dev/null
+++ b/packages/gatsby-theme-learning/src/hooks/use-course-details.ts
@@ -0,0 +1,63 @@
+import useSWR from 'swr';
+import { useContext } from 'react';
+import ConfigContext, {
+ EFeatureFlag,
+ isFeatureEnabled,
+} from '../components/config-context';
+import { useAuth0 } from '@auth0/auth0-react';
+import type {
+ ApiCallResult,
+ CourseWithDetails,
+ CourseTopic,
+} from '../external-types';
+import { fetcherWithToken } from './hooks.utils';
+import { useAuthToken } from './use-auth-token';
+
+type UseFetchCoursesIdResponse = {
+ data: ApiCallResult | undefined;
+ error: string;
+ isLoading: boolean;
+};
+
+export const useFetchCourseDetails = (
+ courseId: number
+): {
+ data: ApiCallResult | undefined;
+ error: string | undefined;
+ isLoading: boolean;
+} => {
+ const { learnApiBaseUrl, features } = useContext(ConfigContext);
+ const { isAuthenticated } = useAuth0();
+ const { getAuthToken } = useAuthToken();
+ const apiEndpoint = `/api/courses/${courseId}`;
+
+ // fetch data only if course status feature flag is true and the user is logged in
+ const shouldFetchData =
+ courseId &&
+ isFeatureEnabled(EFeatureFlag.CourseStatus, features) &&
+ isAuthenticated;
+
+ const { data, error, isLoading } = useSWR(
+ shouldFetchData ? apiEndpoint : null,
+ (url) => fetcherWithToken(url, getAuthToken, learnApiBaseUrl)
+ ) as UseFetchCoursesIdResponse;
+ return {
+ data,
+ error,
+ isLoading,
+ };
+};
+
+export const getTopicStatusByPageTitle = (
+ topics: CourseTopic[],
+ pageTitle: string
+) => {
+ const matchingTopic = topics.find(
+ (topic) =>
+ topic.name.trim().toLowerCase() === pageTitle.trim().toLowerCase()
+ );
+ if (matchingTopic) {
+ return matchingTopic.completed ? 'completed' : 'notCompleted';
+ }
+ return 'notAvailable';
+};
diff --git a/packages/gatsby-theme-learning/src/hooks/use-course-status.ts b/packages/gatsby-theme-learning/src/hooks/use-course-status.ts
index 541cb1c682..f88cd70df5 100644
--- a/packages/gatsby-theme-learning/src/hooks/use-course-status.ts
+++ b/packages/gatsby-theme-learning/src/hooks/use-course-status.ts
@@ -1,6 +1,9 @@
import useSWR from 'swr';
import { useContext } from 'react';
-import ConfigContext from '../components/config-context';
+import ConfigContext, {
+ EFeatureFlag,
+ isFeatureEnabled,
+} from '../components/config-context';
import { useAuth0 } from '@auth0/auth0-react';
import type {
ApiCallResult,
@@ -28,16 +31,14 @@ export const useFetchCourses = (): {
error: string | undefined;
isLoading: boolean;
} => {
- const {
- learnApiBaseUrl,
- features: { courseStatusIndicator },
- } = useContext(ConfigContext);
+ const { learnApiBaseUrl, features } = useContext(ConfigContext);
const { isAuthenticated } = useAuth0();
const { getAuthToken } = useAuthToken();
const apiEndpoint = `/api/courses`;
// fetch data only if course status feature flag is true and the user is logged in
- const shouldFetchData = courseStatusIndicator && isAuthenticated;
+ const shouldFetchData =
+ isFeatureEnabled(EFeatureFlag.CourseStatus, features) && isAuthenticated;
const { data, error, isLoading } = useSWR(
shouldFetchData ? apiEndpoint : null,
diff --git a/packages/gatsby-theme-learning/src/hooks/use-submit-attempt.ts b/packages/gatsby-theme-learning/src/hooks/use-submit-attempt.ts
index 55cb37805f..7f457e6b1f 100644
--- a/packages/gatsby-theme-learning/src/hooks/use-submit-attempt.ts
+++ b/packages/gatsby-theme-learning/src/hooks/use-submit-attempt.ts
@@ -27,7 +27,10 @@ export const useSubmitAttempt = (submitAttemptParams: SubmitAttemptParams) => {
attemptData: SubmissionAttempt,
finish: boolean
) => {
- const invalidateCache = () => mutate('/api/courses');
+ const invalidateCache = () => {
+ mutate('/api/courses');
+ mutate(`/api/courses/${courseId}`);
+ };
const apiEndpoint = `${learnApiBaseUrl}/api/courses/${courseId}/quizzes/${quizId}/attempts/${attemptId}?finish=${finish}`;
const accessToken = await getAuthToken();
const data = await fetch(apiEndpoint, {
diff --git a/packages/gatsby-theme-learning/src/index.ts b/packages/gatsby-theme-learning/src/index.ts
index 4883038dbf..0264263aac 100644
--- a/packages/gatsby-theme-learning/src/index.ts
+++ b/packages/gatsby-theme-learning/src/index.ts
@@ -1,4 +1,5 @@
export { default as Quiz } from './components/quiz';
export * from './components/quiz';
export * from './components/quiz.types';
-export { default as PageCourseStatus } from './components/page-course-status';
+export { default as SidebarCourseStatus } from './components/sidebar-course-status';
+export { default as SidebarTopicStatus } from './components/sidebar-topic-status';
diff --git a/websites/docs-smoke-test/gatsby-config.mjs b/websites/docs-smoke-test/gatsby-config.mjs
index b57d754104..be93716363 100644
--- a/websites/docs-smoke-test/gatsby-config.mjs
+++ b/websites/docs-smoke-test/gatsby-config.mjs
@@ -32,6 +32,7 @@ const config = {
options: {
auth0Domain: 'auth.id.commercetools.com',
learnApiBaseUrl: 'https://api.learn.commercetools.com',
+ features: ['status-indicator']
},
},
{
diff --git a/websites/docs-smoke-test/src/content/self-learning/2-quiz.mdx b/websites/docs-smoke-test/src/content/self-learning/2-quiz.mdx
index afdb3a80e6..be2ff9d01d 100644
--- a/websites/docs-smoke-test/src/content/self-learning/2-quiz.mdx
+++ b/websites/docs-smoke-test/src/content/self-learning/2-quiz.mdx
@@ -1,8 +1,9 @@
---
title: Quiz component
-courseId: 55
+courseId: 66
+topicName: Quiz 2
---
# Test your knowledge
-
+
diff --git a/websites/docs-smoke-test/src/content/self-learning/overview.mdx b/websites/docs-smoke-test/src/content/self-learning/overview.mdx
index dfb7f632b6..86844ba5c8 100644
--- a/websites/docs-smoke-test/src/content/self-learning/overview.mdx
+++ b/websites/docs-smoke-test/src/content/self-learning/overview.mdx
@@ -1,6 +1,7 @@
---
title: Course overview
-courseId: 55
+courseId: 66
+topicName: 'Overview'
---
The purpose of this page is to provide an introduction to the quiz component.
diff --git a/websites/docs-smoke-test/src/content/self-learning/quiz.mdx b/websites/docs-smoke-test/src/content/self-learning/quiz.mdx
index 80ddc42501..a5c8f7d52a 100644
--- a/websites/docs-smoke-test/src/content/self-learning/quiz.mdx
+++ b/websites/docs-smoke-test/src/content/self-learning/quiz.mdx
@@ -1,8 +1,9 @@
---
title: Quiz component
-courseId: 55
+courseId: 66
+topicName: 'Quiz 1'
---
# Test your knowledge
-
+
diff --git a/websites/documentation/src/content/writing/quizzes.mdx b/websites/documentation/src/content/writing/quizzes.mdx
index 0c04cecf8b..cc4548b791 100644
--- a/websites/documentation/src/content/writing/quizzes.mdx
+++ b/websites/documentation/src/content/writing/quizzes.mdx
@@ -14,8 +14,8 @@ Works against commercetools' learning-api, which is not a part of this open sour
- `auth0Domain`: the auth0 application domain url (it is defined in the auth0 management app)
- `learnApiBaseUrl`: the learn API base url. It can be omitted if the host running the site matches the api host.
-- `features`: a list of feature flags (boolean values) to toggle specific functionalities.
- - `courseStatusIndicator`: feature flag to toggle the course status indicator.
+- `features`: an array of strings representing feature flags used to toggle specific functionalities. Expected values:
+ - `status-indicator`: feature flag to toggle the course status indicator.
In order to enable the plugin, the following configuration should be added to the `gatsby-config.js` plugin section: