Skip to content
Open
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
1 change: 1 addition & 0 deletions backend/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
path("requirement_info/", requirements.requirement_info, name="requirement_info"),
path("course/details/", details.course_details, name="course_details"),
path("course/comments/", details.course_comments_view, name="course_comments"),
path("course/terms/", details.course_terms, name="course_terms"),
path("upload/", upload.upload_file, name="upload_file"),
path("almost_completed/", almost_completed_api.almost_completed, name="almost_completed"),
path("program_details/<str:code>/", program_details_api.program_details, name="program_details"),
Expand Down
17 changes: 17 additions & 0 deletions backend/hoagieplan/api/dashboard/details.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,20 @@ def course_comments_view(request):
return JsonResponse({"error": "Course not found"}, status=404)

return JsonResponse(comments)


@api_view(["GET"])
def course_terms(request):
"""API endpoint for course terms."""
course_id = request.GET.get("course_id", "")
if not course_id:
return JsonResponse({"error": "Missing course_id parameter"}, status=400)
guids = (
Course.objects.filter(course_id=course_id)
.values_list("guid", flat=True)
.order_by("-guid")
)
terms = [guid[:4] for guid in guids if guid]
return JsonResponse({"terms": terms})


Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,13 @@
outline: none;
box-shadow: 0 0 0 2px rgba(9, 62, 112, 0.3);
}

.prevTerms {
margin-top: 4px;
font-size: 0.75rem;
color: #6b7280;
font-style: italic;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
24 changes: 24 additions & 0 deletions frontend/components/DashboardSearchItem/DashboardSearchItem.tsx
Copy link
Copy Markdown
Collaborator

@issacli-0821 issacli-0821 Apr 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now, each DashboardSearchItem is firing a separate backend request. This would be bad for large queries (for example, look at the network requests under the developer console when you query 'c' in the search bar). Not sure what the best way to do this is, but one idea is to do these queries in DashboardSearchResults or Canvas

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that if we were to do these in the initial search for the terms, the time it takes for the cards to load would increase. Having a quick loading for the cards then this happening next felt like it made sense, but I also understand the issue with a separate backend request. I can try to do that if we believe that would be better.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's try doing it in the initial search and see if we can make it fast enough. If we can't, then we can discuss what the alternative is.

Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { FC, ReactNode } from 'react';
import { useEffect, useState } from 'react'; // to store the value of previous terms the course was offered in

import type { Course } from '@/types';
import { getRatingBackground } from '@/utils/ratingColors';
import { termsInverse } from '@/utils/terms';

import styles from './DashboardSearchItem.module.css';

Expand All @@ -22,6 +24,27 @@ export const DashboardSearchItem: FC<DashboardSearchItemProps> = ({
}
};

const [prevTerms, setPrevTerms] = useState<string[]>([]);

useEffect(() => {
// to display previous terms the course was offered in
if (!course.guid) {
return;
}
const courseId = course.guid.slice(4);
const currentTermCode = course.guid.slice(0, 4);
fetch(`/api/hoagie/course/terms/?course_id=${courseId}`)
.then((res) => res.json())
.then((data: { terms: string[] }) => {
const prior = data.terms
.filter((code) => code <= currentTermCode)
.map((code) => termsInverse[code]) // convert term codes to readable format
.filter(Boolean);
setPrevTerms(prior);
})
.catch(console.error);
}, [course.guid]);

return (
<div className={styles.card} onClick={handleClick}>
<div className={styles.content}>
Expand All @@ -36,6 +59,7 @@ export const DashboardSearchItem: FC<DashboardSearchItemProps> = ({
</div>
)}
</div>
{prevTerms.length > 0 && <div className={styles.prevTerms}>{prevTerms.join(', ')}</div>}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we using the .svg files?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no sorry I added these because I previously thought my task was to display the semester tags. I can remove these when I fix this pr. I'll get this fix in when I get clarification on how I should approach DashboardSearchItem firing a separate backend request

{children && <div className={styles.chipContainer}>{children}</div>}
</div>
</div>
Expand Down
5 changes: 5 additions & 0 deletions frontend/public/fall tag.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions frontend/public/multiple tag.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions frontend/public/spring tag.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.