Skip to content

Commit

Permalink
Merge pull request #6031 from usu/feat/activity-list
Browse files Browse the repository at this point in the history
feat(print): activity list for courses
  • Loading branch information
pmattmann authored Sep 29, 2024
2 parents 5eb64f1 + c940e55 commit 582d0a2
Show file tree
Hide file tree
Showing 18 changed files with 524 additions and 37 deletions.
2 changes: 1 addition & 1 deletion common/helpers/materialListsSorted.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { sortBy } from 'lodash'
import sortBy from 'lodash/sortBy.js'

export default function (materialLists) {
return sortBy(
Expand Down
7 changes: 5 additions & 2 deletions common/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"contentNode": {
"checklist": {
"icon": "mdi-clipboard-list-outline",
"info": "Wähle die relevanten Checklistenpunkte, welche durch diese Aktivität abgedeckt werden (Ausbildungsziele, J+S-Checklisten, etc.).",
"name": "Checkliste"
},
"columnLayout": {
Expand Down Expand Up @@ -261,6 +261,9 @@
"shortScheduleEntryDescription": "Tag\u202f{dayNumber} {startTime}"
},
"print": {
"activityList": {
"title": "Aktivitätsübersicht"
},
"config": {
"periods": "Lagerabschnitt(e)"
},
Expand Down Expand Up @@ -291,4 +294,4 @@
"title": "Inhaltsverzeichnis"
}
}
}
}
10 changes: 9 additions & 1 deletion common/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
}
},
"contentNode": {
"checklist": {
"icon": "mdi-clipboard-list-outline",
"info": "Select the relevant checklist items, which are covered by this activity (course objectives, etc.).",
"name": "Checklists"
},
"columnLayout": {
"entity": {
"column": {
Expand Down Expand Up @@ -264,6 +269,9 @@
"shortScheduleEntryDescription": "day {dayNumber} {startTime}"
},
"print": {
"activityList": {
"title": "Activity list"
},
"config": {
"periods": "Period(s)"
},
Expand Down Expand Up @@ -294,4 +302,4 @@
"title": "Table of contents"
}
}
}
}
3 changes: 3 additions & 0 deletions frontend/src/components/print/PrintConfigurator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ import PicassoConfig from './config/PicassoConfig.vue'
import SummaryConfig from './config/SummaryConfig.vue'
import ProgramConfig from './config/ProgramConfig.vue'
import ActivityConfig from './config/ActivityConfig.vue'
import ActivityListConfig from './config/ActivityListConfig.vue'
import TocConfig from './config/TocConfig.vue'
import PagesOverview from './configurator/PagesOverview.vue'
import PagesConfig from './configurator/PagesConfig.vue'
Expand Down Expand Up @@ -132,6 +133,7 @@ export default {
ProgramConfig,
ActivityConfig,
TocConfig,
ActivityListConfig,
},
props: {
camp: {
Expand All @@ -149,6 +151,7 @@ export default {
Program: ProgramConfig,
Activity: ActivityConfig,
Toc: TocConfig,
ActivityList: ActivityListConfig,
},
previewTab: null,
}
Expand Down
100 changes: 100 additions & 0 deletions frontend/src/components/print/__tests__/repairPrintConfig.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ProgramConfig from '../config/ProgramConfig.vue'
import StoryConfig from '../config/StoryConfig.vue'
import SafetyConsiderationsConfig from '../config/SafetyConsiderationsConfig.vue'
import TocConfig from '../config/TocConfig.vue'
import ActivityListConfig from '../config/ActivityListConfig.vue'

describe('repairConfig', () => {
const camp = {
Expand All @@ -30,6 +31,7 @@ describe('repairConfig', () => {
SafetyConsiderationsConfig,
StoryConfig,
TocConfig,
ActivityListConfig,
].map((component) => [component.name.replace(/Config$/, ''), component.repairConfig])
)
const defaultContents = [
Expand Down Expand Up @@ -1126,4 +1128,102 @@ describe('repairConfig', () => {
})
})
})

describe('activityList', () => {
test('adds missing options', async () => {
// given
const config = {
camp: '/camps/1a2b3c4d',
contents: [
{
type: 'ActivityList',
},
],
documentName: 'test camp',
language: 'en-GB',
}

// when
const result = repairConfig(config, ...args)

// then
expect(result).toEqual({
camp: '/camps/1a2b3c4d',
contents: [
{
type: 'ActivityList',
options: { periods: [] },
},
],
documentName: 'test camp',
language: 'en-GB',
})
})

test('allows empty periods', async () => {
// given
const config = {
camp: '/camps/1a2b3c4d',
contents: [
{
type: 'ActivityList',
options: { periods: [] },
},
],
documentName: 'test camp',
language: 'en-GB',
}

// when
const result = repairConfig(config, ...args)

// then
expect(result).toEqual({
camp: '/camps/1a2b3c4d',
contents: [
{
type: 'ActivityList',
options: { periods: [] },
},
],
documentName: 'test camp',
language: 'en-GB',
})
})

test('filters out unknown periods', async () => {
// given
const config = {
camp: '/camps/1a2b3c4d',
contents: [
{
type: 'ActivityList',
options: {
periods: ['/periods/11112222', '/periods/1a2b3c4d'],
},
},
],
documentName: 'test camp',
language: 'en-GB',
}

// when
const result = repairConfig(config, ...args)

// then
expect(result).toEqual({
camp: '/camps/1a2b3c4d',
contents: [
{
type: 'ActivityList',
options: {
periods: ['/periods/1a2b3c4d'],
},
},
],
documentName: 'test camp',
language: 'en-GB',
})
})
})
})
55 changes: 55 additions & 0 deletions frontend/src/components/print/config/ActivityListConfig.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<div>
<e-select
v-model="options.periods"
:items="periods"
:label="$tc('print.config.periods')"
multiple
:filled="false"
@input="$emit('input')"
/>
</div>
</template>

<script>
export default {
name: 'ActivityListConfig',
props: {
value: { type: Object, required: true },
camp: { type: Object, required: true },
},
computed: {
options: {
get() {
return this.value
},
set(v) {
this.$emit('input', v)
},
},
periods() {
return this.camp.periods().items.map((p) => ({
value: p._meta.self,
text: p.description,
}))
},
},
defaultOptions() {
return {
periods: [],
}
},
design: {
multiple: true,
},
repairConfig(config, camp) {
if (!config.options) config.options = {}
if (!config.options.periods) config.options.periods = []
const knownPeriods = camp.periods().items.map((p) => p._meta.self)
config.options.periods = config.options.periods.filter((period) => {
return knownPeriods.includes(period)
})
return config
},
}
</script>
3 changes: 2 additions & 1 deletion frontend/src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,8 @@
"Program": "Detailprogramm",
"SafetyConsiderations": "Sicherheits­überlegungen",
"Story": "Roter Faden",
"Toc": "Inhaltsverzeichnis"
"Toc": "Inhaltsverzeichnis",
"ActivityList": "Aktivitätsübersicht (Kurse)"
}
},
"printNuxt": {
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,8 @@
"Program": "Program",
"SafetyConsiderations": "Safety considerations",
"Story": "Story",
"Toc": "Table of contents"
"Toc": "Table of contents",
"ActivityList": "Activity list (courses)"
}
},
"printNuxt": {
Expand Down
71 changes: 71 additions & 0 deletions print/components/activityList/ActivityListPeriod.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<template>
<div class="tw-break-after-page">
<h1
:id="`content_${index}_period_${period.id}`"
class="tw-text-center tw-font-semibold tw-mb-6"
>
{{ $t('print.activityList.title') }}:
{{ period.description }}
</h1>

<generic-error-message v-if="error" :error="error" />

<activity-list-schedule-entry
v-for="scheduleEntry in data.scheduleEntries"
:key="scheduleEntry.id"
:schedule-entry="scheduleEntry"
:content-types="data.contentTypes"
:content-nodes="data.contentNodes"
:index="index"
/>
</div>
</template>

<script setup>
const props = defineProps({
camp: { type: Object, required: true },
period: {
type: Object,
required: true,
},
contentTypeNames: {
type: Array,
required: true,
},
index: { type: Number, required: true },
})
const { $api } = useNuxtApp()
const { data, error } = await useAsyncData(
`ActivityListPeriod-${props.period._meta.self}`,
async () => {
const allContentTypes = (await $api.get().contentTypes().$loadItems()).items
const contentTypes = props.contentTypeNames.map((contentTypeName) =>
allContentTypes.find((contentType) => contentType.name === contentTypeName)
)
const contentNodePromises = contentTypes.map((contentType) =>
$api
.get()
.contentNodes({
period: props.period._meta.self,
contentType: contentType._meta.self,
})
.$loadItems()
)
const contentNodes = await Promise.all(contentNodePromises)
const [scheduleEntries] = await Promise.all([
props.period.scheduleEntries().$loadItems(),
])
return {
scheduleEntries: scheduleEntries.items,
contentNodes,
contentTypes,
}
}
)
</script>
Loading

0 comments on commit 582d0a2

Please sign in to comment.