feat: добавил поддержку подписки на календарь и экспорт расписания лекций в формате .ics
Backend CI / build-and-test (push) Successful in 57s
Frontend CI / build-and-check (push) Failing after 26s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 11s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 2m33s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 33s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 8s

This commit is contained in:
2026-06-02 21:26:48 +03:00
parent 7050851bd4
commit 136bcce7db
16 changed files with 639 additions and 8 deletions
+27
View File
@@ -81,3 +81,30 @@ export function extractItems<T>(payload: T[] | { items?: T[] } | undefined): T[]
if (Array.isArray(payload)) return payload
return payload?.items ?? []
}
export async function apiRequestBlob(
path: string,
options: RequestInit & { query?: Record<string, unknown> } = {},
): Promise<Blob> {
const headers = new Headers(options.headers)
if (!headers.has('Accept')) headers.set('Accept', 'text/calendar')
if (accessToken) headers.set('Authorization', `Bearer ${accessToken}`)
const response = await fetch(makeUrl(path, options.query), {
...options,
headers,
credentials: 'include',
})
if (!response.ok) {
const body = await parseResponse(response)
const message =
typeof body === 'object' && body && 'message' in body
? String((body as { message: unknown }).message)
: `API request failed with status ${response.status}`
throw new ApiError(message, response.status, body)
}
return response.blob()
}
+7 -1
View File
@@ -1,4 +1,4 @@
import { apiRequest, extractItems } from './client'
import { apiRequest, apiRequestBlob, extractItems } from './client'
import type {
AchievementDto,
AuthResponse,
@@ -19,6 +19,7 @@ import type {
UpdateReviewPromptRequest,
UserAchievementDto,
AdminDashboardStatsDto,
CalendarSubscriptionDto,
CurrentUserDto,
UserDto,
UserQuery,
@@ -76,6 +77,11 @@ export const usersApi = {
)
return extractItems(payload)
},
downloadMyEnrollmentsIcs: () => apiRequestBlob('/users/me/enrollments.ics'),
downloadEnrollmentIcs: (lectureId: string | number) =>
apiRequestBlob(`/users/me/enrollments/${lectureId}.ics`),
getCalendarSubscription: () =>
apiRequest<CalendarSubscriptionDto>('/users/me/enrollments/calendar-subscription'),
async myAchievements() {
const payload = await apiRequest<
PagedResult<UserAchievementDto> | UserAchievementDto[] | AchievementDto[]
+4
View File
@@ -83,6 +83,10 @@ export interface AdminDashboardStatsDto {
pendingReviewsCount: number
}
export interface CalendarSubscriptionDto {
feedUrl: string
}
export interface EnrollmentSlotRuleDto {
level: number
slots: number