Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance: combine scheduling logic for practice quizzes into publication logic #4374

Open
wants to merge 4 commits into
base: v3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,13 @@ interface PracticeQuizElementProps {
'id' | 'name' | 'status' | 'availableFrom' | 'numOfStacks'
>
courseId: string
courseStartDate: string
}

function PracticeQuizElement({
practiceQuiz,
courseId,
courseStartDate,
}: PracticeQuizElementProps) {
const t = useTranslations()
const router = useRouter()
Expand Down Expand Up @@ -205,7 +207,10 @@ function PracticeQuizElement({
<div className="flex flex-row items-center gap-3 text-sm">
{practiceQuiz.status === PublicationStatus.Draft && (
<>
<PublishPracticeQuizButton practiceQuiz={practiceQuiz} />
<PublishPracticeQuizButton
practiceQuiz={practiceQuiz}
courseStartDate={courseStartDate}
/>
<Dropdown
data={{ cy: `practice-quiz-actions-${practiceQuiz.name}` }}
className={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ interface PracticeQuizTileProps {
'id' | 'name' | 'status' | 'availableFrom' | 'numOfStacks'
>[]
courseId: string
courseStartDate: string
userCatalyst?: boolean
}

function PracticeQuizList({
practiceQuizzes,
courseId,
courseStartDate,
userCatalyst,
}: PracticeQuizTileProps) {
const t = useTranslations()
Expand All @@ -25,9 +27,10 @@ function PracticeQuizList({
<div className="flex flex-col gap-2">
{practiceQuizzes.map((quiz) => (
<PracticeQuizElement
key={quiz.id}
practiceQuiz={quiz}
courseId={courseId}
key={quiz.id}
courseStartDate={courseStartDate}
/>
))}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
import { faUserGroup } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import {
ElementInstanceType,
PracticeQuiz,
} from '@klicker-uzh/graphql/dist/ops'
import { PracticeQuiz } from '@klicker-uzh/graphql/dist/ops'
import { Button } from '@uzh-bf/design-system'
import dayjs from 'dayjs'
import { useTranslations } from 'next-intl'
import { useState } from 'react'
import PublishConfirmationModal from '../modals/PublishConfirmationModal'

interface PublishPracticeQuizButtonProps {
practiceQuiz: Partial<PracticeQuiz>
}
import PracticeQuizPublishingModal from '../modals/PracticeQuizPublishingModal'

function PublishPracticeQuizButton({
practiceQuiz,
}: PublishPracticeQuizButtonProps) {
courseStartDate,
}: {
practiceQuiz: Pick<PracticeQuiz, 'id' | 'name'>
courseStartDate: string
}) {
const t = useTranslations()
const [publishModal, setPublishModal] = useState(false)
const startFuture =
practiceQuiz.availableFrom &&
dayjs(practiceQuiz.availableFrom).isAfter(dayjs())

return (
<>
<Button
basic
className={{ root: 'text-primary-100' }}
className={{ root: 'text-primary-100 flex flex-row gap-3' }}
onClick={() => setPublishModal(true)}
data={{ cy: `publish-practice-quiz-${practiceQuiz.name}` }}
>
Expand All @@ -36,21 +29,12 @@ function PublishPracticeQuizButton({
</Button.Icon>
<Button.Label>{t('manage.course.publishPracticeQuiz')}</Button.Label>
</Button>
<PublishConfirmationModal
elementType={ElementInstanceType.PracticeQuiz}
elementId={practiceQuiz.id!}
title={practiceQuiz.name!}
publicationHint={
startFuture
? t('manage.course.practiceSchedulingHint', {
date: dayjs(practiceQuiz.availableFrom).format(
'DD.MM.YYYY HH:mm'
),
})
: t('manage.course.practicePublishingHint')
}
<PracticeQuizPublishingModal
elementId={practiceQuiz.id}
title={practiceQuiz.name}
open={publishModal}
setOpen={setPublishModal}
courseStartDate={courseStartDate}
/>
</>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { useMutation } from '@apollo/client'
import { faClock } from '@fortawesome/free-regular-svg-icons'
import { faUserGroup } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { PublishPracticeQuizDocument } from '@klicker-uzh/graphql/dist/ops'
import { Button, FormikDateField, H3, Modal } from '@uzh-bf/design-system'
import dayjs from 'dayjs'
import { Form, Formik } from 'formik'
import { useTranslations } from 'next-intl'
import { twMerge } from 'tailwind-merge'
import * as yup from 'yup'

interface PracticeQuizPublishingModalProps {
elementId: string
title: string
courseStartDate: string
open: boolean
setOpen: (value: boolean) => void
}

function PracticeQuizPublishingModal({
elementId,
title,
courseStartDate,
open,
setOpen,
}: PracticeQuizPublishingModalProps) {
const t = useTranslations()
const [publishPracticeQuiz, { loading: practiceQuizPublishing }] =
useMutation(PublishPracticeQuizDocument)

return (
<Modal
title={`${t('shared.generic.practiceQuiz')}: ${title}`}
onClose={(): void => setOpen(false)}
open={open}
className={{ content: '!w-full max-w-4xl text-base' }}
dataCloseButton={{ cy: 'cancel-practice-quiz-publication' }}
>
<div className="flex w-full flex-col gap-4 md:flex-row">
<div className="border-uzh-grey-80 w-full border-b border-solid pb-3 md:w-1/2 md:border-b-0 md:border-r md:pr-5">
<div className="mb-2 flex flex-row items-center gap-2">
<FontAwesomeIcon icon={faUserGroup} />
<H3 className={{ root: 'mb-0' }}>
{t('manage.course.practiceQuizPublishImmediately')}
</H3>
</div>
<div>{t('manage.course.practiceQuizPublishingHint', { title })}</div>
<Button
className={{
root: twMerge(
'bg-primary-100 float-right mt-3 text-white',
practiceQuizPublishing &&
'hover:bg-primary-40 bg-primary-40 cursor-not-allowed'
),
}}
onClick={async () => {
await publishPracticeQuiz({
variables: {
id: elementId,
},
})
setOpen(false)
}}
loading={practiceQuizPublishing}
data={{ cy: 'publish-practice-quiz-immediately' }}
>
{t('manage.course.confirmPublication')}
</Button>
</div>

<div className="w-full md:w-1/2 md:pl-3">
<div className="mb-2 flex flex-row items-center gap-2">
<FontAwesomeIcon icon={faClock} />
<H3 className={{ root: 'mb-0' }}>
{t('manage.course.schedulePublication')}
</H3>
</div>
<div className="mb-2">
{t('manage.course.practiceQuizSchedulingHint', { title })}
</div>
<Formik
validateOnMount
initialValues={{ availableFrom: undefined }}
onSubmit={async (values, { setSubmitting }) => {
setSubmitting(true)
await publishPracticeQuiz({
variables: {
id: elementId,
availableFrom: dayjs(values.availableFrom).utc().format(),
},
})
setOpen(false)
}}
validationSchema={yup.object().shape({
availableFrom: yup
.date()
.required(t('manage.sessionForms.practiceQuizStartReqeuired'))
.test(
'afterCourseStart',
t('manage.sessionForms.practiceQuizStartAfterCourseStart'),
(value) => dayjs(value) > dayjs(courseStartDate)
),
})}
>
{({ isValid }) => {
return (
<Form>
<FormikDateField
required
label={t('shared.generic.availableFrom')}
name="availableFrom"
className={{
root: 'w-full',
field: 'w-full',
error: 'z-20',
}}
data={{ cy: 'practice-quiz-available-from' }}
/>
<Button
type="submit"
className={{
root: twMerge(
'bg-primary-100 float-right mt-3 text-white',
(!isValid || practiceQuizPublishing) &&
'hover:bg-primary-40 bg-primary-40 cursor-not-allowed'
),
}}
loading={practiceQuizPublishing}
disabled={!isValid}
data={{ cy: 'schedule-practice-quiz-publication' }}
>
{t('manage.course.confirmScheduling')}
</Button>
</Form>
)
}}
</Formik>
</div>
</div>
</Modal>
)
}

export default PracticeQuizPublishingModal
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ import {
ElementInstanceType,
PublishGroupActivityDocument,
PublishMicroLearningDocument,
PublishPracticeQuizDocument,
} from '@klicker-uzh/graphql/dist/ops'
import { Button, H3, Modal } from '@uzh-bf/design-system'
import { useTranslations } from 'next-intl'

interface PublishConfirmationModalProps {
elementType:
| ElementInstanceType.Microlearning
| ElementInstanceType.PracticeQuiz
| ElementInstanceType.GroupActivity
elementId: string
title: string
Expand All @@ -29,14 +27,7 @@ function PublishConfirmationModal({
setOpen,
}: PublishConfirmationModalProps) {
const t = useTranslations()
const [publishPracticeQuiz, { loading: pqPublishLoading }] = useMutation(
PublishPracticeQuizDocument,
{
variables: {
id: elementId,
},
}
)

const [publishMicroLearning, { loading: mlPublishLoading }] = useMutation(
PublishMicroLearningDocument,
{
Expand All @@ -59,12 +50,10 @@ function PublishConfirmationModal({
title={t(`manage.course.publishItem${elementType}`)}
onPrimaryAction={
<Button
loading={pqPublishLoading || mlPublishLoading || gaPublishLoading}
loading={mlPublishLoading || gaPublishLoading}
onClick={async () => {
if (elementType === ElementInstanceType.Microlearning) {
await publishMicroLearning()
} else if (elementType === ElementInstanceType.PracticeQuiz) {
await publishPracticeQuiz()
} else if (elementType === ElementInstanceType.GroupActivity) {
await publishGroupActivity()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ function CourseSelectionMonitorPracticeQuiz({

setCourseGamified(course.isGamified)
setTouched({
availableFrom: true,
courseStartDate: true,
courseEndDate: true,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ export interface MicroLearningFormValues extends CommonFormValues {
export interface PracticeQuizFormValues extends CommonFormValues {
stacks: ElementStackFormValues[]
order: ElementOrderType
availableFrom?: string
resetTimeDays: string
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { faClock } from '@fortawesome/free-regular-svg-icons'
import { faCrown, faGears } from '@fortawesome/free-solid-svg-icons'
import { faCrown, faGears, faWarning } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { ElementOrderType } from '@klicker-uzh/graphql/dist/ops'
import useGamifiedCourseGrouping from '@lib/hooks/useGamifiedCourseGrouping'
import {
FormikDateField,
FormikNumberField,
FormikSelectField,
UserNotification,
Expand Down Expand Up @@ -161,20 +160,14 @@ function PracticeQuizSettingsStep({
{t('manage.sessionForms.practiceQuizAvailabilityOptional')}
</div>
</div>
<div className="flex flex-col gap-2">
<div className="flex flex-row items-center gap-4">
<FontAwesomeIcon
icon={faWarning}
className="text-orange-500"
/>
<div className="mt-1 text-sm">
{t('manage.sessionForms.practiceQuizAvailableFrom')}
{t('manage.sessionForms.practiceQuizSchedulingMoved')}
</div>
<FormikDateField
label={t('shared.generic.availableFrom')}
name="availableFrom"
className={{
root: 'w-full',
field: 'w-full',
tooltip: 'z-20',
}}
data={{ cy: 'select-available-from' }}
/>
</div>
</div>
</div>
Expand Down
Loading
Loading