Skip to content

Commit 7529a47

Browse files
committed
modify backend and frontend to handle if user tries to create degree with same name as existing one
1 parent 067acc0 commit 7529a47

File tree

3 files changed

+54
-14
lines changed

3 files changed

+54
-14
lines changed

backend/degree/views.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,14 @@ def retrieve(self, request, *args, **kwargs):
8585
return Response(serializer.data, status=status.HTTP_200_OK)
8686

8787
def create(self, request, *args, **kwargs):
88-
if request.data.get("name") is None:
88+
name = request.data.get("name")
89+
if name is None:
8990
raise ValidationError({"name": "This field is required."})
90-
new_degree_plan = DegreePlan(name=request.data.get("name"), person=self.request.user)
91+
92+
if DegreePlan.objects.filter(name=name, person=self.request.user).exists():
93+
return Response({"warning": f"A degree plan with name {name} already exists."}, status=status.HTTP_409_CONFLICT)
94+
95+
new_degree_plan = DegreePlan(name=name, person=self.request.user)
9196
new_degree_plan.save()
9297
serializer = self.get_serializer(new_degree_plan)
9398
return Response(serializer.data, status=status.HTTP_201_CREATED)

frontend/degree-plan/components/OnboardingPanels/CreateWithTranscriptPanel.tsx

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
Column,
1212
ColumnsContainer,
1313
CourseContainer,
14+
ErrorText,
1415
customSelectStylesCourses,
1516
customSelectStylesLeft,
1617
customSelectStylesRight,
@@ -38,7 +39,7 @@ import {
3839
interpolateSemesters,
3940
} from "@/components/FourYearPlan/Semesters";
4041
import { TRANSFER_CREDIT_SEMESTER_KEY } from "@/constants";
41-
import { postFetcher, useSWRCrud } from "@/hooks/swrcrud";
42+
import { postFetcher, getCsrf } from "@/hooks/swrcrud";
4243
import { getMajorOptions } from "@/utils/parseUtils";
4344

4445
type WelcomeLayoutProps = {
@@ -77,13 +78,12 @@ export default function CreateWithTranscriptPanel({
7778
const [loading, setLoading] = useState(false);
7879
const [name, setName] = useState("");
7980

81+
const [nameAlreadyExists, setNameAlreadyExists] = useState(false);
82+
8083
const { data: options } = useSWR<Options>("/api/options");
8184
const { data: degrees, isLoading: isLoadingDegrees } = useSWR<
8285
DegreeListing[]
8386
>(`/api/degree/degrees`);
84-
const { create: createDegreeplan } = useSWRCrud<DegreePlan>(
85-
"/api/degree/degreeplans"
86-
);
8787

8888
// Workaround solution to only input courses once degree has been created and degreeID exists.
8989
// Will likely change in the future!
@@ -124,26 +124,53 @@ export default function CreateWithTranscriptPanel({
124124

125125
const handleAddDegrees = () => {
126126
setLoading(true);
127-
createDegreeplan({ name: name }).then((res) => {
128-
if (res) {
127+
128+
const createDegreeplan = async () => {
129+
// Need to handle the case where degree plan of same name already exists.
130+
const res = await fetch("/api/degree/degreeplans", {
131+
credentials: "include",
132+
mode: "same-origin",
133+
method: "POST",
134+
headers: {
135+
"Content-Type": "application/json",
136+
"X-CSRFToken": getCsrf(),
137+
"Accept": "application/json",
138+
} as HeadersInit,
139+
body: JSON.stringify({ name: name }),
140+
});
141+
142+
if (res.ok) {
143+
const _new = await res.json();
129144
if (startingYear && graduationYear) {
130145
const semesters = interpolateSemesters(
131146
startingYear.value,
132147
graduationYear.value
133148
);
134149
semesters[TRANSFER_CREDIT_SEMESTER_KEY] = [];
135150
window.localStorage.setItem(
136-
getLocalSemestersKey(res.id),
151+
getLocalSemestersKey(_new.id),
137152
JSON.stringify(semesters)
138153
);
139154
}
140-
postFetcher(`/api/degree/degreeplans/${res.id}/degrees`, {
155+
postFetcher(`/api/degree/degreeplans/${_new.id}/degrees`, {
141156
degree_ids: majors.map((m) => m.value.id),
142157
}); // add degree
143-
setActiveDegreeplan(res);
144-
setDegreeID(res.id);
158+
setActiveDegreeplan(_new);
159+
setDegreeID(_new.id);
160+
} else if (res.status === 409) {
161+
// Case where degree plan of same name already exists.
162+
setNameAlreadyExists(true);
163+
setLoading(false);
164+
165+
setTimeout(() => {
166+
setNameAlreadyExists(false);
167+
}, 5000);
168+
} else {
169+
console.error(await res.text());
145170
}
146-
});
171+
}
172+
173+
createDegreeplan().then(() => {});
147174
};
148175

149176
const complete =
@@ -203,6 +230,14 @@ export default function CreateWithTranscriptPanel({
203230
onChange={(e) => setName(e.target.value)}
204231
placeholder=""
205232
/>
233+
<ErrorText
234+
style={{
235+
color: "red",
236+
visibility: nameAlreadyExists ? "visible" : "hidden",
237+
}}
238+
>
239+
A degree plan with this name already exists. Please choose a different name.
240+
</ErrorText>
206241
</FieldWrapper>
207242

208243
<FieldWrapper>

frontend/degree-plan/hooks/swrcrud.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ interface SWRCrudError extends Error {
1212
/**
1313
* @returns {string | boolean} The CSRF token used by the Django REST Framework
1414
*/
15-
const getCsrf = (): string | boolean => {
15+
export const getCsrf = (): string | boolean => {
1616
const result =
1717
document.cookie &&
1818
document.cookie

0 commit comments

Comments
 (0)