Skip to content

Commit 62f9db5

Browse files
authored
Merge branch 'develop' into feature/switchSidebarPosition
2 parents 84999e2 + 18a61f0 commit 62f9db5

File tree

170 files changed

+15430
-2591
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

170 files changed

+15430
-2591
lines changed

.env.example

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
VITE_APP_PROD=0
2+
VITE_APP_STAGING=0
23
VITE_APP_RELEASE_DATE=2023-01-27
34
PORT=3100
45
VITE_APP_PLAUSIBLE_DOMAIN=
56
VITE_APP_PLAUSIBLE_HOST=
67
VITE_APP_SEMESTER=
78
VITE_APP_SITE_TITLE=
89
VITE_APP_BACKEND_URL=
10+
VITE_APP_T_CLASS_CONFLICTS=0 # Specify whether theoretical classes should generate conflicts
911

1012
VITE_APP_SENTRY_DSN=https://01f0882e6aa029a125426e4ad32e6c18@o553498.ingest.us.sentry.io/4507775325437952
1113
VITE_APP_SENTRY_TRACING=0
1214

15+
1316
# To upload source maps to Sentry (Optional)
14-
APP_SENTRY_AUTH_TOKEN=
17+
APP_SENTRY_AUTH_TOKEN=«

dev.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#!/bin/bash
22
set -e # abort script if any command fails
33

4-
docker compose up $1 tts-frontend
4+
docker compose up $1 tts-frontend

docker-compose.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ services:
2525
- ./public:/usr/src/tts-fe/public
2626
ports:
2727
- 3100:80
28+
29+
networks:
30+
default:
31+
name: tts-docker-network

package-lock.json

+5,718-2,024
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+17-5
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,25 @@
66
"dependencies": {
77
"@headlessui/react": "^1.7.17",
88
"@heroicons/react": "^2.1.1",
9+
"@hookform/resolvers": "3.10.0",
910
"@radix-ui/react-alert-dialog": "^1.0.5",
10-
"@radix-ui/react-checkbox": "^1.0.4",
11+
"@radix-ui/react-avatar": "1.1.0",
12+
"@radix-ui/react-checkbox": "1.0.4",
1113
"@radix-ui/react-dialog": "^1.0.5",
1214
"@radix-ui/react-dropdown-menu": "^2.0.6",
13-
"@radix-ui/react-label": "^2.0.2",
15+
"@radix-ui/react-label": "^2.1.0",
1416
"@radix-ui/react-popover": "^1.0.7",
1517
"@radix-ui/react-scroll-area": "^1.0.5",
1618
"@radix-ui/react-separator": "^1.0.3",
17-
"@radix-ui/react-slot": "^1.0.2",
19+
"@radix-ui/react-slot": "^1.1.0",
1820
"@radix-ui/react-tabs": "^1.0.4",
1921
"@radix-ui/react-toast": "^1.1.5",
2022
"@radix-ui/react-toggle": "^1.0.3",
2123
"@radix-ui/react-toggle-group": "^1.0.4",
2224
"@radix-ui/react-tooltip": "^1.0.7",
25+
"@radix-ui/react-hover-card": "1.1.4",
26+
"@radix-ui/react-select": "2.1.1",
27+
"@radix-ui/react-switch": "1.1.0",
2328
"@sentry/react": "^8.33.1",
2429
"@sentry/vite-plugin": "^2.22.1",
2530
"@testing-library/jest-dom": "^5.16.4",
@@ -35,26 +40,33 @@
3540
"classnames": "^2.3.1",
3641
"clsx": "^2.1.0",
3742
"cmdk": "^0.2.1",
43+
"date-fns": "^3.6.0",
3844
"emoji-picker-react": "^4.6.4",
3945
"html-to-image": "^1.11.11",
46+
"js-cookie": "^3.0.5",
4047
"lucide-react": "^0.307.0",
4148
"node-fetch": "^3.2.8",
4249
"plausible-tracker": "^0.3.9",
4350
"react": "^18.2.0",
51+
"react-day-picker": "^9.5.1",
4452
"react-dom": "^18.1.0",
53+
"react-hook-form": "^7.54.2",
4554
"react-router-dom": "^6.3.0",
4655
"react-sortablejs": "^6.1.4",
56+
"react-spinners": "^0.14.1",
4757
"react-toastify": "^9.1.1",
4858
"socket.io-client": "^4.8.0",
4959
"sortablejs": "^1.15.2",
5060
"swr": "^2.2.5",
5161
"tailwind-merge": "^2.2.0",
5262
"tailwindcss-animate": "^1.0.7",
5363
"typescript": "^4.6.4",
64+
"unique-names-generator": "^4.7.1",
5465
"usehooks-ts": "^2.6.0",
55-
"vite": "^5.4.6",
66+
"vite": "^5.4.14",
5667
"vite-tsconfig-paths": "^4.3.2",
57-
"web-vitals": "^2.1.4"
68+
"web-vitals": "^2.1.4",
69+
"zod": "^3.24.1"
5870
},
5971
"scripts": {
6072
"dev": "vite --host",

src/@types/index.d.ts

+134-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { DirectExchangePendingMotive } from "../utils/exchange"
2+
13
enum lesson_type {
24
T = "T", // Ensino teórico
35
TP = "TP", // Ensino teórico-prático
@@ -22,6 +24,7 @@ export type Major = {
2224

2325
export type CourseInfo = {
2426
id: number,
27+
course: number,
2528
course_unit_year: number,
2629
course_unit_id: number,
2730
ects: number,
@@ -94,12 +97,136 @@ export type ImportedCourses = {
9497
[key: string]: string
9598
}
9699

100+
/* Exchange data types */
101+
102+
export type ExchangeOption = {
103+
course_info: CourseInfo,
104+
course_unit_id: number,
105+
class_issuer_goes_from: ClassInfo,
106+
class_issuer_goes_to: ClassInfo,
107+
other_student?: number // The current student will be determined by the backend depending on session data
108+
}
109+
110+
export type CreateRequestCardMetadata = {
111+
courseUnitName: string,
112+
courseUnitAcronym: string,
113+
requesterClassName: string,
114+
availableClasses: Array<string> // Classes from the course unit
115+
}
116+
117+
export type CreateRequestData = {
118+
courseUnitId: number,
119+
courseUnitName: string,
120+
classNameRequesterGoesFrom: string,
121+
classNameRequesterGoesTo: string,
122+
other_student?: Student
123+
}
124+
125+
export type MarketplaceRequest = {
126+
id: number,
127+
type: string,
128+
issuer_name: string,
129+
issuer_nmec: string,
130+
date: string,
131+
options?: Array<ExchangeOption>,
132+
classes?: Array<ClassInfo>,
133+
pending_motive?: DirectExchangePendingMotive,
134+
accepted: boolean,
135+
admin_state: string,
136+
canceled: boolean
137+
}
138+
139+
export type DirectExchangeRequest = {
140+
id: number,
141+
type: string,
142+
issuer_name: string,
143+
issuer_nmec: string,
144+
accepted: boolean,
145+
canceled: boolean,
146+
pending_motive?: DirectExchangePendingMotive,
147+
admin_state: string,
148+
options: DirectExchangeParticipant[],
149+
date: string
150+
}
151+
152+
export type DirectExchangeParticipant = {
153+
id: number,
154+
course_info: CourseInfo,
155+
participant_name: string,
156+
participant_nmec: string,
157+
class_participant_goes_from: ClassInfo,
158+
class_participant_goes_to: ClassInfo,
159+
schedule: Array<ClassDescriptor>,
160+
course_unit: string,
161+
course_unit_id: string,
162+
accepted: boolean
163+
date: string
164+
}
165+
166+
export type UrgentRequest = {
167+
id: number,
168+
user_nmec: string,
169+
date: string,
170+
message: string,
171+
accepted: boolean,
172+
admin_state: string,
173+
options: Array<UrgentRequestOption>
174+
schedule: Array<ClassDescriptor>
175+
}
176+
177+
export type UrgentRequestOption = {
178+
course_unit: CourseInfo,
179+
class_user_goes_from: ClassInfo,
180+
class_user_goes_to: ClassInfo,
181+
}
182+
183+
export type CourseUnitEnrollment = {
184+
id: number,
185+
date: string,
186+
user_nmec: string,
187+
admin_state: string,
188+
accepted: boolean,
189+
schedule: Array<ClassDescriptor>,
190+
options: Array<CourseUnitEnrollmentOption>
191+
}
192+
193+
export type CourseUnitEnrollmentOption = {
194+
course_unit: CourseInfo,
195+
course: Major,
196+
enrolling: boolean,
197+
class_user_goes_to: ClassInfo,
198+
}
199+
200+
export enum AdminRequestType {
201+
DIRECT_EXCHANGE = "direct_exchange",
202+
URGENT_EXCHANGE = "urgent_exchange",
203+
ENROLLMENT = "enrollment"
204+
}
205+
206+
export type StudentCourseMetadata = {
207+
nmec: string,
208+
fest_id: number,
209+
course: CourseInfo
210+
}
211+
212+
export type Student = {
213+
name: string,
214+
mecNumber: number,
215+
classInfo?: ClassInfo
216+
}
217+
218+
export type Participant = {
219+
client_id: string
220+
name: string
221+
}
222+
223+
// TODO(Process-ing): Maybe join Student and Participant into a single type
224+
97225
export type CollabSession = {
98-
id: number
226+
id: string
99227
name: string
100-
lastEdited: string
101-
lifeSpan: number
102-
currentUser: string
103-
link : string
104-
participants: Array<string>
105-
}
228+
lastEdited: number
229+
expirationTime: number
230+
link: string
231+
participants: Array<Participant>
232+
}

src/App.tsx

+37-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ import { Toaster } from './components/ui/toaster'
33
import { BrowserRouter, Routes, Route, Navigate, useLocation, useNavigationType, createRoutesFromChildren, matchRoutes } from 'react-router-dom'
44
import './app.css'
55
import CombinedProvider from './contexts/CombinedProvider'
6-
import { AboutPage, TimeTableSelectorPage, FaqsPage, NotFoundPage } from './pages'
6+
import { AboutPage, TimeTableSelectorPage, FaqsPage, NotFoundPage, PrivacyPolicyPage, ExchangeVerifyPage, AdminPage } from './pages'
77
import { getPath, config, dev_config, plausible } from './utils'
88
import Layout from './components/layout'
9+
import Exchange from './pages/Exchange'
10+
import { useEffect } from 'react'
11+
import api from './api/backend'
912
import * as Sentry from "@sentry/react";
1013

1114
const configToUse = Number(import.meta.env.VITE_APP_PROD) ? config : dev_config
@@ -16,6 +19,8 @@ const pages = [
1619
{ path: getPath(configToUse.paths.planner), location: 'Horários', element: TimeTableSelectorPage, liquid: true },
1720
{ path: getPath(configToUse.paths.faqs), location: 'FAQs', element: FaqsPage, liquid: true },
1821
{ path: getPath(configToUse.paths.notfound), location: 'NotFound', element: NotFoundPage, liquid: true },
22+
{ path: getPath(config.paths.exchange), location: 'Turmas', element: Exchange, liquid: true },
23+
{ path: getPath(config.paths.privacypolicy), location: 'Privacidade', element: PrivacyPolicyPage, liquid: true },
1924
]
2025

2126
const redirects = [
@@ -33,6 +38,11 @@ const App = () => {
3338
const { enableAutoPageviews } = plausible
3439
enableAutoPageviews()
3540

41+
useEffect(() => {
42+
fetch(`${api.BACKEND_URL}/csrf/`, { credentials: "include" }).then(() => {
43+
}).catch((e) => console.error(e));
44+
});
45+
3646
// Enable Error Tracking, Performance Monitoring and Session Replay
3747
Sentry.init({
3848
environment: Number(import.meta.env.VITE_APP_PROD) ? "production" : "development",
@@ -77,13 +87,39 @@ const App = () => {
7787
}
7888
/>
7989
))}
90+
<Route
91+
path="/exchange/verify/:token"
92+
key="exchange-verify-page"
93+
element={
94+
<Layout location="Exchange" title="Exchange" liquid={true}>
95+
<div>
96+
<ExchangeVerifyPage />
97+
<Toaster />
98+
</div>
99+
</Layout>
100+
}
101+
/>
80102
{redirects.map((redirect, redirectIdx) => (
81103
<Route
82104
path={redirect.from}
83105
key={`redirect-${redirectIdx}`}
84106
element={<Navigate replace to={redirect.to} />}
85107
/>
86108
))}
109+
<Route
110+
path="admin"
111+
key="page-admin"
112+
element={
113+
<AdminPage page="pedidos"/>
114+
}
115+
/>
116+
<Route
117+
path="admin/settings"
118+
key="page-admin-settings"
119+
element={
120+
<AdminPage page="settings"/>
121+
}
122+
/>
87123
</SentryRoutes>
88124
</CombinedProvider>
89125
</BrowserRouter>

src/api/backend.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { Major, CourseInfo } from "../@types"
22
import { dev_config, getSemester, config } from "../utils"
3+
import Cookies from 'js-cookie'
34

45
const prod_val = import.meta.env.VITE_APP_PROD
56
const BE_CONFIG = Number(prod_val) ? config : dev_config
67
const BACKEND_URL = import.meta.env.VITE_APP_BACKEND_URL || `${BE_CONFIG.api.protocol}://${BE_CONFIG.api.host}:${BE_CONFIG.api.port}${BE_CONFIG.api.pathPrefix}`
8+
const OIDC_LOGIN_URL = `${BACKEND_URL}/oidc-auth/authenticate/`
9+
const OIDC_LOGOUT_URL = `${BACKEND_URL}/oidc-auth/logout/`
710
const SEMESTER = import.meta.env.VITE_APP_SEMESTER || getSemester()
811

912
// If we are in september 2024 we use 2024, if we are january 2025 we use 2024 because the first year of the academic year (2024/2025)
@@ -120,7 +123,13 @@ const getCourseUnitHashes = async (ids: number[]) => {
120123
}
121124
};
122125

126+
const getCSRFToken = () => {
127+
return Cookies.get('csrftoken');
128+
}
123129

130+
const csrfTokenName = (): string => {
131+
return "X-CSRFToken";
132+
}
124133

125134
const api = {
126135
getMajors,
@@ -131,7 +140,11 @@ const api = {
131140
getCourseUnit,
132141
getInfo,
133142
getCourseUnitHashes,
134-
BACKEND_URL
143+
getCSRFToken,
144+
csrfTokenName,
145+
BACKEND_URL,
146+
OIDC_LOGIN_URL,
147+
OIDC_LOGOUT_URL
135148
}
136149

137150
export default api

src/api/services/authService.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import api from "../backend";
2+
3+
const logout = async (token, setSignedIn, setLoggingOut) => {
4+
fetch(`${api.OIDC_LOGOUT_URL}/`, {
5+
method: "POST", credentials: "include", headers: {
6+
"X-CSRFToken": api.getCSRFToken()
7+
}
8+
}).then(() => {
9+
setSignedIn(false);
10+
setLoggingOut(false);
11+
}).catch((e) => {
12+
console.error(e);
13+
});
14+
15+
}
16+
17+
const authService = {
18+
logout
19+
};
20+
21+
export default authService;

0 commit comments

Comments
 (0)