Skip to content

Commit

Permalink
feat: add optional start date and cronjob for automated practice quiz…
Browse files Browse the repository at this point in the history
… publication (#4109)
  • Loading branch information
sjschlapbach committed May 6, 2024
1 parent a03b927 commit efd7d16
Show file tree
Hide file tree
Showing 31 changed files with 873 additions and 171 deletions.
4 changes: 1 addition & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
"debug.internalConsoleOptions": "neverOpen",
"azureFunctions.preDeployTask": "npm prune (serverless)",
"azureFunctions.projectSubpath": "apps/func-migration-v2-export",
"githubPullRequests.ignoredPullRequestBranches": [
"v3"
],
"githubPullRequests.ignoredPullRequestBranches": ["v3"],
"editor.formatOnSave": true,
"sonarlint.connectedMode.project": {
"connectionId": "uzh-bf",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { useMutation } from '@apollo/client'
import { WizardMode } from '@components/sessions/creation/SessionCreation'
import { faTrashCan } from '@fortawesome/free-regular-svg-icons'
import { faClock, faTrashCan } from '@fortawesome/free-regular-svg-icons'
import {
faCopy,
faHandPointer,
faHourglassStart,
faLock,
faPencil,
faUserGroup,
} from '@fortawesome/free-solid-svg-icons'
Expand All @@ -13,9 +15,11 @@ import {
GetSingleCourseDocument,
PracticeQuiz,
PublicationStatus,
UnpublishPracticeQuizDocument,
} from '@klicker-uzh/graphql/dist/ops'
import { Ellipsis } from '@klicker-uzh/markdown'
import { Dropdown, Toast } from '@uzh-bf/design-system'
import dayjs from 'dayjs'
import { useTranslations } from 'next-intl'
import { useRouter } from 'next/router'
import { useState } from 'react'
Expand All @@ -41,6 +45,15 @@ function PracticeQuizElement({

const [deletePracticeQuiz] = useMutation(DeletePracticeQuizDocument, {
variables: { id: practiceQuiz.id! },
// TODO: add optimistic response and update cache
refetchQueries: [
{ query: GetSingleCourseDocument, variables: { courseId: courseId } },
],
})

const [unpublishPracticeQuiz] = useMutation(UnpublishPracticeQuizDocument, {
variables: { id: practiceQuiz.id! },
// TODO: add optimistic response and update cache
refetchQueries: [
{ query: GetSingleCourseDocument, variables: { courseId: courseId } },
],
Expand Down Expand Up @@ -142,13 +155,55 @@ function PracticeQuizElement({
</>
)}

{practiceQuiz.status === PublicationStatus.Scheduled && (
<>
<PracticeQuizAccessLink
practiceQuiz={practiceQuiz}
href={href}
/>
<Dropdown
data={{ cy: `practice-quiz-actions-${practiceQuiz.name}` }}
className={{
item: 'p-1 hover:bg-gray-200',
viewport: 'bg-white',
}}
trigger={t('manage.course.otherActions')}
items={[
{
label: (
<div className="flex flex-row text-red-600 items-center gap-1 cursor-pointer">
<FontAwesomeIcon
icon={faLock}
className="w-[1.1rem]"
/>

<div>{t('manage.course.unpublishPracticeQuiz')}</div>
</div>
),
onClick: async () => await unpublishPracticeQuiz(),
data: {
cy: `unpublish-practiceQuiz-${practiceQuiz.name}`,
},
},
]}
triggerIcon={faHandPointer}
/>
<StatusTag
color="bg-green-300"
status={t('shared.generic.scheduled')}
icon={faClock}
/>
</>
)}

{practiceQuiz.status === PublicationStatus.Published && (
<>
<PracticeQuizAccessLink
practiceQuiz={practiceQuiz}
href={href}
/>
<Dropdown
data={{ cy: `practice-quiz-actions-${practiceQuiz.name}` }}
className={{
item: 'p-1 hover:bg-gray-200',
viewport: 'bg-white',
Expand Down Expand Up @@ -184,6 +239,20 @@ function PracticeQuizElement({
number: practiceQuiz.numOfStacks || '0',
})}
</div>
{practiceQuiz.availableFrom && (
<div className="flex flex-row gap-4 text-sm">
<div className="flex flex-row items-center gap-2">
<FontAwesomeIcon icon={faHourglassStart} />
<div>
{t('manage.course.startAt', {
time: dayjs(practiceQuiz.availableFrom)
.local()
.format('DD.MM.YYYY, HH:mm'),
})}
</div>
</div>
</div>
)}
</div>
<Toast
openExternal={copyToast}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ function PublishMicroLearningButton({
elementType={ElementInstanceType.Microlearning}
elementId={microLearning.id}
title={microLearning.name}
publicationHint={t('manage.course.microPublishingHint')}
open={publishModal}
setOpen={setPublishModal}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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'
Expand All @@ -18,6 +19,9 @@ function PublishPracticeQuizButton({
}: PublishPracticeQuizButtonProps) {
const t = useTranslations()
const [publishModal, setPublishModal] = useState(false)
const startFuture =
practiceQuiz.availableFrom &&
dayjs(practiceQuiz.availableFrom).isAfter(dayjs())

return (
<>
Expand All @@ -36,6 +40,15 @@ function PublishPracticeQuizButton({
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')
}
open={publishModal}
setOpen={setPublishModal}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
PublishMicroLearningDocument,
PublishPracticeQuizDocument,
} from '@klicker-uzh/graphql/dist/ops'
import { Button, H2, H3, Modal } from '@uzh-bf/design-system'
import { Button, H3, Modal } from '@uzh-bf/design-system'
import { useTranslations } from 'next-intl'

interface PublishConfirmationModalProps {
Expand All @@ -13,6 +13,7 @@ interface PublishConfirmationModalProps {
| ElementInstanceType.PracticeQuiz
elementId: string
title: string
publicationHint: string
open: boolean
setOpen: (value: boolean) => void
}
Expand All @@ -21,6 +22,7 @@ function PublishConfirmationModal({
elementType,
elementId,
title,
publicationHint,
open,
setOpen,
}: PublishConfirmationModalProps) {
Expand All @@ -38,6 +40,7 @@ function PublishConfirmationModal({

return (
<Modal
title={t(`manage.course.publishItem${elementType}`)}
onPrimaryAction={
<Button
onClick={async () => {
Expand All @@ -49,7 +52,7 @@ function PublishConfirmationModal({
setOpen(false)
}}
className={{
root: 'font-bold text-white bg-primary-80',
root: 'font-bold text-white bg-primary-80 text-base',
}}
data={{ cy: 'confirm-publish-action' }}
>
Expand All @@ -60,26 +63,25 @@ function PublishConfirmationModal({
<Button
onClick={(): void => setOpen(false)}
data={{ cy: 'cancel-publish-action' }}
className={{ root: 'text-base' }}
>
{t('shared.generic.cancel')}
</Button>
}
onClose={(): void => setOpen(false)}
open={open}
hideCloseButton={true}
className={{ content: 'w-[40rem] h-max self-center pt-0' }}
className={{
content: 'w-[40rem] h-max self-center pt-0',
title: 'text-xl',
}}
>
<div>
<H2>{t(`manage.course.publishItem${elementType}`)}</H2>
<div>{t('manage.course.confirmPublishing')}</div>
<div className="text-base">{t('manage.course.confirmPublishing')}</div>
<div className="p-2 mt-1 border border-solid rounded border-uzh-grey-40">
<H3>{title}</H3>
</div>
<div className="mt-6 mb-2 text-sm italic">
{t('manage.course.publishingHint')}
{elementType === ElementInstanceType.Microlearning &&
t('manage.course.microPublishingHint')}
</div>
<div className="mt-3 mb-2 text-sm italic">{publicationHint}</div>
</div>
</Modal>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
FormikTextField,
} from '@uzh-bf/design-system'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import { ErrorMessage } from 'formik'
import { useTranslations } from 'next-intl'
import { useRouter } from 'next/router'
Expand Down Expand Up @@ -50,7 +49,6 @@ function MicroLearningWizard({

const [createMicroLearning] = useMutation(CreateMicroLearningDocument)
const [editMicroLearning] = useMutation(EditMicroLearningDocument)
dayjs.extend(utc)

const [selectedCourseId, setSelectedCourseId] = useState('')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export interface PracticeQuizFormValues extends CommonFormValues {
hasSampleSolution: boolean
}[]
order: any
availableFrom?: string
resetTimeDays: string
}

Expand Down

0 comments on commit efd7d16

Please sign in to comment.