Feat: Update and introduce new course content renderings#295
Conversation
This component may be used to show the contents associated to a given check, e.g. within the `OverviewSection` when creating a new course.
This new page is going to show the contents of a given course so that users can familiarize themselves with the contents associated to a given course.
This way users can view the course contents associated to a given course.
Separated the rendering of the `ContentSection` into subcomponents to improve readability and maintainability.
This way the `ContentSection` now mimics the existing `QuetionsSection`. This means it shows the contents in a list instead of a grid. The reason for this change is that this eliminates sudden changes in the app's layout and also makes it easier to re-use this section in the `OverviewSection`.
By redesigning the `ContentSection`'s layout it can be easily re-used in the `OverviewSection` for users to review their changes.
This way the amount of contents associated to a given course are displayed when discovering new or checks a user contributes to.
This component is used to manage state about the content currently shown to the user and to switch between contents.
This component is used to switch between the contents in the `/contents/[courseId]` page.
This component renders the currently selected course content in a readOnly `RichtTextEditor`.
This component is rendered within the course contents page to define custom breadcrumbs for this page.
WalkthroughAdds a course contents feature: a new localized Next.js page for course contents, context-based client components to manage and render content items (reader, navigation, switch buttons), breadcrumbs, updated course UI/actions, editor styling props, and corresponding i18n entries. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant ActionMenu as CourseActionMenu
participant Page as CourseContentsPage
participant Provider as ContentProvider
participant Renderer as ContentRenderer
participant Nav as NavigationButtons
participant Switch as ContentSwitchButtons
User->>ActionMenu: Click "View Contents"
ActionMenu->>Page: Navigate to /courses/contents/[id]
Page->>Page: Fetch course by ID
Page->>Provider: Render with initial contents & index
Provider->>Renderer: Provide contents & currentIndex
Provider->>Nav: Provide contents & currentIndex
Provider->>Switch: Provide contents & currentIndex
User->>Nav: Click Next/Previous
Nav->>Provider: setCurrentIndex(newIndex)
Provider->>Renderer: Update rendered content
User->>Switch: Click category button
Switch->>Provider: setCurrentIndex(categoryIndex)
Provider->>Renderer: Update rendered content
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (3)
src/components/courses/contents/[courseId]/ContentRenderer.tsx (1)
7-11: Consider handling empty content state.When
contents.at(currentContentIndex)returnsundefined(empty contents array or out-of-bounds index), theRichTextEditorwill render with no content. Consider adding a fallback UI for better user experience.💡 Optional: Add empty state handling
export function ContentRenderer() { const { contents, currentContentIndex } = useContentContext() + const currentContent = contents.at(currentContentIndex) + + if (!currentContent) { + return <div className="text-center text-muted-foreground p-8">No content available</div> + } - return <RichTextEditor readOnly defaultContent={contents.at(currentContentIndex)?.content} editorPaneClassname={cn('border-dashed p-4')} /> + return <RichTextEditor readOnly defaultContent={currentContent.content} editorPaneClassname={cn('border-dashed p-4')} /> }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/courses/contents/`[courseId]/ContentRenderer.tsx around lines 7 - 11, ContentRenderer currently passes contents.at(currentContentIndex)?.content directly to RichTextEditor which yields an empty editor when contents is empty or index is out of bounds; update ContentRenderer to detect when contents.at(currentContentIndex) is undefined and render a clear empty-state fallback (e.g., a placeholder message/component or pass an explicit empty string to RichTextEditor) and/or show an action (like "No content available" with a button to add content) so the UI is informative; locate the logic in the ContentRenderer component and use useContentContext, currentContentIndex, and RichTextEditor to implement the check and conditional rendering.src/components/courses/contents/[courseId]/ContentsPageBreadcrumbs.tsx (1)
5-31: Hardcoded breadcrumb labels should use i18n.The breadcrumb labels "Home", "Courses", and "Contents" are hardcoded strings, but the codebase uses
next-internationalfor localization. The existingShared.Breadcrumbsnamespace already has translations forroot("Home") andcourses("Kurse" in German).♻️ Suggested fix using i18n
+'use client' + import Link from 'next/link' import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from '@/src/components/shadcn/breadcrumb' +import { useScopedI18n } from '@/src/i18n/client-localization' import { Course } from '@/src/schemas/CourseSchema' export function ContentPageBreadcrumbs({ course }: { course: Course }) { + const t = useScopedI18n('Shared.Breadcrumbs') + return ( <Breadcrumb> <BreadcrumbList> <BreadcrumbItem> <BreadcrumbLink asChild> - <Link href={`/`}>Home</Link> + <Link href={`/`}>{t('root')}</Link> </BreadcrumbLink> </BreadcrumbItem> <BreadcrumbSeparator /> <BreadcrumbItem> <BreadcrumbLink asChild> - <Link href={`/courses`}>Courses</Link> + <Link href={`/courses`}>{t('courses')}</Link> </BreadcrumbLink> </BreadcrumbItem> <!-- ... --> - <BreadcrumbPage>Contents</BreadcrumbPage> + <BreadcrumbPage>{t('contents')}</BreadcrumbPage> </BreadcrumbList> </Breadcrumb> ) }Note: You may need to add a
contentskey toShared.Breadcrumbsin your locale files if it doesn't exist.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/courses/contents/`[courseId]/ContentsPageBreadcrumbs.tsx around lines 5 - 31, Replace hardcoded breadcrumb labels in ContentPageBreadcrumbs with localized strings from next-international: import and call the translation hook (e.g., useTranslations) inside ContentPageBreadcrumbs, then use the Shared.Breadcrumbs namespace keys (Shared.Breadcrumbs.root for "Home", Shared.Breadcrumbs.courses for "Courses") and add/use a Shared.Breadcrumbs.contents key for "Contents" (add it to locale files if missing). Update the Link label for the courses and the BreadcrumbPage text to use the translated values while leaving links/paths unchanged.src/components/courses/(hamburger-menu)/CourseActionMenu.tsx (1)
113-115: Use relative App Router navigation for internal routes.At line 114, change
router.push(${window.location.origin}/courses/contents/${id})torouter.push('/courses/contents/${id}'). According to Next.js documentation, relative paths without the origin are preferred for internal navigation—they enable route optimizations like prefetching and caching, and avoid unnecessary browser-origin coupling. The same pattern appears in otherrouter.pushcalls in this file and should also be updated.♻️ Proposed change
onClick={() => { - router.push(`${window.location.origin}/courses/contents/${id}`) + router.push(`/courses/contents/${id}`) }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/courses/`(hamburger-menu)/CourseActionMenu.tsx around lines 113 - 115, The router.push call in the onClick handler inside CourseActionMenu.tsx uses an absolute URL via window.location.origin; replace router.push(`${window.location.origin}/courses/contents/${id}`) with a relative route router.push(`/courses/contents/${id}`) to use Next.js App Router optimizations, and similarly update any other router.push occurrences in this file to use relative paths (search for router.push(...) inside CourseActionMenu component and adjust).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/`[locale]/courses/contents/[courseId]/page.tsx:
- Line 23: The page currently renders ContentProvider with initialProps={{
contents: course.contents }} unconditionally, which breaks when course.contents
is empty; before returning the JSX that mounts ContentProvider (or in the page
loader), check course.contents.length and handle the empty-case server-side by
calling notFound() or returning an explicit server-rendered empty-state UI,
e.g., if (!course.contents || course.contents.length === 0) invoke notFound() or
render a simple EmptyContents component instead of mounting ContentProvider so
the route won't render a broken workspace when contents are absent.
In `@src/components/courses/contents/`[courseId]/ContentNavigationButtons.tsx:
- Around line 12-18: The component ContentNavigationButtons currently only
checks for the "next" boundary in a narrow way; add robust guards so empty lists
and invalid currentContentIndex values never render the next button or allow
out-of-range increments: return early (or render null) when contents.length ===
0, ensure you only render the next button when currentContentIndex >= 0 &&
currentContentIndex < contents.length - 1, and change the onClick updater used
with setCurrentIndex to clamp the new index (e.g., setCurrentIndex(prev =>
Math.min(prev + 1, contents.length - 1))) so clicks cannot push the index past
the end; reference variables/functions: contents, currentContentIndex,
setCurrentIndex, and component ContentNavigationButtons.
In `@src/components/courses/contents/`[courseId]/ContentSwitchButton.tsx:
- Around line 14-17: The computed index from useMemo (index) can be -1 when
contents.findIndex fails; update the ContentSwitchButton so it guards against
this by disabling the Button and preventing setCurrentIndex from being called
when index === -1. Locate the useMemo that defines index and the Button onClick
that calls setCurrentIndex(index) and change behavior: set the Button's disabled
prop (or add a conditional class) when index === -1 and make the onClick a no-op
unless index >= 0 (or early-return) so you never write an invalid index into
state; keep existing styling logic using currentContentIndex for the
active/underline state.
In `@src/components/courses/create/`(sections)/ContentSection.tsx:
- Around line 57-60: In the ContentSection component remove the invalid empty
JSX expression in the span (the "{ }" after content.title) — either delete the
entire span element or replace the empty expression with a real value (for
example a field from the content object like content.subtitle or content.meta)
so the JSX compiles; locate the div with className 'flex items-center
justify-between' containing the h2 that renders content.title and update the
adjacent span accordingly.
- Around line 98-103: The empty-state message in EmptyCourseContentBody is
hardcoded; replace the literal string with the localized string from the
component's i18n scope by calling useScopedI18n() (or using the existing scoped
`t` if already available) and render t('empty_state_label') instead of the
English text, and add the corresponding key "empty_state_label" under
Courses.Create.ContentSection in the translations files for all locales; ensure
the key name matches exactly and preserves punctuation/spacing needed for the
UI.
- Around line 54-55: The CourseContent items lack a unique id causing key and
deletion bugs: add a unique id field to the CourseContent type/schema and ensure
each created item gets a stable id; update the render in the map (where contents
is iterated and GenericCard currently uses key={i + content.categoryId}) to use
key={content.id} instead of the index-based key; and change removeCourseContent
to accept an id (or use content.id) and filter the contents array by item.id !==
id (instead of filtering by categoryId) so only the targeted item is removed.
- Around line 37-44: The wrapper <div> is currently the direct child of the
dialog trigger so it becomes the trigger and lets the dialog open even when the
Button is visually disabled; to fix, move the surrounding div outside and make
the Button (the <Button> element inside CourseContentDialog) the direct
child/trigger of CourseContentDialog (the component that renders DialogTrigger
asChild), ensuring the Button's disabled prop is respected—update the JSX so
CourseContentDialog is nested inside the outer wrapper and the <Button
disabled={disabled}> (with the <Plus /> icon) is the direct trigger element.
---
Nitpick comments:
In `@src/components/courses/`(hamburger-menu)/CourseActionMenu.tsx:
- Around line 113-115: The router.push call in the onClick handler inside
CourseActionMenu.tsx uses an absolute URL via window.location.origin; replace
router.push(`${window.location.origin}/courses/contents/${id}`) with a relative
route router.push(`/courses/contents/${id}`) to use Next.js App Router
optimizations, and similarly update any other router.push occurrences in this
file to use relative paths (search for router.push(...) inside CourseActionMenu
component and adjust).
In `@src/components/courses/contents/`[courseId]/ContentRenderer.tsx:
- Around line 7-11: ContentRenderer currently passes
contents.at(currentContentIndex)?.content directly to RichTextEditor which
yields an empty editor when contents is empty or index is out of bounds; update
ContentRenderer to detect when contents.at(currentContentIndex) is undefined and
render a clear empty-state fallback (e.g., a placeholder message/component or
pass an explicit empty string to RichTextEditor) and/or show an action (like "No
content available" with a button to add content) so the UI is informative;
locate the logic in the ContentRenderer component and use useContentContext,
currentContentIndex, and RichTextEditor to implement the check and conditional
rendering.
In `@src/components/courses/contents/`[courseId]/ContentsPageBreadcrumbs.tsx:
- Around line 5-31: Replace hardcoded breadcrumb labels in
ContentPageBreadcrumbs with localized strings from next-international: import
and call the translation hook (e.g., useTranslations) inside
ContentPageBreadcrumbs, then use the Shared.Breadcrumbs namespace keys
(Shared.Breadcrumbs.root for "Home", Shared.Breadcrumbs.courses for "Courses")
and add/use a Shared.Breadcrumbs.contents key for "Contents" (add it to locale
files if missing). Update the Link label for the courses and the BreadcrumbPage
text to use the translated values while leaving links/paths unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: fe62ed82-29c9-4492-bdef-c19d3ac97b2e
📒 Files selected for processing (15)
src/app/[locale]/courses/contents/[courseId]/page.tsxsrc/components/courses/(hamburger-menu)/CourseActionMenu.tsxsrc/components/courses/CourseCard.tsxsrc/components/courses/contents/[courseId]/ContentNavigationButtons.tsxsrc/components/courses/contents/[courseId]/ContentProvider.tsxsrc/components/courses/contents/[courseId]/ContentRenderer.tsxsrc/components/courses/contents/[courseId]/ContentSwitchButton.tsxsrc/components/courses/contents/[courseId]/ContentsPageBreadcrumbs.tsxsrc/components/courses/create/(sections)/ContentSection.tsxsrc/components/courses/create/(sections)/OverviewSection.tsxsrc/components/tiptap-examples/RichTextEditor.tsxsrc/i18n/locales/de.jsonsrc/i18n/locales/de.tssrc/i18n/locales/en.jsonsrc/i18n/locales/en.ts
These new button components allow users to continue to the next content in line or go back to the previous content.
This way the navigation buttons are in the same "column" as the `RichTextEditor` which is visually more appealing.
This way the buttons show a label on top of the content title ("previous", "next") to indicate it as an call-to-action.
This way the classes of the container wrapping the `RichtTextEditor` can be easily adjusted. This is especially useful to dynamically update the max-height constraint when needed.
This way users on mobile screens can use the navigation buttons below the editor to navigate between contents. The reason for this is that displaying the navigation next to the editor on small screens leads to both elements not being properly visible. Similarly, showing the navigation above or below the editor reduces the overall UX and appearance.
This reverts commit 81a8cdc.
Previously, if users would navigate to a course page where the respective check has no contents, it would try to display contents that don't exist. This meant that the page looked kind of broken. By adding a safeguard that redirects users back to where they came from or to the courses page when the a course has no contents eliminates this edge-case.
The reason for this change is that when the index retrieval was unsuccessful the button previously would have switched to the invalid index (-1). Now the button is disabled when index retrieval is unsuccessful, thus when switching is not possible.
Added a missing translation in the `EmptyCourseContentBody` subcomponent.
The reason for this change is that in the wrapping div element that is the direct child of the DialogTrigger that in turn renders a button is considered the trigger. This would mean that the disabled state between button and trigger could be out-of-sync. However, the div wrapper was previously also consumed by the trigger, because it wasn't rendered. By moving the wrapping div outside the trigger, the potential issue is resolved anyhow.
2e203c5 to
7005501
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
src/components/courses/create/(sections)/ContentSection.tsx (2)
54-55:⚠️ Potential issue | 🔴 CriticalStop using
categoryIdas the content item's identity.Lines 55 and 81 still treat
categoryIdas unique per content item. If two contents share a category, React keys collide and the delete handler removes both. The new reader sidebar repeats the same assumption onsrc/app/[locale]/courses/contents/[courseId]/page.tsxLine 47 and insrc/components/courses/contents/[courseId]/ContentSwitchButton.tsx. Please add a stablecontent.idand key/delete/switch on that instead.Suggested fix
- {contents.map((content, i) => ( - <GenericCard disableInteractions key={i + content.categoryId} className={cn('relative flex h-fit gap-3 p-2 first:mt-3 hover:bg-none')}> + {contents.map((content) => ( + <GenericCard disableInteractions key={content.id} className={cn('relative flex h-fit gap-3 p-2 first:mt-3 hover:bg-none')}>- <ConfirmationDialog confirmAction={() => removeCourseContent(content.categoryId)} confirmLabel={t('Actions.delete_content_confirm_label')} body={t('Actions.delete_content_dialog_body')}> + <ConfirmationDialog confirmAction={() => removeCourseContent(content.id)} confirmLabel={t('Actions.delete_content_confirm_label')} body={t('Actions.delete_content_dialog_body')}>Also applies to: 81-81
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/courses/create/`(sections)/ContentSection.tsx around lines 54 - 55, Replace use of categoryId as the content identity with a stable unique content.id: update the map in ContentSection (the GenericCard key and any delete handler invocation inside that map) to key on content.id instead of i + content.categoryId, and change the delete handler to accept and use content.id; likewise update the reader sidebar page (page.tsx) and ContentSwitchButton to use content.id for keys and switch/delete operations so items with the same categoryId no longer collide. Ensure all references to categoryId for identity are preserved only for category semantics, not for React keys or item operations.
57-60:⚠️ Potential issue | 🔴 CriticalRemove the empty JSX expression.
Line 59's
{}is invalid TSX and will fail the build. Drop the span or render a real value.Suggested fix
<div className='flex items-center justify-between'> <h2 className='text-neutral-700 dark:text-neutral-300'>{content.title}</h2> - <span className='text-neutral-700 dark:text-neutral-300'>{}</span> </div>Run this to confirm the empty JSX expression is still present:
#!/bin/bash sed -n '57,60p' "src/components/courses/create/(sections)/ContentSection.tsx"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/courses/create/`(sections)/ContentSection.tsx around lines 57 - 60, Remove the invalid empty JSX expression in the ContentSection render: the span containing `{}` should be deleted or replaced with a real value; update the JSX inside the component that renders content.title (the <div className='flex items-center justify-between'> block in ContentSection) to either remove the empty <span> or render a concrete property (e.g., content.someField) instead so the TSX builds cleanly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/courses/create/`(sections)/ContentSection.tsx:
- Around line 38-42: The create trigger Button inside CourseContentDialog
(mode='create') is missing an explicit type and should be set to type='button'
to match the edit/delete triggers and avoid accidental form submissions; update
the Button element used as the create trigger (the one rendering <Plus
className='size-5' /> and t('Actions.create_new_button_label')) to include
type='button'.
---
Duplicate comments:
In `@src/components/courses/create/`(sections)/ContentSection.tsx:
- Around line 54-55: Replace use of categoryId as the content identity with a
stable unique content.id: update the map in ContentSection (the GenericCard key
and any delete handler invocation inside that map) to key on content.id instead
of i + content.categoryId, and change the delete handler to accept and use
content.id; likewise update the reader sidebar page (page.tsx) and
ContentSwitchButton to use content.id for keys and switch/delete operations so
items with the same categoryId no longer collide. Ensure all references to
categoryId for identity are preserved only for category semantics, not for React
keys or item operations.
- Around line 57-60: Remove the invalid empty JSX expression in the
ContentSection render: the span containing `{}` should be deleted or replaced
with a real value; update the JSX inside the component that renders
content.title (the <div className='flex items-center justify-between'> block in
ContentSection) to either remove the empty <span> or render a concrete property
(e.g., content.someField) instead so the TSX builds cleanly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9f086c4d-1bad-4da7-856a-4c837dd349c0
📒 Files selected for processing (9)
src/app/[locale]/courses/contents/[courseId]/page.tsxsrc/components/courses/contents/[courseId]/ContentNavigationButtons.tsxsrc/components/courses/contents/[courseId]/ContentSwitchButton.tsxsrc/components/courses/create/(sections)/ContentSection.tsxsrc/components/tiptap-examples/RichTextEditor.tsxsrc/i18n/locales/de.jsonsrc/i18n/locales/de.tssrc/i18n/locales/en.jsonsrc/i18n/locales/en.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- src/components/courses/contents/[courseId]/ContentNavigationButtons.tsx
- src/components/courses/contents/[courseId]/ContentSwitchButton.tsx
- src/i18n/locales/de.ts
- src/i18n/locales/en.ts
- src/i18n/locales/en.json
- src/i18n/locales/de.json
|
🎉 This PR is included in version 4.0.0-canary.4 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Note
This pull request implements missing parts of the recently introduces course-content feature. It adds the new
ContentSectionto theOverviewSectionby externalizing and redesigning theContentSection. Additionally, it introduces a new course-content page that shows the contents of a given course, accessible through a newShow Contentsitem in theCourseActionMenu.Redesigned `ContentSection`
Updated `OverviewSection`
New course content page
Summary
New Features
Improvements
ContentSectionwhen section is emptyDetailed Chagenlog
Changelog
Features:
Drafted
CourseContentOverviewcomponent (ba67ddd4)This component may be used to show the contents associated to a given check, e.g. within the
OverviewSectionwhen creating a new course.Drafted
/courses/contents/[courseId]page (97a6bfca)This new page is going to show the contents of a given course so that users can familiarize themselves with the contents associated to a given course.
Added "show contents" item to
CourseActionMenu(57740ba5)This way users can view the course contents associated to a given course.
Redesigned
ContentSectionlayout (510a43fd)This way the
ContentSectionnow mimics the existingQuetionsSection. This means it shows the contents in a list instead of a grid. The reason for this change is that this eliminates sudden changes in the app's layout and also makes it easier to re-use this section in theOverviewSection.Drafted course contents navigation panel (
5248f24a)Created
ContentProvidercomponent (5d0e918f)This component is used to manage state about the content currently shown to the user and to switch between contents.
Introduced
ContentSwitchButtoncomponent (7028762c)This component is used to switch between the contents in the
/contents/[courseId]page.Added
ContentRenderercomponent (9b53b647)This component renders the currently selected course content in a readOnly
RichtTextEditor.Created
ContentsPageBreadcrumbscomponent (3ca2726c)This component is rendered within the course contents page to define custom breadcrumbs for this page.
Added
ContentNavigationButtonscomponents (a7f39785)These new button components allow users to continue to the next content in line or go back to the previous content.
Redesigned course content navigation btn appearances (
f6dfcf77)This way the buttons show a label on top of the content title ("previous", "next") to indicate it as an call-to-action.
Refactors:
Added course contents to
OverviewSection(5c228594)Modularized
ContentSectioninto subcomponents (f5a34b4d)Separated the rendering of the
ContentSectioninto subcomponents to improve readability and maintainability.Re-used
ContentSectioninOverviewSection(26bc7a18)By redesigning the
ContentSection's layout it can be easily re-used in theOverviewSectionfor users to review their changes.Added course content count to
CourseCard(d5ff9496)This way the amount of contents associated to a given course are displayed when discovering new or checks a user contributes to.
Updated course contents page layout (
45fe45c0)This way the navigation buttons are in the same "column" as the
RichTextEditorwhich is visually more appealing.Added
editorContainerClassnamearg toRichTextEditor(da8c70ae)This way the classes of the container wrapping the
RichtTextEditorcan be easily adjusted. This is especially useful to dynamically update the max-height constraint when needed.Adjusted
RichTextEditorheight in course content page (470cb208)Localized course contents page and components (
d6989464)Removed contents navigation on small screens (
62daab8e)This way users on mobile screens can use the navigation buttons below the editor to navigate between contents. The reason for this is that displaying the navigation next to the editor on small screens leads to both elements not being properly visible. Similarly, showing the navigation above or below the editor reduces the overall UX and appearance.
Added empty course page safeguard redirect (
2ca081b7)Previously, if users would navigate to a course page where the respective check has no contents, it would try to display contents that don't exist. This meant that the page looked kind of broken. By adding a safeguard that redirects users back to where they came from or to the courses page when the a course has no contents eliminates this edge-case.
Added safeguard against invalid index in
ContentSBtn(b8815d02)The reason for this change is that when the index retrieval was unsuccessful the button previously would have switched to the invalid index (-1). Now the button is disabled when index retrieval is unsuccessful, thus when switching is not possible.
Added missing translation in
ContentSection(1156afa5)Added a missing translation in the
EmptyCourseContentBodysubcomponent.Restructured internal
CreateContentButtoncomponent (70055012)The reason for this change is that in the wrapping div element that is the direct child of the DialogTrigger that in turn renders a button is considered the trigger. This would mean that the disabled state between button and trigger could be out-of-sync. However, the div wrapper was previously also consumed by the trigger, because it wasn't rendered. By moving the wrapping div outside the trigger, the potential issue is resolved anyhow.