Finish refactoring the Assignment component
This commit is contained in:
parent
cfb20c00c3
commit
8cd508ce31
|
|
@ -23,6 +23,8 @@ const documents = {
|
||||||
"\n fragment AssignmentParts on AssignmentNode {\n id\n title\n assignment\n solution\n submission {\n ...SubmissionParts\n }\n }\n": types.AssignmentPartsFragmentDoc,
|
"\n fragment AssignmentParts on AssignmentNode {\n id\n title\n assignment\n solution\n submission {\n ...SubmissionParts\n }\n }\n": types.AssignmentPartsFragmentDoc,
|
||||||
"\n query AssignmentQuery($id: ID!) {\n assignment(id: $id) {\n ...AssignmentParts\n }\n }\n ": types.AssignmentQueryDocument,
|
"\n query AssignmentQuery($id: ID!) {\n assignment(id: $id) {\n ...AssignmentParts\n }\n }\n ": types.AssignmentQueryDocument,
|
||||||
"\n mutation UpdateAssignment($input: UpdateAssignmentInput!) {\n updateAssignment(input: $input) {\n updatedAssignment {\n ...AssignmentParts\n }\n }\n }\n ": types.UpdateAssignmentDocument,
|
"\n mutation UpdateAssignment($input: UpdateAssignmentInput!) {\n updateAssignment(input: $input) {\n updatedAssignment {\n ...AssignmentParts\n }\n }\n }\n ": types.UpdateAssignmentDocument,
|
||||||
|
"\n mutation UpdateAssignmentWithSuccess($input: UpdateAssignmentInput!) {\n updateAssignment(input: $input) {\n successful\n updatedAssignment {\n ...AssignmentParts\n }\n }\n }\n ": types.UpdateAssignmentWithSuccessDocument,
|
||||||
|
"\n mutation SpellCheck($input: SpellCheckInput!) {\n spellCheck(input: $input) {\n correct\n results {\n sentence\n offset\n sentenceOffset\n length\n affected\n corrected\n }\n }\n }\n ": types.SpellCheckDocument,
|
||||||
"\n fragment ModuleHighlightsFragment on ModuleNode {\n id\n __typename\n slug\n highlights {\n ...HighlightParts\n }\n }\n": types.ModuleHighlightsFragmentFragmentDoc,
|
"\n fragment ModuleHighlightsFragment on ModuleNode {\n id\n __typename\n slug\n highlights {\n ...HighlightParts\n }\n }\n": types.ModuleHighlightsFragmentFragmentDoc,
|
||||||
"\n fragment ModuleLevelFragment on ModuleLevelNode {\n name\n id\n filterAttributeType\n }\n": types.ModuleLevelFragmentFragmentDoc,
|
"\n fragment ModuleLevelFragment on ModuleLevelNode {\n name\n id\n filterAttributeType\n }\n": types.ModuleLevelFragmentFragmentDoc,
|
||||||
"\n query ModuleFilterQuery {\n moduleLevels {\n ...ModuleLevelFragment\n }\n moduleCategories {\n name\n id\n filterAttributeType\n }\n me {\n language @client\n }\n }\n ": types.ModuleFilterQueryDocument,
|
"\n query ModuleFilterQuery {\n moduleLevels {\n ...ModuleLevelFragment\n }\n moduleCategories {\n name\n id\n filterAttributeType\n }\n me {\n language @client\n }\n }\n ": types.ModuleFilterQueryDocument,
|
||||||
|
|
@ -36,6 +38,11 @@ const documents = {
|
||||||
"\n mutation UpdateHighlight($input: UpdateHighlightInput!) {\n updateHighlight(input: $input) {\n highlight {\n ...HighlightParts\n }\n }\n }\n": types.UpdateHighlightDocument,
|
"\n mutation UpdateHighlight($input: UpdateHighlightInput!) {\n updateHighlight(input: $input) {\n highlight {\n ...HighlightParts\n }\n }\n }\n": types.UpdateHighlightDocument,
|
||||||
"\n mutation AddHighlight($input: AddHighlightInput!) {\n addHighlight(input: $input) {\n __typename\n highlight {\n ...HighlightParts\n }\n }\n }\n": types.AddHighlightDocument,
|
"\n mutation AddHighlight($input: AddHighlightInput!) {\n addHighlight(input: $input) {\n __typename\n highlight {\n ...HighlightParts\n }\n }\n }\n": types.AddHighlightDocument,
|
||||||
"\n mutation AddContentHighlight($input: AddContentHighlightInput!) {\n addContentHighlight(input: $input) {\n __typename\n highlight {\n ...HighlightParts\n }\n }\n }\n": types.AddContentHighlightDocument,
|
"\n mutation AddContentHighlight($input: AddContentHighlightInput!) {\n addContentHighlight(input: $input) {\n __typename\n highlight {\n ...HighlightParts\n }\n }\n }\n": types.AddContentHighlightDocument,
|
||||||
|
"\n fragment SchoolClassParts on SchoolClassNode {\n id\n name\n }\n": types.SchoolClassPartsFragmentDoc,
|
||||||
|
"\n fragment UserParts on PrivateUserNode {\n id\n pk\n username\n email\n firstName\n lastName\n avatarUrl\n expiryDate\n readOnly\n lastModuleLevel {\n id\n name\n filterAttributeType\n }\n lastModule {\n id\n slug\n }\n lastTopic {\n id\n slug\n }\n selectedClass {\n id\n readOnly\n }\n recentModules(orderBy: \"-visited\") {\n edges {\n node {\n ...ModuleParts\n }\n }\n }\n schoolClasses {\n ...SchoolClassParts\n }\n }\n": types.UserPartsFragmentDoc,
|
||||||
|
"\n fragment TeamParts on TeamNode {\n name\n code\n id\n members {\n firstName\n lastName\n id\n isMe\n }\n }\n": types.TeamPartsFragmentDoc,
|
||||||
|
"\n fragment ModuleParts on ModuleNode {\n id\n title\n metaTitle\n teaser\n intro\n slug\n heroImage\n heroSource\n solutionsEnabled\n highlights {\n ...HighlightParts\n }\n language\n inEditMode @client\n level {\n id\n name\n }\n category {\n id\n name\n }\n topic {\n slug\n title\n }\n bookmark {\n note {\n id\n text\n }\n }\n }\n": types.ModulePartsFragmentDoc,
|
||||||
|
"\n query MeQuery {\n me {\n ...UserParts\n team {\n ...TeamParts\n }\n isTeacher\n permissions\n onboardingVisited\n }\n }\n ": types.MeQueryDocument,
|
||||||
"\n fragment InstrumentHighlightsWithIdOnlyFragment on InstrumentNode {\n highlights {\n id\n }\n }\n ": types.InstrumentHighlightsWithIdOnlyFragmentFragmentDoc,
|
"\n fragment InstrumentHighlightsWithIdOnlyFragment on InstrumentNode {\n highlights {\n id\n }\n }\n ": types.InstrumentHighlightsWithIdOnlyFragmentFragmentDoc,
|
||||||
"\n fragment ChapterHighlightsWithIdOnlyFragment on ChapterNode {\n highlights {\n id\n }\n }\n ": types.ChapterHighlightsWithIdOnlyFragmentFragmentDoc,
|
"\n fragment ChapterHighlightsWithIdOnlyFragment on ChapterNode {\n highlights {\n id\n }\n }\n ": types.ChapterHighlightsWithIdOnlyFragmentFragmentDoc,
|
||||||
"\n fragment ModuleHighlightsWithIdOnlyFragment on ModuleNode {\n highlights {\n id\n }\n }\n ": types.ModuleHighlightsWithIdOnlyFragmentFragmentDoc,
|
"\n fragment ModuleHighlightsWithIdOnlyFragment on ModuleNode {\n highlights {\n id\n }\n }\n ": types.ModuleHighlightsWithIdOnlyFragmentFragmentDoc,
|
||||||
|
|
@ -107,6 +114,14 @@ export function graphql(source: "\n query AssignmentQuery($id: ID!) {\n
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n mutation UpdateAssignment($input: UpdateAssignmentInput!) {\n updateAssignment(input: $input) {\n updatedAssignment {\n ...AssignmentParts\n }\n }\n }\n "): (typeof documents)["\n mutation UpdateAssignment($input: UpdateAssignmentInput!) {\n updateAssignment(input: $input) {\n updatedAssignment {\n ...AssignmentParts\n }\n }\n }\n "];
|
export function graphql(source: "\n mutation UpdateAssignment($input: UpdateAssignmentInput!) {\n updateAssignment(input: $input) {\n updatedAssignment {\n ...AssignmentParts\n }\n }\n }\n "): (typeof documents)["\n mutation UpdateAssignment($input: UpdateAssignmentInput!) {\n updateAssignment(input: $input) {\n updatedAssignment {\n ...AssignmentParts\n }\n }\n }\n "];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n mutation UpdateAssignmentWithSuccess($input: UpdateAssignmentInput!) {\n updateAssignment(input: $input) {\n successful\n updatedAssignment {\n ...AssignmentParts\n }\n }\n }\n "): (typeof documents)["\n mutation UpdateAssignmentWithSuccess($input: UpdateAssignmentInput!) {\n updateAssignment(input: $input) {\n successful\n updatedAssignment {\n ...AssignmentParts\n }\n }\n }\n "];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n mutation SpellCheck($input: SpellCheckInput!) {\n spellCheck(input: $input) {\n correct\n results {\n sentence\n offset\n sentenceOffset\n length\n affected\n corrected\n }\n }\n }\n "): (typeof documents)["\n mutation SpellCheck($input: SpellCheckInput!) {\n spellCheck(input: $input) {\n correct\n results {\n sentence\n offset\n sentenceOffset\n length\n affected\n corrected\n }\n }\n }\n "];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
|
@ -159,6 +174,26 @@ export function graphql(source: "\n mutation AddHighlight($input: AddHighlightI
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
export function graphql(source: "\n mutation AddContentHighlight($input: AddContentHighlightInput!) {\n addContentHighlight(input: $input) {\n __typename\n highlight {\n ...HighlightParts\n }\n }\n }\n"): (typeof documents)["\n mutation AddContentHighlight($input: AddContentHighlightInput!) {\n addContentHighlight(input: $input) {\n __typename\n highlight {\n ...HighlightParts\n }\n }\n }\n"];
|
export function graphql(source: "\n mutation AddContentHighlight($input: AddContentHighlightInput!) {\n addContentHighlight(input: $input) {\n __typename\n highlight {\n ...HighlightParts\n }\n }\n }\n"): (typeof documents)["\n mutation AddContentHighlight($input: AddContentHighlightInput!) {\n addContentHighlight(input: $input) {\n __typename\n highlight {\n ...HighlightParts\n }\n }\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n fragment SchoolClassParts on SchoolClassNode {\n id\n name\n }\n"): (typeof documents)["\n fragment SchoolClassParts on SchoolClassNode {\n id\n name\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n fragment UserParts on PrivateUserNode {\n id\n pk\n username\n email\n firstName\n lastName\n avatarUrl\n expiryDate\n readOnly\n lastModuleLevel {\n id\n name\n filterAttributeType\n }\n lastModule {\n id\n slug\n }\n lastTopic {\n id\n slug\n }\n selectedClass {\n id\n readOnly\n }\n recentModules(orderBy: \"-visited\") {\n edges {\n node {\n ...ModuleParts\n }\n }\n }\n schoolClasses {\n ...SchoolClassParts\n }\n }\n"): (typeof documents)["\n fragment UserParts on PrivateUserNode {\n id\n pk\n username\n email\n firstName\n lastName\n avatarUrl\n expiryDate\n readOnly\n lastModuleLevel {\n id\n name\n filterAttributeType\n }\n lastModule {\n id\n slug\n }\n lastTopic {\n id\n slug\n }\n selectedClass {\n id\n readOnly\n }\n recentModules(orderBy: \"-visited\") {\n edges {\n node {\n ...ModuleParts\n }\n }\n }\n schoolClasses {\n ...SchoolClassParts\n }\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n fragment TeamParts on TeamNode {\n name\n code\n id\n members {\n firstName\n lastName\n id\n isMe\n }\n }\n"): (typeof documents)["\n fragment TeamParts on TeamNode {\n name\n code\n id\n members {\n firstName\n lastName\n id\n isMe\n }\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n fragment ModuleParts on ModuleNode {\n id\n title\n metaTitle\n teaser\n intro\n slug\n heroImage\n heroSource\n solutionsEnabled\n highlights {\n ...HighlightParts\n }\n language\n inEditMode @client\n level {\n id\n name\n }\n category {\n id\n name\n }\n topic {\n slug\n title\n }\n bookmark {\n note {\n id\n text\n }\n }\n }\n"): (typeof documents)["\n fragment ModuleParts on ModuleNode {\n id\n title\n metaTitle\n teaser\n intro\n slug\n heroImage\n heroSource\n solutionsEnabled\n highlights {\n ...HighlightParts\n }\n language\n inEditMode @client\n level {\n id\n name\n }\n category {\n id\n name\n }\n topic {\n slug\n title\n }\n bookmark {\n note {\n id\n text\n }\n }\n }\n"];
|
||||||
|
/**
|
||||||
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
|
*/
|
||||||
|
export function graphql(source: "\n query MeQuery {\n me {\n ...UserParts\n team {\n ...TeamParts\n }\n isTeacher\n permissions\n onboardingVisited\n }\n }\n "): (typeof documents)["\n query MeQuery {\n me {\n ...UserParts\n team {\n ...TeamParts\n }\n isTeacher\n permissions\n onboardingVisited\n }\n }\n "];
|
||||||
/**
|
/**
|
||||||
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -58,13 +58,18 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { nextTick, onMounted, ref, watch } from 'vue';
|
import { defineAsyncComponent, nextTick, onMounted, ref, watch } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
import { useMutation, useQuery } from '@vue/apollo-composable';
|
import { useMutation, useQuery } from '@vue/apollo-composable';
|
||||||
import { graphql } from '@/__generated__';
|
import { graphql } from '@/__generated__';
|
||||||
import { HighlightNode } from '@/__generated__/graphql';
|
import { HighlightNode } from '@/__generated__/graphql';
|
||||||
import { computed } from '@vue/reactivity';
|
import { computed } from '@vue/reactivity';
|
||||||
import { markHighlight } from '@/helpers/highlight';
|
import { markHighlight } from '@/helpers/highlight';
|
||||||
|
import { sanitize } from '@/helpers/text';
|
||||||
|
import { matomoTrackEvent } from '@/helpers/matomo-client';
|
||||||
|
import { getMe } from '@/graphql/queries';
|
||||||
|
import { PAGE_LOAD_TIMEOUT } from '@/consts/navigation.consts';
|
||||||
|
import debounce from 'lodash/debounce';
|
||||||
import Mark from 'mark.js';
|
import Mark from 'mark.js';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
|
|
@ -72,6 +77,10 @@ export interface Props {
|
||||||
highlights: HighlightNode[];
|
highlights: HighlightNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SubmissionForm = defineAsyncComponent(() => import('@/components/content-blocks/assignment/SubmissionForm.vue'));
|
||||||
|
const Solution = defineAsyncComponent(() => import('@/components/content-blocks/Solution.vue'));
|
||||||
|
const SpellCheck = defineAsyncComponent(() => import('@/components/content-blocks/assignment/SpellCheck.vue'));
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const assignmentDiv = ref<HTMLElement | null>(null);
|
const assignmentDiv = ref<HTMLElement | null>(null);
|
||||||
|
|
||||||
|
|
@ -84,6 +93,9 @@ const initialSubmission = {
|
||||||
};
|
};
|
||||||
const assignment = ref({ submission: initialSubmission });
|
const assignment = ref({ submission: initialSubmission });
|
||||||
const corrections = ref('');
|
const corrections = ref('');
|
||||||
|
const unsaved = ref(false);
|
||||||
|
const saving = ref(0);
|
||||||
|
const spellcheckLoading = ref(false);
|
||||||
|
|
||||||
graphql(`
|
graphql(`
|
||||||
fragment SubmissionParts on StudentSubmissionNode {
|
fragment SubmissionParts on StudentSubmissionNode {
|
||||||
|
|
@ -102,7 +114,7 @@ graphql(`
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
graphql(`
|
const assignmentFragment = graphql(`
|
||||||
fragment AssignmentParts on AssignmentNode {
|
fragment AssignmentParts on AssignmentNode {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
|
|
@ -114,6 +126,8 @@ graphql(`
|
||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
const { me } = getMe();
|
||||||
|
|
||||||
const { result, onResult } = useQuery(
|
const { result, onResult } = useQuery(
|
||||||
graphql(`
|
graphql(`
|
||||||
query AssignmentQuery($id: ID!) {
|
query AssignmentQuery($id: ID!) {
|
||||||
|
|
@ -136,6 +150,35 @@ const { mutate: doUpdateAssignment } = useMutation(
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
);
|
);
|
||||||
|
const { mutate: doUpdateAssignmentWithSuccess } = useMutation(
|
||||||
|
graphql(`
|
||||||
|
mutation UpdateAssignmentWithSuccess($input: UpdateAssignmentInput!) {
|
||||||
|
updateAssignment(input: $input) {
|
||||||
|
successful
|
||||||
|
updatedAssignment {
|
||||||
|
...AssignmentParts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
);
|
||||||
|
const { mutate: doSpellCheck } = useMutation(
|
||||||
|
graphql(`
|
||||||
|
mutation SpellCheck($input: SpellCheckInput!) {
|
||||||
|
spellCheck(input: $input) {
|
||||||
|
correct
|
||||||
|
results {
|
||||||
|
sentence
|
||||||
|
offset
|
||||||
|
sentenceOffset
|
||||||
|
length
|
||||||
|
affected
|
||||||
|
corrected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (assignmentDiv.value !== null) {
|
if (assignmentDiv.value !== null) {
|
||||||
|
|
@ -152,6 +195,23 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const submission = computed(() => {
|
||||||
|
return assignment.value?.submission || {};
|
||||||
|
});
|
||||||
|
const isStudent = computed(() => {
|
||||||
|
return !me.value.isTeacher;
|
||||||
|
});
|
||||||
|
const solution = computed(() => {
|
||||||
|
return {
|
||||||
|
text: assignment.value.solution,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const feedbackText = computed(() => {
|
||||||
|
let feedback = assignment.value.submission.submissionFeedback;
|
||||||
|
let sanitizedFeedbackText = sanitize(feedback.text);
|
||||||
|
return `<span class="inline-title">Feedback von ${feedback.teacher.firstName} ${feedback.teacher.lastName}:</span> ${sanitizedFeedbackText}`;
|
||||||
|
});
|
||||||
|
|
||||||
const childElements = computed(() => {
|
const childElements = computed(() => {
|
||||||
// todo: refactor and merge with the one in ContentComponent.vue
|
// todo: refactor and merge with the one in ContentComponent.vue
|
||||||
if (assignmentDiv.value) {
|
if (assignmentDiv.value) {
|
||||||
|
|
@ -264,87 +324,20 @@ const reopen = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
const _save = debounce(function (submission) {
|
||||||
|
saving.value++;
|
||||||
<script lang="ts">
|
const variables = {
|
||||||
import { mapActions, mapGetters } from 'vuex';
|
|
||||||
import ASSIGNMENT_QUERY from '@/graphql/gql/queries/assignmentQuery.gql';
|
|
||||||
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
|
|
||||||
import UPDATE_ASSIGNMENT_MUTATION_WITH_SUCCESS from '@/graphql/gql/mutations/updateAssignmentMutationWithSuccess.gql';
|
|
||||||
import SPELL_CHECK_MUTATION from '@/graphql/gql/mutations/spellCheck.gql';
|
|
||||||
import debounce from 'lodash/debounce';
|
|
||||||
import { sanitize } from '@/helpers/text';
|
|
||||||
import { defineAsyncComponent } from 'vue';
|
|
||||||
import { matomoTrackEvent } from '@/helpers/matomo-client';
|
|
||||||
import { PAGE_LOAD_TIMEOUT } from '@/consts/navigation.consts';
|
|
||||||
|
|
||||||
const SubmissionForm = defineAsyncComponent(() => import('@/components/content-blocks/assignment/SubmissionForm.vue'));
|
|
||||||
const Solution = defineAsyncComponent(() => import('@/components/content-blocks/Solution.vue'));
|
|
||||||
const SpellCheck = defineAsyncComponent(() => import('@/components/content-blocks/assignment/SpellCheck.vue'));
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Solution,
|
|
||||||
SubmissionForm,
|
|
||||||
SpellCheck,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
me: {
|
|
||||||
permissions: [],
|
|
||||||
},
|
|
||||||
inputType: 'text',
|
|
||||||
unsaved: false,
|
|
||||||
saving: 0,
|
|
||||||
spellcheckLoading: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
...mapGetters(['scrollToAssignmentId']),
|
|
||||||
final() {
|
|
||||||
return !!this.submission && this.submission.final;
|
|
||||||
},
|
|
||||||
submission() {
|
|
||||||
return this.assignment.submission ? this.assignment.submission : {};
|
|
||||||
},
|
|
||||||
isStudent() {
|
|
||||||
return !this.me.permissions.includes('users.can_manage_school_class_content');
|
|
||||||
},
|
|
||||||
solution() {
|
|
||||||
return {
|
|
||||||
text: this.assignment.solution,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
id() {
|
|
||||||
return this.assignment.id ? this.assignment.id.replace(/=/g, '') : '';
|
|
||||||
},
|
|
||||||
feedbackText() {
|
|
||||||
let feedback = this.assignment.submission.submissionFeedback;
|
|
||||||
let sanitizedFeedbackText = sanitize(feedback.text);
|
|
||||||
return `<span class="inline-title">Feedback von ${feedback.teacher.firstName} ${feedback.teacher.lastName}:</span> ${sanitizedFeedbackText}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
...mapActions(['scrollToAssignmentReady']),
|
|
||||||
_save: debounce(function (submission) {
|
|
||||||
this.saving++;
|
|
||||||
this.$apollo
|
|
||||||
.mutate({
|
|
||||||
mutation: UPDATE_ASSIGNMENT_MUTATION_WITH_SUCCESS,
|
|
||||||
variables: {
|
|
||||||
input: {
|
input: {
|
||||||
assignment: {
|
assignment: {
|
||||||
id: this.assignment.id,
|
id: assignment.value.id,
|
||||||
answer: this.assignment.submission.text,
|
answer: assignment.value.submission.text,
|
||||||
document: this.assignment.submission.document,
|
document: assignment.value.submission.document,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
doUpdateAssignmentWithSuccess(variables, {
|
||||||
update(
|
update(
|
||||||
store,
|
cache,
|
||||||
{
|
{
|
||||||
data: {
|
data: {
|
||||||
updateAssignment: { successful, updatedAssignment },
|
updateAssignment: { successful, updatedAssignment },
|
||||||
|
|
@ -353,92 +346,76 @@ export default {
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
if (successful) {
|
if (successful) {
|
||||||
const query = ASSIGNMENT_QUERY;
|
const id = cache.identify({
|
||||||
const variables = {
|
|
||||||
id: updatedAssignment.id,
|
id: updatedAssignment.id,
|
||||||
};
|
__typename: updatedAssignment.__typename,
|
||||||
const assignment = Object.assign({}, updatedAssignment, {
|
});
|
||||||
submission,
|
const fragment = assignmentFragment;
|
||||||
|
const fragmentName = 'AssignmentParts';
|
||||||
|
cache.writeFragment({
|
||||||
|
fragment,
|
||||||
|
fragmentName,
|
||||||
|
id,
|
||||||
|
data: {
|
||||||
|
...updatedAssignment,
|
||||||
|
submission,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const data = {
|
|
||||||
assignment,
|
|
||||||
};
|
|
||||||
store.writeQuery({ query, variables, data });
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
// Query did not exist in the cache, and apollo throws a generic Error. Do nothing
|
// Query did not exist in the cache, and apollo throws a generic Error. Do nothing
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
}).then(() => {
|
||||||
.then(() => {
|
saving.value--;
|
||||||
this.saving--;
|
if (saving.value === 0) {
|
||||||
if (this.saving === 0) {
|
unsaved.value = false;
|
||||||
this.unsaved = false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 500),
|
}, 500);
|
||||||
saveInput: function (answer) {
|
const saveInput = (answer: string) => {
|
||||||
// reset corrections on input
|
// reset corrections on input
|
||||||
this.corrections = '';
|
corrections.value = '';
|
||||||
this.unsaved = true;
|
unsaved.value = true;
|
||||||
/*
|
/*
|
||||||
We update the assignment on this component, so the changes are reflected on it. The server does not return
|
We update the assignment on this component, so the changes are reflected on it. The server does not return
|
||||||
the updated entity, to prevent the UI to update when the user is entering his input
|
the updated entity, to prevent the UI to update when the user is entering his input
|
||||||
*/
|
*/
|
||||||
this.assignment.submission.text = answer;
|
assignment.value.submission.text = answer;
|
||||||
this._save(this.assignment.submission);
|
_save(assignment.value.submission);
|
||||||
|
|
||||||
if (this.assignment.submission.text.length > 0) {
|
if (assignment.value.submission.text.length > 0) {
|
||||||
matomoTrackEvent('Auftrag', 'Text mit Eingabe gespeichert', this.assignment.title);
|
matomoTrackEvent('Auftrag', 'Text mit Eingabe gespeichert', assignment.value.title);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
changeDocumentUrl(documentUrl) {
|
const changeDocumentUrl = (documentUrl: string) => {
|
||||||
this.assignment.submission.document = documentUrl;
|
assignment.value.submission.document = documentUrl;
|
||||||
this._save(this.assignment.submission);
|
_save(assignment.value.submission);
|
||||||
},
|
};
|
||||||
initialSubmission() {
|
const spellcheck = () => {
|
||||||
return {
|
spellcheckLoading.value = true;
|
||||||
text: '',
|
const variables = {
|
||||||
document: '',
|
|
||||||
final: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
spellcheck() {
|
|
||||||
let self = this;
|
|
||||||
this.spellcheckLoading = true;
|
|
||||||
this.$apollo
|
|
||||||
.mutate({
|
|
||||||
mutation: SPELL_CHECK_MUTATION,
|
|
||||||
variables: {
|
|
||||||
input: {
|
input: {
|
||||||
assignment: this.assignment.id,
|
assignment: assignment.value.id,
|
||||||
text: this.assignment.submission.text,
|
text: assignment.value.submission.text,
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
doSpellCheck(variables, {
|
||||||
update(
|
update(
|
||||||
store,
|
_cache,
|
||||||
{
|
{
|
||||||
data: {
|
data: {
|
||||||
spellCheck: { results },
|
spellCheck: { results },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
self.corrections = results;
|
corrections.value = results;
|
||||||
},
|
},
|
||||||
})
|
}).then(() => {
|
||||||
.then(() => {
|
spellcheckLoading.value = false;
|
||||||
this.spellcheckLoading = false;
|
|
||||||
});
|
});
|
||||||
matomoTrackEvent('Auftrag', 'Rechtschreibung geprüft', this.assignment.title);
|
matomoTrackEvent('Auftrag', 'Rechtschreibung geprüft', assignment.value.title);
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
apollo: {
|
|
||||||
me: {
|
|
||||||
query: ME_QUERY,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ export default {
|
||||||
slug,
|
slug,
|
||||||
});
|
});
|
||||||
const fragment = MODULE_FRAGMENT;
|
const fragment = MODULE_FRAGMENT;
|
||||||
const fragmentName = 'ModuleParts';
|
const fragmentName = 'ModuleLegacyParts';
|
||||||
const module = store.readFragment({ fragment, fragmentName, id });
|
const module = store.readFragment({ fragment, fragmentName, id });
|
||||||
const data = {
|
const data = {
|
||||||
...module,
|
...module,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#import "./highlightParts.gql"
|
#import "./highlightParts.gql"
|
||||||
fragment ModuleParts on ModuleNode {
|
fragment ModuleLegacyParts on ModuleNode {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
metaTitle
|
metaTitle
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@ fragment RoomParts on RoomNode {
|
||||||
description
|
description
|
||||||
restricted
|
restricted
|
||||||
schoolClass {
|
schoolClass {
|
||||||
...SchoolClassParts
|
...SchoolClassLegacyParts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
fragment SchoolClassParts on SchoolClassNode {
|
fragment SchoolClassLegacyParts on SchoolClassNode {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
fragment TeamParts on TeamNode {
|
fragment TeamLegacyParts on TeamNode {
|
||||||
name
|
name
|
||||||
code
|
code
|
||||||
id
|
id
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@ fragment TopicParts on TopicNode {
|
||||||
vimeoId
|
vimeoId
|
||||||
instructions
|
instructions
|
||||||
modules {
|
modules {
|
||||||
...ModuleParts
|
...ModuleLegacyParts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#import "./schoolClassParts.gql"
|
#import "./schoolClassParts.gql"
|
||||||
#import "./moduleParts.gql"
|
#import "./moduleParts.gql"
|
||||||
fragment UserParts on PrivateUserNode {
|
fragment UserLegacyParts on PrivateUserNode {
|
||||||
id
|
id
|
||||||
pk
|
pk
|
||||||
username
|
username
|
||||||
|
|
@ -30,11 +30,11 @@ fragment UserParts on PrivateUserNode {
|
||||||
recentModules(orderBy: "-visited") {
|
recentModules(orderBy: "-visited") {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...ModuleParts
|
...ModuleLegacyParts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
schoolClasses {
|
schoolClasses {
|
||||||
...SchoolClassParts
|
...SchoolClassLegacyParts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ mutation CreateTeamMutation($input: CreateTeamInput!) {
|
||||||
result {
|
result {
|
||||||
__typename
|
__typename
|
||||||
... on TeamNode {
|
... on TeamNode {
|
||||||
...TeamParts
|
...TeamLegacyParts
|
||||||
}
|
}
|
||||||
... on DuplicateName {
|
... on DuplicateName {
|
||||||
reason
|
reason
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ mutation JoinTeamMutation($input: JoinTeamInput!) {
|
||||||
joinTeam(input: $input) {
|
joinTeam(input: $input) {
|
||||||
success
|
success
|
||||||
team {
|
team {
|
||||||
...TeamParts
|
...TeamLegacyParts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ mutation ApplySnapshot($input: ApplySnapshotInput!) {
|
||||||
applySnapshot(input: $input) {
|
applySnapshot(input: $input) {
|
||||||
success
|
success
|
||||||
module {
|
module {
|
||||||
...ModuleParts
|
...ModuleLegacyParts
|
||||||
objectiveGroups {
|
objectiveGroups {
|
||||||
...ObjectiveGroupParts
|
...ObjectiveGroupParts
|
||||||
objectives {
|
objectives {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
mutation UpdateLastModule($input: UpdateLastModuleInput!) {
|
mutation UpdateLastModule($input: UpdateLastModuleInput!) {
|
||||||
updateLastModule(input: $input) {
|
updateLastModule(input: $input) {
|
||||||
lastModule {
|
lastModule {
|
||||||
...ModuleParts
|
...ModuleLegacyParts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ query ModulesQuery {
|
||||||
modules {
|
modules {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...ModuleParts
|
...ModuleLegacyParts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ query AssignmentWithSubmissions($id: ID!) {
|
||||||
firstName
|
firstName
|
||||||
lastName
|
lastName
|
||||||
schoolClasses {
|
schoolClasses {
|
||||||
...SchoolClassParts
|
...SchoolClassLegacyParts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
submissionFeedback {
|
submissionFeedback {
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
#import "../fragments/teamParts.gql"
|
#import "../fragments/teamParts.gql"
|
||||||
query MeQuery {
|
query MeQuery {
|
||||||
me {
|
me {
|
||||||
...UserParts
|
...UserLegacyParts
|
||||||
team {
|
team {
|
||||||
...TeamParts
|
...TeamLegacyParts
|
||||||
}
|
}
|
||||||
isTeacher
|
isTeacher
|
||||||
permissions
|
permissions
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#import "../fragments/moduleParts.gql"
|
#import "../fragments/moduleParts.gql"
|
||||||
query ModuleQuery($id: ID, $slug: String) {
|
query ModuleQuery($id: ID, $slug: String) {
|
||||||
module(id: $id, slug: $slug) {
|
module(id: $id, slug: $slug) {
|
||||||
...ModuleParts
|
...ModuleLegacyParts
|
||||||
chapters {
|
chapters {
|
||||||
id
|
id
|
||||||
contentBlocks {
|
contentBlocks {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#import "gql/fragments/contentBlockParts.gql"
|
#import "gql/fragments/contentBlockParts.gql"
|
||||||
query ModuleDetailsQuery($slug: String, $id: ID) {
|
query ModuleDetailsQuery($slug: String, $id: ID) {
|
||||||
module(slug: $slug, id: $id) {
|
module(slug: $slug, id: $id) {
|
||||||
...ModuleParts
|
...ModuleLegacyParts
|
||||||
objectiveGroups {
|
objectiveGroups {
|
||||||
...ObjectiveGroupParts
|
...ObjectiveGroupParts
|
||||||
objectives {
|
objectives {
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,120 @@ export function meQuery() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export { getModule };
|
graphql(`
|
||||||
|
fragment SchoolClassParts on SchoolClassNode {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
graphql(`
|
||||||
|
fragment UserParts on PrivateUserNode {
|
||||||
|
id
|
||||||
|
pk
|
||||||
|
username
|
||||||
|
email
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
avatarUrl
|
||||||
|
expiryDate
|
||||||
|
readOnly
|
||||||
|
lastModuleLevel {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
filterAttributeType
|
||||||
|
}
|
||||||
|
lastModule {
|
||||||
|
id
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
lastTopic {
|
||||||
|
id
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
selectedClass {
|
||||||
|
id
|
||||||
|
readOnly
|
||||||
|
}
|
||||||
|
recentModules(orderBy: "-visited") {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
...ModuleParts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
schoolClasses {
|
||||||
|
...SchoolClassParts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
graphql(`
|
||||||
|
fragment TeamParts on TeamNode {
|
||||||
|
name
|
||||||
|
code
|
||||||
|
id
|
||||||
|
members {
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
id
|
||||||
|
isMe
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
graphql(`
|
||||||
|
fragment ModuleParts on ModuleNode {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
metaTitle
|
||||||
|
teaser
|
||||||
|
intro
|
||||||
|
slug
|
||||||
|
heroImage
|
||||||
|
heroSource
|
||||||
|
solutionsEnabled
|
||||||
|
highlights {
|
||||||
|
...HighlightParts
|
||||||
|
}
|
||||||
|
language
|
||||||
|
inEditMode @client
|
||||||
|
level {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
category {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
topic {
|
||||||
|
slug
|
||||||
|
title
|
||||||
|
}
|
||||||
|
bookmark {
|
||||||
|
note {
|
||||||
|
id
|
||||||
|
text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const getMe = () => {
|
||||||
|
const query = graphql(`
|
||||||
|
query MeQuery {
|
||||||
|
me {
|
||||||
|
...UserParts
|
||||||
|
team {
|
||||||
|
...TeamParts
|
||||||
|
}
|
||||||
|
isTeacher
|
||||||
|
permissions
|
||||||
|
onboardingVisited
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
const { result } = useQuery(query);
|
||||||
|
const me = computed(() => result.value?.me || { isTeacher: false });
|
||||||
|
|
||||||
|
return { me };
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getModule, getMe };
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ export const constructNoteMutation = (n) => {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
let fragment = MODULE_FRAGMENT;
|
let fragment = MODULE_FRAGMENT;
|
||||||
const fragmentName = 'ModuleParts';
|
const fragmentName = 'ModuleLegacyParts';
|
||||||
let id = store.identify({
|
let id = store.identify({
|
||||||
__typename: 'ModuleNode',
|
__typename: 'ModuleNode',
|
||||||
slug: n.parent,
|
slug: n.parent,
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ export default {
|
||||||
slug: slug,
|
slug: slug,
|
||||||
});
|
});
|
||||||
|
|
||||||
const fragmentName = 'ModuleParts';
|
const fragmentName = 'ModuleLegacyParts';
|
||||||
const module = store.readFragment({
|
const module = store.readFragment({
|
||||||
fragment,
|
fragment,
|
||||||
id,
|
id,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue