Client can complete learning contents
This commit is contained in:
parent
0d54437bf8
commit
c334c25f1c
|
|
@ -1,5 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
|
||||
defineProps(['learningSequence'])
|
||||
|
||||
</script>
|
||||
|
|
@ -28,7 +29,10 @@ defineProps(['learningSequence'])
|
|||
v-for="learningContent in learningUnit.learningContents"
|
||||
class="flex items-center gap-4 pb-3"
|
||||
>
|
||||
<it-icon-checkbox-unchecked/>
|
||||
<div @click="$emit('toggleLearningContentCheckbox', learningContent)">
|
||||
<it-icon-checkbox-checked v-if="learningContent.completed" />
|
||||
<it-icon-checkbox-unchecked v-else />
|
||||
</div>
|
||||
<div>{{ learningContent.contents[0].type }}: {{ learningContent.title }}</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
import { getCookieValue } from '@/router/guards';
|
||||
|
||||
class FetchError extends Error {
|
||||
constructor(response, message = 'HTTP error ' + response.status) {
|
||||
super(message);
|
||||
this.response = response;
|
||||
}
|
||||
}
|
||||
|
||||
export const itFetch = (url, options) => {
|
||||
return fetch(url, options).then(response => {
|
||||
if (!response.ok) {
|
||||
throw new FetchError(response);
|
||||
}
|
||||
|
||||
return response;
|
||||
});
|
||||
};
|
||||
|
||||
export const itPost = (url, data, options) => {
|
||||
options = Object.assign({}, options);
|
||||
|
||||
const headers = Object.assign({
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json;charset=UTF-8',
|
||||
}, options?.headers);
|
||||
|
||||
if (options?.headers) {
|
||||
delete options.headers;
|
||||
}
|
||||
|
||||
options = Object.assign({
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: JSON.stringify(data)
|
||||
}, options);
|
||||
|
||||
options.headers['X-CSRFToken'] = getCookieValue('csrftoken');
|
||||
|
||||
if (options.method === 'GET') {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
return itFetch(url, options).then((response) => {
|
||||
return response.json().catch(() => {
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const itGet = (url) => {
|
||||
return itPost(url, {}, {method: 'GET'});
|
||||
};
|
||||
|
|
@ -22,7 +22,7 @@ export const redirectToLoginIfRequired: NavigationGuardWithThis<undefined> = (to
|
|||
}
|
||||
}
|
||||
|
||||
const getCookieValue = (cookieName: string): string => {
|
||||
export const getCookieValue = (cookieName: string): string => {
|
||||
// https://stackoverflow.com/questions/5639346/what-is-the-shortest-function-for-reading-a-cookie-by-name-in-javascript
|
||||
const cookieValue = document.cookie.match('(^|[^;]+)\\s*' + cookieName + '\\s*=\\s*([^;]+)')
|
||||
if (!cookieValue) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<script>
|
||||
import axios from 'axios';
|
||||
import * as log from 'loglevel';
|
||||
|
||||
import MainNavigationBar from '../components/MainNavigationBar.vue';
|
||||
import LearningSequence from '../components/circle/LearningSequence.vue';
|
||||
import { itGet, itPost } from '../fetchHelpers';
|
||||
|
||||
export default {
|
||||
components: { LearningSequence, MainNavigationBar },
|
||||
|
|
@ -15,14 +15,23 @@ export default {
|
|||
learningSequences: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleLearningContentCheckbox(learningContent) {
|
||||
log.debug('toggleLearningContentCheckbox', learningContent);
|
||||
console.log(learningContent);
|
||||
|
||||
itPost('/api/completion/complete_learning_content/', {
|
||||
learning_content_key: learningContent.translation_key,
|
||||
}).then((data) => {
|
||||
console.log(data);
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
log.debug('CircleView mounted', this.circleSlug);
|
||||
axios({
|
||||
method: 'get',
|
||||
url: `/learnpath/api/circle/${this.circleSlug}/`,
|
||||
}).then((response) => {
|
||||
log.debug(response.data);
|
||||
this.circleData = response.data;
|
||||
itGet(`/learnpath/api/circle/${this.circleSlug}/`).then((data) => {
|
||||
this.circleData = data;
|
||||
itGet(`/api/completion/user_circle_completion/${this.circleData.translation_key}/`).then((completionData) => {
|
||||
|
||||
// aggregate wagtail data into LearningSequence > LearningUnit > LearningPackage hierarchy
|
||||
let learningSequence = null;
|
||||
|
|
@ -45,6 +54,9 @@ export default {
|
|||
learningUnit = Object.assign(child, { learningContents: [] });
|
||||
} else {
|
||||
// must be a LearningContent
|
||||
if (child.translation_key in completionData.json_data.completed_learning_contents) {
|
||||
child.completed = true;
|
||||
}
|
||||
learningUnit.learningContents.push(child);
|
||||
}
|
||||
});
|
||||
|
|
@ -68,6 +80,8 @@ export default {
|
|||
|
||||
log.debug(this.learningSequences);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -107,7 +121,7 @@ export default {
|
|||
|
||||
<div class="flex-auto bg-gray-100 px-4 py-8 lg:px-24">
|
||||
<div v-for="learningSequence in learningSequences">
|
||||
<LearningSequence :learning-sequence="learningSequence"></LearningSequence>
|
||||
<LearningSequence :learning-sequence="learningSequence" @toggleLearningContentCheckbox="toggleLearningContentCheckbox"></LearningSequence>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ FIXTURE_DIRS = (str(APPS_DIR / "fixtures"),)
|
|||
# https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-httponly
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly
|
||||
CSRF_COOKIE_HTTPONLY = True
|
||||
CSRF_COOKIE_HTTPONLY = False
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-browser-xss-filter
|
||||
SECURE_BROWSER_XSS_FILTER = True
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
from django.urls import path
|
||||
|
||||
from vbv_lernwelt.completion.views import complete_learning_content
|
||||
from vbv_lernwelt.completion.views import complete_learning_content, request_user_circle_completion
|
||||
|
||||
urlpatterns = [
|
||||
path(r"user_circle_completion/<uuid:circle_key>/", request_user_circle_completion, name="request_user_circle_completion"),
|
||||
path(r"complete_learning_content/", complete_learning_content, name="complete_learning_content"),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,16 @@ from vbv_lernwelt.learnpath.models import LearningContent
|
|||
logger = structlog.get_logger(__name__)
|
||||
|
||||
|
||||
@api_view(['GET'])
|
||||
def request_user_circle_completion(request, circle_key):
|
||||
ucc = UserCircleCompletion.objects.get(
|
||||
user=request.user,
|
||||
circle_key=circle_key,
|
||||
)
|
||||
|
||||
return Response(status=200, data=UserCircleCompletionSerializer(ucc).data)
|
||||
|
||||
|
||||
@api_view(['POST'])
|
||||
def complete_learning_content(request):
|
||||
learning_content_key = request.data.get('learning_content_key')
|
||||
|
|
@ -18,7 +28,7 @@ def complete_learning_content(request):
|
|||
|
||||
circle_key = learning_content.get_parent().translation_key
|
||||
|
||||
LearningContentCompletion.objects.create(
|
||||
LearningContentCompletion.objects.get_or_create(
|
||||
user=request.user,
|
||||
learning_content_key=learning_content_key,
|
||||
circle_key=circle_key,
|
||||
|
|
|
|||
Loading…
Reference in New Issue