Finish refactoring the Assignment component

This commit is contained in:
Ramon Wenger 2024-03-13 14:43:20 +01:00
parent cfb20c00c3
commit 8cd508ce31
22 changed files with 372 additions and 192 deletions

View File

@ -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 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 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 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,
@ -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 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 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 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,
@ -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.
*/
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.
*/
@ -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.
*/
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.
*/

File diff suppressed because one or more lines are too long

View File

@ -58,13 +58,18 @@
</template>
<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 { useMutation, useQuery } from '@vue/apollo-composable';
import { graphql } from '@/__generated__';
import { HighlightNode } from '@/__generated__/graphql';
import { computed } from '@vue/reactivity';
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';
export interface Props {
@ -72,6 +77,10 @@ export interface Props {
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 assignmentDiv = ref<HTMLElement | null>(null);
@ -84,6 +93,9 @@ const initialSubmission = {
};
const assignment = ref({ submission: initialSubmission });
const corrections = ref('');
const unsaved = ref(false);
const saving = ref(0);
const spellcheckLoading = ref(false);
graphql(`
fragment SubmissionParts on StudentSubmissionNode {
@ -102,7 +114,7 @@ graphql(`
}
`);
graphql(`
const assignmentFragment = graphql(`
fragment AssignmentParts on AssignmentNode {
id
title
@ -114,6 +126,8 @@ graphql(`
}
`);
const { me } = getMe();
const { result, onResult } = useQuery(
graphql(`
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(() => {
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(() => {
// todo: refactor and merge with the one in ContentComponent.vue
if (assignmentDiv.value) {
@ -264,181 +324,98 @@ const reopen = () => {
},
});
};
</script>
<script lang="ts">
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: [],
const _save = debounce(function (submission) {
saving.value++;
const variables = {
input: {
assignment: {
id: assignment.value.id,
answer: assignment.value.submission.text,
document: assignment.value.submission.document,
},
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: {
assignment: {
id: this.assignment.id,
answer: this.assignment.submission.text,
document: this.assignment.submission.document,
},
};
doUpdateAssignmentWithSuccess(variables, {
update(
cache,
{
data: {
updateAssignment: { successful, updatedAssignment },
},
}
) {
try {
if (successful) {
const id = cache.identify({
id: updatedAssignment.id,
__typename: updatedAssignment.__typename,
});
const fragment = assignmentFragment;
const fragmentName = 'AssignmentParts';
cache.writeFragment({
fragment,
fragmentName,
id,
data: {
...updatedAssignment,
submission,
},
},
update(
store,
{
data: {
updateAssignment: { successful, updatedAssignment },
},
}
) {
try {
if (successful) {
const query = ASSIGNMENT_QUERY;
const variables = {
id: updatedAssignment.id,
};
const assignment = Object.assign({}, updatedAssignment, {
submission,
});
const data = {
assignment,
};
store.writeQuery({ query, variables, data });
}
} catch (e) {
console.error(e);
// Query did not exist in the cache, and apollo throws a generic Error. Do nothing
}
},
})
.then(() => {
this.saving--;
if (this.saving === 0) {
this.unsaved = false;
}
});
}, 500),
saveInput: function (answer) {
// reset corrections on input
this.corrections = '';
this.unsaved = true;
/*
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
*/
this.assignment.submission.text = answer;
this._save(this.assignment.submission);
if (this.assignment.submission.text.length > 0) {
matomoTrackEvent('Auftrag', 'Text mit Eingabe gespeichert', this.assignment.title);
});
}
} catch (e) {
console.error(e);
// Query did not exist in the cache, and apollo throws a generic Error. Do nothing
}
},
changeDocumentUrl(documentUrl) {
this.assignment.submission.document = documentUrl;
this._save(this.assignment.submission);
},
initialSubmission() {
return {
text: '',
document: '',
final: false,
};
},
spellcheck() {
let self = this;
this.spellcheckLoading = true;
this.$apollo
.mutate({
mutation: SPELL_CHECK_MUTATION,
variables: {
input: {
assignment: this.assignment.id,
text: this.assignment.submission.text,
},
},
update(
store,
{
data: {
spellCheck: { results },
},
}
) {
self.corrections = results;
},
})
.then(() => {
this.spellcheckLoading = false;
});
matomoTrackEvent('Auftrag', 'Rechtschreibung geprüft', this.assignment.title);
},
},
}).then(() => {
saving.value--;
if (saving.value === 0) {
unsaved.value = false;
}
});
}, 500);
const saveInput = (answer: string) => {
// reset corrections on input
corrections.value = '';
unsaved.value = true;
/*
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
*/
assignment.value.submission.text = answer;
_save(assignment.value.submission);
apollo: {
me: {
query: ME_QUERY,
if (assignment.value.submission.text.length > 0) {
matomoTrackEvent('Auftrag', 'Text mit Eingabe gespeichert', assignment.value.title);
}
};
const changeDocumentUrl = (documentUrl: string) => {
assignment.value.submission.document = documentUrl;
_save(assignment.value.submission);
};
const spellcheck = () => {
spellcheckLoading.value = true;
const variables = {
input: {
assignment: assignment.value.id,
text: assignment.value.submission.text,
},
},
};
doSpellCheck(variables, {
update(
_cache,
{
data: {
spellCheck: { results },
},
}
) {
corrections.value = results;
},
}).then(() => {
spellcheckLoading.value = false;
});
matomoTrackEvent('Auftrag', 'Rechtschreibung geprüft', assignment.value.title);
};
</script>

View File

@ -68,7 +68,7 @@ export default {
slug,
});
const fragment = MODULE_FRAGMENT;
const fragmentName = 'ModuleParts';
const fragmentName = 'ModuleLegacyParts';
const module = store.readFragment({ fragment, fragmentName, id });
const data = {
...module,

View File

@ -1,5 +1,5 @@
#import "./highlightParts.gql"
fragment ModuleParts on ModuleNode {
fragment ModuleLegacyParts on ModuleNode {
id
title
metaTitle

View File

@ -8,6 +8,6 @@ fragment RoomParts on RoomNode {
description
restricted
schoolClass {
...SchoolClassParts
...SchoolClassLegacyParts
}
}

View File

@ -1,4 +1,4 @@
fragment SchoolClassParts on SchoolClassNode {
fragment SchoolClassLegacyParts on SchoolClassNode {
id
name
}

View File

@ -1,4 +1,4 @@
fragment TeamParts on TeamNode {
fragment TeamLegacyParts on TeamNode {
name
code
id

View File

@ -8,6 +8,6 @@ fragment TopicParts on TopicNode {
vimeoId
instructions
modules {
...ModuleParts
...ModuleLegacyParts
}
}

View File

@ -1,6 +1,6 @@
#import "./schoolClassParts.gql"
#import "./moduleParts.gql"
fragment UserParts on PrivateUserNode {
fragment UserLegacyParts on PrivateUserNode {
id
pk
username
@ -30,11 +30,11 @@ fragment UserParts on PrivateUserNode {
recentModules(orderBy: "-visited") {
edges {
node {
...ModuleParts
...ModuleLegacyParts
}
}
}
schoolClasses {
...SchoolClassParts
...SchoolClassLegacyParts
}
}

View File

@ -4,7 +4,7 @@ mutation CreateTeamMutation($input: CreateTeamInput!) {
result {
__typename
... on TeamNode {
...TeamParts
...TeamLegacyParts
}
... on DuplicateName {
reason

View File

@ -3,7 +3,7 @@ mutation JoinTeamMutation($input: JoinTeamInput!) {
joinTeam(input: $input) {
success
team {
...TeamParts
...TeamLegacyParts
}
}
}

View File

@ -9,7 +9,7 @@ mutation ApplySnapshot($input: ApplySnapshotInput!) {
applySnapshot(input: $input) {
success
module {
...ModuleParts
...ModuleLegacyParts
objectiveGroups {
...ObjectiveGroupParts
objectives {

View File

@ -2,7 +2,7 @@
mutation UpdateLastModule($input: UpdateLastModuleInput!) {
updateLastModule(input: $input) {
lastModule {
...ModuleParts
...ModuleLegacyParts
}
}
}

View File

@ -3,7 +3,7 @@ query ModulesQuery {
modules {
edges {
node {
...ModuleParts
...ModuleLegacyParts
}
}
}

View File

@ -12,7 +12,7 @@ query AssignmentWithSubmissions($id: ID!) {
firstName
lastName
schoolClasses {
...SchoolClassParts
...SchoolClassLegacyParts
}
}
submissionFeedback {

View File

@ -2,9 +2,9 @@
#import "../fragments/teamParts.gql"
query MeQuery {
me {
...UserParts
...UserLegacyParts
team {
...TeamParts
...TeamLegacyParts
}
isTeacher
permissions

View File

@ -1,7 +1,7 @@
#import "../fragments/moduleParts.gql"
query ModuleQuery($id: ID, $slug: String) {
module(id: $id, slug: $slug) {
...ModuleParts
...ModuleLegacyParts
chapters {
id
contentBlocks {

View File

@ -6,7 +6,7 @@
#import "gql/fragments/contentBlockParts.gql"
query ModuleDetailsQuery($slug: String, $id: ID) {
module(slug: $slug, id: $id) {
...ModuleParts
...ModuleLegacyParts
objectiveGroups {
...ObjectiveGroupParts
objectives {

View File

@ -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 };

View File

@ -135,7 +135,7 @@ export const constructNoteMutation = (n) => {
});
} else {
let fragment = MODULE_FRAGMENT;
const fragmentName = 'ModuleParts';
const fragmentName = 'ModuleLegacyParts';
let id = store.identify({
__typename: 'ModuleNode',
slug: n.parent,

View File

@ -163,7 +163,7 @@ export default {
slug: slug,
});
const fragmentName = 'ModuleParts';
const fragmentName = 'ModuleLegacyParts';
const module = store.readFragment({
fragment,
id,