From f0d780ecd53645c351f1844320ccd4925bfa47a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Palma?= Date: Mon, 10 Feb 2025 18:04:11 +0000 Subject: [PATCH] feat: unaccepted requests in admin page --- src/@types/index.d.ts | 2 + src/components/admin/AdminMainContent.tsx | 16 ++- .../admin/AdminMarketplaceExchangesCard.tsx | 126 ++++++++++++++++++ .../requests/AdminMarketplaceExhanges.tsx | 24 ++++ .../requests/cards/AdminRequestCardFooter.tsx | 4 +- .../admin/requests/cards/ExchangeStatus.tsx | 6 +- .../admin/useAdminAllMarketplaceExchanges.tsx | 42 ++++++ 7 files changed, 212 insertions(+), 8 deletions(-) create mode 100644 src/components/admin/AdminMarketplaceExchangesCard.tsx create mode 100644 src/components/admin/requests/AdminMarketplaceExhanges.tsx create mode 100644 src/hooks/admin/useAdminAllMarketplaceExchanges.tsx diff --git a/src/@types/index.d.ts b/src/@types/index.d.ts index cc0a1a10..f187d2c8 100644 --- a/src/@types/index.d.ts +++ b/src/@types/index.d.ts @@ -130,8 +130,10 @@ export type MarketplaceRequest = { date: string, options?: Array, classes?: Array, + admin_state: string, pending_motive?: DirectExchangePendingMotive, accepted: boolean, + admin_state: string, canceled: boolean } diff --git a/src/components/admin/AdminMainContent.tsx b/src/components/admin/AdminMainContent.tsx index ef279fa1..1a0ecb32 100644 --- a/src/components/admin/AdminMainContent.tsx +++ b/src/components/admin/AdminMainContent.tsx @@ -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(undefined); @@ -39,24 +40,30 @@ export const AdminMainContent = () => { - setCurrPage(1)} > Trocas entre estudantes - setCurrPage(1)} > Trocas individuais - setCurrPage(1)} > Inscrições + setCurrPage(1)} + > + Marketplace + @@ -67,6 +74,9 @@ export const AdminMainContent = () => { + + +
diff --git a/src/components/admin/AdminMarketplaceExchangesCard.tsx b/src/components/admin/AdminMarketplaceExchangesCard.tsx new file mode 100644 index 00000000..6b0fa538 --- /dev/null +++ b/src/components/admin/AdminMarketplaceExchangesCard.tsx @@ -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(false); + const [exchangeState, setExchangeState] = useState(exchange); + + const { schedule } = useStudentsSchedule(exchange.issuer_nmec); + + return (<> + + +
+
+
+ +

+ {`#${exchange.id}`} +

+
+ +
+ +
+ {!open && <> + + } + +
+
+ +
+
+ + + {open && +
+
+ +
+
+ <>{exchange.options.map((option) => ( +
+

{option.course_info.acronym}

+
+

{option.class_issuer_goes_from.name}

+ +

{option.class_issuer_goes_to.name}

+
+
+ ))} + +
+
+
+ { + return { + classInfo: option.class_issuer_goes_to, + courseInfo: option.course_info, + slotInfo: null + } + }) + } + + /> +
+
+
+ } +
+ + {open && + ({ + 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)} + /> + } +
+ + ) +} \ No newline at end of file diff --git a/src/components/admin/requests/AdminMarketplaceExhanges.tsx b/src/components/admin/requests/AdminMarketplaceExhanges.tsx new file mode 100644 index 00000000..97ae4fc2 --- /dev/null +++ b/src/components/admin/requests/AdminMarketplaceExhanges.tsx @@ -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 && } + + {exchanges?.map((exchange) => ( + + ))} + + ) +} \ No newline at end of file diff --git a/src/components/admin/requests/cards/AdminRequestCardFooter.tsx b/src/components/admin/requests/cards/AdminRequestCardFooter.tsx index 8b948c96..0ee952df 100644 --- a/src/components/admin/requests/cards/AdminRequestCardFooter.tsx +++ b/src/components/admin/requests/cards/AdminRequestCardFooter.tsx @@ -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" @@ -15,7 +15,7 @@ type Props = { requestType: AdminRequestType, requestId: number, showTreatButton?: boolean, - setExchange?: Dispatch> + setExchange?: Dispatch> courseId: Array } diff --git a/src/components/admin/requests/cards/ExchangeStatus.tsx b/src/components/admin/requests/cards/ExchangeStatus.tsx index 6f062008..5f0ac3fd 100644 --- a/src/components/admin/requests/cards/ExchangeStatus.tsx +++ b/src/components/admin/requests/cards/ExchangeStatus.tsx @@ -1,8 +1,8 @@ -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 = { @@ -10,7 +10,7 @@ type ExchangeStatusProperty = { color: string } -const exchangeStatusProperties = (exchange: DirectExchangeRequest | UrgentRequest | CourseUnitEnrollment) => { +const exchangeStatusProperties = (exchange: DirectExchangeRequest | UrgentRequest | CourseUnitEnrollment | MarketplaceRequest) => { switch (exchange.admin_state) { case "accepted": case "treated": return { diff --git a/src/hooks/admin/useAdminAllMarketplaceExchanges.tsx b/src/hooks/admin/useAdminAllMarketplaceExchanges.tsx new file mode 100644 index 00000000..5a972b37 --- /dev/null +++ b/src/hooks/admin/useAdminAllMarketplaceExchanges.tsx @@ -0,0 +1,42 @@ +import { useMemo } from "react"; +import api from "../../api/backend"; +import useSWR from "swr"; +import { RequestFiltersContextContent } from "../../contexts/admin/RequestFiltersContext"; +import { buildUrlWithFilterParams } from "../../utils/admin/filters"; + +/** + * Gets the exchanges that a student made not involving any other student. +*/ +export default (filterContext: RequestFiltersContextContent, pageIndex: number) => { + const getExchanges = async (url: string) => { + try { + const res = await fetch(url, { + credentials: "include" + }); + + if(res.ok) { + return await res.json(); + } + } catch (error) { + console.error(error); + } + }; + + const { data, error, mutate } = useSWR( + buildUrlWithFilterParams(`${api.BACKEND_URL}/exchange/admin/marketplace?page=${pageIndex}`, filterContext), + getExchanges + ); + + const exchanges = useMemo(() => data ? [].concat(...data["exchanges"]) : null, [data]); + const totalPages = useMemo(() => data ? data["total_pages"] : null, [data]); + + return { + exchanges, + totalPages, + error, + loading: !data, + mutate, + }; +}; + +