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

WIP: Add collapsible submissions view #1560

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions src/components/Results/Answer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export default {

&__text {
white-space: pre-line;
margin-left: 1rem;
}
}

Expand Down
163 changes: 163 additions & 0 deletions src/components/Results/SubmissionItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<!--
- @copyright Copyright (c) 2023 Ferdinand Thiessen <[email protected]>
-
- @author Ferdinand Thiessen <[email protected]>
-
- @license AGPL-3.0-or-later
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-->

<template>
<NcListItem :title="submissionDateTime"
:bold="false"
:details="submissionAge"
:link-aria-label="t('forms', 'Click to expand submission')"
@click="onExpand">
<template #icon>
<NcAvatar v-if="!submission.userId.startsWith('anon-user-')"
:size="44"
:user="submission.userId"
:display-name="submission.userDisplayName" />
<IconAccountOff v-else :size="44" />
</template>
<template #subtitle>
{{ submission.userDisplayName }}
</template>
<template #extra>
<div v-if="expanded" class="submission">
<Answer v-for="question in answeredQuestions"
:key="question.id"
:answer-text="question.squashedAnswers"
:question-text="question.text" />
</div>
</template>
<template v-if="!viewed" #indicator>
<IconCheckboxBlankCircle :size="14" fill-color="var(--color-primary)" />
</template>
<template #actions>
<NcActionButton v-if="canDeleteSubmission" @click="onDelete">
<template #icon>
<IconDelete :size="20" />
</template>
{{ t('forms', 'Delete this response') }}
</NcActionButton>
</template>
</NcListItem>
</template>

<script>
import Answer from './Answer.vue'

import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js'
import moment from '@nextcloud/moment'
import IconAccountOff from 'vue-material-design-icons/AccountOff.vue'
import IconCheckboxBlankCircle from 'vue-material-design-icons/CheckboxBlankCircle.vue'
import IconDelete from 'vue-material-design-icons/Delete.vue'

export default {
name: 'SubmissionItem',

components: {
Answer,
IconAccountOff,
IconCheckboxBlankCircle,
IconDelete,
NcActionButton,
NcAvatar,
NcListItem,
},

props: {
submission: {
type: Object,
required: true,
},
questions: {
type: Array,
required: true,
},
canDeleteSubmission: {
type: Boolean,
required: true,
},
},

data() {
return {
expanded: false,
viewed: false,
}
},

computed: {
// Format submission-timestamp to DateTime
submissionDateTime() {
return moment(this.submission.timestamp, 'X').format('LLLL')
},

/**
* Age of the submission, e.g. '11 hours' or '1 year'
*/
submissionAge() {
return moment(this.submission.timestamp, 'X').fromNow(true)
},

/**
* Join answered Questions with corresponding answers.
* Multiple answers to a question are squashed into one string.
*
* @return {Array}
*/
answeredQuestions() {
const answeredQuestionsArray = []

this.questions.forEach(question => {
const answers = this.submission.answers.filter(answer => answer.questionId === question.id)
if (!answers.length) {
return // no answers, go to next question
}
const squashedAnswers = answers.map(answer => answer.text).join('; ')

answeredQuestionsArray.push({
id: question.id,
text: question.text,
squashedAnswers,
})
})
return answeredQuestionsArray
},
},

methods: {
onDelete() {
this.$emit('delete')
},

onExpand() {
this.expanded = !this.expanded
this.viewed = true
},
},
}
</script>

<style scoped lang="scss">
.session {
padding-left: 1em;
}
</style>
49 changes: 35 additions & 14 deletions src/views/Results.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,23 +44,33 @@
<div class="response-actions">
<div class="response-actions__radio">
<input id="show-summary--true"
v-model="showSummary"
v-model="viewMode"
value="summary"
type="radio"
:value="true"
class="hidden">
<label for="show-summary--true"
class="response-actions__radio__item"
:class="{ 'response-actions__radio__item--active': showSummary }">
:class="{ 'response-actions__radio__item--active': viewMode === 'summary' }">
{{ t('forms', 'Summary') }}
</label>
<input id="show-list"
v-model="viewMode"
value="list"
type="radio"
class="hidden">
<label for="show-list"
class="response-actions__radio__item"
:class="{ 'response-actions__radio__item--active': viewMode === 'list' }">
{{ t('forms', 'List') }}
</label>
<input id="show-summary--false"
v-model="showSummary"
v-model="viewMode"
value="items"
type="radio"
:value="false"
class="hidden">
<label for="show-summary--false"
class="response-actions__radio__item"
:class="{ 'response-actions__radio__item--active': !showSummary }">
:class="{ 'response-actions__radio__item--active': viewMode === 'items' }">
{{ t('forms', 'Responses') }}
</label>
</div>
Expand Down Expand Up @@ -110,15 +120,27 @@
</section>

<!-- Summary view for visualization -->
<section v-if="!noSubmissions && showSummary">
<section v-else-if="viewMode === 'summary'">
<ResultsSummary v-for="question in form.questions"
:key="question.id"
:question="question"
:submissions="form.submissions" />
</section>

<!-- Responses view for individual responses using list style -->
<section v-else-if="viewMode === 'list'">
<ul>
<SubmissionItem v-for="submission in form.submissions"
:key="submission.id"
:submission="submission"
:questions="form.questions"
:can-delete-submission="canDeleteSubmissions"
@delete="deleteSubmission(submission.id)" />
</ul>
</section>

<!-- Responses view for individual responses -->
<section v-if="!noSubmissions && !showSummary">
<section v-else>
<Submission v-for="submission in form.submissions"
:key="submission.id"
:submission="submission"
Expand Down Expand Up @@ -151,6 +173,7 @@ import IconShareVariant from 'vue-material-design-icons/ShareVariant.vue'

import ResultsSummary from '../components/Results/ResultsSummary.vue'
import Submission from '../components/Results/Submission.vue'
import SubmissionItem from '../components/Results/SubmissionItem.vue'
import TopBar from '../components/TopBar.vue'
import ViewsMixin from '../mixins/ViewsMixin.js'
import answerTypes from '../models/AnswerTypes.js'
Expand Down Expand Up @@ -183,6 +206,7 @@ export default {
NcLoadingIcon,
ResultsSummary,
Submission,
SubmissionItem,
TopBar,
},

Expand All @@ -191,7 +215,7 @@ export default {
data() {
return {
loadingResults: true,
showSummary: true,
viewMode: 'summary',
}
},

Expand Down Expand Up @@ -368,20 +392,17 @@ export default {
margin-right: 8px;

&__item {
border-radius: var(--border-radius-pill);
padding: 8px 16px;
font-weight: bold;
background-color: var(--color-background-dark);

&:first-of-type {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-radius: var(--border-radius-pill) 0 0 var(--border-radius-pill);
padding-right: 8px;
}

&:last-of-type {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-radius: 0 var(--border-radius-pill) var(--border-radius-pill) 0;
padding-left: 8px;
}

Expand Down