Skip to content

Commit

Permalink
Merge pull request #459 from NIAEFEUP/main
Browse files Browse the repository at this point in the history
Merge hotfixes in main to develop
  • Loading branch information
tomaspalma authored Feb 13, 2025
2 parents cb5c7f9 + fefba1c commit 18a61f0
Show file tree
Hide file tree
Showing 26 changed files with 330 additions and 53 deletions.
2 changes: 1 addition & 1 deletion dev.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/bash
set -e # abort script if any command fails

docker compose up $1 tts-frontend
docker compose up $1 tts-frontend
1 change: 1 addition & 0 deletions src/@types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export type MarketplaceRequest = {
classes?: Array<ClassInfo>,
pending_motive?: DirectExchangePendingMotive,
accepted: boolean,
admin_state: string,
canceled: boolean
}

Expand Down
2 changes: 1 addition & 1 deletion src/api/services/exchangeRequestService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const submitExchangeRequest = async (requests: Map<number, CreateRequestData>, u
const retrieveMarketplaceRequest = async (url: string): Promise<MarketplaceRequest[]> => {
return fetch(url).then(async (res) => {
const json = await res.json();
return json.data;
return json;
}).catch((e) => {
console.error(e);
return [];
Expand Down
16 changes: 13 additions & 3 deletions src/components/admin/AdminMainContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AdminRequestState } from "../../contexts/admin/RequestFiltersContext";
import RequestFiltersContext from "../../contexts/admin/RequestFiltersContext";
import { AdminPagination } from "./AdminPagination";
import AdminPaginationContext from "../../contexts/admin/AdminPaginationContext";
import { AdminMarketplaceExhanges } from "./requests/AdminMarketplaceExhanges";

export const AdminMainContent = () => {
const [activeCourse, setActiveCourse] = useState<number | undefined>(undefined);
Expand Down Expand Up @@ -39,24 +40,30 @@ export const AdminMainContent = () => {
</div>
<Tabs defaultValue="exchange-with-student">
<TabsList className="w-full">
<TabsTrigger
<TabsTrigger
value="exchange-with-student"
onClick={() => setCurrPage(1)}
>
Trocas entre estudantes
</TabsTrigger>
<TabsTrigger
<TabsTrigger
value="exchange-singular"
onClick={() => setCurrPage(1)}
>
Trocas individuais
</TabsTrigger>
<TabsTrigger
<TabsTrigger
value="enrollments"
onClick={() => setCurrPage(1)}
>
Inscrições
</TabsTrigger>
<TabsTrigger
value="admin-marketplace"
onClick={() => setCurrPage(1)}
>
Marketplace
</TabsTrigger>
</TabsList>
<TabsContent value="exchange-with-student">
<MultipleStudentExchanges />
Expand All @@ -67,6 +74,9 @@ export const AdminMainContent = () => {
<TabsContent value="enrollments">
<StudentEnrollments />
</TabsContent>
<TabsContent value="admin-marketplace">
<AdminMarketplaceExhanges />
</TabsContent>

<div className="mt-8">
<AdminPagination />
Expand Down
126 changes: 126 additions & 0 deletions src/components/admin/AdminMarketplaceExchangesCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { useState } from "react"
import { Button } from "../ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"
import { ExchangeStatus } from "./requests/cards/ExchangeStatus"
import { Person } from "./requests/cards/Person"
import { RequestDate } from "./requests/cards/RequestDate"
import { ArrowRightIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
import { AdminPreviewSchedule } from "./requests/AdminPreviewSchedule"
import useStudentsSchedule from "../../hooks/admin/useStudentsSchedule"
import { ClassDescriptor, MarketplaceRequest } from "../../@types"
import { AdminRequestCardFooter } from "./requests/cards/AdminRequestCardFooter"
import { listEmailExchanges } from "../../utils/mail"
import { AdminRequestType } from "../../utils/exchange"

type Props = {
exchange: MarketplaceRequest
}

export const AdminMarketplaceExhangesCard = ({
exchange
}: Props) => {
const [open, setOpen] = useState<boolean>(false);
const [exchangeState, setExchangeState] = useState(exchange);

const { schedule } = useStudentsSchedule(exchange.issuer_nmec);

return (<>
<Card>
<CardHeader className="flex flex-row justify-between items-center">
<div className="flex gap-4 items-center">
<div className="flex flex-col gap-1 ">
<div className="flex gap-2 items-center">
<CardTitle>
<h2 className="font-bold">
{`#${exchange.id}`}
</h2>
</CardTitle>
<ExchangeStatus exchange={exchangeState} />
</div>
<RequestDate
date={exchange.date}
/>
</div>
{!open && <>
<Person name={exchange.issuer_name} nmec={exchange.issuer_nmec} />
</>}

</div>
<div>
<Button
onClick={() => setOpen(prev => !prev)}
className="bg-white text-black border-2 border-black hover:text-white"
>
{open
? <ChevronUpIcon className="w-5 h-5" />
: <ChevronDownIcon className="w-5 h-5" />
}
</Button>
</div>
</CardHeader>

<CardContent className="w-full ">
{open &&
<div className="flex flex-col gap-y-8" key={crypto.randomUUID()}>
<div className="flex justify-between">
<Person name={exchange.issuer_name} nmec={exchange.issuer_nmec} />
<div>
<div
key={crypto.randomUUID()}
className="flex flex-col gap-y-2 items-center border-gray-200 border-2 rounded-md p-2 px-4"
>
<>{exchange.options.map((option) => (
<div key={crypto.randomUUID()} className="flex gap-5 items-center">
<h2 className="font-bold">{option.course_info.acronym}</h2>
<div className="flex gap-2 items-center">
<p>{option.class_issuer_goes_from.name}</p>
<ArrowRightIcon className="w-5 h-5" />
<p>{option.class_issuer_goes_to.name}</p>
</div>
</div>
))}
</>
</div>
</div>
<div>
<AdminPreviewSchedule
originalSchedule={schedule}
classesToAdd={
exchange.options.map((option): ClassDescriptor => {
return {
classInfo: option.class_issuer_goes_to,
courseInfo: option.course_info,
slotInfo: null
}
})
}

/>
</div>
</div>
</div>
}
</CardContent>

{open &&
<AdminRequestCardFooter
nmecs={[exchange.issuer_nmec]}
exchangeMessage={listEmailExchanges(
exchange.options.map(option => ({
participant_name: undefined,
participant_nmec: exchange.issuer_nmec,
goes_from: option.class_issuer_goes_from?.name,
goes_to: option.class_issuer_goes_to.name,
course_acronym: option.course_info.acronym
}))
)}
requestType={AdminRequestType.URGENT_EXCHANGE}
requestId={exchange.id}
setExchange={setExchangeState}
courseId={exchange.options.map(option => option.course_info.course)}
/>
}
</Card>
</>
)
}
24 changes: 24 additions & 0 deletions src/components/admin/requests/AdminMarketplaceExhanges.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useContext } from "react";
import RequestFiltersContext from "../../../contexts/admin/RequestFiltersContext";
import useAdminAllMarketplaceExchanges from "../../../hooks/admin/useAdminAllMarketplaceExchanges"
import AdminPaginationContext from "../../../contexts/admin/AdminPaginationContext";
import { BarLoader } from "react-spinners";
import { AdminMarketplaceExhangesCard } from "../AdminMarketplaceExchangesCard";

export const AdminMarketplaceExhanges = () => {
const filterContext = useContext(RequestFiltersContext);
const { currPage } = useContext(AdminPaginationContext);
const { exchanges, loading } = useAdminAllMarketplaceExchanges(filterContext, currPage);

return (<>
{loading && <BarLoader className="w-full" />}

{exchanges?.map((exchange) => (
<AdminMarketplaceExhangesCard
key={`admin-marketplace-${exchange.id}`}
exchange={exchange}
/>
))}
</>
)
}
6 changes: 5 additions & 1 deletion src/components/admin/requests/AdminSendEmail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ export const AdminSendEmail = ({
message=""
}: Props) => {
return <>
<a href={`${mailtoStringBuilder(nmec)}?subject=${subject}&[email protected]&body=Viva,%0D%0A%0D%0A ${message} %0D%0A%0D%0ACmpts,%0D%0ADaniel Silva%0D%0A(pela comissão de inscrição em turmas)`}>
<a
target="_blank"
rel="noopener noreferrer"
href={`${mailtoStringBuilder(nmec)}?subject=${subject}&[email protected]&body=Viva,%0D%0A%0D%0A ${message} %0D%0A%0D%0ACmpts,%0D%0ADaniel Silva%0D%0A(pela comissão de inscrição em turmas)`}
>
<Button
variant="secondary"
>
Expand Down
10 changes: 8 additions & 2 deletions src/components/admin/requests/cards/AdminRequestCardFooter.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Dispatch, SetStateAction } from "react"
import { CourseUnitEnrollment, DirectExchangeRequest, UrgentRequest } from "../../../../@types"
import { CourseUnitEnrollment, DirectExchangeRequest, MarketplaceRequest, UrgentRequest } from "../../../../@types"
import { AdminRequestType } from "../../../../utils/exchange"
import exchangeRequestService from "../../../../api/services/exchangeRequestService"
import { mailtoStringBuilder } from "../../../../utils/mail"
Expand All @@ -15,7 +15,7 @@ type Props = {
requestType: AdminRequestType,
requestId: number,
showTreatButton?: boolean,
setExchange?: Dispatch<SetStateAction<DirectExchangeRequest | UrgentRequest | CourseUnitEnrollment>>
setExchange?: Dispatch<SetStateAction<DirectExchangeRequest | UrgentRequest | CourseUnitEnrollment | MarketplaceRequest>>
courseId: Array<number>
}

Expand All @@ -29,6 +29,9 @@ const rejectRequest = async (
await exchangeRequestService.adminRejectExchangeRequest(requestType, id);

const a = document.createElement('a');
a.target = "_blank";
a.rel = "noopener noreferrer";

if(requestType === AdminRequestType.DIRECT_EXCHANGE || requestType === AdminRequestType.URGENT_EXCHANGE) {
a.href = `${mailtoStringBuilder(nmecs)}?subject=Pedido de Alteração de Turma&[email protected]&body=Viva,%0D%0A%0D%0AA alteração pedida não pode ser efetuada.%0D%0A${exchangeMessage}%0D%0A%0D%0ACmpts,%0D%0ADaniel Silva%0D%0A(pela comissão de inscrição em turmas)`;
} else {
Expand All @@ -51,11 +54,14 @@ const acceptRequest = async (
await exchangeRequestService.adminAcceptExchangeRequest(requestType, id);

const a = document.createElement('a');
a.target = "_blank";
a.rel = "noopener noreferrer";
if(requestType === AdminRequestType.DIRECT_EXCHANGE || requestType === AdminRequestType.URGENT_EXCHANGE) {
a.href = `${mailtoStringBuilder(nmecs)}?subject=Pedido de Troca de Turma&[email protected]&body=Viva,%0D%0A%0D%0AA alteração pedida foi efetuada.%0D%0A${exchangeMessage}%0D%0A%0D%0ACmpts,%0D%0ADaniel Silva%0D%0A(pela comissão de inscrição em turmas)`;
} else {
a.href = `${mailtoStringBuilder(nmecs)}?subject=Pedido de Inscrição em Unidades Curriculares&[email protected]&body=Viva,%0D%0A%0D%0AA alteração pedida foi efetuada.%0D%0A${exchangeMessage}%0D%0A%0D%0ACmpts,%0D%0ADaniel Silva%0D%0A(pela comissão de inscrição em turmas)`;
}

a.click();
} catch (e) {
console.error(e);
Expand Down
6 changes: 3 additions & 3 deletions src/components/admin/requests/cards/ExchangeStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { CourseUnitEnrollment, DirectExchangeRequest, UrgentRequest } from "../../../../@types"
import { CourseUnitEnrollment, DirectExchangeRequest, MarketplaceRequest, UrgentRequest } from "../../../../@types"
import { cn } from "../../../../utils"

type Props = {
exchange: DirectExchangeRequest | UrgentRequest | CourseUnitEnrollment
exchange: DirectExchangeRequest | UrgentRequest | CourseUnitEnrollment | MarketplaceRequest
}

type ExchangeStatusProperty = {
message: string,
color: string
}

const exchangeStatusProperties = (exchange: DirectExchangeRequest | UrgentRequest | CourseUnitEnrollment) => {
const exchangeStatusProperties = (exchange: DirectExchangeRequest | UrgentRequest | CourseUnitEnrollment | MarketplaceRequest) => {
switch (exchange.admin_state) {
case "accepted": case "treated":
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Dispatch, SetStateAction, useState } from "react"
import { Dispatch, SetStateAction, useEffect, useState } from "react"
import { CourseInfo } from "../../../@types"
import { Button } from "../../ui/button"
import { Card, CardHeader, CardTitle } from "../../ui/card"
Expand All @@ -22,6 +22,12 @@ export const AlreadyEnrolledCourseUnitCard = ({
}: Props) => {
const [removeSelected, setRemoveSelected] = useState<boolean>(false);

useEffect(() => {
if(enrollmentChoices.size === 0) {
setRemoveSelected(false);
}
}, [enrollmentChoices])

return (
<Card>
<CardHeader className="flex flex-row justify-between items-center py-2 px-4">
Expand All @@ -47,7 +53,7 @@ export const AlreadyEnrolledCourseUnitCard = ({
>
{removeSelected
? "Cancelar pedido"
: "Anular inscrição"
: "Anular inscrição em turma"
}
</Button>
</CardHeader>
Expand Down
7 changes: 4 additions & 3 deletions src/components/exchange/enrollments/Enrollments.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { Button } from "../../ui/button";
import courseUnitEnrollmentService from "../../../api/services/courseUnitEnrollmentService";
import { ExchangeSidebarStatus } from "../../../pages/Exchange"
import { useToast } from "../../ui/use-toast";
import useLocalStorage from "../../../hooks/useLocalStorage";
import useStudentCourseUnits from "../../../hooks/useStudentCourseUnits";
import { AlreadyEnrolledCourseUnitCard } from "./AlreadyEnrolledCourseUnitCard";
import { EnrollingCourseUnitCard } from "./EnrollingCourseUnitCard";
Expand All @@ -32,7 +31,7 @@ export const Enrollments = ({
}: Props) => {
const parentCourseContext = useContext(CourseContext);

const [enrollCourses, setEnrollCourses] = useLocalStorage<CourseInfo[]>("enrollCourses", []);
const [enrollCourses, setEnrollCourses] = useState<CourseInfo[]>([]);
const [enrollmentChoices, setEnrollmentChoices] = useState<Map<number, EnrollmentOption>>(new Map());
const [disenrollmentChoices, setDisenrollmentChoices] = useState<Map<number, EnrollmentOption>>(new Map());
const [coursesInfo, setCoursesInfo] = useState<CourseInfo[]>([]);
Expand Down Expand Up @@ -112,7 +111,7 @@ export const Enrollments = ({
))}
</div>

{(enrollmentChoices.size > 0) &&
{(enrollmentChoices.size > 0 || disenrollmentChoices.size > 0) &&
<form onSubmit={async (e) => {
e.preventDefault();

Expand All @@ -125,6 +124,8 @@ export const Enrollments = ({
description: 'Pedido de inscrição submetida com sucesso',
});
setEnrollCourses([]);
setEnrollmentChoices(new Map());
setDisenrollmentChoices(new Map());
} else {
const json = await res.json();
toast({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const CustomizeRequest = ({
const [submittingRequest, setSubmittingRequest] = useState<boolean>(false);
const [previewingForm, setPreviewingForm] = useState<boolean>(false);


const submitRequest = async (urgentMessage: string) => {
setSubmittingRequest(true);
const res = await exchangeRequestService.submitExchangeRequest(requests, urgentMessage);
Expand Down
Loading

0 comments on commit 18a61f0

Please sign in to comment.