diff --git a/client/src/components/circle/LearningSequence.vue b/client/src/components/circle/LearningSequence.vue
index 1d5a2405..23780eec 100644
--- a/client/src/components/circle/LearningSequence.vue
+++ b/client/src/components/circle/LearningSequence.vue
@@ -1,5 +1,6 @@
@@ -28,7 +29,10 @@ defineProps(['learningSequence'])
v-for="learningContent in learningUnit.learningContents"
class="flex items-center gap-4 pb-3"
>
-
+
+
+
+
{{ learningContent.contents[0].type }}: {{ learningContent.title }}
diff --git a/client/src/fetchHelpers.js b/client/src/fetchHelpers.js
new file mode 100644
index 00000000..bc397aa1
--- /dev/null
+++ b/client/src/fetchHelpers.js
@@ -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'});
+};
diff --git a/client/src/router/guards.ts b/client/src/router/guards.ts
index a4a909d8..b96e1849 100644
--- a/client/src/router/guards.ts
+++ b/client/src/router/guards.ts
@@ -22,7 +22,7 @@ export const redirectToLoginIfRequired: NavigationGuardWithThis = (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) {
diff --git a/client/src/stores/user.ts b/client/src/stores/user.ts
index 7b450ba0..15a52714 100644
--- a/client/src/stores/user.ts
+++ b/client/src/stores/user.ts
@@ -1,4 +1,4 @@
-import { defineStore } from 'pinia'
+import {defineStore} from 'pinia'
// typed state https://stackoverflow.com/questions/71012513/when-using-pinia-and-typescript-how-do-you-use-an-action-to-set-the-state
export type UserState = {
diff --git a/client/src/views/CircleView.vue b/client/src/views/CircleView.vue
index f8d26c7e..55154b55 100644
--- a/client/src/views/CircleView.vue
+++ b/client/src/views/CircleView.vue
@@ -1,9 +1,9 @@