Skip to content

Commit ed9de8c

Browse files
committed
Migrate remaining fetch calls to baseService for consistency and improved error handling. Add new service methods across the codebase.
1 parent d0ab309 commit ed9de8c

15 files changed

Lines changed: 138 additions & 194 deletions

assets/vue/composables/locale.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { usePlatformConfig } from "../store/platformConfig"
22
import { useSecurityStore } from "../store/securityStore"
33
import { useCidReqStore } from "../store/cidReq"
44
import { useCourseSettings } from "../store/courseSettingStore"
5+
import languageService from "../services/languageService"
56
import { computed, ref, watch } from "vue"
67
import { useRoute, useRouter } from "vue-router"
78

@@ -133,11 +134,12 @@ export function useLocale() {
133134
const { db, bcp47, parent } = normalizeIso(iso)
134135

135136
async function hit(q) {
136-
const r = await fetch(`/api/languages?isocode=${encodeURIComponent(q)}`)
137-
if (!r.ok) return null
138-
const j = await r.json()
139-
const arr = j["hydra:member"] || j
140-
return Array.isArray(arr) && arr.length ? arr[0] : null
137+
try {
138+
const { items } = await languageService.searchByIsocode(q)
139+
return Array.isArray(items) && items.length ? items[0] : null
140+
} catch {
141+
return null
142+
}
141143
}
142144

143145
let row = await hit(db)

assets/vue/composables/useFileManager.js

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { getCourseContext } from "../utils/courseContext"
88
import { RESOURCE_LINK_PUBLISHED } from "../constants/entity/resourcelink"
99
import { useCidReqStore } from "../store/cidReq"
1010
import documentsService from "../services/documents"
11+
import baseService from "../services/baseService"
1112

1213
export function useFileManager(entity, apiEndpoint, uploadRoute, isCourseDocument = false) {
1314
const route = useRoute()
@@ -135,19 +136,12 @@ export function useFileManager(entity, apiEndpoint, uploadRoute, isCourseDocumen
135136
isLoading.value = true
136137

137138
try {
138-
const response = await fetch(`${apiEndpoint}?${new URLSearchParams(params).toString()}`, {
139-
method: "GET",
140-
headers: {
141-
"Content-Type": "application/json",
142-
},
143-
})
144-
145-
const data = await response.json()
146-
if (data["hydra:member"]) {
147-
files.value = data["hydra:member"]
148-
totalFiles.value = data["hydra:totalItems"]
139+
const { items, totalItems } = await baseService.getCollection(apiEndpoint, params)
140+
if (items) {
141+
files.value = items
142+
totalFiles.value = totalItems
149143
} else {
150-
console.error("[FILEMANAGER] Unexpected API response format", data)
144+
console.error("[FILEMANAGER] Unexpected API response format", items)
151145
}
152146
} catch (error) {
153147
console.error("[FILEMANAGER] Error fetching files:", error)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import baseService from "./baseService"
2+
3+
export default {
4+
/**
5+
* Fetches the sessions/events schedule for the current teacher's students.
6+
* @param {Object} [params={}] - Optional filters (sid, start, end).
7+
* @returns {Promise<Object[]>}
8+
*/
9+
getMyStudentsSchedule(params = {}) {
10+
return baseService.get("/api/calendar/my-students-schedule", params)
11+
},
12+
13+
/**
14+
* Fetches the sessions plan for a given year.
15+
* @param {number|string} year
16+
* @returns {Promise<Object>}
17+
*/
18+
getSessionsPlan(year) {
19+
return baseService.get("/api/calendar/sessions-plan", { year })
20+
},
21+
}

assets/vue/services/courseService.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ export default {
3232
return await baseService.get("/plugin/CourseHomeNotify/ajax.php", { cid, sid, gid: 0 })
3333
},
3434

35+
/**
36+
* Lists the current user's courses (paginated). Returns the raw response body
37+
* so the caller can normalize it; accepts extra axios options (e.g. a signal).
38+
* @param {number} page
39+
* @param {number} itemsPerPage
40+
* @param {Object} [options={}] - Extra axios options (e.g. AbortController signal).
41+
* @returns {Promise<Object>}
42+
*/
43+
listMyCourses: async (page, itemsPerPage, options = {}) => {
44+
const response = await baseService.getRaw("/api/me/courses", { params: { page, itemsPerPage }, ...options })
45+
46+
return response.data
47+
},
48+
3549
/**
3650
* @param {Object} searchParams
3751
* @param {boolean} disablePagination

assets/vue/services/documents.js

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,7 @@ async function getQuotaUsage(courseId, { sid = 0, gid = 0, force = false, staleM
8686
}
8787

8888
try {
89-
const url = `/api/documents/${cid}/usage?sid=${s}&gid=${g}`
90-
const response = await window.fetch(url, {
91-
credentials: "same-origin",
92-
headers: { Accept: "application/json" },
93-
})
94-
95-
if (!response.ok) return null
96-
97-
const json = await response.json()
89+
const json = await baseService.get(`/api/documents/${cid}/usage`, { sid: s, gid: g })
9890
const quota = json?.quota || {}
9991
const availableBytes = Number(quota.availableBytes)
10092
const availablePercent = Number(quota.availablePercent)
@@ -249,33 +241,19 @@ export default {
249241
},
250242

251243
async createCloudLink(payload) {
252-
const response = await window.fetch("/api/documents", {
253-
method: "POST",
254-
credentials: "same-origin",
255-
headers: {
256-
Accept: "application/json",
257-
"Content-Type": "application/json",
258-
},
259-
body: JSON.stringify({
260-
...payload,
261-
filetype: "link",
262-
}),
263-
})
264-
265-
const data = await response.json().catch(() => ({}))
266-
267-
if (!response.ok) {
244+
try {
245+
return await baseService.post("/api/documents", { ...payload, filetype: "link" }, true)
246+
} catch (error) {
247+
const data = error?.response?.data || {}
268248
const message =
269249
data?.message ||
270250
data?.detail ||
271251
data?.["hydra:description"] ||
272252
(Array.isArray(data?.violations) && data.violations.length ? data.violations[0].message : null) ||
273253
"Unable to create cloud link."
274254

275-
throw new Error(message)
255+
throw new Error(message, { cause: error })
276256
}
277-
278-
return data
279257
},
280258

281259
/**
Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
import makeService from "./api"
2+
import baseService from "./baseService"
23

3-
const legalExtensions = {
4+
const languageExtensions = {
45
async findAllAvailable() {
56
try {
6-
const response = await fetch("/api/languages?available=true")
7-
if (!response.ok) {
8-
throw new Error("Network response was not ok")
9-
}
10-
return await response.json()
7+
return await baseService.get("/api/languages", { available: true })
118
} catch (error) {
129
console.error("Error fetching available languages:", error)
1310
throw error
1411
}
1512
},
13+
14+
/**
15+
* Searches languages by isocode (includes unavailable ones).
16+
* @param {string} isocode
17+
* @returns {Promise<{totalItems, items}>}
18+
*/
19+
async searchByIsocode(isocode) {
20+
return baseService.getCollection("/api/languages", { isocode })
21+
},
1622
}
17-
export default makeService("languages", legalExtensions)
23+
export default makeService("languages", languageExtensions)

assets/vue/services/legalService.js

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,41 @@
11
import makeService from "./api"
2+
import baseService from "./baseService"
23

34
const legalExtensions = {
45
async findAllByLanguage(languageId) {
5-
const params = new URLSearchParams({
6-
languageId: languageId,
6+
return baseService.getCollection("/api/legals", {
7+
languageId,
78
"order[version]": "desc",
89
})
9-
return fetch(`/api/legals?${params.toString()}`)
1010
},
1111

1212
async findLatestByLanguage(languageId) {
13-
const params = new URLSearchParams({
14-
languageId: languageId,
13+
return baseService.getCollection("/api/legals", {
14+
languageId,
1515
"order[version]": "desc",
16-
itemsPerPage: "1",
16+
itemsPerPage: 1,
1717
})
18-
return fetch(`/api/legals?${params.toString()}`)
1918
},
2019

2120
async findByLanguageAndVersion(languageId, version) {
22-
const params = new URLSearchParams({
23-
languageId: languageId,
24-
version: version,
21+
return baseService.getCollection("/api/legals", {
22+
languageId,
23+
version,
2524
"order[type]": "asc",
26-
itemsPerPage: "50",
25+
itemsPerPage: 50,
2726
})
28-
return fetch(`/api/legals?${params.toString()}`)
2927
},
3028

3129
async findAllType0() {
32-
const params = new URLSearchParams({
33-
type: "0",
30+
return baseService.getCollection("/api/legals", {
31+
type: 0,
3432
"order[version]": "desc",
35-
itemsPerPage: "50",
33+
itemsPerPage: 50,
3634
})
37-
return fetch(`/api/legals?${params.toString()}`)
3835
},
3936

37+
// TODO: /legal/save is a controller endpoint (not API Platform); pending the
38+
// non-API fetch migration decision, it still uses native fetch.
4039
async saveOrUpdateLegal(payload) {
4140
return fetch(`/legal/save`, {
4241
method: "POST",
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
11
import makeService from "./api"
2+
import baseService from "./baseService"
23

3-
export default makeService("resource_nodes")
4+
export default makeService("resource_nodes", {
5+
/**
6+
* Fetches a single resource node by id.
7+
* @param {number|string} id
8+
* @returns {Promise<Object>}
9+
*/
10+
async findById(id) {
11+
return baseService.get(`/api/resource_nodes/${id}`)
12+
},
13+
})

assets/vue/views/ccalendarevent/CalendarMyStudentsSchedule.vue

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ import timeGridPlugin from "@fullcalendar/timegrid"
117117
import interactionPlugin from "@fullcalendar/interaction"
118118
import Dialog from "primevue/dialog"
119119
import { DateTime } from "luxon"
120+
import calendarService from "../../services/calendarService"
120121
121122
import CalendarSectionHeader from "../../components/ccalendarevent/CalendarSectionHeader.vue"
122123
@@ -258,20 +259,7 @@ async function fetchSessions() {
258259
isLoadingSessions.value = true
259260
errorMessage.value = ""
260261
261-
const resp = await fetch("/api/calendar/my-students-schedule", {
262-
method: "GET",
263-
headers: { Accept: "application/json" },
264-
})
265-
266-
if (!resp.ok) {
267-
const text = await resp.text().catch(() => "")
268-
console.error("[MyStudentsSchedule] Sessions request failed", resp.status, text)
269-
errorMessage.value = t("Failed to load sessions")
270-
sessions.value = []
271-
return
272-
}
273-
274-
const data = await resp.json()
262+
const data = await calendarService.getMyStudentsSchedule()
275263
sessions.value = Array.isArray(data) ? data : []
276264
} catch (e) {
277265
console.error("[MyStudentsSchedule] Unexpected error", e)
@@ -378,26 +366,18 @@ async function fetchEvents(info, successCallback, failureCallback) {
378366
const start = DateTime.fromJSDate(info.start).toISO()
379367
const end = DateTime.fromJSDate(info.end).toISO()
380368
381-
const url = `/api/calendar/my-students-schedule?sid=${encodeURIComponent(sid)}&start=${encodeURIComponent(
382-
start,
383-
)}&end=${encodeURIComponent(end)}`
384-
385-
const resp = await fetch(url, { method: "GET", headers: { Accept: "application/json" } })
386-
387-
if (!resp.ok) {
388-
const text = await resp.text().catch(() => "")
389-
console.error("[MyStudentsSchedule] Events request failed", resp.status, text)
390-
errorMessage.value = resp.status === 403 ? t("Not allowed") : t("Failed to load events")
391-
successCallback([])
392-
return
393-
}
394-
395-
const data = await resp.json()
369+
const data = await calendarService.getMyStudentsSchedule({ sid, start, end })
396370
successCallback(Array.isArray(data) ? data : [])
397371
} catch (e) {
398-
console.error("[MyStudentsSchedule] Unexpected error", e)
399-
errorMessage.value = t("Failed to load events")
400-
failureCallback?.(e)
372+
if (e?.response) {
373+
console.error("[MyStudentsSchedule] Events request failed", e.response.status)
374+
errorMessage.value = e.response.status === 403 ? t("Not allowed") : t("Failed to load events")
375+
successCallback([])
376+
} else {
377+
console.error("[MyStudentsSchedule] Unexpected error", e)
378+
errorMessage.value = t("Failed to load events")
379+
failureCallback?.(e)
380+
}
401381
}
402382
}
403383

assets/vue/views/ccalendarevent/CalendarSessionsPlan.vue

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
import { ref, watch } from "vue"
123123
import { useI18n } from "vue-i18n"
124124
import { DateTime } from "luxon"
125+
import calendarService from "../../services/calendarService"
125126
import CalendarSectionHeader from "../../components/ccalendarevent/CalendarSectionHeader.vue"
126127
import BaseButton from "../../components/basecomponents/BaseButton.vue"
127128
import { useRoute, useRouter } from "vue-router"
@@ -206,22 +207,7 @@ async function fetchPlan() {
206207
isLoading.value = true
207208
errorMessage.value = ""
208209
209-
const url = `/api/calendar/sessions-plan?year=${encodeURIComponent(String(year.value))}`
210-
const resp = await fetch(url, { method: "GET", headers: { Accept: "application/ld+json, application/json" } })
211-
212-
if (!resp.ok) {
213-
const text = await resp.text().catch(() => "")
214-
console.error("[SessionsPlan] Request failed", resp.status, text)
215-
if (resp.status === 403) {
216-
errorMessage.value = tOrFallback("TooMuchSessionsInPlanification", "Too many sessions in your plan")
217-
} else {
218-
errorMessage.value = tOrFallback("Failed to load sessions plan", "Failed to load sessions plan")
219-
}
220-
sessions.value = []
221-
return
222-
}
223-
224-
const data = await resp.json()
210+
const data = await calendarService.getSessionsPlan(year.value)
225211
const items = Array.isArray(data) ? data : Array.isArray(data?.["hydra:member"]) ? data["hydra:member"] : []
226212
227213
sessions.value = items.map((x) => ({
@@ -235,8 +221,12 @@ async function fetchPlan() {
235221
color: x.color || "rgba(70,130,180,0.9)",
236222
}))
237223
} catch (e) {
238-
console.error("[SessionsPlan] Unexpected error", e)
239-
errorMessage.value = tOrFallback("Failed to load sessions plan", "Failed to load sessions plan")
224+
console.error("[SessionsPlan] Request failed", e?.response?.status ?? e)
225+
if (e?.response?.status === 403) {
226+
errorMessage.value = tOrFallback("TooMuchSessionsInPlanification", "Too many sessions in your plan")
227+
} else {
228+
errorMessage.value = tOrFallback("Failed to load sessions plan", "Failed to load sessions plan")
229+
}
240230
sessions.value = []
241231
} finally {
242232
isLoading.value = false

0 commit comments

Comments
 (0)