Hiyve Components - v1.0.0
    Preparing search index...

    Module @hiyve/react-courses

    @hiyve/react-courses

    React components for structured courses — a provider builds courses and assigns them to their own clients, who work through them at their own pace.

    • Clients get a course library (MyCourses) and a course player (CoursePlayer): a sidebar outline showing what is done, what is open and why the rest is locked; each lesson's video, audio, documents, text, embeds, quizzes, live session rooms and your app's own blocks; and Complete & continue. Video and audio pick up where the client left off. Progress saves itself.
    • Providers get a course library (CourseLibrary), a builder (CourseBuilder) — an outline of sections and lessons, each lesson's blocks, and unlock rules (prerequisites, drip timers, approval) — and a clients screen (CourseClients) to enroll clients from your roster, approve or send back lessons, and follow progress.

    The host app owns branding (through the MUI theme), users and notifications: the components report what happens through event callbacks. A client's events (a completed lesson, a submission, a reply) are reported once their progress is saved, so a notification never points at work that isn't stored; a submission, reply or question that can't be saved fails rather than looking sent, the player shows where saving stands, and leaving the page with unsaved work gets the browser's warning.

    npm install @hiyve/react-courses @hiyve/courses @hiyve/react @hiyve/core @hiyve/react-media-player \
    @hiyve/react-assignments @hiyve/react-collaboration @hiyve/react-clips @hiyve/react-ui \
    @hiyve/react-semantic-relay @hiyve/rtc-client @hiyve/utilities

    The player, PDF viewer, text editor and recorder are Hiyve's own components, so their packages are needed even when you bring your own CourseStorage.

    import { useState } from 'react';
    import { createMemoryCourseStorage } from '@hiyve/courses';
    import { CourseProvider, CoursePlayer, MyCourses } from '@hiyve/react-courses';

    const storage = createMemoryCourseStorage({ userId: 'alex@example.com' });

    function ClientCourses() {
    const [enrollmentId, setEnrollmentId] = useState<string | null>(null);
    return (
    <CourseProvider storage={storage} currentUser={{ userId: 'alex@example.com', userName: 'Alex' }}>
    {enrollmentId ? (
    <CoursePlayer enrollmentId={enrollmentId} onBack={() => setEnrollmentId(null)} />
    ) : (
    <MyCourses onOpenCourse={setEnrollmentId} />
    )}
    </CourseProvider>
    );
    }

    A provider's side looks the same with CourseLibrary, CourseBuilder and CourseClients:

    import { CourseBuilder, CourseClients, CourseLibrary, CourseProvider } from '@hiyve/react-courses';

    <CourseProvider storage={storage} currentUser={{ userId: 'coach@example.com', userName: 'Jordan' }} onApprovalRequested={notifyProvider}>
    {route.view === 'library' && <CourseLibrary onOpenCourse={openBuilder} onOpenClients={openClients} />}
    {route.view === 'builder' && <CourseBuilder courseId={route.courseId} onBack={openLibrary} onPreview={openPreview} onOpenClients={openClients} />}
    {route.view === 'clients' && <CourseClients courseId={route.courseId} roster={myClients} onBack={openLibrary} onOpenBuilder={openBuilder} />}
    {route.view === 'preview' && <CoursePlayer previewCourseId={route.courseId} onBack={openBuilder} />}
    </CourseProvider>

    Keep courses in Hiyve user files with useHiyveCourseStorage from the /hiyve entry, inside HiyveProvider:

    import { CourseProvider, CourseLibrary } from '@hiyve/react-courses';
    import { useHiyveCourseStorage } from '@hiyve/react-courses/hiyve';

    function Courses({ userId }: { userId: string }) {
    const { storage } = useHiyveCourseStorage({ userId });
    return (
    <CourseProvider storage={storage} currentUser={{ userId }}>
    <CourseLibrary onOpenCourse={openBuilder} />
    </CourseProvider>
    );
    }
    Option Type Description
    userId string The signed-in user
    location string Folder for course documents. Default /Courses
    isProviderTrusted (providerId: string) => boolean Which providers this user accepts courses from, by cleaned user id (cleanUserId from @hiyve/core). Everyone by default. A new function on a later render is used from then on without rebuilding the storage

    Set isProviderTrusted in any app where anyone can share files with the user — by default a course from anyone who can is accepted and listed. It returns { storage, canWrite }. Changing a saved course or saved progress needs a room or no-video room connection: keep one open while course screens are in use. Until it is, canWrite is false and the components hold changes and save them once it opens.

    Wraps every course component.

    Prop Type Description
    storage CourseStorage Where courses are kept (see @hiyve/courses)
    currentUser CourseUser { userId, userName? } of the signed-in user
    blockTypes AnyCourseBlockType[] Block types your app adds (see Custom blocks)
    labels Partial<CourseLabels> Text overrides
    colors Partial<CourseColors> Color overrides
    allowedEmbedOrigins string[] Sites embed blocks may show. Default: YouTube, Vimeo, Loom
    resolveUserName (userId) => string | undefined Display names for providers and clients
    now () => number Clock in epoch ms
    maxUploadBytes number Largest file anyone can upload through the components — lesson media, submissions, recordings, photos, comment attachments and custom blocks. A larger file is refused with a message before anything is sent. No limit by default; set it to what your storage accepts
    onError (error: Error) => void Something failed to load or save (the clients screen also shows its own failures inline)
    onLessonCompleted ({ enrollment, lessonId }) => void A client completed a lesson
    onApprovalRequested ({ enrollment, lessonId }) => void A client completed a lesson that needs approval — notify the provider
    onCourseCompleted ({ enrollment }) => void A client finished a course
    onLessonsUnlocked ({ courseId, enrollmentId, clientId, lessonIds }) => void Lessons opened to a client — notify the client
    onClientEnrolled ({ enrollment }) => void The provider enrolled a client
    onLessonDecided ({ enrollment, lessonId, approved, note? }) => void The provider approved a lesson or sent it back
    onJoinLiveSession ({ block, courseId, lessonId }) => void A client wants to join a live session's room — route them to it. Without it, the Join button is disabled
    onSubmissionCreated ({ enrollment, lessonId, submission }) => void A client submitted work — notify the provider
    onSubmissionReviewed ({ enrollment, submissionId, comment?, status? }) => void The provider commented or gave a status — notify the client
    onReplyAdded ({ enrollment, submissionId, reply }) => void A client replied to a review — notify the provider
    onLessonCommentAdded (event: { enrollment, lessonId, comment }) => void A client asked a question or left a note on a lesson
    onLessonReplyAdded (event: { enrollment, lessonId, reply }) => void The provider replied on a lesson

    The client's courses: where they left off, and every course with its progress, filterable by All / In progress / Completed.

    Prop Type Description
    onOpenCourse (enrollmentId: string) => void The client opens a course
    sx SxProps<Theme> Styles
    Prop Type Description
    enrollmentId string The client's enrollment to play
    previewCourseId string A course of the current provider's to preview as a client sees it; everything is open and nothing is saved
    initialLessonId string Lesson to open first. Default: where the client left off
    onBack () => void Shows a back button
    sx SxProps<Theme> Styles

    Submissions: a submission block lets the client answer by recording video or audio in the browser (with the Hiyve clip recorder), taking a photo, uploading a file or writing — whichever the provider allowed. Every submission is kept; the latest shows the provider's status and comments (each pinned to a moment, page or area — select one to jump to it), and the client can reply or submit again. Recording and photos need a secure page (HTTPS) and the browser's camera and microphone permission; on a page that isn't secure, the client is offered an upload instead (on phones this can still open the camera).

    If one block can't be shown — a player or viewer fails in the browser — the rest of the lesson keeps working: that block shows a short notice with a link to open its file and, for a required video, audio or custom block, Mark as done so the client isn't stuck. The failure is reported to onError.

    How lessons complete: Complete & continue completes the lesson and moves to the next open one. Until every required video and audio block has played (to completeAt, default 90%, or the end), every required quiz is submitted (or passed, with a pass mark), every required submission block has a submission and every required custom block reports completion, the button is disabled and says what is left. Text, files, embeds and live sessions complete when the client continues. A lesson that needs approval then waits for the provider; if sent back, the client sees the note and can submit again.

    The provider's courses with client counts, average progress and lessons waiting for approval; create, publish, archive and delete courses. With more than a few courses, a search box and a sort (recently edited, title, most clients, needs attention) appear above the list. A new course starts as a draft: it can be built and previewed, but no one can be enrolled until it is published. Archive takes a course out of the list and the dashboard, keeps it for the clients who have it, and closes it to new ones; Restore brings it back.

    Prop Type Description
    onOpenCourse (courseId: string) => void Open a course in the builder (also called after creating one)
    onOpenClients (courseId: string) => void Open a course's clients. Shows the Clients button and the review banner
    onCourseCreated (course: Course) => void A course was created (also from a template)
    templates CourseTemplate[] Starter templates your app offers
    sx SxProps<Theme> Styles

    An outline of sections and lessons (drag to reorder, or use each item's menu), the selected lesson's details and blocks, and its unlock rules. Details sets the description and the cover — an image shown with the course in lists, or a color from coverPalette when there's no image (without a choice, a color is picked from the course id). A draft shows a Publish course button in place of Enroll clients. Each lesson's unlock rules include a Due date (days after enrollment, or a date); clients see it in the outline and on the lesson, and an unfinished lesson past it reads Overdue, without locking. Changes save in the background; the header shows whether everything is saved. A failed save is tried again after a pause, and if the course or a lesson was saved from somewhere else (another tab) while open, the edits made here are saved over it and onError is told, so your app can say so. A release date is the start of that day in the provider's time zone. A quiz can be auto-scored (answers travel with the lesson) or reviewed by the provider (answers never reach clients); an auto-scored quiz can limit how many tries a client gets, after which it waits for the provider.

    Prop Type Description
    courseId string The course to edit
    onBack () => void Shows a back button
    onPreview (courseId: string) => void Shows a Preview as client button
    onOpenClients (courseId: string) => void Shows an Enroll clients button
    sx SxProps<Theme> Styles

    Enroll clients from your roster, review what is waiting — completed lessons that need approval (with the client's quiz answers and your private score) and new submissions — and follow each client's progress: where they are, when they were last active, pause or resume, unlock a lesson by hand, or remove them.

    Prop Type Description
    courseId string The course
    roster ClientRef[] Your clients, { userId, userName? }, to enroll from
    onBack () => void Shows the Courses breadcrumb as a link
    onOpenBuilder (courseId: string) => void Shows an Edit course button
    sx SxProps<Theme> Styles

    Every lesson ends with Questions & notes: a client can ask a question or leave a note (with a file attached if they like), and the provider answers from the review queue, where a lesson with an unanswered question appears alongside completions and submissions; the reply shows up under the lesson for the client. A lesson's review shows the client's latest quiz answers and, when they tried more than once, how many tries and their best score. Reviewing a submission: play the recording, look at the photo or page through the document, and add comments — pinned to the current moment, page or a dragged-out area of a photo, with an optional recorded or attached reply. Give a status from your own list (Edit statuses sets labels, tone, and whether a status approves the lesson or sends it back); the note is sent with it.

    CourseLibrary and CourseClients open lessons that come due (drip timers, approvals) while they are on screen. To keep lessons opening from elsewhere in your provider app, call useAccessSync(courseIds).

    The provider's practice at a glance: active clients, what's waiting for review, who is falling behind and how many have finished; a review inbox across every course (the same review cards as CourseClients); a falling behind list (lessons overdue, never started, or no activity for behindAfterDays); and each course's clients, average progress, finishes and pending reviews.

    Prop Type Description
    onOpenClients (courseId: string) => void Open a course's clients
    onOpenCourse (courseId: string) => void Open a course in the builder. Shows an Edit course button
    behindAfterDays number Days without activity before a client counts as behind. Default 7
    sx SxProps<Theme> Styles
    • CourseLibrary lists the provider's own templates and your app's starters (templates prop, CourseTemplate[] from @hiyve/courses) with Use template; each course's menu has Duplicate and Save as template. Templates are edited in the builder like any course but can't have clients.
    • CourseClients lists everyone in the course; pausing a client takes their open lessons back until they are resumed; with more than a few, a search box and a sort (name, progress, last active, status) appear above the list. A client's menu has Mark a lesson complete (it counts as done and approved, and opens what follows) and Reset progress (asks first; what they did stops counting and their app clears it when they next open the course), as well as Customize for {name}: hide course lessons from them, and add, edit or remove lessons just for them (the same block editors as the builder). The client sees their own lessons marked Just for you; no one else sees them.

    Add your app's own activities to courses. Register them on CourseProvider and they appear in the builder's block palette and in the player.

    import type { CourseBlockType } from '@hiyve/react-courses';

    const checklist: CourseBlockType<{ items: string[] }> = {
    kind: 'acme.checklist',
    version: 1,
    label: 'Checklist',
    createDefault: () => ({ items: ['First step'] }),
    summarize: (data) => `${data.items.length} steps`,
    Editor: ({ data, onChange }) => <ChecklistEditor items={data.items} onChange={(items) => onChange({ items })} />,
    View: ({ data, readOnly, onComplete }) => <Checklist items={data.items} disabled={readOnly} onAllChecked={() => onComplete()} />,
    };

    <CourseProvider storage={storage} currentUser={user} blockTypes={[checklist]}>…</CourseProvider>
    Field Description
    kind, version Unique namespaced kind, and the version of data's shape
    label, icon Shown in the block palette
    createDefault() Data for a new block
    migrate(data, fromVersion) Turn older data into the current shape
    fileRefs(data) Stored files the block uses, shared with clients along with the lesson
    summarize(data) One line for the builder's block list
    Editor CustomBlockEditorProps: data, onChange, uploadMedia (rejects with a message ready to show when a file is over maxUploadBytes), resolveMediaUrl
    View CustomBlockViewProps: block, data, progress, readOnly, onProgress, onComplete(result?), resolveMediaUrl

    A block whose kind isn't registered shows a short notice, keeps its data, and completes when the client continues.

    Build your own screens on the same logic.

    Hook Returns
    useMyCourses() The client's courses with progress, next lesson and next drip (MyCourseItem[])
    useCoursePlayer({ enrollmentId | previewCourseId, initialLessonId? }) Course, enrollment, progress, lesson states, current lesson and content, what is still pending, and actions (UseCoursePlayerResult)
    useCourses() The provider's courses and templates with client counts, average progress and pending approvals, plus createCourse, deleteCourse, duplicate and createFromTemplate (CourseListItem[])
    useCourseBuilder(courseId) The course being edited, the selected lesson's blocks, save state, and every edit action (UseCourseBuilderResult)
    useCourseClients(courseId) Client rows, the review queue, the provider's statuses, and enroll / decide / review / pause / remove / unlock / loadReview / saveStatuses (UseCourseClientsResult)
    useProviderDashboard({ behindAfterDays? }) Totals, the clients falling behind and each course's numbers (UseProviderDashboardResult, BehindClient, DashboardCourse)
    useAccessSync(courseIds) Opens due lessons while mounted
    useCourseContext() The surrounding CourseProvider's values (CourseContextValue)

    BlockView shows a single block as the player does. SubmissionViewer shows one submission with its comments and a comment box (SubmissionViewerProps), for building your own review screens. SaveState is 'saved' | 'unsaved' | 'saving' | 'waiting' | 'error'. Other result types: PendingBlock, ClientRow, ClientStatus, ReviewItem, ReviewAnswers, ReviewDetails, NewBlockType, UseMyCoursesResult, UseCoursesResult, UseCoursePlayerOptions, BlockViewProps.

    • Narrow screens. Below about 860px of width (measured on the component, so a narrow panel counts too) the builder shows the outline, then the chosen lesson with a way back to it; the player shows the lesson first, with the course outline a tap away; CourseClients lists clients as cards. Wide tables scroll inside their own box rather than widening the page.
    • Keyboard. Everything works without a mouse: sections and lessons move with their menus (including to another section) as well as by dragging; on a photo, Enter marks an area to comment on, arrow keys move it and Shift + arrow keys resize it.
    • Screen readers. The outline reads out each lesson's status; moving to another lesson moves focus to its title; the text editor is named; saving status is announced.
    • Text: every string is in CourseLabels; pass overrides as labels. defaultCourseLabels and mergeCourseLabels are exported. Labels that take values are functions (e.g. lessonsProgress(done, total), formatDate(iso), relativeTime(iso, now)).
    • Colors: buttons, links and progress use your MUI theme. Status colors (completed, awaiting, awaitingBackground, locked, activeBackground; the defaults are darkened from your theme where needed so text in them is readable) and course cover colors (coverPalette, offered in the builder and used when a course has no cover image) are in CourseColors; buildDefaultCourseColors(theme) and mergeCourseColors(colors, theme) are exported.
    • Timing: PROGRESS_SAVE_IDLE_MS (5 s), PROGRESS_SAVE_MAX_WAIT_MS (30 s), BUILDER_SAVE_IDLE_MS (1.2 s) and ACCESS_SYNC_INTERVAL_MS (5 min) say how often things save and lessons are checked.

    CourseProviderProps, ProviderDashboardProps, CourseUser, CourseEvents, CourseLabels, CourseColors, CourseBlockType, AnyCourseBlockType, CustomBlockEditorProps, CustomBlockViewProps, MyCoursesProps, CoursePlayerProps, CourseLibraryProps, CourseBuilderProps, CourseClientsProps.

    • React 18 or 19, MUI v9 (@mui/material, @mui/icons-material), Emotion.
    • @hiyve/courses, @hiyve/react, @hiyve/core, @hiyve/react-media-player, @hiyve/react-assignments, @hiyve/react-collaboration, @hiyve/react-clips, @hiyve/react-ui, @hiyve/react-semantic-relay, @hiyve/rtc-client, @hiyve/utilities.
    • Recording and photos: a secure context (HTTPS or localhost) and camera/microphone permission.
    • For the /hiyve entry: HiyveProvider above the components.

    Modules

    @hiyve/react-courses
    @hiyve/react-courses/hiyve