Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add print/download all feedbacks feature #275 #325

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 45 additions & 5 deletions cypress/e2e/requirements-matrix.cy.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import { useDatabaseResets } from "../support/databaseTransactions"
import {useDatabaseResets} from "../support/databaseTransactions"

describe('requirements matrix', () => {
useDatabaseResets()

beforeEach(() => {
cy.then(() => {
cy.login().then(user => {
cy.artisan('e2e:scenario', { '--user-id': user.id })
cy.artisan('e2e:scenario', {'--user-id': user.id})
})
})
})

it('can be displayed and edited', function () {
cy.courseId().then((courseId) => {
cy.create('App\\Models\\RequirementStatus', 1, { course_id: courseId, name: 'E2E status', color: 'green', icon: 'book' })
cy.create('App\\Models\\RequirementStatus', 1, {
course_id: courseId,
name: 'E2E status',
color: 'green',
icon: 'book'
})

cy.php(`App\\Models\\FeedbackData::orderBy('id', 'desc')->first();`).then(feedback => {
cy.visit(`/course/${courseId}`)
Expand Down Expand Up @@ -42,8 +47,13 @@ describe('requirements matrix', () => {

it('can be displayed and edited if there are multiple feedbacks in course', function () {
cy.courseId().then((courseId) => {
cy.create('App\\Models\\RequirementStatus', 1, { course_id: courseId, name: 'E2E status', color: 'green', icon: 'book' })
cy.create('App\\Models\\FeedbackData', 1, { course_id: courseId, name: 'E2E Feedback' })
cy.create('App\\Models\\RequirementStatus', 1, {
course_id: courseId,
name: 'E2E status',
color: 'green',
icon: 'book'
})
cy.create('App\\Models\\FeedbackData', 1, {course_id: courseId, name: 'E2E Feedback'})

cy.php(`App\\Models\\FeedbackData::orderBy('id', 'desc')->offset(1)->first();`).then(feedback => {
cy.visit(`/course/${courseId}`)
Expand Down Expand Up @@ -71,4 +81,34 @@ describe('requirements matrix', () => {

})
})

it('edits and prints all feedbacks', function () {
cy.courseId().then((courseId) => {
cy.create('App\\Models\\RequirementStatus', 1, {
course_id: courseId,
name: 'E2E status',
color: 'green',
icon: 'book'
})

cy.php(`App\\Models\\FeedbackData::orderBy('id', 'desc')->first();`).then(feedback => {
cy.visit(`/course/${courseId}`)
cy.contains(feedback.name).click()

cy.contains('Anforderungs-Matrix')

cy.contains(`Anforderungs-Matrix ${feedback.name}`)

cy.get('[title="Drucken"]').first().click()
cy.contains('PDF wird generiert...')
cy.contains('PDF wurde heruntergeladen', {timeout: 20000})

cy.task('findFiles', 'cypress/downloads/*').then((foundZip) => {
expect(foundZip).to.be.a('string')
cy.log(`found PDF ${foundZip}`)
cy.readFile(foundZip)
})
})
})
})
})
1 change: 1 addition & 0 deletions lang/de/t.php
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@
"title" => ":name",
"requirements_matrix" => "Anforderungs-Matrix :name",
"view_as" => "Aus Sicht von",
"download_all" => "Alle Rückmeldungen herunterladen"
)
),
"invitation" => array(
Expand Down
50 changes: 32 additions & 18 deletions package-lock.json

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

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,8 @@
"vue-template-compiler": "^2.6.14",
"y-webrtc": "^10.2.0",
"yjs": "^13.5.12"
},
"dependencies": {
"jszip": "^3.10.1"
}
}
149 changes: 149 additions & 0 deletions resources/js/components/print/ButtonPrintAllFeedbacks.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
<template>
<a
class="btn btn-primary"
v-b-tooltip
:title="tooltip"
@click="generateAndDownloadPdfs">
<slot>
<i class="fas fa-print" />
</slot>
</a>
</template>

<script>
import sanitizeFilename from 'sanitize-filename'
import { saveAs } from 'file-saver'
import JSZip from "jszip";

export default {
name: 'ButtonPrintAllFeedbacks',
props: {
feedbacks: { type: Array, required: true },
},
data() {
return {
clicked: false,
url: null,
error: null,
course: null,
feedback: null,
feedbackContents: null,
participant: null,
observations: null,
statuses: null,
totalParticipants: 0,
completedPDFs: 0,
}
},
computed: {
filename() {
if (!this.feedback || !this.participant) {
return 'qualix.pdf'
}
return sanitizeFilename(this.feedback?.name + '.zip', { replacement: '_' })
},
tooltip() {
if (!this.clicked) return this.$t('t.global.print')
if (this.error) return this.error
return this.url ? this.$t('t.views.feedback.print.pdf_downloaded') : this.$t('t.views.feedback.print.pdf_is_being_generated') + " (" + this.completedPDFs + "/" + this.totalParticipants + ")"
},
},
unmounted() {
this.revokeOldObjectUrl()
},
methods: {
async generateAndDownloadPdfs() {
this.clicked = true
this.error = null
this.totalParticipants = this.feedbacks.length;
this.completedPDFs = 0;

await this.generatePdfs()
if (!this.url) return

saveAs(this.url, this.filename)
},
async fetchData() {
this.course = null
this.feedback = null
this.feedbackContents = null
this.participant = null
this.observations = null
this.statuses = null
return window.axios({
url: this.routeUri('feedbackContent.print', { course: this.courseId, participant: this.participantId, feedback: this.feedbackId }),
}).then(response => {
if (response.status !== 200) {
console.error('Unexpected response status when fetching feedback data for printing:', JSON.stringify(response))
this.error = this.$t('t.views.feedback.print.error_fetching_data')
return
}
this.course = response.data.course
this.feedback = response.data.feedback
this.feedbackContents = response.data.feedbackContents
this.participant = response.data.participant
this.observations = response.data.observations
this.statuses = response.data.statuses
}).catch(error => {
if (error.response?.status === 401 || error.response?.status === 404) {
window.location.reload()
return
}
console.error('Error fetching feedback data for printing:', JSON.stringify(error))
this.error = this.$t('t.views.feedback.print.error_fetching_data')
})
},
async generatePdfs() {
this.error = null
this.revokeOldObjectUrl()

// Only load the whole react chain once we really need it
const renderPdf = (await import('./feedback/index.js')).default

const resultZip = new JSZip();

for (const feedback of this.feedbacks) {
this.courseId = feedback['feedback_data']['course_id'];
this.participantId = feedback['participant_id'];
this.feedbackId = feedback.id;

await this.fetchData();
const { error, blob } = await renderPdf({
course: this.course,
feedback: this.feedback,
feedbackContents: this.feedbackContents,
participant: this.participant,
observations: this.observations,
statuses: this.statuses,
}, document.documentElement.lang)

if (error) {
console.error('Error creating pdf for feedback:', error,
'course id:', this.courseId,
'participant id:', this.participantId,
'feedback id:', this.feedbackId,
'data used for creating the pdf:',
this.course, this.feedback, this.feedbackContents, this.participant, this.observations, this.statuses)
this.error = this.$t('t.views.feedback.print.error_creating_pdf')
} else {
resultZip.file(sanitizeFilename(this.participant?.scout_name + '.pdf', { replacement: '_' }), blob);
this.completedPDFs = this.completedPDFs + 1;
}
}

if (!this.error) {
this.url = URL.createObjectURL(await resultZip.generateAsync({type: "blob"}));
}
},
revokeOldObjectUrl() {
const oldUrl = this.url
if (oldUrl) {
this.url = null
URL.revokeObjectURL(oldUrl)
}
},
},
}
</script>

<style scoped></style>
5 changes: 5 additions & 0 deletions resources/views/feedback/progress-overview.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
@section('content')

<b-card>
<button-print-all-feedbacks :feedbacks="{{ json_encode($feedbacks) }}">
{{__('t.views.feedback.progress_overview.download_all')}}
<i class="fas fa-print pl-2"></i>
</button-print-all-feedbacks>

@if(count($feedbackRequirements))
<template #header>{{__('t.views.feedback.progress_overview.requirements_matrix', ['name' => $feedbackData->name])}}</template>
@else
Expand Down
Loading