Skip to content

Commit

Permalink
Merge pull request #284 from KTH/issues/KUI-1374-add-heading-recommen…
Browse files Browse the repository at this point in the history
…ded-prerequisites

Recommended prerequisites now has its own page with editable fields
  • Loading branch information
axelbjo authored Oct 4, 2024
2 parents a75be1a + 5173178 commit dd6e00d
Show file tree
Hide file tree
Showing 23 changed files with 482 additions and 17 deletions.
32 changes: 29 additions & 3 deletions i18n/messages.en.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,36 @@ module.exports = {
header: 'Choose part to edit',
options: {
description: 'Image, course introduction and course disposition',
recommendedPrerequisites: 'Recommended prerequisites',
otherInformation: 'Supplementary information',
},
nextButton: 'Edit',
},
editRecommendedPrerequisites: {
pageHeader: 'Recommended prerequisites',
step1: {
title: 'Edit text',
intro: `The heading “Recommended prerequisites” should describe what knowledge and skills (in addition to the eligibility requirements) the students need to be able to take the course.
Students can use the information to prepare for the course or as a basis for choosing the course or not.
Preferably state explicit knowledge and skills and not just course names, for example “programming in Python” or “boundary value calculations”.`,
alert: `Note that the texts are displayed for all course offerings. If the course is offered at multiple times in the same semester, you may need to customize the text. Specify a maximum of 2000 characters per text.`,
nextButton: 'Preview',
fields: {
recommendedPrerequisitesSv: 'Recommended prerequisites (SV)',
recommendedPrerequisitesEn: 'Recommended prerequisites (EN)',
},
},
step2: {
title: 'Preview and publish',
header: 'Preview',
nextButton: 'Publish',
backButton: 'Edit text',
fields: {
recommendedPrerequisitesSv: 'Rekommenderade förkunskaper',
recommendedPrerequisitesEn: 'Recommended prerequisites',
},
},
},
editOtherInformation: {
pageHeader: 'Supplementary information',
step1: {
Expand All @@ -204,7 +230,7 @@ module.exports = {
alert: `Note that the texts are displayed for all course offerings. If the course is offered at multiple times in the same semester, you may need to customize the text. Specify a maximum of 2000 characters per text.`,
nextButton: 'Preview',
fields: {
supplementaryInfoSv: 'Supplementary information (SW)',
supplementaryInfoSv: 'Supplementary information (SV)',
supplementaryInfoEn: 'Supplementary information (EN)',
},
},
Expand Down Expand Up @@ -260,9 +286,9 @@ module.exports = {
nextButton: 'Preview',
backButton: 'Choose image',
fields: {
sellingTextSv: 'Introduction to the course (SW)',
sellingTextSv: 'Introduction to the course (SV)',
sellingTextEn: 'Introduction to the course (EN)',
courseDispositionSv: 'Course disposition (SW)',
courseDispositionSv: 'Course disposition (SV)',
courseDispositionEn: 'Course disposition (EN)',
},
},
Expand Down
27 changes: 27 additions & 0 deletions i18n/messages.se.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Please make sure to use the correct quotes:
// - in Swedish, use closing double quote (” ,\u201d) both before and after the text to be quoted,

// - in English, use opening double quote (“, \u201c) before and closing double quote (” \u201d) after the text.
module.exports = {
shortNames: ['sv', 'se'],
Expand Down Expand Up @@ -187,11 +188,37 @@ module.exports = {
header: 'Välj del att redigera',
options: {
description: 'Bild, introduktion till kursen och kursupplägg',
recommendedPrerequisites: 'Rekommenderade förkunskaper',
otherInformation: 'Övrig information',
},
nextButton: 'Redigera',
},

editRecommendedPrerequisites: {
pageHeader: 'Rekommenderade förkunskaper',
step1: {
title: 'Redigera text',
intro: `Rubriken ”Rekommenderade förkunskaper” ska beskriva vilka kunskaper och färdigheter (utöver behörighetskraven) som studenterna behöver för att kunna ta till sig kursen.
Studenterna kan använda informationen för att förbereda sig för kursen eller som underlag för om de ska välja kursen eller inte.
Ange helst explicita kunskaper och färdigheter och inte bara kursnamn, till exempel ”programmering i Python” eller ”gränsvärdesberäkningar”.`,
alert: `Observera att texterna visas för samtliga kurstillfällen. Om kursen ges vid flera tillfällen samma termin kan du behöva anpassa texten. Ange max 2000 tecken per text.`,
nextButton: 'Granska',
fields: {
recommendedPrerequisitesSv: 'Rekommenderade förkunskaper (SV)',
recommendedPrerequisitesEn: 'Rekommenderade förkunskaper (EN)',
},
},
step2: {
title: 'Granska och publicera',
header: 'Granska',
nextButton: 'Publicera',
backButton: 'Redigera text',
fields: {
recommendedPrerequisitesSv: 'Rekommenderade förkunskaper',
recommendedPrerequisitesEn: 'Recommended prerequisites',
},
},
},
editOtherInformation: {
pageHeader: 'Övrig information',
step1: {
Expand Down
14 changes: 8 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions public/js/app/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import AdminStartPage from './pages/AdminStartPage'
import DescriptionPage from './pages/DescriptionPage'
import CourseEditStartPage from './pages/CourseEditStartPage'
import OtherInformationPage from './pages/OtherInformationPage'
import RecommendedPrerequisitesPage from './pages/RecommendedPrerequisitesPage'
import '../../css/kursinfo-admin-web.scss'

function appFactory(applicationStore, context) {
return (
<WebContextProvider configIn={context}>
<SiteHeaderUrlWrapper>
<Routes>
<Route exact path="/edit/:courseCode/recommendedPrerequisites" element={<RecommendedPrerequisitesPage />} />
<Route exact path="/edit/:courseCode/otherInformation" element={<OtherInformationPage />} />
<Route exact path="/edit/:courseCode/description" element={<DescriptionPage />} />
<Route exact path="/edit/:courseCode" element={<CourseEditStartPage />} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ function doUpsertItem(courseCode, values) {
return doPostApiCall(url, data)
}

function doUpsertRecommendedPrerequisites(courseCode, values) {
const url = this.buildApiUrl(this.paths.course.updateRecommendedPrerequisites.uri, { courseCode })
const data = { ...values, user: this.user }
return doPostApiCall(url, data)
}

function doUpsertOtherInformation(courseCode, values) {
const url = this.buildApiUrl(this.paths.course.updateOtherInformation.uri, { courseCode })
const data = { ...values, user: this.user }
Expand All @@ -65,6 +71,7 @@ function addClientFunctionsToWebContext() {
const functions = {
buildApiUrl,
doUpsertItem,
doUpsertRecommendedPrerequisites,
doUpsertOtherInformation,
}
return functions
Expand Down
2 changes: 1 addition & 1 deletion public/js/app/pages/CourseEditStartPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import KoppsErrorPage from '../components/KoppsErrorPage'
import PageTitle from '../components/PageTitle'
import { useWebContext } from '../context/WebContext'

const options = ['description', 'otherInformation']
const options = ['description', 'recommendedPrerequisites', 'otherInformation']

function CourseEditStartPage() {
const [selectedOption, setSelectedOption] = React.useState(options[0])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react'

import i18n from '../../../../../i18n'
import PageTitle from '../../components/PageTitle'
import KoppsErrorPage from '../../components/KoppsErrorPage'
import ProgressBar, { useProgressBar } from '../../components-shared/ProgressBar'
import { useWebContextTextInput } from '../../components/WebContextTextInput/useWebContextTextInput'
import { useWebContext } from '../../context/WebContext'
import RecommendedPrerequisitesTextEdit from './components/RecommendedPrerequisitesTextEdit'
import RecommendedPrerequisitesPreview from './components/RecommendedPrerequisitesPreview'

const useRecommendedPrerequisitesPageState = steps => {
const [context] = useWebContext()
const progress = useProgressBar(steps)
const textInput = useWebContextTextInput()

const { courseCode } = context.routeData.courseData
const { hasChanges } = textInput

return { progress, textInput, hasChanges, courseCode }
}

/**
* Page for editing recommended prerequisites
*/
function RecommendedPrerequisitesPage() {
const [context] = useWebContext()
const labels = i18n.messages[context.langIndex].editRecommendedPrerequisites
const pageState = useRecommendedPrerequisitesPageState([labels.step1, labels.step2])
const pageTitleProps = { courseTitleData: context.routeData.courseData, pageTitle: labels.pageHeader }

if (context.koppsApiError) {
return <KoppsErrorPage pageTitleProps={pageTitleProps} />
}

return (
<div className="kursinfo-main-page">
<PageTitle {...pageTitleProps} />
<ProgressBar {...pageState.progress} />

{pageState.progress.current === 0 && <RecommendedPrerequisitesTextEdit pageState={pageState} />}
{pageState.progress.current === 1 && <RecommendedPrerequisitesPreview pageState={pageState} />}
</div>
)
}

export { RecommendedPrerequisitesPage }
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react'

import i18n from '../../../../../../i18n'
import Alert from '../../../components-shared/Alert'
import ControlButtons from '../../../components/ControlButtons'
import { useWebContext } from '../../../context/WebContext'
import { goToAdminStartPage } from '../../../util/links'

import RecommendedPrerequisitesPreviewSection from './RecommendedPrerequisitesPreviewSection'

export default function RecommendedPrerequisitesPreview({ pageState }) {
const [context] = useWebContext()
const textInputValues = pageState.textInput.values
const { courseCode } = pageState
const texts = i18n.messages[context.langIndex].editRecommendedPrerequisites.step2
const apiErrorMsg = i18n.messages[context.langIndex].pageTitles.alertMessages.api_error

const [submitState, setSubmitState] = React.useState({
inProgress: false,
isError: false,
})

async function onConfirm() {
setSubmitState({
inProgress: true,
isError: false,
})
try {
await context.doUpsertRecommendedPrerequisites(courseCode, textInputValues)
goToAdminStartPage(courseCode, context.lang, 'pub')
} catch (err) {
setSubmitState({
inProgress: false,
isError: true,
})
}
}

return (
<div>
<h2>{texts.header}</h2>

<RecommendedPrerequisitesPreviewSection
header={texts.fields.recommendedPrerequisitesSv}
value={textInputValues.recommendedPrerequisitesSv}
/>
<RecommendedPrerequisitesPreviewSection
header={texts.fields.recommendedPrerequisitesEn}
value={textInputValues.recommendedPrerequisitesEn}
/>

{submitState.isError && (
<Alert type="warning">
<p>{apiErrorMsg}</p>
</Alert>
)}

<ControlButtons
pageState={pageState}
back={{ label: texts.backButton }}
next={{
confirmPublish: true,
onClick: onConfirm,
disabled: submitState.inProgress,
label: texts.nextButton,
}}
/>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react'

const RecommendedPrerequisitesPreviewSection = ({ header, value }) => (
<div className="PreviewSection">
<h3>{header}</h3>
<div>{value && <div className="PreviewSection__textBlock" dangerouslySetInnerHTML={{ __html: value }} />}</div>
</div>
)

export default RecommendedPrerequisitesPreviewSection
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react'

import i18n from '../../../../../../i18n'
import Alert from '../../../components-shared/Alert'
import ControlButtons from '../../../components/ControlButtons'
import EditorSection from '../../../components/Editor/EditorSection'
import { useWebContext } from '../../../context/WebContext'

export default function RecommendedPrerequisitesTextEdit({ pageState }) {
const [context] = useWebContext()
const texts = i18n.messages[context.langIndex].editRecommendedPrerequisites.step1
const { hasErrors, getEditorSectionProps } = pageState.textInput

const onNext = () => {
if (!hasErrors) pageState.progress.goToNext()
}

return (
<form>
<h2>{texts.title}</h2>

<Alert type="info">{texts.alert}</Alert>

<EditorSection
title={texts.fields.recommendedPrerequisitesSv}
{...getEditorSectionProps('recommendedPrerequisitesSv')}
/>
<EditorSection
title={texts.fields.recommendedPrerequisitesEn}
{...getEditorSectionProps('recommendedPrerequisitesEn')}
/>

<ControlButtons
pageState={pageState}
next={{
onClick: onNext,
disabled: hasErrors,
label: texts.nextButton,
}}
/>
</form>
)
}
3 changes: 3 additions & 0 deletions public/js/app/pages/RecommendedPrerequisitesPage/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { RecommendedPrerequisitesPage } from './RecommendedPrerequisitesPage'

export default RecommendedPrerequisitesPage
3 changes: 3 additions & 0 deletions server/__mocks__/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ module.exports = {
editOtherInformation: {
uri: `/mockBase/edit/:courseCode/otherInformation`,
},
editRecommendedPrerequisites: {
uri: `/mockBase/edit/:courseCode/recommendedPrerequisites`,
},
},
}),
}
Loading

0 comments on commit dd6e00d

Please sign in to comment.