Skip to content

Frontend #397

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: feat/section_swap/main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion csm_web/frontend/src/components/section/Section.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { NavLink, useParams } from "react-router-dom";
import StudentSection from "./StudentSection";
import MentorSection from "./MentorSection";
Expand Down
33 changes: 32 additions & 1 deletion csm_web/frontend/src/components/section/StudentSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
import { Mentor, Override, Spacetime } from "../../utils/types";
import Modal from "../Modal";
import { ATTENDANCE_LABELS, InfoCard, ROLES, SectionDetail, SectionSpacetime } from "./Section";
import { SwapSectionModal } from "./SwapSectionModal";
import { dateSortWord, formatDateISOToWord } from "./utils";

import XIcon from "../../../static/frontend/img/x.svg";
Expand Down Expand Up @@ -52,6 +53,7 @@ export default function StudentSection({
index
element={
<StudentSectionInfo
id={id}
mentor={mentor}
spacetimes={spacetimes}
override={override}
Expand All @@ -65,14 +67,15 @@ export default function StudentSection({
}

interface StudentSectionInfoProps {
id: number;
mentor: Mentor;
spacetimes: Spacetime[];
override?: Override;
associatedProfileId: number;
}

// eslint-disable-next-line no-unused-vars
function StudentSectionInfo({ mentor, spacetimes, override, associatedProfileId }: StudentSectionInfoProps) {
function StudentSectionInfo({ id, mentor, spacetimes, override, associatedProfileId }: StudentSectionInfoProps) {
return (
<React.Fragment>
<h3 className="section-detail-page-title">My Section</h3>
Expand All @@ -92,12 +95,40 @@ function StudentSectionInfo({ mentor, spacetimes, override, associatedProfileId
override={override}
/>
))}
<SwapSection sectionId={id} />
<DropSection profileId={associatedProfileId} />
</div>
</React.Fragment>
);
}

interface SwapSectionProps {
sectionId: number;
}

enum SwapSectionStage {
INITIAL = "INITIAL",
DISPLAY = "DISPLAY"
}

export function SwapSection({ sectionId }: SwapSectionProps) {
const [stage, setStage] = useState<SwapSectionStage>(SwapSectionStage.INITIAL);

switch (stage) {
case SwapSectionStage.INITIAL:
return (
<InfoCard title="Swap Section" showTitle={false}>
<h5>Swap Section</h5>
<button className="view-swap-btn" onClick={() => setStage(SwapSectionStage.DISPLAY)}>
Swap
</button>
</InfoCard>
);
case SwapSectionStage.DISPLAY:
return <SwapSectionModal sectionId={sectionId} closeModal={() => setStage(SwapSectionStage.INITIAL)} />;
}
}

interface DropSectionProps {
profileId: number;
}
Expand Down
107 changes: 107 additions & 0 deletions csm_web/frontend/src/components/section/SwapSectionModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { useEffect, useState } from "react";
import Modal from "../Modal";
import { useUserEmails } from "../../utils/queries/base";
import {
useSwapRequestQuery,
useSwapPostMutation,
useReceivedSwapRequestQuery,
useAllSwapRequestQuery
} from "../../utils/queries/sections";

interface SwapSectionModalProps {
sectionId: number;
closeModal: () => void;
}

export const SwapSectionModal = ({ sectionId, closeModal }: SwapSectionModalProps) => {
// Need API endpoints

return (
<Modal className="swap-display" closeModal={closeModal}>
<SwapRequestDashboard sectionId={sectionId} />
<SwapRequestForm sectionId={sectionId} />
</Modal>
);
};

export const SwapRequestDashboard = ({ sectionId }: SwapRequestDashBoardProps) => {
const [myRequest, setMyRequest] = useState<string[]>([]);
const [sendRequest, setSendRequest] = useState<string[]>([]);
const requests = useAllSwapRequestQuery(sectionId, studentId).data;
const send_requests = useReceivedSwapRequestQuery(sectionId, studentId).data;
useEffect(() => {
// How do i get student id?
}, []);

return (
<div className="swap-dashboard">
<div className="my-request">
<div className="swap-title">My Swap Requests</div>
<ul>
{/* {myRequest.map(request => (
<li key={request.id}>
{request.sender.user.first_name} {request.sender.user.last_name} (id: {request.sender.id})
</li>
))} */}
</ul>
</div>
<div className="received-request">
<div className="swap-title">Receive Swap Requests</div>
<ul>
{/* {myRequest.map(request => (
<li key={request.id}>
{request.sender.user.first_name} {request.sender.user.last_name} (id: {request.sender.id})
</li>
))} */}
</ul>
</div>
</div>
);
};
interface SwapRequestDashBoardProps {
sectionId: number;
}

interface SwapRequestFormProps {
sectionId: number;
email: string;
}

export const SwapRequestForm = ({ sectionId }: SwapRequestFormProps) => {
const { data: userEmails, isSuccess: userEmailsLoaded } = useUserEmails();
// const swapSectionMutation = useSwapRequestMutation(sectionId); can be changed, not too sure
const [email, setEmail] = useState<string>();
const body = { sectionId: sectionId, email: email };
const studentDropMutation = useSwapPostMutation(sectionId, email);

const handleSubmit = (event: React.FormEvent<HTMLFormElement>): void => {
event.preventDefault();
};

return (
<form id="swap-request-form" className="csm-form" onSubmit={handleSubmit}>
<div id="swap-request-form-contents">
<label>
Email
<input
onChange={event => {
setEmail(event.target.value);
}}
type="email"
list="swap-student-email-list"
required
name="email"
pattern=".+@berkeley.edu"
title="Please enter a valid @berkeley.edu email address"
value={email}
autoFocus
/>
{/* <datalist id="swap-student-email-list">
{userEmailsLoaded ? userEmails.map(email => <option key={email} value={email} />) : null}
</datalist> */}
</label>
<input type="submit" value="Request Swap" />
</div>
</form>
);
};
125 changes: 125 additions & 0 deletions csm_web/frontend/src/utils/queries/sections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -274,11 +274,136 @@ export const useStudentSubmitWordOfTheDayMutation = (
return mutationResult;
};

export interface SectionSwapMutationBody {
email: string;
}

/**
* Hook to request a swap with another student
* Wait, what is this for? I wrote another one for posting a new request below this. -Kevin
*/
export const useSwapRequestQuery = (
sectionId: number
): UseMutationResult<Section, ServerError, SectionSwapMutationBody> => {
const queryClient = useQueryClient();
const queryResult = useMutation<Section, Error, SectionSwapMutationBody>(
async (body: SectionSwapMutationBody) => {
const response = await fetchWithMethod(`sections/<section_id:int>/swap`, HTTP_METHODS.GET, body);
if (response.ok) {
return response.json();
} else {
handlePermissionsError(response.status);
throw new ServerError(`Failed to load swap`);
}
},
{
retry: handleRetry,
onSuccess: () => {
queryClient.invalidateQueries(["section_swap", sectionId]);
}
}
);

handleError(queryResult);
return queryResult;
};

/*
*Posting new queries
*/

export const useSwapPostMutation = (
sectionId: number,
email: string
): UseMutationResult<Section, ServerError, SectionSwapMutationBody> => {
const queryClient = useQueryClient();
const queryResult = useMutation<Section, Error, SectionSwapMutationBody>(
async (body: SectionSwapMutationBody) => {
const response = await fetchWithMethod(`sections/${sectionId}}/swap`, HTTP_METHODS.POST, body);
if (response.ok) {
return response.json();
} else {
handlePermissionsError(response.status);
throw new ServerError(`Failed to load swap`);
}
},
{
retry: handleRetry,
onSuccess: () => {
queryClient.invalidateQueries(["section_swap", sectionId]);
}
}
);

handleError(queryResult);
return queryResult;
};

export interface StudentDropMutationBody {
banned: boolean;
blacklisted: boolean;
}

/**
* Get all swap requests where the user is the receiver
*/
export const useAllSwapRequestQuery = (
sectionId: number,
studentId: number
): UseMutationResult<Section, ServerError, SectionSwapMutationBody> => {
const queryClient = useQueryClient();
const queryResult = useMutation<Section, Error, SectionSwapMutationBody>(
async (body: SectionSwapMutationBody) => {
const response = await fetchWithMethod(`sections/${sectionId}/my_swap`, HTTP_METHODS.GET, body);
if (response.ok) {
return response.json();
} else {
handlePermissionsError(response.status);
throw new ServerError(`Failed to load swap`);
}
},
{
retry: handleRetry,
onSuccess: () => {
queryClient.invalidateQueries(["section_swap", sectionId]);
}
}
);

handleError(queryResult);
return queryResult;
};

/**
* Get all swap requests where the user is the receiver
*/
export const useReceivedSwapRequestQuery = (
sectionId: number,
studentId: number
): UseMutationResult<Section, ServerError, SectionSwapMutationBody> => {
const queryClient = useQueryClient();
const queryResult = useMutation<Section, Error, SectionSwapMutationBody>(
async (body: SectionSwapMutationBody) => {
const response = await fetchWithMethod(`sections/${sectionId}/my_swap`, HTTP_METHODS.GET, body);
if (response.ok) {
return response.json();
} else {
handlePermissionsError(response.status);
throw new ServerError(`Failed to load swap`);
}
},
{
retry: handleRetry,
onSuccess: () => {
queryClient.invalidateQueries(["section_swap", sectionId]);
}
}
);

handleError(queryResult);
return queryResult;
};

/**
* Hook to drop a student from their section.
*
Expand Down
Loading