Skip to content
Snippets Groups Projects
Select Git revision
  • 653fcaba5a06b8098f9bdb98c051c35a567a4513
  • main default protected
  • studip-rector
  • ci-opt
  • course-members-export-as-word
  • data-vue-app
  • pipeline-improvements
  • webpack-optimizations
  • rector
  • icon-renewal
  • http-client-and-factories
  • jsonapi-atomic-operations
  • vueify-messages
  • tic-2341
  • 135-translatable-study-areas
  • extensible-sorm-action-parameters
  • sorm-configuration-trait
  • jsonapi-mvv-routes
  • docblocks-for-magic-methods
19 results

QuestionnaireInfoEdit.vue

Blame
  • Forked from Stud.IP / Stud.IP
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    QuestionnaireEditor.vue 15.82 KiB
    <template>
        <form action="#"
              method="post"
              enctype="multipart/form-data"
              class="questionnaire_edit default"
              @submit.prevent="submit()"
              :data-dialog="asDialog ? true : null"
              :data-secure="activateFormSecure"
        >
            <div class="editor">
                <div class="rightside" aria-live="polite" tabindex="0" ref="rightside">
                    <div class="admin" v-if="activeTab === 'admin'">
    
                        <article aria-live="assertive" class="validation_notes studip">
                            <header>
                                <h1>
                                    <studip-icon shape="info-circle" role="info" class="text-bottom validation_notes_icon"></studip-icon>
                                    {{ $gettext('Hinweise zum Ausfüllen des Formulars') }}
                                </h1>
                            </header>
                            <div class="required_note">
                                <div aria-hidden="true">
                                    {{ $gettext('Pflichtfelder sind mit Sternchen gekennzeichnet.') }}
                                </div>
                                <div class="sr-only">
                                    {{ $gettext('Dieses Formular enthält Pflichtfelder.') }}
                                </div>
                            </div>
                            <div v-if="validationNotice && !data.title">
                                {{ $gettext('Folgende Angaben müssen korrigiert werden, um das Formular abschicken zu können:') }}
                                <ul>
                                    <li aria-describedby="questionnaire_title">{{ $gettext('Titel des Fragebogens') }}</li>
                                </ul>
                            </div>
                        </article>
    
                        <div class="formpart">
                            <label class="studiprequired" for="questionnaire_title">
                                <span class="textlabel">{{ $gettext('Titel des Fragebogens') }}</span>
                                <span title="Dies ist ein Pflichtfeld" aria-hidden="true" class="asterisk">*</span>
                            </label>
                            <input type="text" id="questionnaire_title" v-model="data.title" ref="autofocus">
                        </div>
    
                        <div class="hgroup">
                            <label>
                                {{ $gettext('Startzeitpunkt') }}
                                <datetimepicker v-model="data.startdate"></datetimepicker>
                            </label>
                            <label>
                                {{ $gettext('Endzeitpunkt') }}
                                <datetimepicker v-model="data.stopdate"></datetimepicker>
                            </label>
                        </div>
                        <label>
                            <input type="checkbox" v-model="data.copyable" true-value="1" false-value="0">
                            {{ $gettext('Fragebogen zum Kopieren freigeben') }}
                        </label>
                        <label>
                            <input type="checkbox" v-model="data.anonymous" true-value="1" false-value="0">
                            {{ $gettext('Teilnehmende anonymisieren') }}
                        </label>
                        <label>
                            <input type="checkbox" v-model="data.editanswers" true-value="1" false-value="0">
                            {{ $gettext('Teilnehmende dürfen ihre Antworten revidieren') }}
                        </label>
                        <label>
                            {{ $gettext('Ergebnisse einsehbar') }}
                            <select v-model="data.resultvisibility">
                                <option value="always">{{ $gettext('Immer') }}</option>
                                <option value="afterending">{{ $gettext('Nach Ende der Befragung') }}</option>
                                <option value="afterparticipation">{{ $gettext('Nach der Teilnahme') }}</option>
                                <option value="never">{{ $gettext('Niemals') }}</option>
                            </select>
                        </label>
                    </div>
                    <div class="add_question file_select_possibilities" v-else-if="activeTab === 'add_question'">
                        <div>
                            <button v-for="(questiontype, key) in questionTypes" :key="key"
                                    :ref="key == Object.keys(questionTypes)[0] ? 'autofocus' : ''"
                                    href=""
                                    @click.prevent="addQuestion(questiontype.type)"
                            >
                                <studip-icon :shape="questiontype.icon" :size="40"></studip-icon>
                                {{questiontype.name}}
                            </button>
                        </div>
                    </div>
                    <div v-else>
                        <component :is="componentForQuestionIndex(indexForQuestion)"
                                   v-model="data.questions[indexForQuestion].questiondata"
                                   :question_id="data.questions[indexForQuestion].id"
                                   :key="data.questions[indexForQuestion].id">
                        </component>
                    </div>
                </div>
                <aside>
                    <a class="admin"
                       :class="{active: activeTab === 'admin'}"
                       href="#"
                       @click.prevent="switchTab('admin')">
                        <span class="icon"><studip-icon shape="evaluation" :size="30" alt=""></studip-icon></span>
                        {{ $gettext('Einstellungen') }}
                    </a>
                    <draggable v-if="data.questions.length > 0" v-model="data.questions" handle=".drag-handle" group="questions" class="questions_container questions">
                        <div v-for="question in data.questions"
                             :key="question.id"
                             @mouseenter="hoverTab = question.id"
                             @mouseleave="hoverTab = null"
                             :class="(activeTab === question.id || activeTab === 'meta_' + question.id ? 'active' : '') + (hoverTab === question.id ? ' hovered' : '')">
                            <a href="#"
                               @click.prevent="switchTab(question.id)">
                                <span class="drag-handle"></span>
                                <span class="icon type">
                                    <studip-icon :shape="questionTypes[question.questiontype].icon" :size="30" alt=""></studip-icon>
                                </span>
    
                                <div v-if="editInternalName !== question.id">{{ question.internal_name || questionTypes[question.questiontype].name}}</div>
                                <div v-else class="inline_editing">
                                    <input type="text" ref="editInternalName" v-model="tempInternalName" class="inlineediting_internal_name">
                                    <button @click="saveInternalName(question.id)">
                                        <studip-icon shape="accept" :size="20" :title="$gettext('Internen Namen speichern')"></studip-icon>
                                    </button>
                                    <button @click="editInternalName = null">
                                        <studip-icon shape="decline" :size="20" :title="$gettext('Internen Namen nicht speichern')"></studip-icon>
                                    </button>
                                </div>
                            </a>
    
                            <studip-action-menu :items="actionMenuItems"
                                                @copy="duplicateQuestion(question.id)"
                                                @rename="renameInternalName(question.id)"
                                                @moveup="moveQuestionUp(question.id)"
                                                @movedown="moveQuestionDown(question.id)"
                                                @delete="deleteQuestion(question.id)"></studip-action-menu>
                        </div>
                    </draggable>
                    <a :class="activeTab === 'add_question' ? 'add_question active' : 'add_question'"
                       href="#"
                       @click.prevent="switchTab('add_question')">
                        <span class="icon"><studip-icon shape="add" :size="30" alt=""></studip-icon></span>
                        {{ $gettext('Element hinzufügen') }}
                    </a>
                </aside>
            </div>
    
    
            <footer data-dialog-button>
                <button class="button" name="questionnaire_store">
                    {{ $gettext('Speichern') }}
                </button>
                <a href="#" class="button cancel">
                    {{ $gettext('Abbrechen') }}
                </a>
            </footer>
        </form>
    </template>
    <script>
    import draggable from 'vuedraggable';
    import md5 from 'md5';
    import StudipIcon from '../StudipIcon.vue';
    import StudipActionMenu from '../StudipActionMenu.vue';
    import Datetimepicker from '../Datetimepicker.vue';
    
    const loadedComponents = {};
    
    export default {
        name: 'questionnaireeditor',
        components: {
            Datetimepicker,
            StudipActionMenu,
            StudipIcon,
            draggable,
        },
        props: {
            asDialog: {
                type: Boolean,
                default: false,
            },
            questionData: Object,
            questionTypes: Object,
            rangeId: String,
            rangeType: String,
        },
        data() {
            return {
                activeTab: 'admin',
                data: {...this.questionData},
                editInternalName: null,
                form_secured: true,
                hoverTab: null,
                oldData: JSON.parse(JSON.stringify(this.questionData)),
                tempInternalName: '',
                validationNotice: false,
            };
        },
        methods: {
            componentForQuestionIndex(index) {
                const componentInfo = this.questionTypes[this.data.questions[index].questiontype].component;
                if (loadedComponents[componentInfo[0]] === undefined) {
                    loadedComponents[componentInfo[0]] = componentInfo[1] === ''
                        ? () => import(`./${componentInfo[0]}.vue`)
                        : () => import(/* webpackIgnore: true */componentInfo[1]);
                }
    
                return loadedComponents[componentInfo[0]];
            },
            addQuestion(questiontype) {
                let id = md5(`${STUDIP.USER_ID}_QUESTIONTYPE_${Math.random()}`);
    
                this.data.questions.push({
                    id: id,
                    questiontype: questiontype,
                    internal_name: '',
                    questiondata: {},
                });
    
                this.activeTab = id;
            },
            submit() {
                if (!this.data.title) {
                    this.switchTab('admin');
                    this.validationNotice = true;
                    return;
                }
                const data = {
                    title: this.data.title,
                    copyable: this.data.copyable,
                    anonymous: this.data.anonymous,
                    editanswers: this.data.editanswers,
                    startdate: this.data.startdate,
                    stopdate: this.data.stopdate,
                    resultvisibility: this.data.resultvisibility
                };
                const questions = this.data.questions.map(question => ({
                    id: question.id,
                    questiontype: question.questiontype,
                    internal_name: question.internal_name,
                    questiondata: question.questiondata,
                }));
                $.post(STUDIP.URLHelper.getURL('dispatch.php/questionnaire/store/' + (this.data.id || '')), {
                    questionnaire: data,
                    questions_data: JSON.stringify(questions),
                    range_type: this.rangeType,
                    range_id: this.rangeId
                }).done(() => {
                    this.form_secured = false;
                    this.$nextTick(() => {
                        location.reload();
                    });
                }).fail(() => {
                    STUDIP.Report.error('Could not save questionnaire.', '');
                });
            },
            getIndexForQuestion(question_id) {
                for (let i in this.data.questions) {
                    if (
                        this.data.questions[i].id === question_id
                        || this.data.questions[i].id === question_id.substring(5)
                    ) {
                        return parseInt(i, 10);
                    }
                }
    
                return null;
            },
            duplicateQuestion(question_id) {
                const i = this.getIndexForQuestion(question_id);
                const id = md5(`${STUDIP.USER_ID}_QUESTIONTYPE_${Math.random()}`);
                this.data.questions.push({
                    id: id,
                    questiontype: this.data.questions[i].questiontype,
                    internal_name: this.data.questions[i].internal_name,
                    questiondata: JSON.parse(JSON.stringify(this.data.questions[i].questiondata)),
                });
                this.activeTab = id;
            },
            deleteQuestion(question_id) {
                STUDIP.Dialog.confirm(this.$gettext('Wirklich löschen?')).done(() => {
                    this.$delete(this.data.questions, this.getIndexForQuestion(question_id));
                    this.switchTab('add_question');
                })
            },
            switchTab(tab_id) {
                this.activeTab = tab_id;
                this.$nextTick(function () {
                    if (this.$refs.autofocus !== undefined) {
                        if (Array.isArray(this.$refs.autofocus)) {
                            if (typeof this.$refs.autofocus[0] !== "undefined") {
                                this.$refs.autofocus[0].focus();
                            }
                        } else {
                            this.$refs.autofocus.focus();
                        }
                    }
                });
            },
            objectsEqual(obj1, obj2) {
                return _.isEqual(obj1, obj2);
            },
            renameInternalName(question_id) {
                this.editInternalName = question_id;
                let index = this.getIndexForQuestion(question_id);
                this.tempInternalName = this.data.questions[index].internal_name;
                this.$nextTick(() => {
                    this.$refs.editInternalName[0].focus();
                });
            },
            saveInternalName(question_id) {
                let index = this.getIndexForQuestion(question_id);
                this.data.questions[index].internal_name = this.tempInternalName;
                this.editInternalName = null;
            },
            moveQuestionDown(question_id) {
                let index = this.getIndexForQuestion(question_id);
                if (index < this.data.questions.length - 1) {
                    let question = this.data.questions[index];
                    this.data.questions[index] = this.data.questions[index + 1];
                    this.data.questions[index + 1] = question;
                    this.$forceUpdate();
                }
            },
            moveQuestionUp(question_id) {
                let index = this.getIndexForQuestion(question_id);
                if (index > 0) {
                    let question = this.data.questions[index];
                    this.data.questions[index] = this.data.questions[index - 1];
                    this.data.questions[index - 1] = question;
                    this.$forceUpdate();
                }
            }
        },
        computed: {
            actionMenuItems() {
                return [
                    {label: this.$gettext('Umbenennen'), icon: 'edit', emit: 'rename'},
                    {label: this.$gettext('Frage kopieren'), icon: 'copy', emit: 'copy'},
                    {label: this.$gettext('Frage nach oben verschieben'), icon: 'arr_1up', emit: 'moveup'},
                    {label: this.$gettext('Frage nach unten verschieben'), icon: 'arr_1down', emit: 'movedown'},
                    {label: this.$gettext('Frage löschen'), icon: 'trash', emit: 'delete'},
                ];
            },
            activateFormSecure() {
                return this.form_secured && !this.objectsEqual(this.oldData, this.data);
            },
            indexForQuestion() {
                return this.getIndexForQuestion(this.activeTab);
            },
        },
        mounted() {
            this.$refs.autofocus.focus();
        },
    }
    </script>