Skip to content

Commit

Permalink
Merge pull request #6045 from usu/feat/activity-list-print-react
Browse files Browse the repository at this point in the history
feat(print): activity list implementation for react print
  • Loading branch information
pmattmann authored Sep 29, 2024
2 parents a2ab70c + e49df4f commit 4ba5011
Show file tree
Hide file tree
Showing 14 changed files with 2,985 additions and 927 deletions.
2 changes: 1 addition & 1 deletion frontend/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default [
'error',
{
ignoreKeysRegex:
'^(global|entity|contentNode\\.[a-z][a-zA-Z]+|print\\.(global|activity|cover|picasso|program|config|summary|toc))\\..+',
'^(global|entity|contentNode\\.[a-z][a-zA-Z]+|print\\.(global|activity|cover|picasso|program|config|summary|toc|activityList))\\..+',
translationKeyPropRegex: '[a-zA-Z0-9]-i18n-key$',
},
],
Expand Down
2 changes: 1 addition & 1 deletion pdf/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export default [
'error',
{
ignoreKeysRegex:
'^(global|entity|contentNode\\.[a-z][a-zA-Z]+|print\\.(global|activity|cover|picasso|program|config|summary|toc))\\..+',
'^(global|entity|contentNode\\.[a-z][a-zA-Z]+|print\\.(global|activity|cover|picasso|program|config|summary|toc|activityList))\\..+',
translationKeyPropRegex: '[a-zA-Z0-9]-i18n-key$',
},
],
Expand Down
2 changes: 2 additions & 0 deletions pdf/src/CampPrint.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import Story from '@/campPrint/summary/Story.vue'
import SafetyConsiderations from '@/campPrint/summary/SafetyConsiderations.vue'
import Program from '@/campPrint/program/Program.vue'
import Activity from '@/campPrint/activity/Activity.vue'
import ActivityList from '@/campPrint/activityList/ActivityList.vue'
import { wordHyphenation } from '@react-pdf/textkit'
const originalHyphenationCallback = wordHyphenation()
Expand All @@ -47,6 +48,7 @@ export default {
Activity,
Story,
SafetyConsiderations,
ActivityList,
}
},
},
Expand Down
30 changes: 30 additions & 0 deletions pdf/src/campPrint/activityList/ActivityList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<template>
<Page :id="id" class="page">
<ActivityListPeriod
v-for="period in periods"
:id="id"
:period="period"
:config="config"
:content-type-names="['LearningObjectives', 'LearningTopics', 'Checklist']"
/>
</Page>
</template>
<script>
import PdfComponent from '@/PdfComponent.js'
import ActivityListPeriod from './ActivityListPeriod.vue'
export default {
name: 'ActivityList',
components: { ActivityListPeriod },
extends: PdfComponent,
props: {
content: { type: Object, required: true },
config: { type: Object, required: true },
},
computed: {
periods() {
return this.content.options.periods.map((periodUri) => this.api.get(periodUri))
},
},
}
</script>
62 changes: 62 additions & 0 deletions pdf/src/campPrint/activityList/ActivityListPeriod.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<template>
<Text
:id="`${id}-${period.id}`"
:bookmark="{ title: title + ': ' + period.description, fit: true }"
class="activity-list-period-title"
>{{ title }}: {{ period.description }}</Text
>
<ActivityListScheduleEntry
v-for="scheduleEntry in scheduleEntries"
:id="id"
:schedule-entry="scheduleEntry"
:content-types="contentTypes"
:content-nodes="contentNodes"
/>
</template>
<script>
import PdfComponent from '@/PdfComponent.js'
import ActivityListScheduleEntry from './ActivityListScheduleEntry.vue'
import camelCase from 'lodash/camelCase.js'
export default {
name: 'ActivityListPeriod',
components: { ActivityListScheduleEntry },
extends: PdfComponent,
props: {
period: { type: Object, required: true },
contentTypeNames: { type: Array, required: true },
config: { type: Object, required: true },
},
computed: {
scheduleEntries() {
return this.period.scheduleEntries().items
},
allContentTypes() {
return this.api.get('/content_types').items
},
contentTypes() {
return this.contentTypeNames.map((contentTypeName) =>
this.allContentTypes.find((contentType) => contentType.name === contentTypeName)
)
},
contentNodes() {
return this.contentTypes.map((contentType) =>
this.period.contentNodes().items.filter((contentNode) => {
return contentNode.contentType()._meta.self === contentType._meta.self
})
)
},
title() {
return this.$tc('print.activityList.title')
},
},
methods: { camelCase },
}
</script>
<pdf-style>
.activity-list-period-title {
font-size: 10pt;
font-weight: bold;
text-align: center;
}
</pdf-style>
44 changes: 44 additions & 0 deletions pdf/src/campPrint/activityList/ActivityListScheduleEntry.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<template>
<ScheduleEntryTitle :schedule-entry="scheduleEntry" :show-header="false" />

<View style="margin-top: 10pt; padding-bottom: 20pt; font-size: 10pt">
<ContentNode
v-for="contentNode in contentNodeEntries"
:key="contentNode.id"
:content-node="contentNode"
/>
</View>
</template>
<script>
import PdfComponent from '@/PdfComponent.js'
import ScheduleEntryTitle from '../scheduleEntry/ScheduleEntryTitle.vue'
import ContentNode from '../scheduleEntry/contentNode/ContentNode.vue'
import sortBy from 'lodash/sortBy.js'
export default {
name: 'ScheduleEntry',
components: { ContentNode, ScheduleEntryTitle },
extends: PdfComponent,
props: {
scheduleEntry: { type: Object, required: true },
contentTypes: { type: Array, required: true },
contentNodes: { type: Array, required: true },
},
computed: {
contentNodeEntries() {
return sortBy(
this.contentNodes.map((contentNodeList) =>
contentNodeList.filter(
(contentNode) =>
contentNode.root()._meta.self ===
this.scheduleEntry.activity().rootContentNode()._meta.self
)
),
['parent', 'slot', 'position']
).flat()
},
},
}
</script>
<pdf-style>
</pdf-style>
31 changes: 24 additions & 7 deletions pdf/src/campPrint/scheduleEntry/InstanceName.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<template>
<Text class="content-node-instance-name">{{ instanceName }}</Text>
<View class="content-node-title">
<Text class="content-node-instance-name">{{ instanceName }}</Text>
<Text v-if="contentNode.instanceName" class="content-type-name">{{
contentTypeName
}}</Text>
</View>
</template>
<script>
import PdfComponent from '@/PdfComponent.js'
Expand All @@ -13,20 +18,32 @@ export default {
},
computed: {
instanceName() {
return (
this.contentNode.instanceName ||
this.$tc(`contentNode.${camelCase(this.contentNode.contentTypeName)}.name`)
)
return this.contentNode.instanceName || this.contentTypeName
},
contentTypeName() {
return this.$tc(`contentNode.${camelCase(this.contentNode.contentTypeName)}.name`)
},
},
}
</script>
<pdf-style>
.content-node-title {
border-bottom: 1.5pt solid black;
margin-bottom: 1pt;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: baseline;
}
.content-node-instance-name {
flex-grow: 1;
font-weight: bold;
font-size: 11pt;
padding-bottom: 3pt;
border-bottom: 1.5pt solid black;
margin-bottom: 1pt;
}
.content-type-name {
font-size:8pt;
font-weight:normal;
color:grey;
}
</pdf-style>
124 changes: 3 additions & 121 deletions pdf/src/campPrint/scheduleEntry/ScheduleEntry.vue
Original file line number Diff line number Diff line change
@@ -1,49 +1,12 @@
<template>
<View :wrap="false" :min-presence-ahead="75">
<View
class="schedule-entry-header-title"
:style="{ borderBottomColor: activity.category().color }"
>
<View :id="`scheduleEntry_${scheduleEntry.id}`" class="schedule-entry-title">
<CategoryLabel
:category="activity.category()"
class="schedule-entry-category-label"
/>
<Text :id="id" :bookmark="bookmarkTitle" class="schedule-entry-number-and-title">
{{ scheduleEntry.number }} {{ activity.title }}
</Text>
</View>
<Text class="schedule-entry-date">{{ startAt }} - {{ endAt }}</Text>
</View>
<View v-if="showHeader" class="schedule-entry-header">
<View class="schedule-entry-header-metadata">
<View class="schedule-entry-header-metadata-entry"
><Text v-if="activity.location" class="schedule-entry-header-metadata-label"
>{{ $tc('entity.activity.fields.location') }}:</Text
><Text>{{ activity.location }}</Text></View
>
</View>
<View class="schedule-entry-header-divider" />
<View class="schedule-entry-header-metadata">
<View class="schedule-entry-header-metadata-entry">
<Text
v-if="activity.activityResponsibles().items.length"
class="schedule-entry-header-metadata-label"
>{{ $tc('entity.activity.fields.responsible') }}:</Text
>
<Responsibles :activity="activity" style="max-width: 200pt" />
</View>
</View>
</View>
</View>
<ScheduleEntryTitle :schedule-entry="scheduleEntry" />
<View style="padding-bottom: 20pt; font-size: 10pt">
<ContentNode :content-node="activity.rootContentNode()" />
</View>
</template>
<script>
import PdfComponent from '@/PdfComponent.js'
import CategoryLabel from '../CategoryLabel.vue'
import Responsibles from '../Responsibles.vue'
import ScheduleEntryTitle from './ScheduleEntryTitle.vue'
import ContentNode from './contentNode/ContentNode.vue'
import { setContentNodeComponent as setContentNodeComponentColumn } from './contentNode/ColumnLayout.vue'
import { setContentNodeComponent as setContentNodeComponentDefault } from './contentNode/ResponsiveLayout.vue'
Expand All @@ -53,7 +16,7 @@ setContentNodeComponentDefault(ContentNode)
export default {
name: 'ScheduleEntry',
components: { Responsibles, CategoryLabel, ContentNode },
components: { ScheduleEntryTitle, ContentNode },
extends: PdfComponent,
props: {
scheduleEntry: { type: Object, required: true },
Expand All @@ -62,89 +25,8 @@ export default {
activity() {
return this.scheduleEntry.activity()
},
bookmarkTitle() {
return [
this.activity.category().short,
this.scheduleEntry.number,
this.activity.title,
]
.filter((entry) => entry)
.join(' ')
},
start() {
return this.$date.utc(this.scheduleEntry.start)
},
end() {
return this.$date.utc(this.scheduleEntry.end)
},
startAt() {
return this.start.format('ddd l LT')
},
endAt() {
return this.start.format('ddd l') === this.end.format('ddd l')
? this.end.format('LT')
: this.end.format('ddd l LT')
},
showHeader() {
return (
this.activity.location.length || this.activity.activityResponsibles().items.length
)
},
},
}
</script>
<pdf-style>
.schedule-entry-header-title {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: baseline;
padding-bottom: 2pt;
border-bottom: 2pt solid #aaaaaa;
}
.schedule-entry-title {
flex-grow: 1;
display: flex;
flex-direction: row;
font-size: 14;
font-weight: semibold;
}
.schedule-entry-category-label {
margin: 4pt 0;
font-size: 12pt;
}
.schedule-entry-number-and-title {
margin: 4pt 4pt;
max-width: 345pt;
}
.schedule-entry-date {
font-size: 11pt;
}
.schedule-entry-header {
display: flex;
flex-direction: row;
justify-content: space-between;
border-bottom: 0.5pt solid black;
font-size: 10pt;
margin-bottom: 10pt;
}
.schedule-entry-header-divider {
border-left: 0.5pt solid black;
margin-left: 3.5pt;
padding-left: 5pt
}
.schedule-entry-header-metadata {
width: 50%;
padding: 2pt 0;
}
.schedule-entry-header-metadata-entry {
flex-direction: row;
align-items: flex-start;
column-gap: 6pt;
}
.schedule-entry-header-metadata-label {
font-weight: semibold;
flex-shrink: 0;
flex-grow: 0;
}
</pdf-style>
Loading

0 comments on commit 4ba5011

Please sign in to comment.