diff --git a/.env b/.env index d18e2c94..9a621ebc 100644 --- a/.env +++ b/.env @@ -2,10 +2,10 @@ VITE_EVENTS_API_URL=https://api.innohassle.ru/events/v0 VITE_ACCOUNTS_API_URL=https://api.innohassle.ru/accounts/v0 VITE_SEARCH_API_URL=https://api.innohassle.ru/search/v0 -VITE_SPORTS_API_URL=https://api.innohassle.ru/sports/v0 VITE_MAPS_API_URL=https://api.innohassle.ru/maps/v0 VITE_MUSIC_ROOM_API_URL=https://api.innohassle.ru/music-room/v0 VITE_BOOKING_API_URL=https://api.innohassle.ru/room-booking/v0 +VITE_INNOSPORT_API_URL=https://sport.innopolis.university/api # Should be the name of provider supported by Accounts API VITE_AUTH_PROVIDER=innopolis diff --git a/components/icons/logo-invert.svg b/components/icons/logo-invert.svg old mode 100644 new mode 100755 index 5fdfa255..d38bf059 --- a/components/icons/logo-invert.svg +++ b/components/icons/logo-invert.svg @@ -1,126 +1,23 @@ - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + \ No newline at end of file diff --git a/components/icons/logo.svg b/components/icons/logo.svg old mode 100644 new mode 100755 index 762b6f10..27b42175 --- a/components/icons/logo.svg +++ b/components/icons/logo.svg @@ -1,126 +1,23 @@ - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + \ No newline at end of file diff --git a/public/snow-1.svg b/public/snow-1.svg deleted file mode 100644 index 567d50eb..00000000 --- a/public/snow-1.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - diff --git a/public/snow-2.svg b/public/snow-2.svg deleted file mode 100644 index 9dd2a453..00000000 --- a/public/snow-2.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/public/snow-3.svg b/public/snow-3.svg deleted file mode 100644 index 34bcaa39..00000000 --- a/public/snow-3.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - diff --git a/redocly.yaml b/redocly.yaml index a854e6d1..12eeef99 100644 --- a/redocly.yaml +++ b/redocly.yaml @@ -29,8 +29,8 @@ apis: x-openapi-ts: output: ./src/api/search/types.ts # --root-types --enum - sports: - root: https://api.innohassle.ru/sports/staging-v0/openapi.json + sport: + root: https://sport.innopolis.university/api/openapi.json x-openapi-ts: - output: ./src/api/sports/types.ts + output: ./src/api/sport/types.ts # --root-types --enum diff --git a/src/api/accounts/types.ts b/src/api/accounts/types.ts index 96ac0a3f..d1a37faa 100644 --- a/src/api/accounts/types.ts +++ b/src/api/accounts/types.ts @@ -252,6 +252,26 @@ export interface paths { patch?: never; trace?: never; }; + "/tokens/generate-my-sport-token": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Generate My Sport Token + * @description Generate access token for current user for access https://sport.innopolis.university/api/swagger/ + */ + get: operations["tokens_generate_my_sport_token"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/logout": { parameters: { query?: never; @@ -875,6 +895,33 @@ export interface operations { }; }; }; + tokens_generate_my_sport_token: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Token */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TokenData"]; + }; + }; + /** @description User does not have a session cookie or `uid` in the session */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; logout: { parameters: { query: { diff --git a/src/api/events/types.ts b/src/api/events/types.ts index f28cc85c..bc060aa0 100644 --- a/src/api/events/types.ts +++ b/src/api/events/types.ts @@ -107,6 +107,26 @@ export interface paths { patch?: never; trace?: never; }; + "/event-groups/by-tag-alias": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** + * Delete Event Group By Tag Alias + * @description Delete event groups by its tag alias + */ + delete: operations["event_groups_delete_event_group_by_tag_alias"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/event-groups/{event_group_id}/schedule.ics": { parameters: { query?: never; @@ -455,6 +475,46 @@ export interface paths { patch?: never; trace?: never; }; + "/tags/by-alias": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** + * Delete Tag + * @description Delete tag by alias + */ + delete: operations["tags_delete_tag"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/tags/by-type": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + /** + * Delete Tag By Type + * @description Delete tag by type + */ + delete: operations["tags_delete_tag_by_type"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/users/me": { parameters: { query?: never; @@ -882,15 +942,7 @@ export interface components { * ListEventGroupsResponse * @description Represents a list of event groups. */ - "ListEventGroupsResponse-Input": { - /** Event Groups */ - event_groups: components["schemas"]["ViewEventGroup"][]; - }; - /** - * ListEventGroupsResponse - * @description Represents a list of event groups. - */ - "ListEventGroupsResponse-Output": { + ListEventGroupsResponse: { /** Event Groups */ event_groups: components["schemas"]["ViewEventGroup"][]; }; @@ -1078,10 +1130,8 @@ export type SchemaLinkedCalendarCreate = components["schemas"]["LinkedCalendarCreate"]; export type SchemaLinkedCalendarView = components["schemas"]["LinkedCalendarView"]; -export type SchemaListEventGroupsResponseInput = - components["schemas"]["ListEventGroupsResponse-Input"]; -export type SchemaListEventGroupsResponseOutput = - components["schemas"]["ListEventGroupsResponse-Output"]; +export type SchemaListEventGroupsResponse = + components["schemas"]["ListEventGroupsResponse"]; export type SchemaListTagsResponse = components["schemas"]["ListTagsResponse"]; export type SchemaUpdateEventGroup = components["schemas"]["UpdateEventGroup"]; export type SchemaUserPredefinedGroupsResponse = @@ -1112,7 +1162,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["ListEventGroupsResponse-Input"]; + "application/json": components["schemas"]["ListEventGroupsResponse"]; }; }; }; @@ -1183,7 +1233,7 @@ export interface operations { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["ListEventGroupsResponse-Input"]; + "application/json": components["schemas"]["ListEventGroupsResponse"]; }; }; /** @description Unable to verify credentials OR Credentials not provided */ @@ -1412,6 +1462,37 @@ export interface operations { }; }; }; + event_groups_delete_event_group_by_tag_alias: { + parameters: { + query: { + tag_alias: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Event groups deleted successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": number; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; event_groups_set_event_group_ics: { parameters: { query?: never; @@ -2075,6 +2156,82 @@ export interface operations { }; }; }; + tags_delete_tag: { + parameters: { + query: { + tag_alias: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Tag deleted successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Unable to verify credentials OR Credentials not provided */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + tags_delete_tag_by_type: { + parameters: { + query: { + tag_type: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Tag deleted successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": unknown; + }; + }; + /** @description Unable to verify credentials OR Credentials not provided */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; users_get_me: { parameters: { query?: never; diff --git a/src/api/helpers/AuthManager.tsx b/src/api/helpers/AuthManager.tsx index 818243ad..4d5569b4 100644 --- a/src/api/helpers/AuthManager.tsx +++ b/src/api/helpers/AuthManager.tsx @@ -4,6 +4,10 @@ import { invalidateMyAccessToken, useMyAccessToken, } from "@/api/helpers/access-token.ts"; +import { + invalidateMySportAccessToken, + useMySportAccessToken, +} from "@/api/helpers/sport-access-token.ts"; import { useQueryClient } from "@tanstack/react-query"; import { PropsWithChildren, useEffect } from "react"; import { useLocalStorage } from "usehooks-ts"; @@ -23,11 +27,20 @@ export function AuthManager({ children }: PropsWithChildren) { null, ); + const [sportToken, setSportToken] = useMySportAccessToken(); + const { refetch: refetchMySportToken } = $accounts.useQuery( + "get", + "/tokens/generate-my-sport-token", + {}, + { enabled: false }, + ); + useEffect(() => { if (me || !isPending) { setStoredMe(me ?? null); if (!me) { invalidateMyAccessToken(); + invalidateMySportAccessToken(); if (shouldAutoSignIn()) { navigateToSignIn(window.location.href, "none"); } @@ -47,5 +60,17 @@ export function AuthManager({ children }: PropsWithChildren) { } }, [me, token, setToken, refetchMyToken, queryClient]); + useEffect(() => { + // If the user doesn't have personal access token for services, we should fetch it + if (!sportToken && me) { + refetchMySportToken().then((result) => { + if (result.isSuccess) { + setSportToken(result.data.access_token); + queryClient.clear(); + } + }); + } + }, [me, sportToken, setSportToken, refetchMySportToken, queryClient]); + return <>{children}; } diff --git a/src/api/helpers/sport-access-token.ts b/src/api/helpers/sport-access-token.ts new file mode 100644 index 00000000..8caf6dbe --- /dev/null +++ b/src/api/helpers/sport-access-token.ts @@ -0,0 +1,26 @@ +import { queryClient } from "@/app/query-client.ts"; +import { useLocalStorage } from "usehooks-ts"; + +const TOKEN_KEY = "accessTokenSport"; + +export function getMySportAccessToken() { + // Remove quotes as this is stored as JSON + return localStorage.getItem(TOKEN_KEY)?.slice(1, -1) ?? null; +} + +export function invalidateMySportAccessToken() { + // Check if the token is already invalid + const prevToken = getMySportAccessToken(); + if (!prevToken) return false; + + localStorage.removeItem(TOKEN_KEY); + // Notify usehooks-ts about the change + window.dispatchEvent(new StorageEvent("local-storage", { key: TOKEN_KEY })); + // Clear the query cache + queryClient.clear(); + return true; +} + +export function useMySportAccessToken() { + return useLocalStorage(TOKEN_KEY, null); +} diff --git a/src/api/helpers/sport-auth-middleware.ts b/src/api/helpers/sport-auth-middleware.ts new file mode 100644 index 00000000..d70fd0b4 --- /dev/null +++ b/src/api/helpers/sport-auth-middleware.ts @@ -0,0 +1,39 @@ +import { + getMySportAccessToken, + invalidateMySportAccessToken, +} from "@/api/helpers/sport-access-token.ts"; +import { Middleware } from "openapi-fetch"; + +export const sportAuthMiddleware: Middleware = { + async onRequest({ request }) { + // Check the requested URL to add token only to InnoSport API + if (!isSportUrl(request.url)) return; + + const token = getMySportAccessToken(); + if (token) { + const newRequest = request.clone(); + newRequest.headers.set("Authorization", `Bearer ${token}`); + return newRequest; + } + + throw new Error("No sport access token available"); + }, + async onResponse({ response }) { + // Check the final URL to ensure we are handling only InnoSport API + if (!isSportUrl(response.url)) return; + + if (response.status === 401) { + invalidateMySportAccessToken(); + throw new Error("Unauthorized"); + } + return response; + }, +}; + +const isSportUrl = (url: string) => { + return ( + url.startsWith("https://sport.innopolis.university/api/") || + url.startsWith("https://stage.sport.innopolis.university/api/") || + url.startsWith("http://localhost/api/") + ); +}; diff --git a/src/api/music-room/types.ts b/src/api/music-room/types.ts index cbf77bcb..5cbba751 100644 --- a/src/api/music-room/types.ts +++ b/src/api/music-room/types.ts @@ -268,13 +268,13 @@ export interface components { /** * Time Start * Format: date-time - * @example 2024-10-27T20:33:00 + * @example 2025-02-19T16:08:00 */ time_start: string; /** * Time End * Format: date-time - * @example 2024-10-27T21:33:00 + * @example 2025-02-19T17:08:00 */ time_end: string; }; @@ -555,8 +555,9 @@ export interface operations { bookings_form_schedule: { parameters: { query?: { - /** @example 2024-10-21 */ + /** @example 2025-02-17 */ start_of_week?: string | null; + from_user_id?: number | null; }; header?: never; path?: never; @@ -702,7 +703,7 @@ export interface operations { query?: { /** * @description Date for which to get remaining hours (iso format). Default: server-side today - * @example 2024-10-27 + * @example 2025-02-19 */ date?: string | null; }; @@ -737,7 +738,7 @@ export interface operations { query?: { /** * @description Date for which to get remaining hours (iso format). Default: server-side today - * @example 2024-10-27 + * @example 2025-02-19 */ date?: string | null; }; @@ -801,6 +802,7 @@ export interface operations { }; } export enum UserStatus { + banned = "banned", free = "free", middle = "middle", senior = "senior", diff --git a/src/api/room-booking/types.ts b/src/api/room-booking/types.ts index 0702d766..bc8291a2 100644 --- a/src/api/room-booking/types.ts +++ b/src/api/room-booking/types.ts @@ -21,6 +21,40 @@ export interface paths { patch?: never; trace?: never; }; + "/room/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Room Route */ + get: operations["rooms_room_route"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/room/{id}/bookings": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Room Bookings Route */ + get: operations["rooms_room_bookings_route"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/bookings/": { parameters: { query?: never; @@ -204,12 +238,89 @@ export interface operations { }; }; }; + rooms_room_route: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Room info */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Room"]; + }; + }; + /** @description Room not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + rooms_room_bookings_route: { + parameters: { + query: { + start: string; + end: string; + }; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Room bookings */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Booking"][]; + }; + }; + /** @description Room not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; bookings_bookings: { parameters: { query: { - /** @example 2024-10-27T21:01+00:00 */ start: string; - /** @example 2024-10-28T06:01+00:00 */ end: string; }; header?: never; diff --git a/src/api/sport/index.ts b/src/api/sport/index.ts new file mode 100644 index 00000000..f7acc38e --- /dev/null +++ b/src/api/sport/index.ts @@ -0,0 +1,12 @@ +import createQueryClient from "@/api/helpers/create-query-client.ts"; +import { sportAuthMiddleware } from "@/api/helpers/sport-auth-middleware.ts"; +import createFetchClient from "openapi-fetch"; +import * as sportTypes from "./types.ts"; + +export type { sportTypes }; + +export const sportFetch = createFetchClient({ + baseUrl: import.meta.env.VITE_INNOSPORT_API_URL, +}); +sportFetch.use(sportAuthMiddleware); +export const $sport = createQueryClient(sportFetch, "sport"); diff --git a/src/api/sport/types.ts b/src/api/sport/types.ts new file mode 100644 index 00000000..536ed269 --- /dev/null +++ b/src/api/sport/types.ts @@ -0,0 +1,2412 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/analytics/attendance": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["analytics_attendance_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/attendance/{group_id}/report": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["attendance_report_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/attendance/{student_id}/better_than": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["attendance_better_than_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/attendance/{student_id}/hours": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["attendance_hours_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/attendance/{student_id}/negative_hours": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["attendance_negative_hours_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/attendance/{training_id}/grades": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["attendance_grades_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/attendance/{training_id}/grades.csv": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["attendance_grades.csv_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/attendance/mark": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["attendance_mark_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/attendance/suggest_student": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["attendance_suggest_student_list"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/calendar/{sport_id}/schedule": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["calendar_schedule_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/calendar/trainings": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["calendar_trainings_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enrollment/enroll": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** @description Enroll student + * + * error codes: + * 2 - Group you chosen is full + * 3 - You have too much secondary groups + * 4 - You can't enroll to a group you have already enrolled to + * 6 - Enroll with insufficient medical group */ + post: operations["enrollment_enroll_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enrollment/unenroll": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** @description Unenroll student + * + * Error codes: + * 5 - Can't unenroll from primary group */ + post: operations["enrollment_unenroll_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/enrollment/unenroll_by_trainer": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** @description Unenroll student + * + * Error codes: + * 5 - Can't unenroll from primary group */ + post: operations["enrollment_unenroll_by_trainer_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/fitnesstest/exercises": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Get all exercises by `semester_id`. If `semester_id` is not set, returns current semester exercises. */ + get: operations["fitnesstest_exercises_list"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/fitnesstest/result": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["fitnesstest_result_list"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/fitnesstest/sessions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Get all sessions by `semester_id`. If `semester_id` is not set, returns all sessions. */ + get: operations["fitnesstest_sessions_list"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/fitnesstest/sessions/{session_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["fitnesstest_sessions_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/fitnesstest/suggest_student": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["fitnesstest_suggest_student_list"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/fitnesstest/upload": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["fitnesstest_upload_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/fitnesstest/upload/{session_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["fitnesstest_upload_create_2"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/group/{group_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["group_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/measurement/get_measurements": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["measurement_get_measurements_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/measurement/get_results": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["measurement_get_results_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/measurement/student_measurement": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["measurement_student_measurement_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/medical_groups/": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["medical_groups_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/profile/QR/toggle": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** @description Toggles has_QR status */ + post: operations["profile_QR_toggle_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/profile/change_gender": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["profile_change_gender_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/profile/history/{semester_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Get student's trainings per_semester */ + get: operations["profile_history_list"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/profile/history/by_date": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["profile_history_by_date_list"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/profile/history_with_self/{semester_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Get student's trainings per_semester */ + get: operations["profile_history_with_self_list"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/profile/student": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Get info about current student. */ + get: operations["profile_student_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/reference/upload": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["reference_upload_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/select_sport": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["select_sport_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/selfsport/strava_parsing": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Strava link parsing */ + get: operations["selfsport_strava_parsing_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/selfsport/types": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["selfsport_types_list"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/selfsport/upload": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** @description One link to Strava required (begins with http(s)://) */ + post: operations["selfsport_upload_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/semester": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Get semesters. */ + get: operations["semester_list"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/sports": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["sports_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/training/{training_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["training_retrieve"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/training/{training_id}/cancel_check_in": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["training_cancel_check_in_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/training/{training_id}/check_in": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["training_check_in_create"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + Attendance: { + hours: number; + training_id: number; + /** Format: date */ + date: string; + training_class: string; + group_id: number; + group_name: string; + trainers_emails: unknown[]; + }; + AttendanceMarkRequest: { + training_id: number; + students_hours: components["schemas"]["GradeSetRequest"][]; + }; + BadGradeReport: { + code: number; + description: string; + negative_marks?: components["schemas"]["BadGradeReportGrade"][]; + overflow_marks?: components["schemas"]["BadGradeReportGrade"][]; + }; + BadGradeReportGrade: { + /** Format: email */ + email: string; + hours: number; + }; + BetterThanInfo: { + /** Format: double */ + better_than: number; + }; + Calendar: { + title: string; + /** Format: date-time */ + start: string; + /** Format: date-time */ + end: string; + extendedProps: components["schemas"]["ScheduleExtendedProps"]; + }; + EnrollRequest: { + group_id: number; + }; + Error: { + code: number; + detail: string; + }; + FitnessTestDetail: { + exercise: string; + unit: string | null; + value: number | string; + score: number; + max_score: number; + }; + FitnessTestExercise: { + readonly id: number; + semester: components["schemas"]["Semester"]; + name: string; + unit: string; + select: string[]; + }; + FitnessTestResult: { + student: components["schemas"]["Student"]; + value?: number | null; + }; + FitnessTestSession: { + readonly id: number; + semester: components["schemas"]["Semester"]; + retake: boolean; + /** Format: date-time */ + date: string; + teacher: string; + }; + FitnessTestSessionWithResult: { + session: components["schemas"]["FitnessTestSession"]; + exercises: components["schemas"]["FitnessTestExercise"][]; + results: { + [key: string]: components["schemas"]["FitnessTestResult"][]; + }; + }; + FitnessTestStudentResult: { + semester: string; + retake: boolean; + grade: boolean; + total_score: number; + details: components["schemas"]["FitnessTestDetail"][]; + }; + FitnessTestUpdateEntryRequest: { + student_id: number; + exercise_id: number; + value: string; + }; + FitnessTestUploadRequest: { + semester_id: number; + retake: boolean; + results: components["schemas"]["FitnessTestUpdateEntryRequest"][]; + }; + GenderRequest: { + student_id: number; + gender: number; + }; + GradeReport: { + student_id: number; + first_name: string; + last_name: string; + full_name: string; + /** Format: email */ + email: string; + hours?: number; + }; + GradeSetRequest: { + student_id: number; + hours: number; + }; + GroupInfo: { + group_id: number; + group_name: string; + capacity: number; + current_load: number; + trainer_first_name: string; + trainer_last_name: string; + trainer_email: string; + trainers: components["schemas"]["Trainer"][]; + is_enrolled: boolean; + can_enroll: boolean; + schedule: components["schemas"]["Schedule"][]; + }; + HasQR: { + has_QR: boolean; + }; + HourInfoSemesterChild: { + id_sem: number; + hours_not_self: number; + hours_self_not_debt: number; + hours_self_debt: number; + hours_sem_max: number; + debt: number; + }; + HoursInfo: { + last_semesters_hours: components["schemas"]["HourInfoSemesterChild"][]; + ongoing_semester: components["schemas"]["HourInfoSemesterChild"]; + }; + HoursInfoFull: { + final_hours: number; + }; + InbuiltError: { + readonly detail: string; + }; + LastAttendedDates: { + last_attended_dates: components["schemas"]["LastAttendedStat"][]; + }; + LastAttendedStat: { + student_id: number; + first_name: string; + last_name: string; + full_name: string; + /** Format: email */ + email: string; + last_attended: string; + }; + MeasurementPostRequest: { + student_id: number; + measurement_id: number; + value: number; + }; + MeasurementResult: { + measurement: string; + uint: string; + value: number; + approved: boolean; + /** Format: date */ + date: string; + }; + MeasurementResults: { + semester: string; + result: components["schemas"]["MeasurementResult"][]; + }; + MedicalGroup: { + id: number; + name: string; + description: string; + }; + MedicalGroups: { + medical_groups: components["schemas"]["MedicalGroup"][]; + }; + NewGroup: { + readonly id: number; + name: string; + capacity?: number; + is_club?: boolean; + sport: components["schemas"]["NewSport"]; + semester: components["schemas"]["Semester"]; + teachers: components["schemas"]["NewTrainer"][]; + accredited: boolean; + }; + NewSport: { + readonly id: number; + name: string; + description?: string; + }; + NewTrainer: { + id: number; + first_name: string; + last_name: string; + email: string; + }; + NewTrainingInfo: { + readonly id: number; + custom_name?: string | null; + group: components["schemas"]["NewGroup"]; + /** Format: date-time */ + start: string; + /** Format: date-time */ + end: string; + readonly load: number; + place: string; + }; + NewTrainingInfoStudent: { + training: components["schemas"]["NewTrainingInfo"]; + can_check_in: boolean; + checked_in: boolean; + hours?: number | null; + }; + NotFound: { + /** @default Not found */ + readonly detail: string; + }; + ParsedStrava: { + training_type: string; + pace?: string; + speed?: string; + distance_km: number; + hours: number; + approved: boolean; + }; + PostStudentExerciseResult: { + /** @default ok */ + result: string; + session_id: number; + }; + ReferenceUploadRequest: { + /** Format: binary */ + image: string; + /** Format: date */ + start: string; + /** Format: date */ + end: string; + student_comment?: string | null; + }; + Schedule: { + weekday: components["schemas"]["WeekdayEnum"]; + /** Format: time */ + start: string; + /** Format: time */ + end: string; + training_class?: number | null; + }; + ScheduleExtendedProps: { + id: number; + group_id: number; + training_class: string; + current_load: number; + capacity: number; + }; + SelfSportReportUploadRequest: { + /** Format: uri */ + link: string; + hours: number; + training_type: number; + student_comment?: string; + parsed_data?: unknown; + }; + SelfSportTypes: { + /** ID */ + readonly pk: number; + name: string; + application_rule: string; + }; + Semester: { + readonly id: number; + name: string; + /** Format: date */ + start?: string; + /** Format: date */ + end?: string; + }; + Sport: { + id: number; + name: string; + special: boolean; + }; + SportEnrollRequest: { + sport_id: number; + }; + Sports: { + sports: components["schemas"]["Sport"][]; + }; + Student: { + id: string; + name: string; + /** Format: email */ + email: string; + medical_group: string; + }; + Suggestion: { + value: string; + label: string; + }; + Trainer: { + trainer_first_name: string; + trainer_last_name: string; + trainer_email: string; + }; + TrainingGrades: { + group_name: string; + /** Format: date-time */ + start: string; + grades: components["schemas"]["GradeReport"][]; + academic_duration: number; + }; + TrainingHour: { + group: string; + /** Format: date-time */ + timestamp: string; + hours: number; + }; + UnenrollStudentRequest: { + group_id: number; + student_id: number; + }; + /** + * @description * `0` - Monday + * * `1` - Tuesday + * * `2` - Wednesday + * * `3` - Thursday + * * `4` - Friday + * * `5` - Saturday + * * `6` - Sunday + * @enum {integer} + */ + WeekdayEnum: WeekdayEnum; + training_history404: { + /** @default 404 */ + readonly code: string; + /** @default Not found */ + readonly detail: string; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type SchemaAttendance = components["schemas"]["Attendance"]; +export type SchemaAttendanceMarkRequest = + components["schemas"]["AttendanceMarkRequest"]; +export type SchemaBadGradeReport = components["schemas"]["BadGradeReport"]; +export type SchemaBadGradeReportGrade = + components["schemas"]["BadGradeReportGrade"]; +export type SchemaBetterThanInfo = components["schemas"]["BetterThanInfo"]; +export type SchemaCalendar = components["schemas"]["Calendar"]; +export type SchemaEnrollRequest = components["schemas"]["EnrollRequest"]; +export type SchemaError = components["schemas"]["Error"]; +export type SchemaFitnessTestDetail = + components["schemas"]["FitnessTestDetail"]; +export type SchemaFitnessTestExercise = + components["schemas"]["FitnessTestExercise"]; +export type SchemaFitnessTestResult = + components["schemas"]["FitnessTestResult"]; +export type SchemaFitnessTestSession = + components["schemas"]["FitnessTestSession"]; +export type SchemaFitnessTestSessionWithResult = + components["schemas"]["FitnessTestSessionWithResult"]; +export type SchemaFitnessTestStudentResult = + components["schemas"]["FitnessTestStudentResult"]; +export type SchemaFitnessTestUpdateEntryRequest = + components["schemas"]["FitnessTestUpdateEntryRequest"]; +export type SchemaFitnessTestUploadRequest = + components["schemas"]["FitnessTestUploadRequest"]; +export type SchemaGenderRequest = components["schemas"]["GenderRequest"]; +export type SchemaGradeReport = components["schemas"]["GradeReport"]; +export type SchemaGradeSetRequest = components["schemas"]["GradeSetRequest"]; +export type SchemaGroupInfo = components["schemas"]["GroupInfo"]; +export type SchemaHasQr = components["schemas"]["HasQR"]; +export type SchemaHourInfoSemesterChild = + components["schemas"]["HourInfoSemesterChild"]; +export type SchemaHoursInfo = components["schemas"]["HoursInfo"]; +export type SchemaHoursInfoFull = components["schemas"]["HoursInfoFull"]; +export type SchemaInbuiltError = components["schemas"]["InbuiltError"]; +export type SchemaLastAttendedDates = + components["schemas"]["LastAttendedDates"]; +export type SchemaLastAttendedStat = components["schemas"]["LastAttendedStat"]; +export type SchemaMeasurementPostRequest = + components["schemas"]["MeasurementPostRequest"]; +export type SchemaMeasurementResult = + components["schemas"]["MeasurementResult"]; +export type SchemaMeasurementResults = + components["schemas"]["MeasurementResults"]; +export type SchemaMedicalGroup = components["schemas"]["MedicalGroup"]; +export type SchemaMedicalGroups = components["schemas"]["MedicalGroups"]; +export type SchemaNewGroup = components["schemas"]["NewGroup"]; +export type SchemaNewSport = components["schemas"]["NewSport"]; +export type SchemaNewTrainer = components["schemas"]["NewTrainer"]; +export type SchemaNewTrainingInfo = components["schemas"]["NewTrainingInfo"]; +export type SchemaNewTrainingInfoStudent = + components["schemas"]["NewTrainingInfoStudent"]; +export type SchemaNotFound = components["schemas"]["NotFound"]; +export type SchemaParsedStrava = components["schemas"]["ParsedStrava"]; +export type SchemaPostStudentExerciseResult = + components["schemas"]["PostStudentExerciseResult"]; +export type SchemaReferenceUploadRequest = + components["schemas"]["ReferenceUploadRequest"]; +export type SchemaSchedule = components["schemas"]["Schedule"]; +export type SchemaScheduleExtendedProps = + components["schemas"]["ScheduleExtendedProps"]; +export type SchemaSelfSportReportUploadRequest = + components["schemas"]["SelfSportReportUploadRequest"]; +export type SchemaSelfSportTypes = components["schemas"]["SelfSportTypes"]; +export type SchemaSemester = components["schemas"]["Semester"]; +export type SchemaSport = components["schemas"]["Sport"]; +export type SchemaSportEnrollRequest = + components["schemas"]["SportEnrollRequest"]; +export type SchemaSports = components["schemas"]["Sports"]; +export type SchemaStudent = components["schemas"]["Student"]; +export type SchemaSuggestion = components["schemas"]["Suggestion"]; +export type SchemaTrainer = components["schemas"]["Trainer"]; +export type SchemaTrainingGrades = components["schemas"]["TrainingGrades"]; +export type SchemaTrainingHour = components["schemas"]["TrainingHour"]; +export type SchemaUnenrollStudentRequest = + components["schemas"]["UnenrollStudentRequest"]; +export type SchemaWeekdayEnum = components["schemas"]["WeekdayEnum"]; +export type SchemaTrainingHistory404 = + components["schemas"]["training_history404"]; +export type $defs = Record; +export interface operations { + analytics_attendance_retrieve: { + parameters: { + query?: { + medical_group_id?: number; + sport_id?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: number; + }; + }; + }; + }; + }; + attendance_report_retrieve: { + parameters: { + query?: never; + header?: never; + path: { + group_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["LastAttendedDates"]; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InbuiltError"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + attendance_better_than_retrieve: { + parameters: { + query?: never; + header?: never; + path: { + student_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BetterThanInfo"]; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InbuiltError"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + attendance_hours_retrieve: { + parameters: { + query?: never; + header?: never; + path: { + student_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HoursInfo"]; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InbuiltError"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + attendance_negative_hours_retrieve: { + parameters: { + query?: never; + header?: never; + path: { + student_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HoursInfoFull"]; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InbuiltError"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + attendance_grades_retrieve: { + parameters: { + query?: never; + header?: never; + path: { + training_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TrainingGrades"]; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InbuiltError"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + "attendance_grades.csv_retrieve": { + parameters: { + query?: never; + header?: never; + path: { + training_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "text/csv": string; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InbuiltError"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + attendance_mark_create: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["AttendanceMarkRequest"]; + "application/x-www-form-urlencoded": components["schemas"]["AttendanceMarkRequest"]; + "multipart/form-data": components["schemas"]["AttendanceMarkRequest"]; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BadGradeReportGrade"][]; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["BadGradeReport"]; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["InbuiltError"]; + }; + }; + }; + }; + attendance_suggest_student_list: { + parameters: { + query: { + group_id: number; + term: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Suggestion"][]; + }; + }; + }; + }; + calendar_schedule_retrieve: { + parameters: { + query: { + end: string; + start: string; + }; + header?: never; + path: { + sport_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Calendar"]; + }; + }; + }; + }; + calendar_trainings_retrieve: { + parameters: { + query: { + end: string; + start: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Calendar"]; + }; + }; + }; + }; + enrollment_enroll_create: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["EnrollRequest"]; + "application/x-www-form-urlencoded": components["schemas"]["EnrollRequest"]; + "multipart/form-data": components["schemas"]["EnrollRequest"]; + }; + }; + responses: { + /** @description No response body */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + enrollment_unenroll_create: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["EnrollRequest"]; + "application/x-www-form-urlencoded": components["schemas"]["EnrollRequest"]; + "multipart/form-data": components["schemas"]["EnrollRequest"]; + }; + }; + responses: { + /** @description No response body */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + enrollment_unenroll_by_trainer_create: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UnenrollStudentRequest"]; + "application/x-www-form-urlencoded": components["schemas"]["UnenrollStudentRequest"]; + "multipart/form-data": components["schemas"]["UnenrollStudentRequest"]; + }; + }; + responses: { + /** @description No response body */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + fitnesstest_exercises_list: { + parameters: { + query?: { + semester_id?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FitnessTestExercise"][]; + }; + }; + }; + }; + fitnesstest_result_list: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FitnessTestStudentResult"][]; + }; + }; + }; + }; + fitnesstest_sessions_list: { + parameters: { + query?: { + semester_id?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FitnessTestSession"][]; + }; + }; + }; + }; + fitnesstest_sessions_retrieve: { + parameters: { + query?: never; + header?: never; + path: { + session_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["FitnessTestSessionWithResult"]; + }; + }; + }; + }; + fitnesstest_suggest_student_list: { + parameters: { + query: { + term: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Suggestion"][]; + }; + }; + }; + }; + fitnesstest_upload_create: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["FitnessTestUploadRequest"]; + "application/x-www-form-urlencoded": components["schemas"]["FitnessTestUploadRequest"]; + "multipart/form-data": components["schemas"]["FitnessTestUploadRequest"]; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PostStudentExerciseResult"]; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + fitnesstest_upload_create_2: { + parameters: { + query?: never; + header?: never; + path: { + session_id: number; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["FitnessTestUploadRequest"]; + "application/x-www-form-urlencoded": components["schemas"]["FitnessTestUploadRequest"]; + "multipart/form-data": components["schemas"]["FitnessTestUploadRequest"]; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["PostStudentExerciseResult"]; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + group_retrieve: { + parameters: { + query?: never; + header?: never; + path: { + group_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["GroupInfo"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + measurement_get_measurements_retrieve: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MeasurementResults"]; + }; + }; + }; + }; + measurement_get_results_retrieve: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MeasurementResults"]; + }; + }; + }; + }; + measurement_student_measurement_create: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["MeasurementPostRequest"]; + "application/x-www-form-urlencoded": components["schemas"]["MeasurementPostRequest"]; + "multipart/form-data": components["schemas"]["MeasurementPostRequest"]; + }; + }; + responses: { + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + medical_groups_retrieve: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["MedicalGroups"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + profile_QR_toggle_create: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HasQR"]; + }; + }; + }; + }; + profile_change_gender_create: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["GenderRequest"]; + "application/x-www-form-urlencoded": components["schemas"]["GenderRequest"]; + "multipart/form-data": components["schemas"]["GenderRequest"]; + }; + }; + responses: { + /** @description No response body */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + profile_history_list: { + parameters: { + query?: never; + header?: never; + path: { + semester_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TrainingHour"][]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["training_history404"]; + }; + }; + }; + }; + profile_history_by_date_list: { + parameters: { + query: { + /** @description date in format YYYY-MM-DD */ + date_end: string; + /** @description date in format YYYY-MM-DD */ + date_start: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Attendance"][]; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + profile_history_with_self_list: { + parameters: { + query?: never; + header?: never; + path: { + semester_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["TrainingHour"][]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["training_history404"]; + }; + }; + }; + }; + profile_student_retrieve: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Student"]; + }; + }; + }; + }; + reference_upload_create: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "multipart/form-data": components["schemas"]["ReferenceUploadRequest"]; + }; + }; + responses: { + /** @description No response body */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + select_sport_create: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["SportEnrollRequest"]; + "application/x-www-form-urlencoded": components["schemas"]["SportEnrollRequest"]; + "multipart/form-data": components["schemas"]["SportEnrollRequest"]; + }; + }; + responses: { + /** @description No response body */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + selfsport_strava_parsing_retrieve: { + parameters: { + query: { + link: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["ParsedStrava"]; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + selfsport_types_list: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SelfSportTypes"][]; + }; + }; + }; + }; + selfsport_upload_create: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "multipart/form-data": components["schemas"]["SelfSportReportUploadRequest"]; + }; + }; + responses: { + /** @description No response body */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; + semester_list: { + parameters: { + query?: { + current?: boolean; + with_ft_exercises?: boolean; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Semester"][]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + sports_retrieve: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Sports"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + training_retrieve: { + parameters: { + query?: never; + header?: never; + path: { + training_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NewTrainingInfoStudent"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + training_cancel_check_in_create: { + parameters: { + query?: never; + header?: never; + path: { + training_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description No response body */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; + training_check_in_create: { + parameters: { + query?: never; + header?: never; + path: { + training_id: number; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description No response body */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["NotFound"]; + }; + }; + }; + }; +} +export enum WeekdayEnum { + Value0 = 0, + Value1 = 1, + Value2 = 2, + Value3 = 3, + Value4 = 4, + Value5 = 5, + Value6 = 6, +} diff --git a/src/api/sports/index.ts b/src/api/sports/index.ts deleted file mode 100644 index 806dd5f2..00000000 --- a/src/api/sports/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { authMiddleware } from "@/api/helpers/auth-middleware.ts"; -import createQueryClient from "@/api/helpers/create-query-client.ts"; -import createFetchClient from "openapi-fetch"; -import * as sportsTypes from "./types.ts"; - -export type { sportsTypes }; - -export const sportsFetch = createFetchClient({ - baseUrl: import.meta.env.VITE_SPORTS_API_URL, -}); -sportsFetch.use(authMiddleware); -export const $sports = createQueryClient(sportsFetch, "sports"); diff --git a/src/api/sports/types.ts b/src/api/sports/types.ts deleted file mode 100644 index 757e090e..00000000 --- a/src/api/sports/types.ts +++ /dev/null @@ -1,201 +0,0 @@ -/** - * This file was auto-generated by openapi-typescript. - * Do not make direct changes to the file. - */ - -export interface paths { - "/users/sport_info": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get Me - * @description Get current sport hours, semesters info, and nearest check-ins - */ - get: operations["users_get_me"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; -} -export type webhooks = Record; -export interface components { - schemas: { - /** Checkin */ - Checkin: { - /** - * Title - * @description Sport name - */ - title: string; - /** - * Start - * @description Training start time - */ - start: string; - /** - * End - * @description End time of training - */ - end: string; - /** - * Training Class - * @description Place of training - */ - training_class: string; - /** - * Group Accredited - * @description Accreditation of training - */ - group_accredited: boolean; - }; - /** Profile */ - Profile: { - /** - * Id - * @description User id on sports website - */ - id: string; - /** - * Name - * @description User's first and last name - */ - name: string; - /** - * Email - * @description User university email - */ - email: string; - /** - * Medical Group - * @description User medical group for sport - */ - medical_group: string; - }; - /** Semester */ - Semester: { - /** - * Id Sem - * @description Semester id - */ - id_sem: number; - /** - * Hours Not Self - * @description Earned hours of training by appointment - */ - hours_not_self: number; - /** - * Hours Self Not Debt - * @description Hours earned in personal training - */ - hours_self_not_debt: number; - /** - * Hours Self Debt - * @description Earned hours in personal training to cover hours debt - */ - hours_self_debt: number; - /** - * Hours Sem Max - * @description Number of hours required to complete the semester - */ - hours_sem_max: number; - /** - * Debt - * @description User's hours debt for previous semesters - */ - debt: number; - }; - /** Training */ - Training: { - /** - * Hours - * @description Duration of training - */ - hours: number; - /** - * Group - * @description Sport name - */ - group: string; - /** - * Timestamp - * Format: date-time - * @description Training start time - */ - timestamp: string; - /** - * Approved - * @description Hours approval - */ - approved: boolean; - }; - /** ViewUser */ - ViewUser: { - profile: components["schemas"]["Profile"]; - /** - * Checkins - * @description List of workouts the user has signed up for in the coming week - */ - checkins: components["schemas"]["Checkin"][]; - /** - * Old Semesters - * @description Hours earned for past semesters - */ - old_semesters: components["schemas"]["Semester"][]; - /** @description Current semester */ - ongoing_semester: components["schemas"]["Semester"]; - /** - * Trainings History - * @description List of attended trainings for the current semester - */ - trainings_history: components["schemas"]["Training"][]; - }; - }; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; -} -export type SchemaCheckin = components["schemas"]["Checkin"]; -export type SchemaProfile = components["schemas"]["Profile"]; -export type SchemaSemester = components["schemas"]["Semester"]; -export type SchemaTraining = components["schemas"]["Training"]; -export type SchemaViewUser = components["schemas"]["ViewUser"]; -export type $defs = Record; -export interface operations { - users_get_me: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Current sport info for user */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ViewUser"]; - }; - }; - /** @description Unable to verify credentials OR Credentials not provided */ - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; -} diff --git a/src/app/styles-snow.css b/src/app/styles-snow.css deleted file mode 100644 index 38992a14..00000000 --- a/src/app/styles-snow.css +++ /dev/null @@ -1,23 +0,0 @@ -@keyframes snow { - 0% { - background-position: - 0 0, - 0 0, - 0 0; - } - 100% { - background-position: - 3600px 4800px, - 3200px 3200px, - 3500px 5000px; - } -} - -.snow body { - animation: snow 210s linear infinite; - background-image: url("/snow-1.svg"), url("/snow-2.svg"), url("/snow-3.svg"); - background-size: - 600px 600px, - 400px 400px, - 250px 250px; -} diff --git a/src/components/calendar/CalendarPage.tsx b/src/components/calendar/CalendarPage.tsx index 117da7cc..71b917c0 100644 --- a/src/components/calendar/CalendarPage.tsx +++ b/src/components/calendar/CalendarPage.tsx @@ -39,6 +39,9 @@ export function CalendarPage() { predefined.event_groups, eventGroups, eventsUser.id, + eventsUser.music_room_hidden, + eventsUser.sports_hidden, + eventsUser.moodle_hidden, ) } initialView={ @@ -61,8 +64,11 @@ function getCalendarsToShow( favorites: number[], hidden: number[], predefined: number[], - eventGroups: eventsTypes.SchemaListEventGroupsResponseOutput, + eventGroups: eventsTypes.SchemaListEventGroupsResponse, userId: number | undefined, + music_room_hidden: boolean, + sports_hidden: boolean, + moodle_hidden: boolean, ): URLType[] { // Remove hidden calendars const toShow: URLType[] = favorites.concat(predefined).flatMap((v) => { @@ -73,25 +79,33 @@ function getCalendarsToShow( }); // Add personal calendars - toShow.push({ - url: getMyMusicRoomLink(), - color: "seagreen", - sourceLink: "https://t.me/InnoMusicRoomBot", - updatedAt: new Date().toISOString(), - }); - toShow.push({ - url: getMySportLink(), - color: "seagreen", - sourceLink: "https://sport.innopolis.university", - updatedAt: new Date().toISOString(), - }); - toShow.push({ - url: getMyMoodleLink(), - color: "seagreen", - sourceLink: - "https://moodle.innopolis.university/calendar/view.php?view=month", - updatedAt: new Date().toISOString(), - }); + if (!music_room_hidden) { + toShow.push({ + url: getMyMusicRoomLink(), + color: "seagreen", + sourceLink: "https://t.me/InnoMusicRoomBot", + updatedAt: new Date().toISOString(), + }); + } + + if (!sports_hidden) { + toShow.push({ + url: getMySportLink(), + color: "seagreen", + sourceLink: "https://sport.innopolis.university", + updatedAt: new Date().toISOString(), + }); + } + + if (!moodle_hidden) { + toShow.push({ + url: getMyMoodleLink(), + color: "seagreen", + sourceLink: + "https://moodle.innopolis.university/calendar/view.php?view=month", + updatedAt: new Date().toISOString(), + }); + } // Return unique items return toShow.filter((value, index, array) => array.indexOf(value) === index); diff --git a/src/components/calendar/SourcesDialog.tsx b/src/components/calendar/SourcesDialog.tsx index 2746be27..f5da9158 100644 --- a/src/components/calendar/SourcesDialog.tsx +++ b/src/components/calendar/SourcesDialog.tsx @@ -14,6 +14,7 @@ import { useTransitionStyles, } from "@floating-ui/react"; import { Link } from "@tanstack/react-router"; +import { PathsUsersMeTargetHidePostParametersPathTarget as Type } from "@/api/events/types.ts"; export function SourcesDialog({ open, @@ -86,11 +87,12 @@ export function SourcesDialog({ name="Sport" description="Your sport schedule" pageUrl="/sport" + targetType={Type.sports} buttons={ + } tooltip="Open Telegram bot" /> @@ -101,11 +103,12 @@ export function SourcesDialog({ name="Music room" description="Your room bookings" pageUrl="/music-room" + targetType={Type.music_room} buttons={ + } tooltip="Open Telegram bot" /> @@ -122,11 +125,12 @@ export function SourcesDialog({ } description="Your Moodle deadlines" + targetType={Type.moodle} buttons={ + } tooltip="Install the browser extension to sync Moodle calendar" /> diff --git a/src/components/dashboard/AdvWidget.tsx b/src/components/dashboard/AdvWidget.tsx index 1530c417..165f58cb 100644 --- a/src/components/dashboard/AdvWidget.tsx +++ b/src/components/dashboard/AdvWidget.tsx @@ -1,6 +1,8 @@ import Logo108 from "@/components/icons/Logo108.tsx"; export function AdvWidget() { + return null; + return (
{ - setWidgetShown((v) => (v && isPending) || !!sportInfo); - }, [setWidgetShown, isPending, sportInfo]); + setWidgetShown((v) => (v && profileIsPending) || hasSportProfile); + }, [setWidgetShown, profileIsPending, hasSportProfile]); - if (!sportInfo) { + if (!hasSportProfile || !hours || !semesters) { if (!widgetShown) return null; return (
@@ -23,14 +37,17 @@ export function SportsWidget() { } const earnedHours = - sportInfo.ongoing_semester.hours_not_self + - sportInfo.ongoing_semester.hours_self_not_debt + - sportInfo.ongoing_semester.hours_self_debt; - const semesterHours = sportInfo.ongoing_semester.hours_sem_max; - const debtHours = sportInfo.ongoing_semester.debt; + hours.ongoing_semester.hours_not_self + + hours.ongoing_semester.hours_self_not_debt + + hours.ongoing_semester.hours_self_debt; + const semesterHours = hours.ongoing_semester.hours_sem_max; + const debtHours = hours.ongoing_semester.debt; + + const currentSemester = semesters.find( + (s) => s.id === hours.ongoing_semester.id_sem, + ); - // TODO: Fetch the end date of current semester from sports - const deadline = new Date("2025-05-04"); + const deadline = new Date(currentSemester?.end || "2025-05-04"); const daysLeft = Math.max( 0, Math.ceil((deadline.getTime() - nowMs) / (1000 * 60 * 60 * 24)), diff --git a/src/components/icons/logo-invert.svg b/src/components/icons/logo-invert.svg old mode 100644 new mode 100755 index 5fdfa255..d38bf059 --- a/src/components/icons/logo-invert.svg +++ b/src/components/icons/logo-invert.svg @@ -1,126 +1,23 @@ - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + \ No newline at end of file diff --git a/src/components/icons/logo.svg b/src/components/icons/logo.svg old mode 100644 new mode 100755 index 762b6f10..27b42175 --- a/src/components/icons/logo.svg +++ b/src/components/icons/logo.svg @@ -1,126 +1,23 @@ - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + \ No newline at end of file diff --git a/src/components/layout/UserMenu.tsx b/src/components/layout/UserMenu.tsx index 15b5bdd1..448e0230 100644 --- a/src/components/layout/UserMenu.tsx +++ b/src/components/layout/UserMenu.tsx @@ -64,7 +64,7 @@ function UserMenu({ isMobile, isSidebar }: UserMenuProps) { {...getReferenceProps()} className="flex items-center justify-center rounded-xl p-2 hover:bg-secondary" > - + diff --git a/src/components/maps/viewer/MapViewer.tsx b/src/components/maps/viewer/MapViewer.tsx index 9134dd04..a6a11f78 100644 --- a/src/components/maps/viewer/MapViewer.tsx +++ b/src/components/maps/viewer/MapViewer.tsx @@ -353,6 +353,17 @@ export const MapViewer = memo(function MapViewer({ ` : ""} + {mapSvg?.data && (
# diff --git a/src/components/music-room/Section.tsx b/src/components/music-room/Section.tsx index edc984bd..8b220794 100644 --- a/src/components/music-room/Section.tsx +++ b/src/components/music-room/Section.tsx @@ -6,7 +6,7 @@ export function Section({ }: PropsWithChildren>) { return (
{children} diff --git a/src/components/music-room/instructions.mdx b/src/components/music-room/instructions.mdx index d1c121fe..c181d937 100644 --- a/src/components/music-room/instructions.mdx +++ b/src/components/music-room/instructions.mdx @@ -20,20 +20,20 @@ and Innopolis residents. Just follow the steps below. const url = atob(encodedURL); window.open(url, "_blank"); }} - className="group flex flex-row gap-4 rounded-2xl bg-primary px-4 py-6 text-left hover:bg-secondary" + className="group mx-4 flex flex-row gap-4 rounded-2xl bg-primary px-4 py-6 text-left hover:bg-secondary" id="chat" >
-

+

Telegram chat -

-

+

+
Join telegram chat to receive latest news about Music room and ask questions. -

+
diff --git a/src/components/schedule/group-card/FavoriteButton.tsx b/src/components/schedule/group-card/FavoriteButton.tsx index 7546c9d6..06b05cd0 100644 --- a/src/components/schedule/group-card/FavoriteButton.tsx +++ b/src/components/schedule/group-card/FavoriteButton.tsx @@ -33,11 +33,11 @@ export default function FavoriteButton({ groupId }: { groupId: number }) { className="-mr-2 flex h-12 w-12 items-center justify-center rounded-2xl text-4xl text-contrast/50 hover:bg-secondary-hover hover:text-contrast/75" > {isPredefined ? ( - + ) : isFavorite ? ( - + ) : ( - + )} diff --git a/src/components/schedule/group-card/PersonalCard.tsx b/src/components/schedule/group-card/PersonalCard.tsx index 44432e6d..dc0ac47c 100644 --- a/src/components/schedule/group-card/PersonalCard.tsx +++ b/src/components/schedule/group-card/PersonalCard.tsx @@ -1,8 +1,11 @@ import { useNavigate } from "@tanstack/react-router"; +import { PathsUsersMeTargetHidePostParametersPathTarget as Type } from "@/api/events/types.ts"; +import HideButtonPersonal from "@/components/schedule/personal-card/HideButtonPersonal.tsx"; export type PersonalCardProps = { name: React.ReactNode; description: React.ReactNode; + targetType: Type; buttons?: React.ReactNode; pageUrl?: string; canHide?: boolean; @@ -13,9 +16,10 @@ export function PersonalCard({ description, buttons, pageUrl, + targetType, + canHide = true, }: PersonalCardProps) { const navigate = useNavigate(); - return (
{description}

- {/*{canHide && }*/} + {canHide && } {buttons}
diff --git a/src/components/schedule/personal-card/HideButtonPersonal.tsx b/src/components/schedule/personal-card/HideButtonPersonal.tsx new file mode 100644 index 00000000..d3900c9d --- /dev/null +++ b/src/components/schedule/personal-card/HideButtonPersonal.tsx @@ -0,0 +1,79 @@ +import Tooltip from "@/components/common/Tooltip"; +import { $events, eventsTypes } from "@/api/events"; +import { PathsUsersMeTargetHidePostParametersPathTarget as Type } from "@/api/events/types.ts"; +import { useQueryClient } from "@tanstack/react-query"; + +export default function HideButtonPersonal({ target }: { target: Type }) { + const queryClient = useQueryClient(); + const { data: eventsUser } = $events.useQuery("get", "/users/me"); + + const onSettled = () => + queryClient.invalidateQueries({ + queryKey: $events.queryOptions("get", "/users/me").queryKey, + }); + + let isHidden = false; + if (target == Type.sports) { + isHidden = eventsUser?.sports_hidden ?? false; + } else if (target == Type.moodle) { + isHidden = eventsUser?.moodle_hidden ?? false; + } else if (target == Type.music_room) { + isHidden = eventsUser?.music_room_hidden ?? false; + } + + const hide = $events.useMutation("post", "/users/me/{target}/hide", { + onMutate: ({ params }) => { + console.log(params); + queryClient.setQueryData( + $events.queryOptions("get", "/users/me").queryKey, + (prev: eventsTypes.SchemaViewUser) => { + if (prev === undefined) return prev; + const patch: { + sports_hidden?: boolean; + moodle_hidden?: boolean; + music_room_hidden?: boolean; + } = {}; + + if (params.path.target == Type.sports) { + patch.sports_hidden = params.query?.hide; + } else if (params.path.target == Type.moodle) { + patch.moodle_hidden = params.query?.hide; + } else if (params.path.target == Type.music_room) { + patch.music_room_hidden = params.query?.hide; + } + + return { + ...prev, + ...patch, + }; + }, + ); + }, + onSettled, + }); + + const switchHideFavorite = () => { + hide.mutate({ + params: { query: { hide: !isHidden }, path: { target: target } }, + }); + }; + + return ( + + + + ); +}