diff --git a/frontend/src/utils/inputs/inputs.scss b/frontend/src/utils/inputs/inputs.scss index 2d8fb8db5..6f12d81b8 100644 --- a/frontend/src/utils/inputs/inputs.scss +++ b/frontend/src/utils/inputs/inputs.scss @@ -64,12 +64,6 @@ font-weight: 600; } -.form-group--optional { - .form-group-label__caption::after { - content: ''; - } -} - .form-group--submit .form-group__input { grid-column: 2; } diff --git a/messages/uniworx/de.msg b/messages/uniworx/de.msg index b83eb1cf6..5d11a24d7 100644 --- a/messages/uniworx/de.msg +++ b/messages/uniworx/de.msg @@ -1107,6 +1107,8 @@ MenuSchoolList: Institute MenuSchoolNew: Neues Institut anlegen MenuCourseNewsNew: Neue Kursnachricht MenuCourseNewsEdit: Kursnachricht bearbeiten +MenuCourseEventNew: Neuer Kurstermin +MenuCourseEventEdit: Kurstermin bearbeiten AuthPredsInfo: Um eigene Veranstaltungen aus Sicht der Teilnehmer anzusehen, können Veranstalter und Korrektoren hier die Prüfung ihrer erweiterten Berechtigungen temporär deaktivieren. Abgewählte Prädikate schlagen immer fehl. Abgewählte Prädikate werden also nicht geprüft um Zugriffe zu gewähren, welche andernfalls nicht erlaubt wären. Diese Einstellungen gelten nur temporär bis Ihre Sitzung abgelaufen ist, d.h. bis ihr Browser-Cookie abgelaufen ist. Durch Abwahl von Prädikaten kann man sich höchstens temporär aussperren. AuthPredsActive: Aktive Authorisierungsprädikate @@ -1245,6 +1247,7 @@ WeekDay: Wochentag Day: Tag OccurrenceStart: Beginn OccurrenceEnd: Ende +OccurrenceNever: Nie ScheduleExists: Dieser Plan existiert bereits ScheduleExceptions: Termin-Ausnahmen @@ -1917,3 +1920,17 @@ FavouriteVisited: Kürzlich besucht FavouriteParticipant: Ihre Kurse FavouriteManual: Favoriten FavouriteCurrent: Aktueller Kurs + +CourseEvents: Termine +CourseEventType: Art +CourseEventTypePlaceholder: Vorlesung, Zentralübung, ... +CourseEventTime: Zeit +CourseEventRoom: Regulärer Raum +CourseEventActions: Aktionen +CourseEventsActionEdit: Bearbeiten +CourseEventsActionDelete: Löschen +CourseEventsActionCreate: Neuer Termin +CourseEventCreated: Kurstermin erfolgreich angelegt +CourseEventEdited: Kurstermin erfolgreich editiert +CourseEventDeleteQuestion: Wollen Sie den unten aufgeführten Termin wirklich löschen? +CourseEventDeleted: Kurstermin erfolgreich gelöscht diff --git a/models/courses.model b/models/courses.model index 680d0d7dd..bea256759 100644 --- a/models/courses.model +++ b/models/courses.model @@ -25,6 +25,12 @@ Course -- Information about a single course; contained info is always visible TermSchoolCourseShort term school shorthand -- shorthand must be unique within school and semester TermSchoolCourseName term school name -- name must be unique within school and semester deriving Generic +CourseEvent + type (CI Text) + course CourseId + room Text + time Occurrences + lastChanged UTCTime default=now() CourseAppInstructionFile course CourseId diff --git a/routes b/routes index 944e20502..00a90b6e8 100644 --- a/routes +++ b/routes @@ -187,6 +187,11 @@ /delete CNDeleteR GET POST !/download CNArchiveR GET !timeANDparticipant !/download/*FilePath CNFileR GET !timeANDparticipant + !/events/add CEventsNewR GET POST + /events/#CryptoUUIDCourseEvent CourseEventR: + /edit CEvEditR GET POST + /delete CEvDeleteR GET POST + /subs CorrectionsR GET POST !corrector !lecturer /subs/upload CorrectionsUploadR GET POST !corrector !lecturer diff --git a/src/CryptoID.hs b/src/CryptoID.hs index 35ec7af50..803ccd64d 100644 --- a/src/CryptoID.hs +++ b/src/CryptoID.hs @@ -49,6 +49,7 @@ decCryptoIDs [ ''SubmissionId , ''CourseApplicationId , ''CourseId , ''CourseNewsId + , ''CourseEventId ] -- CryptoIDNamespace (CI FilePath) SubmissionId ~ "Submission" diff --git a/src/Foundation.hs b/src/Foundation.hs index a878daacb..3cf3ae881 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -157,6 +157,7 @@ deriving instance Generic AllocationR deriving instance Generic SchoolR deriving instance Generic ExamOfficeR deriving instance Generic CourseNewsR +deriving instance Generic CourseEventR deriving instance Generic (Route UniWorX) data RouteChildren @@ -204,6 +205,10 @@ pattern CApplicationR tid ssh csh appId ptn pattern CNewsR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDCourseNews -> CourseNewsR -> Route UniWorX pattern CNewsR tid ssh csh nId ptn = CourseR tid ssh csh (CourseNewsR nId ptn) + +pattern CEventR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDCourseEvent -> CourseEventR -> Route UniWorX +pattern CEventR tid ssh csh nId ptn + = CourseR tid ssh csh (CourseEventR nId ptn) pluralDE :: (Eq a, Num a) diff --git a/src/Handler/Course.hs b/src/Handler/Course.hs index 454e11c85..9ba6ea9a1 100644 --- a/src/Handler/Course.hs +++ b/src/Handler/Course.hs @@ -18,6 +18,7 @@ import Handler.Course.Users as Handler.Course import Handler.Course.Application as Handler.Course import Handler.ExamOffice.Course as Handler.Course import Handler.Course.News as Handler.Course +import Handler.Course.Events as Handler.Course getCHiWisR :: TermId -> SchoolId -> CourseShorthand -> Handler Html diff --git a/src/Handler/Course/Events.hs b/src/Handler/Course/Events.hs new file mode 100644 index 000000000..75e0f4fd6 --- /dev/null +++ b/src/Handler/Course/Events.hs @@ -0,0 +1,7 @@ +module Handler.Course.Events + ( module Handler.Course.Events + ) where + +import Handler.Course.Events.New as Handler.Course.Events +import Handler.Course.Events.Edit as Handler.Course.Events +import Handler.Course.Events.Delete as Handler.Course.Events diff --git a/src/Handler/Course/Events/Delete.hs b/src/Handler/Course/Events/Delete.hs new file mode 100644 index 000000000..4cad682bf --- /dev/null +++ b/src/Handler/Course/Events/Delete.hs @@ -0,0 +1,50 @@ +module Handler.Course.Events.Delete + ( getCEvDeleteR, postCEvDeleteR + ) where + +import Import + +import Handler.Utils.Occurrences +import Handler.Utils.Delete + +import qualified Data.Set as Set + + +getCEvDeleteR, postCEvDeleteR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDCourseEvent -> Handler Html +getCEvDeleteR = postCEvDeleteR +postCEvDeleteR tid ssh csh cID = do + nId <- decrypt cID + + let + drRecords :: Set (Key CourseEvent) + drRecords = Set.singleton nId + + drGetInfo = return + drUnjoin = id + + drRenderRecord :: Entity CourseEvent -> DB Widget + drRenderRecord (Entity _ CourseEvent{..}) = return + [whamlet| + $newline never + #{courseEventType} + , #{courseEventRoom} + : + ^{occurrencesWidget courseEventTime} + |] + + drRecordConfirmString :: Entity CourseEvent -> DB Text + drRecordConfirmString _ = return "" + + drCaption, drSuccessMessage :: SomeMessage UniWorX + drCaption = SomeMessage MsgCourseEventDeleteQuestion + drSuccessMessage = SomeMessage MsgCourseEventDeleted + + drAbort, drSuccess :: SomeRoute UniWorX + drAbort = SomeRoute $ CourseR tid ssh csh CShowR :#: [st|event-#{toPathPiece cID}|] + drSuccess = SomeRoute $ CourseR tid ssh csh CShowR + + drDelete :: forall a. CourseEventId -> DB a -> DB a + drDelete _ = id + + deleteR DeleteRoute{..} + diff --git a/src/Handler/Course/Events/Edit.hs b/src/Handler/Course/Events/Edit.hs new file mode 100644 index 000000000..aed9e2e41 --- /dev/null +++ b/src/Handler/Course/Events/Edit.hs @@ -0,0 +1,39 @@ +module Handler.Course.Events.Edit + ( getCEvEditR, postCEvEditR + ) where + +import Import +import Handler.Utils + +import Handler.Course.Events.Form + + +getCEvEditR, postCEvEditR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDCourseEvent -> Handler Html +getCEvEditR = postCEvEditR +postCEvEditR tid ssh csh cID = do + eId <- decrypt cID + courseEvent@CourseEvent{..} <- runDB $ get404 eId + + ((eventRes, eventWgt'), eventEnctype) <- runFormPost . courseEventForm . Just $ courseEventToForm courseEvent + + formResult eventRes $ \CourseEventForm{..} -> do + now <- liftIO getCurrentTime + runDB $ do + replace eId CourseEvent + { courseEventCourse + , courseEventType = cefType + , courseEventRoom = cefRoom + , courseEventTime = cefTime + , courseEventLastChanged = now + } + addMessageI Success MsgCourseEventEdited + redirect $ CourseR tid ssh csh CShowR :#: [st|event-#{toPathPiece cID}|] + + siteLayoutMsg MsgMenuCourseEventEdit $ do + setTitleI MsgMenuCourseEventEdit + + wrapForm eventWgt' def + { formAction = Just . SomeRoute $ CEventR tid ssh csh cID CEvEditR + , formEncoding = eventEnctype + } + diff --git a/src/Handler/Course/Events/Form.hs b/src/Handler/Course/Events/Form.hs new file mode 100644 index 000000000..672e30a76 --- /dev/null +++ b/src/Handler/Course/Events/Form.hs @@ -0,0 +1,48 @@ +module Handler.Course.Events.Form + ( CourseEventForm(..) + , courseEventForm + , courseEventToForm + ) where + +import Import +import Handler.Utils +import Handler.Utils.Form.Occurrences + +import qualified Database.Esqueleto as E + + +data CourseEventForm = CourseEventForm + { cefType :: CI Text + , cefRoom :: Text + , cefTime :: Occurrences + } + +courseEventForm :: Maybe CourseEventForm -> Form CourseEventForm +courseEventForm template = identifyForm FIDCourseEvent . renderWForm FormStandard $ do + MsgRenderer mr <- getMsgRenderer + + muid <- maybeAuthId + existingEvents <- liftHandler . runDB $ fromMaybe [] <$> for muid + (\uid -> E.select . E.from $ \(lecturer `E.InnerJoin` event) -> do + E.on $ lecturer E.^. LecturerCourse E.==. event E.^. CourseEventCourse + E.&&. lecturer E.^. LecturerUser E.==. E.val uid + return event + ) + let courseEventTypes = optionsPairs $ [ (courseEventType, courseEventType) | Entity _ CourseEvent{..} <- existingEvents ] + courseEventRooms = optionsPairs $ [ (courseEventRoom, courseEventRoom) | Entity _ CourseEvent{..} <- existingEvents ] + + cefType' <- wreq (textField & cfStrip & cfCI & addDatalist courseEventTypes) (fslI MsgCourseEventType & addPlaceholder (mr MsgCourseEventTypePlaceholder)) (cefType <$> template) + cefRoom' <- wreq (textField & cfStrip & addDatalist courseEventRooms) (fslI MsgCourseEventRoom) (cefRoom <$> template) + cefTime' <- aFormToWForm $ occurrencesAForm ("time" :: Text) (cefTime <$> template) + + return $ CourseEventForm + <$> cefType' + <*> cefRoom' + <*> cefTime' + +courseEventToForm :: CourseEvent -> CourseEventForm +courseEventToForm CourseEvent{..} = CourseEventForm + { cefType = courseEventType + , cefRoom = courseEventRoom + , cefTime = courseEventTime + } diff --git a/src/Handler/Course/Events/New.hs b/src/Handler/Course/Events/New.hs new file mode 100644 index 000000000..248856cad --- /dev/null +++ b/src/Handler/Course/Events/New.hs @@ -0,0 +1,38 @@ +module Handler.Course.Events.New + ( getCEventsNewR, postCEventsNewR + ) where + +import Import +import Handler.Utils + +import Handler.Course.Events.Form + +getCEventsNewR, postCEventsNewR :: TermId -> SchoolId -> CourseShorthand -> Handler Html +getCEventsNewR = postCEventsNewR +postCEventsNewR tid ssh csh = do + cid <- runDB . getKeyBy404 $ TermSchoolCourseShort tid ssh csh + + ((eventRes, eventWgt'), eventEnctype) <- runFormPost $ courseEventForm Nothing + + formResult eventRes $ \CourseEventForm{..} -> do + now <- liftIO getCurrentTime + cID <- runDB $ do + eId <- insert CourseEvent + { courseEventCourse = cid + , courseEventType = cefType + , courseEventRoom = cefRoom + , courseEventTime = cefTime + , courseEventLastChanged = now + } + encrypt eId :: DB CryptoUUIDCourseEvent + addMessageI Success MsgCourseEventCreated + redirect $ CourseR tid ssh csh CShowR :#: [st|event-#{toPathPiece cID}|] + + siteLayoutMsg MsgMenuCourseEventNew $ do + setTitleI MsgMenuCourseEventNew + + wrapForm eventWgt' def + { formAction = Just . SomeRoute $ CourseR tid ssh csh CEventsNewR + , formEncoding = eventEnctype + } + diff --git a/src/Handler/Course/Show.hs b/src/Handler/Course/Show.hs index 4e474c6ac..a95223129 100644 --- a/src/Handler/Course/Show.hs +++ b/src/Handler/Course/Show.hs @@ -26,7 +26,7 @@ import qualified Data.Conduit.List as C getCShowR :: TermId -> SchoolId -> CourseShorthand -> Handler Html getCShowR tid ssh csh = do mbAid <- maybeAuthId - (cid,course,schoolName,participants,registration,lecturers,assistants,correctors,tutors,mAllocation,hasApplicationTemplate,mApplication,news) <- runDB . maybeT notFound $ do + (cid,course,schoolName,participants,registration,lecturers,assistants,correctors,tutors,mAllocation,hasApplicationTemplate,mApplication,news,events) <- runDB . maybeT notFound $ do [(E.Entity cid course, E.Value schoolName, E.Value participants, fmap entityVal -> registration)] <- lift . E.select . E.from $ \((school `E.InnerJoin` course) `E.LeftOuterJoin` participant) -> do @@ -88,7 +88,11 @@ getCShowR tid ssh csh = do mayEdit <- hasWriteAccessTo $ CNewsR tid ssh csh cID CNEditR mayDelete <- hasWriteAccessTo $ CNewsR tid ssh csh cID CNDeleteR return (cID, n, visible, files, lastEditText, mayEdit, mayDelete) - return (cid,course,schoolName,participants,registration,lecturers,assistants,correctors,tutors,mAllocation,hasApplicationTemplate,mApplication,news) + + events' <- fmap (sortOn $ courseEventTime . entityVal) . lift $ selectList [ CourseEventCourse ==. cid ] [] + events <- mapM (\(Entity evId ev) -> (, ev) <$> encrypt evId) events' + + return (cid,course,schoolName,participants,registration,lecturers,assistants,correctors,tutors,mAllocation,hasApplicationTemplate,mApplication,news,events) let mDereg' = maybe id min (allocationOverrideDeregister =<< mAllocation) <$> courseDeregisterUntil course mDereg <- traverse (formatTime SelFormatDateTime) mDereg' @@ -259,6 +263,7 @@ getCShowR tid ssh csh = do , all (notElem pathSeparator . view _2) fs ] mayCreateNews <- hasWriteAccessTo $ CourseR tid ssh csh CNewsNewR + mayCreateEvents <- hasWriteAccessTo $ CourseR tid ssh csh CEventsNewR siteLayout (toWgt $ courseName course) $ do setTitleI $ prependCourseTitle tid ssh csh (""::Text) diff --git a/src/Handler/Utils.hs b/src/Handler/Utils.hs index 40eaadcba..ef9c804ee 100644 --- a/src/Handler/Utils.hs +++ b/src/Handler/Utils.hs @@ -23,6 +23,7 @@ import Handler.Utils.ContentDisposition as Handler.Utils import Handler.Utils.I18n as Handler.Utils import Handler.Utils.Widgets as Handler.Utils import Handler.Utils.Database as Handler.Utils +import Handler.Utils.Occurrences as Handler.Utils import System.FilePath.Posix (takeFileName) diff --git a/src/Handler/Utils/Form/Occurrences.hs b/src/Handler/Utils/Form/Occurrences.hs index 1486ff192..525c75f8c 100644 --- a/src/Handler/Utils/Form/Occurrences.hs +++ b/src/Handler/Utils/Form/Occurrences.hs @@ -53,7 +53,7 @@ occurrencesAForm (toPathPiece -> miIdent') mPrev = wFormToAForm $ do newSched <- multiActionW (Map.fromList [ ( ScheduleKindWeekly , ScheduleWeekly - <$> apreq (selectField optionsFinite) (fslI MsgWeekDay & addName (nudge "occur-week-day")) Nothing + <$> apreq (selectField' Nothing optionsFinite) (fslI MsgWeekDay & addName (nudge "occur-week-day")) Nothing <*> apreq timeFieldTypeTime (fslI MsgOccurrenceStart & addName (nudge "occur-start")) Nothing <*> apreq timeFieldTypeTime (fslI MsgOccurrenceEnd & addName (nudge "occur-end")) Nothing ) diff --git a/src/Handler/Utils/Occurrences.hs b/src/Handler/Utils/Occurrences.hs new file mode 100644 index 000000000..af1c617c1 --- /dev/null +++ b/src/Handler/Utils/Occurrences.hs @@ -0,0 +1,29 @@ +module Handler.Utils.Occurrences + ( occurrencesWidget + ) where + +import Import + +import qualified Data.Set as Set + +import Utils.Occurrences + +import Handler.Utils.DateTime + + +occurrencesWidget :: Occurrences -> Widget +occurrencesWidget (normalizeOccurrences -> Occurrences{..}) = do + let occurrencesScheduled' = flip map (Set.toList occurrencesScheduled) $ \case + ScheduleWeekly{..} -> do + scheduleStart' <- formatTime SelFormatTime scheduleStart + scheduleEnd' <- formatTime SelFormatTime scheduleEnd + $(widgetFile "widgets/occurrence/cell/weekly") + occurrencesExceptions' = flip map (Set.toList occurrencesExceptions) $ \case + ExceptOccur{..} -> do + exceptStart' <- formatTime SelFormatDateTime (LocalTime exceptDay exceptStart) + exceptEnd' <- formatTime SelFormatTime exceptStart + $(widgetFile "widgets/occurrence/cell/except-occur") + ExceptNoOccur{..} -> do + exceptTime' <- formatTime SelFormatDateTime exceptTime + $(widgetFile "widgets/occurrence/cell/except-no-occur") + $(widgetFile "widgets/occurrence/cell") diff --git a/src/Handler/Utils/Table/Cells.hs b/src/Handler/Utils/Table/Cells.hs index f84e5a0ef..295f6ceb6 100644 --- a/src/Handler/Utils/Table/Cells.hs +++ b/src/Handler/Utils/Table/Cells.hs @@ -14,10 +14,7 @@ import Text.Blaze (ToMarkup(..)) import Handler.Utils.Table.Pagination import Handler.Utils.DateTime import Handler.Utils.Widgets - -import Utils.Occurrences - -import qualified Data.Set as Set +import Handler.Utils.Occurrences type CourseLink = (TermId, SchoolId, CourseShorthand) -- TODO: Refactor with WithHoles ! @@ -269,18 +266,4 @@ correctorLoadCell sc = i18nCell $ sheetCorrectorLoad sc occurrencesCell :: IsDBTable m a => Occurrences -> DBCell m a -occurrencesCell (normalizeOccurrences -> Occurrences{..}) = cell $ do - let occurrencesScheduled' = flip map (Set.toList occurrencesScheduled) $ \case - ScheduleWeekly{..} -> do - scheduleStart' <- formatTime SelFormatTime scheduleStart - scheduleEnd' <- formatTime SelFormatTime scheduleEnd - $(widgetFile "widgets/occurrence/cell/weekly") - occurrencesExceptions' = flip map (Set.toList occurrencesExceptions) $ \case - ExceptOccur{..} -> do - exceptStart' <- formatTime SelFormatDateTime (LocalTime exceptDay exceptStart) - exceptEnd' <- formatTime SelFormatTime exceptStart - $(widgetFile "widgets/occurrence/cell/except-occur") - ExceptNoOccur{..} -> do - exceptTime' <- formatTime SelFormatDateTime exceptTime - $(widgetFile "widgets/occurrence/cell/except-no-occur") - $(widgetFile "widgets/occurrence/cell") +occurrencesCell = cell . occurrencesWidget diff --git a/src/Utils/Form.hs b/src/Utils/Form.hs index 392ade039..76e7b70e6 100644 --- a/src/Utils/Form.hs +++ b/src/Utils/Form.hs @@ -197,6 +197,7 @@ data FormIdentifier | FIDsheet | FIDmaterial | FIDCourseNews + | FIDCourseEvent | FIDsubmission | FIDsettings | FIDcorrectors diff --git a/templates/course.hamlet b/templates/course.hamlet index d174fe99a..33c3174cd 100644 --- a/templates/course.hamlet +++ b/templates/course.hamlet @@ -194,6 +194,53 @@ $# $if NTop (Just 0) < NTop (courseCapacity course)
_{MsgCourseExams}
^{examTable} + $if not (null events) || mayCreateEvents +
_{MsgCourseEvents} +
+
+ + + + + $forall (cID, CourseEvent{courseEventType, courseEventTime, courseEventRoom}) <- events + toPathPiece cID}> + + +
+ _{MsgCourseEventType} + + _{MsgCourseEventTime} + + _{MsgCourseEventRoom} + $if mayCreateEvents + + _{MsgCourseEventActions} + \ #{iconInvisible} +
+
+ #{courseEventType} +
+
+ ^{occurrencesWidget courseEventTime} +
+
+ #{courseEventRoom} + $if mayCreateEvents +
+
    +
  • + ^{modal (i18n MsgCourseEventsActionEdit) (Left (SomeRoute (CEventR tid ssh csh cID CEvEditR)))} +
  • + ^{modal (i18n MsgCourseEventsActionDelete) (Left (SomeRoute (CEventR tid ssh csh cID CEvDeleteR)))} + $if mayCreateEvents +
+ + + +
+ ^{modal (i18n MsgCourseEventsActionCreate) (Left (SomeRoute (CourseR tid ssh csh CEventsNewR)))} + + $if hasTutorials
_{MsgCourseTutorials}
diff --git a/templates/default-layout.lucius b/templates/default-layout.lucius index 53682a25c..8558dfbd9 100644 --- a/templates/default-layout.lucius +++ b/templates/default-layout.lucius @@ -342,7 +342,7 @@ input[type="button"].btn-info:hover, .table--hover { - .table__row:not(.no-hover):not(.table__row--sum):not(.table__row--head):hover { + .table__row:not(.no-hover):not(.table__row--sum):not(.table__row--head):not(.table__row--foot):hover { background-color: rgba(0, 0, 0, 0.07); } } diff --git a/templates/widgets/occurrence/cell.hamlet b/templates/widgets/occurrence/cell.hamlet index 295b3ae24..5e7957f20 100644 --- a/templates/widgets/occurrence/cell.hamlet +++ b/templates/widgets/occurrence/cell.hamlet @@ -1,12 +1,16 @@ $newline never -
    - $forall sched <- occurrencesScheduled' -
  • ^{sched} +$if not (null occurrencesScheduled) +
      + $forall sched <- occurrencesScheduled' +
    • ^{sched} $if not (null occurrencesExceptions) $#
      $#
      $#
      -
        +
          $forall exc <- occurrencesExceptions'
        • ^{exc} + +$if null occurrencesScheduled && null occurrencesExceptions + _{MsgOccurrenceNever} diff --git a/templates/widgets/occurrence/cell/except-occur.hamlet b/templates/widgets/occurrence/cell/except-occur.hamlet index 2d8147d8b..1b0fdb086 100644 --- a/templates/widgets/occurrence/cell/except-occur.hamlet +++ b/templates/widgets/occurrence/cell/except-occur.hamlet @@ -1,2 +1,5 @@ $newline never -_{MsgExceptionKindOccur}: #{exceptStart'}–#{exceptEnd'} +$if not (null occurrencesScheduled') + _{MsgExceptionKindOccur}: #{exceptStart'}–#{exceptEnd'} +$else + #{exceptStart'}–#{exceptEnd'} diff --git a/test/FoundationSpec.hs b/test/FoundationSpec.hs index ea477a11a..d995cee9e 100644 --- a/test/FoundationSpec.hs +++ b/test/FoundationSpec.hs @@ -68,6 +68,10 @@ instance Arbitrary ExamOfficeR where instance Arbitrary CourseNewsR where arbitrary = genericArbitrary shrink = genericShrink + +instance Arbitrary CourseEventR where + arbitrary = genericArbitrary + shrink = genericShrink instance Arbitrary (Route UniWorX) where arbitrary = genericArbitrary