Skip to content
Snippets Groups Projects
CoursewareDashboardStudents.vue 16.5 KiB
Newer Older
Ron Lucke's avatar
Ron Lucke committed
<template>
    <div class="cw-dashboard-students-wrapper">
        <table v-if="tasks.length > 0" class="default">
            <colgroup>
                <col />
            </colgroup>
            <thead>
                <tr>
                    <th><translate>Status</translate></th>
                    <th><translate>Aufgabentitel</translate></th>
                    <th><translate>Teilnehmende/Gruppen</translate></th>
                    <th><translate class="responsive-hidden">Seite</translate></th>
                    <th><translate>bearbeitet</translate></th>
                    <th><translate>Abgabefrist</translate></th>
                    <th><translate>Abgabe</translate></th>
                    <th class="responsive-hidden renewal"><translate>Verlängerungsanfrage</translate></th>
                    <th class="responsive-hidden feedback"><translate>Feedback</translate></th>
                </tr>
            </thead>
            <tbody>
                <tr v-for="{ task, taskGroup, status, element, user, group, feedback } in tasks" :key="task.id">
                    <td>
                        <studip-icon
                            v-if="status.shape !== undefined"
                            :shape="status.shape"
                            :role="status.role"
                            :title="status.description"
Ron Lucke's avatar
Ron Lucke committed
                            aria-hidden="true"
Ron Lucke's avatar
Ron Lucke committed
                        />
Ron Lucke's avatar
Ron Lucke committed
                        <span class="sr-only">{{ status.description }}</span>
Ron Lucke's avatar
Ron Lucke committed
                    </td>
                    <td>
                        {{ taskGroup && taskGroup.attributes.title }}
                    </td>
                    <td>
                        <span v-if="user">
Ron Lucke's avatar
Ron Lucke committed
                            <studip-icon 
                                shape="person2"
                                role="info"
                                aria-hidden="true"
                                :title="$gettext('Teilnehmende Person')" 
                            />
                            <span class="sr-only">{{ $gettext('Teilnehmende Person') }}</span>
Ron Lucke's avatar
Ron Lucke committed
                            {{ user.attributes['formatted-name'] }}
Ron Lucke's avatar
Ron Lucke committed

Ron Lucke's avatar
Ron Lucke committed
                        </span>
                        <span v-if="group">
Ron Lucke's avatar
Ron Lucke committed
                            <studip-icon
                                shape="group2"
                                role="info"
                                aria-hidden="true"
                                :title="$gettext('Gruppe')"
                            />
                            <span class="sr-only">{{ $gettext('Gruppe') }}</span>
Ron Lucke's avatar
Ron Lucke committed
                            {{ group.attributes['name'] }}
Ron Lucke's avatar
Ron Lucke committed

Ron Lucke's avatar
Ron Lucke committed
                        </span>
                    </td>
                    <td class="responsive-hidden">
                        <a v-if="task.attributes.submitted" :href="getLinkToElement(element.id)">
                            {{ element.attributes.title }}
                        </a>
                        <span v-else>{{ element.attributes.title }}</span>
                    </td>
                    <td>{{ task.attributes?.progress?.toFixed(2) || '-.--' }}%</td>
Ron Lucke's avatar
Ron Lucke committed
                    <td>{{ getReadableDate(task.attributes['submission-date']) }}</td>
                    <td>
                        <studip-icon v-if="task.attributes.submitted" shape="accept" role="status-green" />
                    </td>
                    <td class="responsive-hidden">
                        <button
                            v-show="task.attributes.renewal === 'pending'"
                            class="button"
                            @click="solveRenewalRequest(task)"
                        >
                            <translate>Anfrage bearbeiten</translate>
                        </button>
                        <span v-show="task.attributes.renewal === 'declined'">
                            <studip-icon shape="decline" role="status-red" />
                            <translate>Anfrage abgelehnt</translate>
                        </span>
                        <span v-show="task.attributes.renewal === 'granted'">
                            <translate>verlängert bis</translate>:
                            {{ getReadableDate(task.attributes['renewal-date']) }}
                        </span>
                        <studip-icon
                            v-if="task.attributes.renewal === 'declined' || task.attributes.renewal === 'granted'"
                            :title="$gettext('Anfrage bearbeiten')"
                            class="edit"
                            shape="edit"
                            role="clickable"
                            @click="solveRenewalRequest(task)"
                        />
                    </td>
                    <td class="responsive-hidden">
                        <span
                            v-if="feedback"
                            :title="
                                $gettext('Feedback geschrieben am:') +
                                ' ' +
                                getReadableDate(feedback.attributes['chdate'])
                            "
                        >
                            <studip-icon shape="accept" role="status-green" />
                            <translate>Feedback gegeben</translate>
                            <studip-icon
                                :title="$gettext('Feedback bearbeiten')"
                                class="edit"
                                shape="edit"
                                role="clickable"
                                @click="editFeedback(feedback)"
                            />
                        </span>

                        <button
                            v-show="!feedback && task.attributes.submitted"
                            class="button"
                            @click="addFeedback(task)"
                        >
                            <translate>Feedback geben</translate>
                        </button>
                    </td>
                </tr>
            </tbody>
        </table>
        <div v-else>
            <courseware-companion-box 
                mood="pointing"
                :msgCompanion="$gettext('Es wurden bisher keine Aufgaben gestellt.')"
Ron Lucke's avatar
Ron Lucke committed
            >
            </courseware-companion-box>
        </div>
        <studip-dialog
            v-if="showRenewalDialog"
            :title="text.renewalDialog.title"
            :confirmText="text.renewalDialog.confirm"
Ron Lucke's avatar
Ron Lucke committed
            confirmClass="accept"
Ron Lucke's avatar
Ron Lucke committed
            :closeText="text.renewalDialog.close"
Ron Lucke's avatar
Ron Lucke committed
            closeClass="cancel"
Ron Lucke's avatar
Ron Lucke committed
            height="350"
            @close="
                showRenewalDialog = false;
                currentDialogTask = {};
            "
            @confirm="updateRenewal"
        >
            <template v-slot:dialogContent>
                <form class="default" @submit.prevent="">
                    <label>
                        <translate>Fristverlängerung</translate>
                        <select v-model="currentDialogTask.attributes.renewal">
                            <option value="declined">
                                <translate>ablehnen</translate>
                            </option>
                            <option value="granted">
                                <translate>gewähren</translate>
                            </option>
                        </select>
                    </label>
                    <label v-if="currentDialogTask.attributes.renewal === 'granted'">
                        <translate>neue Frist</translate>
                        <courseware-date-input v-model="currentDialogTask.attributes['renewal-date']" class="size-l" />
                    </label>
                </form>
            </template>
        </studip-dialog>
        <studip-dialog
            v-if="showEditFeedbackDialog"
            :title="text.editFeedbackDialog.title"
            :confirmText="text.editFeedbackDialog.confirm"
Ron Lucke's avatar
Ron Lucke committed
            confirmClass="accept"
Ron Lucke's avatar
Ron Lucke committed
            :closeText="text.editFeedbackDialog.close"
Ron Lucke's avatar
Ron Lucke committed
            closeClass="cancel"
Ron Lucke's avatar
Ron Lucke committed
            height="420"
            @close="
                showEditFeedbackDialog = false;
                currentDialogFeedback = {};
            "
            @confirm="updateFeedback"
        >
            <template v-slot:dialogContent>
                <courseware-companion-box
                    v-if="currentDialogFeedback.attributes.content === ''"
                    mood="pointing"
                    :msgCompanion="
                        $gettext('Sie haben kein Feedback geschrieben, beim Speichern wird dieses Feedback gelöscht!')
                    "
                />
                <form class="default" @submit.prevent="">
                    <label>
                        <translate>Feedback</translate>
                        <textarea v-model="currentDialogFeedback.attributes.content" />
                    </label>
                </form>
            </template>
        </studip-dialog>
        <studip-dialog
            v-if="showAddFeedbackDialog"
            :title="text.addFeedbackDialog.title"
            :confirmText="text.addFeedbackDialog.confirm"
Ron Lucke's avatar
Ron Lucke committed
            confirmClass="accept"
Ron Lucke's avatar
Ron Lucke committed
            :closeText="text.addFeedbackDialog.close"
Ron Lucke's avatar
Ron Lucke committed
            closeClass="cancel"
Ron Lucke's avatar
Ron Lucke committed
            @close="
                showAddFeedbackDialog = false;
                currentDialogFeedback = {};
            "
            @confirm="createFeedback"
        >
            <template v-slot:dialogContent>
                <form class="default" @submit.prevent="">
                    <label>
                        <translate>Feedback</translate>
                        <textarea v-model="currentDialogFeedback.attributes.content" />
                    </label>
                </form>
            </template>
        </studip-dialog>
        <courseware-tasks-dialog-distribute v-if="showTasksDistributeDialog"/>
Ron Lucke's avatar
Ron Lucke committed
    </div>
</template>

<script>
import StudipIcon from './../StudipIcon.vue';
import StudipDialog from './../StudipDialog.vue';
import CoursewareCompanionBox from './CoursewareCompanionBox.vue';
import CoursewareDateInput from './CoursewareDateInput.vue';
import CoursewareTasksDialogDistribute from './CoursewareTasksDialogDistribute.vue';
Ron Lucke's avatar
Ron Lucke committed
import taskHelperMixin from '../../mixins/courseware/task-helper.js';
import { mapActions, mapGetters } from 'vuex';

Ron Lucke's avatar
Ron Lucke committed
export default {
    name: 'courseware-dashboard-students',
    mixins: [taskHelperMixin],
    components: {
        CoursewareCompanionBox,
        CoursewareDateInput,
        StudipIcon,
        StudipDialog,
Ron Lucke's avatar
Ron Lucke committed
    },
    data() {
        return {
            showRenewalDialog: false,
            showAddFeedbackDialog: false,
            showEditFeedbackDialog: false,
            currentDialogTask: {},
            currentDialogFeedback: {},
            text: {
                renewalDialog: {
                    title: this.$gettext('Verlängerungsanfrage bearbeiten'),
                    confirm: this.$gettext('Speichern'),
                    close: this.$gettext('Schließen'),
                },
                editFeedbackDialog: {
                    title: this.$gettext('Feedback zur Aufgabe ändern'),
                    confirm: this.$gettext('Speichern'),
                    close: this.$gettext('Schließen'),
                },
                addFeedbackDialog: {
                    title: this.$gettext('Feedback zur Aufgabe geben'),
                    confirm: this.$gettext('Speichern'),
                    close: this.$gettext('Schließen'),
                },
            },
        };
    },
    computed: {
        ...mapGetters({
            context: 'context',
            allTasks: 'courseware-tasks/all',
            userById: 'users/byId',
            statusGroupById: 'status-groups/byId',
            getElementById: 'courseware-structural-elements/byId',
            getFeedbackById: 'courseware-task-feedback/byId',
            relatedTaskGroups: 'courseware-task-groups/related',
            showTasksDistributeDialog: 'showTasksDistributeDialog'
Ron Lucke's avatar
Ron Lucke committed
        }),
        tasks() {
            return this.allTasks.map((task) => {
                const result = {
                    task,
                    taskGroup: this.relatedTaskGroups({ parent: task, relationship: 'task-group' }),
                    status: this.getStatus(task),
                    element: this.getElementById({ id: task.relationships['structural-element'].data.id }),
                    user: null,
                    group: null,
                    feedback: null,
                };
                let solver = task.relationships.solver.data;
                if (solver.type === 'users') {
                    result.user = this.userById({ id: solver.id });
                }
                if (solver.type === 'status-groups') {
                    result.group = this.statusGroupById({ id: solver.id });
                }

                const feedbackId = task.relationships['task-feedback'].data?.id;
                if (feedbackId) {
                    result.feedback = this.getFeedbackById({ id: feedbackId });
                }

                return result;
            });
        },
        managerUrl() {
            return STUDIP.URLHelper.getURL('dispatch.php/course/courseware/manager', {cid: this.context.id});
        }
    },
    methods: {
        ...mapActions({
            updateTask: 'updateTask',
            createTaskFeedback: 'createTaskFeedback',
            updateTaskFeedback: 'updateTaskFeedback',
            deleteTaskFeedback: 'deleteTaskFeedback',
            loadRemoteCoursewareStructure: 'loadRemoteCoursewareStructure',
            copyStructuralElement: 'copyStructuralElement',
            companionSuccess: 'companionSuccess',
            companionError: 'companionError',
        }),
        addFeedback(task) {
            this.currentDialogFeedback.attributes = {};
            this.currentDialogFeedback.attributes.content = '';
            this.currentDialogFeedback.relationships = {};
            this.currentDialogFeedback.relationships.task = {};
            this.currentDialogFeedback.relationships.task.data = {};
            this.currentDialogFeedback.relationships.task.data.id = task.id;
            this.currentDialogFeedback.relationships.task.data.type = task.type;
            this.showAddFeedbackDialog = true;
        },
        createFeedback() {
            if (this.currentDialogFeedback.attributes.content === '') {
                this.companionError({
                    info: this.$gettext('Bitte schreiben Sie ein Feedback.'),
                });
                return false;
            }
            this.showAddFeedbackDialog = false;
            this.createTaskFeedback({
                taskFeedback: this.currentDialogFeedback,
            });
            this.currentDialogFeedback = {};
        },
        editFeedback(feedback) {
            this.currentDialogFeedback = _.cloneDeep(feedback);
            this.showEditFeedbackDialog = true;
        },
        async updateFeedback() {
            this.showEditFeedbackDialog = false;
            let attributes = {};
            attributes.content = this.currentDialogFeedback.attributes.content;
            if (attributes.content === '') {
                await this.deleteTaskFeedback({
                    taskFeedbackId: this.currentDialogFeedback.id,
                });
                this.companionSuccess({
                    info: this.$gettext('Feedback wurde gelöscht.'),
                });
            } else {
                await this.updateTaskFeedback({
                    attributes: attributes,
                    taskFeedbackId: this.currentDialogFeedback.id,
                });
                this.companionSuccess({
                    info: this.$gettext('Feedback wurde gespeichert.'),
                });
            }

            this.currentDialogFeedback = {};
        },
        solveRenewalRequest(task) {
            this.currentDialogTask = _.cloneDeep(task);
Ron Lucke's avatar
Ron Lucke committed
            this.currentDialogTask.attributes['renewal-date'] = new Date().toISOString();
Ron Lucke's avatar
Ron Lucke committed
            this.showRenewalDialog = true;
        },
        updateRenewal() {
            this.showRenewalDialog = false;
            let attributes = {};
            attributes.renewal = this.currentDialogTask.attributes.renewal;
            if (attributes.renewal === 'granted') {
                attributes['renewal-date'] = new Date(this.currentDialogTask.attributes['renewal-date'] || Date.now()).toISOString();
Ron Lucke's avatar
Ron Lucke committed
            }

            this.updateTask({
                attributes: attributes,
                taskId: this.currentDialogTask.id,
            });
            this.currentDialogTask = {};
        },
    },
};
</script>