-
Notifications
You must be signed in to change notification settings - Fork 113
E2602 - Reimplement frontend of student task view #182
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,53 +1,58 @@ | ||||||
| import { | ||||||
| ColumnDef, | ||||||
| ColumnFiltersState, | ||||||
| ExpandedState, | ||||||
| flexRender, | ||||||
| getCoreRowModel, | ||||||
| getExpandedRowModel, | ||||||
| getFilteredRowModel, | ||||||
| getPaginationRowModel, | ||||||
| getSortedRowModel, | ||||||
| SortingState, | ||||||
| useReactTable, | ||||||
| ExpandedState, | ||||||
| getExpandedRowModel, | ||||||
| } from "@tanstack/react-table"; | ||||||
| import React, { useEffect, useMemo, useRef, useState } from "react"; | ||||||
| import { Table as BTable, Col, Container, Row } from "react-bootstrap"; | ||||||
| import { BsChevronDown, BsChevronRight } from "react-icons/bs"; | ||||||
| import { FaSearch } from "react-icons/fa"; | ||||||
| import { Col, Container, Row, Table as BTable } from "react-bootstrap"; | ||||||
| import { BsChevronRight, BsChevronDown } from "react-icons/bs"; | ||||||
| import ColumnFilter from "./ColumnFilter"; | ||||||
| import GlobalFilter from "./GlobalFilter"; | ||||||
| import Pagination from "./Pagination"; | ||||||
| import RowSelectCheckBox from "./RowSelectCheckBox"; | ||||||
| import { FaSearch } from "react-icons/fa"; | ||||||
| import ToolTip from "components/ToolTip"; | ||||||
|
|
||||||
| interface TableProps { | ||||||
| data: Record<string, any>[]; | ||||||
| columns: ColumnDef<any, any>[]; | ||||||
| disableGlobalFilter?: boolean; | ||||||
| showGlobalFilter?: boolean; | ||||||
| showColumnFilter?: boolean; | ||||||
| showPagination?: boolean; | ||||||
| tableSize?: { span: number; offset: number }; | ||||||
| columnVisibility?: Record<string, boolean>; | ||||||
| onSelectionChange?: (selectedData: Record<any, any>[]) => void; | ||||||
| onRowClick?: (row: any) => void; | ||||||
| renderSubComponent?: (props: { row: any }) => React.ReactNode; | ||||||
| getRowCanExpand?: (row: any) => boolean; | ||||||
| disableGlobalFilter?: boolean; | ||||||
| // Maps column header text to a tooltip string shown as an info icon in the header. | ||||||
| headingComments?: Record<string, string>; | ||||||
| // When true, passes fluid to Container so the table spans the full viewport width. | ||||||
| fluid?: boolean; | ||||||
| } | ||||||
|
|
||||||
| const Table: React.FC<TableProps> = ({ | ||||||
| data: initialData, | ||||||
| columns, | ||||||
| disableGlobalFilter = false, | ||||||
| showGlobalFilter = false, | ||||||
| showColumnFilter = true, | ||||||
| showPagination = true, | ||||||
| onSelectionChange, | ||||||
| onRowClick, | ||||||
| columnVisibility = {}, | ||||||
| tableSize = { span: 12, offset: 0 }, | ||||||
| renderSubComponent, | ||||||
| getRowCanExpand, | ||||||
| disableGlobalFilter = false, // Disable the Global Search | ||||||
| headingComments = {}, | ||||||
| fluid = false, | ||||||
| }) => { | ||||||
| const [rowSelection, setRowSelection] = useState({}); | ||||||
| const [sorting, setSorting] = useState<SortingState>([]); | ||||||
|
|
@@ -82,7 +87,6 @@ const Table: React.FC<TableProps> = ({ | |||||
| </button> | ||||||
| ); | ||||||
| }, | ||||||
| size: 40, | ||||||
| enableSorting: false, | ||||||
| enableColumnFilter: false, | ||||||
| }; | ||||||
|
|
@@ -110,7 +114,6 @@ const Table: React.FC<TableProps> = ({ | |||||
| }} | ||||||
| /> | ||||||
| ), | ||||||
| size: 40, | ||||||
| enableSorting: false, | ||||||
| enableFilter: false, | ||||||
| }, | ||||||
|
|
@@ -145,12 +148,6 @@ const Table: React.FC<TableProps> = ({ | |||||
| getExpandedRowModel: getExpandedRowModel(), | ||||||
| }); | ||||||
|
|
||||||
|
|
||||||
| //Enable search filters for columns based on page size | ||||||
| const totalItems = initialData.length; | ||||||
| const pageSize = table.getState().pagination.pageSize; | ||||||
| const shouldShowColumnFilters = showColumnFilter && totalItems > pageSize; | ||||||
|
|
||||||
| const flatRows = table.getSelectedRowModel().flatRows; | ||||||
|
|
||||||
| useEffect(() => { | ||||||
|
|
@@ -175,76 +172,72 @@ const Table: React.FC<TableProps> = ({ | |||||
|
|
||||||
| return ( | ||||||
| <> | ||||||
| {!disableGlobalFilter && ( | ||||||
| <Container> | ||||||
| <Row className="mb-md-2" style={{ flex: 1 }}> | ||||||
| <Col md={{ span: 12 }}> | ||||||
| {isGlobalFilterVisible && ( | ||||||
| <GlobalFilter filterValue={globalFilter} setFilterValue={setGlobalFilter} /> | ||||||
| )} | ||||||
| </Col> | ||||||
| {/*<span style={{ marginLeft: "5px" }} onClick={toggleGlobalFilter}> | ||||||
| <Container fluid={fluid}> | ||||||
| <Row className="mb-md-2"> | ||||||
| <Col md={{ span: 12 }}> | ||||||
| {isGlobalFilterVisible && ( | ||||||
| <GlobalFilter filterValue={globalFilter} setFilterValue={setGlobalFilter} /> | ||||||
| )} | ||||||
| </Col> | ||||||
| {!disableGlobalFilter && ( | ||||||
| <span style={{ marginLeft: "5px" }} onClick={toggleGlobalFilter}> | ||||||
| <FaSearch style={{ cursor: "pointer" }} /> | ||||||
| {isGlobalFilterVisible ? " Hide" : " Show"} | ||||||
| </span>*/} | ||||||
| </Row> | ||||||
| </Container> | ||||||
| )} | ||||||
| <Container> | ||||||
| <Row style={{ flex: 1 }}> | ||||||
| </span> | ||||||
| )} | ||||||
| </Row> | ||||||
| </Container> | ||||||
| <Container fluid={fluid}> | ||||||
| <Row> | ||||||
| <Col md={tableSize}> | ||||||
| <BTable striped hover responsive size="sm" className="custom-table-layout"> | ||||||
| <BTable striped hover responsive size="sm"> | ||||||
| <thead className="table-secondary"> | ||||||
| {table.getHeaderGroups().map((headerGroup) => ( | ||||||
| <tr key={headerGroup.id}> | ||||||
| {headerGroup.headers.map((header) => ( | ||||||
| <th key={header.id} colSpan={header.colSpan} style={{ width: `${header.getSize()}px` }}> | ||||||
| {header.isPlaceholder ? null : ( | ||||||
| <> | ||||||
| <div | ||||||
| {...{ | ||||||
| className: header.column.getCanSort() | ||||||
| ? "cursor-pointer select-none" | ||||||
| : "", | ||||||
| onClick: header.column.getToggleSortingHandler(), | ||||||
| }} | ||||||
| > | ||||||
| {flexRender(header.column.columnDef.header, header.getContext())} | ||||||
| {{ | ||||||
| asc: " 🔼", | ||||||
| desc: " 🔽", | ||||||
| }[header.column.getIsSorted() as string] ?? null} | ||||||
| </div> | ||||||
| {shouldShowColumnFilters && header.column.getCanFilter() ? ( | ||||||
| <ColumnFilter column={header.column} /> | ||||||
| ) : null} | ||||||
| </> | ||||||
| )} | ||||||
| </th> | ||||||
| ))} | ||||||
| {headerGroup.headers.map((header) => { | ||||||
| // Add info icon to Heading if comment exists. | ||||||
| const comment = headingComments[header.column.columnDef.header as string]; | ||||||
| return ( | ||||||
| <th key={header.id} colSpan={header.colSpan}> | ||||||
| {header.isPlaceholder ? null : ( | ||||||
| <> | ||||||
| <div | ||||||
| {...{ | ||||||
| className: header.column.getCanSort() | ||||||
| ? "cursor-pointer select-none" | ||||||
| : "", | ||||||
| onClick: header.column.getToggleSortingHandler(), | ||||||
| }} | ||||||
| > | ||||||
| {flexRender(header.column.columnDef.header, header.getContext())} | ||||||
| {comment && <ToolTip id="" info={comment} />} | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pass a unique tooltip id per header cell. Using Proposed fix- {comment && <ToolTip id="" info={comment} />}
+ {comment && <ToolTip id={header.id} info={comment} />}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| {{ | ||||||
| asc: " 🔼", | ||||||
| desc: " 🔽", | ||||||
| }[header.column.getIsSorted() as string] ?? null} | ||||||
| </div> | ||||||
| {/* Previously hidden when data fit a single page; now always driven by the showColumnFilter prop. */} | ||||||
| {showColumnFilter && header.column.getCanFilter() ? ( | ||||||
| <ColumnFilter column={header.column} /> | ||||||
| ) : null} | ||||||
| </> | ||||||
| )} | ||||||
| </th> | ||||||
| ); | ||||||
| })} | ||||||
| </tr> | ||||||
| ))} | ||||||
| </thead> | ||||||
| <tbody> | ||||||
| {table.getRowModel().rows.map((row) => ( | ||||||
| <React.Fragment key={row.id}> | ||||||
| <tr | ||||||
| className={row.original.isSelected ? 'selected-topic-row' : ''} | ||||||
| onClick={() => onRowClick?.(row.original)} | ||||||
| style={{ | ||||||
| cursor: onRowClick ? 'pointer' : 'default', | ||||||
| backgroundColor: row.original.isSelected ? '#fff3cd' : undefined | ||||||
| }} | ||||||
| > | ||||||
| {row.getVisibleCells().map((cell) => { | ||||||
| const selected = !!row.original.isSelected; | ||||||
| return ( | ||||||
| <td key={cell.id} style={selected ? { backgroundColor: '#fff3cd' } : undefined}> | ||||||
| {flexRender(cell.column.columnDef.cell, cell.getContext())} | ||||||
| </td> | ||||||
| ); | ||||||
| })} | ||||||
| </tr> | ||||||
| <tr> | ||||||
| {row.getVisibleCells().map((cell) => ( | ||||||
| <td key={cell.id}> | ||||||
| {flexRender(cell.column.columnDef.cell, cell.getContext())} | ||||||
| </td> | ||||||
| ))} | ||||||
| </tr> | ||||||
| {row.getIsExpanded() && renderSubComponent && ( | ||||||
| <tr> | ||||||
| <td colSpan={row.getVisibleCells().length}> | ||||||
|
|
@@ -266,7 +259,8 @@ const Table: React.FC<TableProps> = ({ | |||||
| setPageSize={table.setPageSize} | ||||||
| getPageCount={table.getPageCount} | ||||||
| getState={table.getState} | ||||||
| totalItems={initialData.length} | ||||||
| // Use pre-pagination row count so the total reflects active filters, not raw data length. | ||||||
| totalItems={table.getPrePaginationRowModel().rows.length} | ||||||
| /> | ||||||
| )} | ||||||
| </Col> | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,25 @@ | ||
| /** | ||
| * Type declarations for CSS Modules. | ||
| * | ||
| * Vite processes any file named *.module.css or *.module.scss as a CSS Module, | ||
| * hashing all local class names so they are scoped to the importing component. | ||
| * These declarations tell TypeScript that `import styles from "*.module.css/scss"` | ||
| * returns a plain object mapping original class names to their hashed equivalents, | ||
| * e.g. styles.tbl_heat → "tbl_heat_a1b2c3". | ||
| * | ||
| * Classes that must remain globally named (used as plain strings by components that | ||
| * don't import the module) should be wrapped in `:global { }` inside the SCSS file. | ||
| * | ||
| * Note: do NOT add `declare module "*.scss"` or `declare module "*.css"` here — | ||
| * those blanket declarations would allow side-effect imports of non-module files | ||
| * without type checking and mask missing-file errors. | ||
| */ | ||
| declare module "*.module.css" { | ||
| const classes: { readonly [key: string]: string }; | ||
| export default classes; | ||
| } | ||
|
|
||
| declare module "*.module.scss" { | ||
| const classes: { readonly [key: string]: string }; | ||
| export default classes; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,6 @@ | ||
| import React from 'react'; | ||
| import { getColorClass } from '../ViewTeamGrades/utils'; | ||
| import { getColorClass } from '../ViewTeamGrades/heatgridUtils'; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Locate class definitions =="
rg -n --type=scss -C2 '\.(grade-circle|underlined|circle-container|weight-circle|circle|tick|c0|c1|c2|c3|c4|c5|cf)\b'
echo
echo "== Locate SCSS imports on ReviewTableau path =="
rg -n --type=tsx -C3 'import .*\.s?css' src/pages/ReviewTableau
echo
echo "== Locate module/global style imports that could provide these classes =="
rg -n --type=tsx -C3 'ViewTeamGrades.*(module\.scss|grades\.scss)' src
echo
echo "== Confirm ScoreWidgets class usage =="
rg -n --type=tsx -C2 'grade-circle|underlined|circle-container|weight-circle|tick|getColorClass' src/pages/ReviewTableau/ScoreWidgets.tsxRepository: expertiza/reimplementation-front-end Length of output: 158 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Find SCSS files in the repo =="
fd -e scss | head -20
echo
echo "== Find CSS files in the repo =="
fd -e css | head -20
echo
echo "== Check what's exported from heatgridUtils =="
cat -n src/pages/ViewTeamGrades/heatgridUtils.ts 2>/dev/null | head -50
echo
echo "== Check imports and class usage in ScoreWidgets =="
cat -n src/pages/ReviewTableau/ScoreWidgets.tsxRepository: expertiza/reimplementation-front-end Length of output: 6306 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Search for getColorClass definition in heatgridUtils =="
rg -n 'getColorClass' src/pages/ViewTeamGrades/heatgridUtils.ts
echo
echo "== Check full heatgridUtils file =="
wc -l src/pages/ViewTeamGrades/heatgridUtils.ts
tail -100 src/pages/ViewTeamGrades/heatgridUtils.ts
echo
echo "== Search for getColorClass anywhere in the codebase =="
rg -n 'getColorClass' src
echo
echo "== Search for grade-circle class definition =="
rg -n 'grade-circle' src --type=scss
rg -n 'grade-circle' src --type=css
echo
echo "== Check ViewTeamGrades.module.scss =="
cat -n src/pages/ViewTeamGrades/ViewTeamGrades.module.scss
echo
echo "== Check ReviewTableau.scss =="
cat -n src/pages/ReviewTableau/ReviewTableau.scss
echo
echo "== Check if ReviewTableau imports the SCSS =="
rg -n 'import' src/pages/ReviewTableau/ReviewTableau.tsx | head -20Repository: expertiza/reimplementation-front-end Length of output: 5437 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Full ViewTeamGrades.module.scss =="
cat -n src/pages/ViewTeamGrades/ViewTeamGrades.module.scss
echo
echo "== Full ReviewTableau.scss =="
cat -n src/pages/ReviewTableau/ReviewTableau.scss
echo
echo "== Search for color classes c1-c5, cf =="
rg -n '\bc[0-5]\b|cf' src/pages/ViewTeamGrades/ViewTeamGrades.module.scss
echo
echo "== Search for grade-circle, underlined, weight-circle, circle, tick =="
rg -n 'grade-circle|underlined|weight-circle|^.*circle|^.*tick' src/pages/ViewTeamGrades/ViewTeamGrades.module.scss
echo
echo "== Check ReviewTableau.tsx imports =="
cat -n src/pages/ReviewTableau/ReviewTableau.tsx | head -50Repository: expertiza/reimplementation-front-end Length of output: 20932 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Verify if ReviewTableau.tsx imports ViewTeamGrades styles =="
rg -n 'ViewTeamGrades' src/pages/ReviewTableau/ReviewTableau.tsx
echo
echo "== Check if there's a global stylesheet import that loads ViewTeamGrades styles =="
rg -n 'ViewTeamGrades.module.scss|import.*scss' src/pages/ReviewTableau
echo
echo "== Check main App or index files for global style imports =="
rg -n 'ViewTeamGrades.module.scss' src
echo
echo "== Check if custom.scss (global stylesheet) imports ViewTeamGrades styles =="
cat -n src/custom.scss | head -50
echo
echo "== Check App.tsx for stylesheet imports =="
fd -n 'App.tsx' -x cat -n {} | head -30Repository: expertiza/reimplementation-front-end Length of output: 144 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Full custom.scss content =="
cat -n src/custom.scss
echo
echo "== Find and check index files =="
fd -n 'index.tsx\|index.ts' src | head -5 | xargs -I {} sh -c 'echo "=== {} ===" && head -30 {}'
echo
echo "== Check main App.tsx or entry point =="
rg -n 'import.*module.scss|import.*custom.scss|import.*ViewTeamGrades' src/index.tsx src/App.tsx 2>/dev/null || echo "No matches found"
echo
echo "== List all SCSS/CSS imports at repository entry point =="
grep -n 'import.*\.\(scss\|css\)' src/index.tsx src/App.tsx 2>/dev/null || echo "No direct stylesheet imports"
echo
echo "== Confirm which files import ReviewTableau.scss =="
rg -n 'ReviewTableau.scss' srcRepository: expertiza/reimplementation-front-end Length of output: 5186 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Check index.tsx/index.ts entry points =="
find src -maxdepth 1 -name 'index.*' -exec cat -n {} \;
echo
echo "== Check App.tsx for stylesheet imports =="
find src -maxdepth 1 -name 'App.tsx' -exec cat -n {} \;
echo
echo "== Verify ReviewTableau.scss is actually imported in ReviewTableau.tsx =="
grep -n 'ReviewTableau.scss' src/pages/ReviewTableau/ReviewTableau.tsx
echo
echo "== Check if ViewTeamGrades.module.scss is globally imported anywhere =="
rg 'ViewTeamGrades.module.scss' src
echo
echo "== Confirm module.scss scoping - check if styles in ReviewTableau.tsx use :global =="
rg -A5 -B5 'grade-circle|underlined|circle-container|weight-circle|tick|c[0-5]' src/pages/ReviewTableau/ReviewTableau.scssRepository: expertiza/reimplementation-front-end Length of output: 23781 Add ViewTeamGrades.module.scss import to ReviewTableau.tsx or ScoreWidgets.tsx.
🤖 Prompt for AI Agents |
||
| import { ScoreWidgetProps } from '../../types/reviewTableau'; | ||
| import '../ViewTeamGrades/grades.scss'; | ||
|
|
||
| /** | ||
| * Reusable circular score widget that matches the design used in ViewTeamGrades | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use a semantic button for the global-search toggle.
The clickable
<span>is not keyboard accessible, so keyboard-only users cannot toggle global search.Proposed fix
🤖 Prompt for AI Agents