Skip to content

Commit

Permalink
Add filters to course activity section
Browse files Browse the repository at this point in the history
  • Loading branch information
acelaya committed Jul 31, 2024
1 parent 6662a23 commit cf8a47b
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { Link } from '@hypothesis/frontend-shared';
import { useMemo } from 'preact/hooks';
import { Link as RouterLink, useParams } from 'wouter-preact';
import {
Link as RouterLink,
useLocation,
useParams,
useSearch,
} from 'wouter-preact';

import type { AssignmentsResponse, Course } from '../../api-types';
import { useConfig } from '../../config';
import { urlPath, useAPIFetch } from '../../utils/api';
import { useAPIFetch } from '../../utils/api';
import { useDashboardFilters } from '../../utils/dashboard/hooks';
import { assignmentURL, courseURL } from '../../utils/dashboard/navigation';
import { useDocumentTitle } from '../../utils/hooks';
import { replaceURLParams } from '../../utils/url';
import DashboardActivityFilters from './DashboardActivityFilters';
import DashboardBreadcrumbs from './DashboardBreadcrumbs';
import FormattedDate from './FormattedDate';
import OrderableActivityTable from './OrderableActivityTable';
Expand All @@ -19,13 +27,12 @@ type AssignmentsTableRow = {
replies: number;
};

const assignmentURL = (id: number) => urlPath`/assignments/${String(id)}`;

/**
* Activity in a list of assignments that are part of a specific course
*/
export default function CourseActivity() {
const { courseId } = useParams<{ courseId: string }>();
const [, navigate] = useLocation();
const { dashboard } = useConfig(['dashboard']);
const { routes } = dashboard;
const course = useAPIFetch<Course>(
Expand All @@ -52,6 +59,10 @@ export default function CourseActivity() {
const title = course.data?.title ?? 'Untitled course';
useDocumentTitle(title);

const { filters, updateFilters } = useDashboardFilters();
const { assignmentIds, studentIds } = filters;
const search = useSearch();

return (
<div className="flex flex-col gap-y-5">
<div>
Expand All @@ -64,6 +75,34 @@ export default function CourseActivity() {
{course.data && title}
</h2>
</div>
<DashboardActivityFilters
selectedCourseIds={[courseId]}
onCoursesChange={newCourseIds => {
// When no courses are selected (which happens if either "All courses" is
// selected or the active course is deselected), navigate to "All courses"
// section and propagate the rest of the filters.
if (newCourseIds.length === 0) {
navigate(`?${search}`);
}

// When a course other than the "active" one (the one represented
// in the URL) is selected, navigate to that course and propagate
// the rest of the filters.
const firstDifferentCourse = newCourseIds.find(c => c !== courseId);
if (firstDifferentCourse) {
navigate(`${courseURL(firstDifferentCourse)}?${search}`);
}
}}
selectedAssignmentIds={assignmentIds}
onAssignmentsChange={assignmentIds => updateFilters({ assignmentIds })}
selectedStudentIds={studentIds}
onStudentsChange={studentIds => updateFilters({ studentIds })}
onClearSelection={() =>
// Clear every filter but courses
updateFilters({ studentIds: [], assignmentIds: [] })
}
hasSelection={assignmentIds.length > 0 || studentIds.length > 0}
/>
<OrderableActivityTable
loading={assignments.isLoading}
title={course.isLoading ? 'Loading...' : title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ export type DashboardActivityFiltersProps = {
selectedStudentIds: string[];
onStudentsChange: (newStudentIds: string[]) => void;
onClearSelection?: () => void;

/**
* Consumers can use this to indicate that there are filters selected.
* If this is not provided, it will be computed based on the presence of
* items in either one of `selectedCourseIds`, `selectedAssignmentIds` or
* `selectedStudentIds`.
*/
hasSelection?: boolean;
};

/**
Expand All @@ -31,11 +39,13 @@ export default function DashboardActivityFilters({
selectedStudentIds,
onStudentsChange,
onClearSelection,
hasSelection,
}: DashboardActivityFiltersProps) {
const hasSelection =
selectedStudentIds.length > 0 ||
selectedAssignmentIds.length > 0 ||
selectedCourseIds.length > 0;
const hasFiltersSelected =
hasSelection ??
(selectedStudentIds.length > 0 ||
selectedAssignmentIds.length > 0 ||
selectedCourseIds.length > 0);
const { dashboard } = useConfig(['dashboard']);
const { routes } = dashboard;

Expand Down Expand Up @@ -146,7 +156,7 @@ export default function DashboardActivityFilters({
</MultiSelect.Option>
))}
</MultiSelect>
{hasSelection && onClearSelection && (
{hasFiltersSelected && onClearSelection && (
<IconButton
title="Clear filters"
icon={CancelIcon}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { CoursesResponse } from '../../api-types';
import { useConfig } from '../../config';
import { urlPath, useAPIFetch } from '../../utils/api';
import { useDashboardFilters } from '../../utils/dashboard/hooks';
import { courseURL } from '../../utils/dashboard/navigation';
import { useDocumentTitle } from '../../utils/hooks';
import DashboardActivityFilters from './DashboardActivityFilters';
import FormattedDate from './FormattedDate';
Expand All @@ -18,8 +19,6 @@ type CoursesTableRow = {
last_launched: string | null;
};

const courseURL = (id: number) => urlPath`/courses/${String(id)}`;

/**
* List of courses that belong to a specific organization
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { urlPath } from '../api';

export function assignmentURL(id: number) {
return urlPath`/assignments/${String(id)}`;
}

export function courseURL(id: number | string) {
return urlPath`/courses/${String(id)}`;
}

0 comments on commit cf8a47b

Please sign in to comment.