From bf59661fe20beb7e6b42840450edceac3989f779 Mon Sep 17 00:00:00 2001 From: HasinduWelarathne Date: Sun, 18 May 2025 04:20:30 +1000 Subject: [PATCH 1/4] WIP:complete migration to Angular 17 + Tailwind --- src/app/doubtfire-angular.module.ts | 10 + src/app/doubtfire-angularjs.module.ts | 10 +- src/app/doubtfire.states.ts | 42 ++- src/app/units/states/states.coffee | 1 - .../states/students-list/students-list.coffee | 123 --------- .../students-list.component.html | 254 ++++++++++++++++++ .../students-list.component.scss | 77 ++++++ .../students-list/students-list.component.ts | 234 ++++++++++++++++ .../states/students-list/students-list.scss | 80 ------ .../students-list/students-list.tpl.html | 182 ------------- 10 files changed, 625 insertions(+), 388 deletions(-) delete mode 100644 src/app/units/states/students-list/students-list.coffee create mode 100644 src/app/units/states/students-list/students-list.component.html create mode 100644 src/app/units/states/students-list/students-list.component.scss create mode 100644 src/app/units/states/students-list/students-list.component.ts delete mode 100644 src/app/units/states/students-list/students-list.scss delete mode 100644 src/app/units/states/students-list/students-list.tpl.html diff --git a/src/app/doubtfire-angular.module.ts b/src/app/doubtfire-angular.module.ts index c714ff0e5a..961721e8b4 100644 --- a/src/app/doubtfire-angular.module.ts +++ b/src/app/doubtfire-angular.module.ts @@ -11,6 +11,10 @@ import { NgxChartsModule } from '@swimlane/ngx-charts'; // Lottie animation module // import {LottieModule, LottieCacheModule} from 'ngx-lottie'; +import { FStudentsListComponent } from './units/states/students-list/students-list.component'; + + + import {provideLottieOptions, LottieComponent} from 'ngx-lottie'; import player from 'lottie-web'; import {ClipboardModule} from '@angular/cdk/clipboard'; @@ -271,6 +275,8 @@ import { UnitStudentEnrolmentModalComponent } from './units/modals/unit-student- @NgModule({ // Components we declare declarations: [ + + FStudentsListComponent, AlertComponent, UnitStudentEnrolmentModalComponent, AboutDoubtfireModalContent, @@ -399,6 +405,7 @@ import { UnitStudentEnrolmentModalComponent } from './units/modals/unit-student- GroupSetService, GroupService, UnitService, + UserService, ProjectService, UnitRoleService, LearningOutcomeService, @@ -471,6 +478,7 @@ import { UnitStudentEnrolmentModalComponent } from './units/modals/unit-student- ], imports: [ FlexLayoutModule, + FormsModule, BrowserModule, BrowserAnimationsModule, FormsModule, @@ -479,6 +487,8 @@ import { UnitStudentEnrolmentModalComponent } from './units/modals/unit-student- DragDropModule, ScrollingModule, MatToolbarModule, + MatTableModule, + MatPaginatorModule, MatSidenavModule, MatFormFieldModule, MatAutocompleteModule, diff --git a/src/app/doubtfire-angularjs.module.ts b/src/app/doubtfire-angularjs.module.ts index d9dc6955d5..1a40a52407 100644 --- a/src/app/doubtfire-angularjs.module.ts +++ b/src/app/doubtfire-angularjs.module.ts @@ -108,7 +108,7 @@ import 'build/src/app/units/states/rollover/directives/unit-dates-selector/unit- import 'build/src/app/units/states/rollover/directives/directives.js'; import 'build/src/app/units/states/rollover/rollover.js'; import 'build/src/app/units/states/index/index.js'; -import 'build/src/app/units/states/students-list/students-list.js'; +import { FStudentsListComponent } from './units/states/students-list/students-list.component'; import 'build/src/app/units/states/analytics/analytics.js'; import 'build/src/app/common/filters/filters.js'; import 'build/src/app/common/content-editable/content-editable.js'; @@ -446,6 +446,14 @@ DoubtfireAngularJSModule.directive( ); DoubtfireAngularJSModule.directive('fUnits', downgradeComponent({component: FUnitsComponent})); +DoubtfireAngularJSModule.directive( + 'fStudentsList', + downgradeComponent({ + component: FStudentsListComponent, + inputs: ['unit', 'tutor'] + }) +) + // Global configuration DoubtfireAngularJSModule.directive( 'taskCommentsViewer', diff --git a/src/app/doubtfire.states.ts b/src/app/doubtfire.states.ts index 7baa910f57..a889434fd2 100644 --- a/src/app/doubtfire.states.ts +++ b/src/app/doubtfire.states.ts @@ -9,11 +9,15 @@ import { UnauthorisedComponent } from './errors/states/unauthorised/unauthorised import {FUsersComponent} from './admin/states/users/users.component'; import {FUnitsComponent} from './admin/states/units/units.component'; import {ProjectDashboardComponent} from './projects/states/dashboard/project-dashboard/project-dashboard.component'; -import {UnitRootState} from './units/unit-root-state.component'; import {ProjectRootState} from './projects/states/project-root-state.component'; import { TaskViewerState } from './units/task-viewer/task-viewer-state.component'; import {ScormPlayerComponent} from './common/scorm-player/scorm-player.component'; import { Ng2ViewDeclaration } from '@uirouter/angular'; +import { FStudentsListComponent } from './units/states/students-list/students-list.component'; +import {UnitService} from 'src/app/api/services/unit.service'; +import { UserService } from 'src/app/api/services/user.service'; +import { Transition } from '@uirouter/core'; +import { UnitRootState } from './units/unit-root-state.component'; /* * Use this file to store any states that are sourced by angular components. @@ -52,6 +56,41 @@ const usersState: NgHybridStateDeclaration = { }, }; +export const studentsListState: NgHybridStateDeclaration = { + name: 'units/students/list', + parent: 'unit-root-state', + url: '/students', + + views: { + unitView: { + component: FStudentsListComponent, + bindings: { + unit: 'unit', + tutor: 'tutor' + } + } + }, + + resolve: [ + { + token: 'unit', + deps: [UnitService, Transition], + resolveFn: (us: UnitService, trans: Transition) => + us.get(trans.params().unitId).toPromise() + }, + { + token: 'tutor', + deps: ['unit', UserService], + resolveFn: (unit: any, userSvc: UserService) => + !!unit && unit.unitRole?.role === 'Tutor' + } + ], + + data: { + pageTitle: 'Students', + roleWhitelist: ['Admin', 'Convenor', 'Tutor', 'Auditor'] + } +}; /** * Define the new home state. */ @@ -422,6 +461,7 @@ export const doubtfireStates = [ EditProfileState, EulaState, usersState, + studentsListState, ViewAllProjectsState, ViewAllUnits, AdministerUnits, diff --git a/src/app/units/states/states.coffee b/src/app/units/states/states.coffee index 7d68872f4a..eed94b6743 100644 --- a/src/app/units/states/states.coffee +++ b/src/app/units/states/states.coffee @@ -4,7 +4,6 @@ angular.module('doubtfire.units.states', [ 'doubtfire.units.states.edit' 'doubtfire.units.states.tasks' 'doubtfire.units.states.groups' - 'doubtfire.units.states.students' 'doubtfire.units.states.analytics' 'doubtfire.units.states.portfolios' 'doubtfire.units.states.rollover' diff --git a/src/app/units/states/students-list/students-list.coffee b/src/app/units/states/students-list/students-list.coffee deleted file mode 100644 index 7d2f363e1c..0000000000 --- a/src/app/units/states/students-list/students-list.coffee +++ /dev/null @@ -1,123 +0,0 @@ -angular.module('doubtfire.units.states.students', []) -# -# State for convenors and tutors to view students -# -.config(($stateProvider) -> - $stateProvider.state 'units/students/list', { - parent: 'units/index' - url: '/students' - templateUrl: "units/states/students-list/students-list.tpl.html" - controller: "UnitStudentsStateCtrl" - data: - task: "Student List" - pageTitle: "_Home_" - roleWhitelist: ['Tutor', 'Convenor', 'Admin', 'Auditor'] - } -) -.controller("UnitStudentsStateCtrl", ($scope, $state, $filter, $timeout, UnitStudentEnrolmentModal, alertService, newTaskService, gradeService, analyticsService, newUserService) -> - # Filtering - applyFilters = -> - filteredProjects = $filter('showStudents')($scope.unit.students, $scope.staffFilter, $scope.tutor) - # At this point know the length of all students - allStudentsLength = filteredProjects.length - # Apply filter for projects and determine to show CSV button - filteredProjects = $filter('projectFilter')(filteredProjects, $scope.searchText) if $scope.searchText?.trim().length > 0 - # Paginate and sort - $scope.filteredProjects = $filter('paginateAndSort')(filteredProjects, $scope.pagination, $scope.tableSort) - - # Pagination values - $scope.pagination = - currentPage: 1 - maxSize: 15 - pageSize: 15 - totalSize: null - show: false - onChange: applyFilters - - # Initial sort orders - $scope.tableSort = - order: 'name' - reverse: false - - # Staff filter options (convenor should see all) - $scope.staffFilter = { - Convenor: 'all', - Tutor: 'mine' - }[$scope.unitRole.role] - - # Scope for student name - $scope.tutor = newUserService.currentUser - - # Send initial apply filter - applyFilters() - - # Table sorting - $scope.sortTableBy = (column) -> - if column == 'flags' - $scope.showSearchOptions = true - $timeout(-> - document.querySelector('#students-list .panel-body.search-options .form-group.flag-sort button:first-child').focus() - , 500) - $scope.tableSort.reverse = !$scope.tableSort.reverse if $scope.tableSort.order == column - return - $scope.tableSort.order = column - $scope.tableSort.reverse = !$scope.tableSort.reverse - applyFilters() - - # Changing staff filter reapplies filter - $scope.staffFilterChanged = (newFilter) -> - $scope.staffFilter = newFilter - applyFilters() - - # Changing search text reapplies filter - $scope.searchTextChanged = applyFilters - - # CSV header func - $scope.getCSVHeader = -> - result = ['username', 'name', 'email', 'portfolio'] - if $scope.unit.tutorialStreamsCache.size > 0 - _.each $scope.unit.tutorialStreams, (ts) -> - result.push ts.abbreviation - else - result.push 'tutorial' - result - - # CSV data row func - $scope.getCSVData = -> - filteredProjects = $filter('filter')($filter('showStudents')($scope.unit.students, $scope.staffFilter, $scope.tutor), $scope.searchText) - result = [] - angular.forEach(filteredProjects, (project) -> - row = {} - row['username'] = project.student.username - row['name'] = project.student.name - row['email'] = project.student.email - row['portfolio'] = project.portfolioStatus - if $scope.unit.tutorialStreamsCache.size > 0 - _.each $scope.unit.tutorialStreams, (ts) -> - row[ts.abbreviation] = project.tutorialForStream(ts)?.abbreviation || '' - else - row['tutorial'] = project.tutorials[0]?.abbreviation || '' - result.push row - ) - result - - # Expose the status labels and classes for the bar stats - $scope.statusClass = newTaskService.statusClass - $scope.statusText = newTaskService.statusText - - # View a student - $scope.viewStudent = (student) -> - $state.go("projects/dashboard", {projectId: student.id, tutor: true, taskAbbr:''}) - - # Sets the flag sorting - $scope.sortTableByFlag = (flag) -> - if $scope.tableSort.order == flag - $scope.tableSort.reverse = !$scope.tableSort.reverse - else - $scope.tableSort.order = flag - - # Shows the enrolment modal - $scope.showEnrolModal = -> - analyticsService.event 'Teacher View - Students Tab', 'Enrol Student' - UnitStudentEnrolmentModal.show $scope.unit -) diff --git a/src/app/units/states/students-list/students-list.component.html b/src/app/units/states/students-list/students-list.component.html new file mode 100644 index 0000000000..73b104144d --- /dev/null +++ b/src/app/units/states/students-list/students-list.component.html @@ -0,0 +1,254 @@ +
+ + + + + + {{ text }} + + + + +
+
+ Show Students From… +
+ + +
+
+ +
+ Sort Flags By… +
+ + + +
+

+ Click a flag sort button twice to reverse its sort order. +

+
+
+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ Username + + {{ tableSort.reverse ? 'expand_more' : 'expand_less' }} + + + Name + + {{ tableSort.reverse ? 'expand_more' : 'expand_less' }} + + + Stats + + {{ tableSort.reverse ? 'expand_more' : 'expand_less' }} + + + Flags + + {{ tableSort.reverse ? 'expand_more' : 'expand_less' }} + + + Campus + + {{ tableSort.reverse ? 'expand_more' : 'expand_less' }} + + + Tutorial + + {{ tableSort.reverse ? 'expand_more' : 'expand_less' }} + +
+ + + {{ project.student.username || 'N/A' }} + + {{ project.student.name }} + +
+
+ No Interaction + + {{ bar.value }}% + +
+
+
+ + + visibility + + menu_book + + +
+ + + + + + +
diff --git a/src/app/units/states/students-list/students-list.component.scss b/src/app/units/states/students-list/students-list.component.scss new file mode 100644 index 0000000000..262c07657e --- /dev/null +++ b/src/app/units/states/students-list/students-list.component.scss @@ -0,0 +1,77 @@ +#students-list { + font-family: inherit; + + /* 1) TABLE COLUMN SIZING */ + table#students-list-table { + th:nth-child(1), + td:nth-child(1) { + width: 5%; + text-align: right; + } + th:nth-child(4), + td:nth-child(4) { + width: 40%; + min-width: 250px; + max-width: 350px; + } + th { + color: #3b82f6; + font-size: 1.25rem; /* smaller header labels */ + font-weight: 500; + } + } + + /* 2) PROGRESS BAR (Stats) */ + .task-progress { + max-width: 350px; /* cap overall width */ + /* height, bg-color & border-radius are handled by Tailwind */ + } + + /* segment colors & text */ + .not_started, + .not-started { + background-color: #d1d5db; /* gray */ + color: #374151; /* dark text */ + } + .working_on_it, + .working-on-it { + background-color: #3b82f6; /* blue */ + color: #fff; + } + .ready_for_feedback, + .ready-for-feedback { + background-color: #f59e0b; /* amber */ + color: #fff; + } + .complete { + background-color: #4caf50; /* green */ + color: #fff; + } + .fail { + background-color: #dc3545; /* red */ + color: #fff; + } + + .progress-segment { + flex: 0 0 auto; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.75rem; + line-height: 1; + white-space: nowrap; + /* corners come from .rounded-full in your HTML */ + } + + /* 3) FILTER BUTTONS ICON CENTERING */ + #students-list-search-options { + button { + display: inline-flex; + align-items: center; + justify-content: center; + } + mat-icon { + margin-right: 0.5rem; + } + } +} diff --git a/src/app/units/states/students-list/students-list.component.ts b/src/app/units/states/students-list/students-list.component.ts new file mode 100644 index 0000000000..b35321f987 --- /dev/null +++ b/src/app/units/states/students-list/students-list.component.ts @@ -0,0 +1,234 @@ +import { Component, Input, OnInit, AfterViewInit, ViewChild, ElementRef, Injector, Inject } from '@angular/core'; +import { UIRouter } from '@uirouter/angular'; +import { unitStudentEnrolmentModal, analyticsService } from 'src/app/ajs-upgraded-providers'; +import { Project, Unit } from 'src/app/api/models/doubtfire-model'; +import { UserService } from 'src/app/api/services/user.service'; + +@Component({ + selector: 'f-students-list', + templateUrl: './students-list.component.html', + styleUrls: ['./students-list.component.scss'] +}) +export class FStudentsListComponent implements OnInit, AfterViewInit { + @Input() unit!: Unit; + // Removed unused @Input() tutor + + @ViewChild('searchInput', { static: false }) + searchInput!: ElementRef; + + // UI state + searchText = ''; + filteredTypeaheadData: string[] = []; + showSearchOptions = false; + staffFilter: 'all' | 'mine' = 'all'; + tableSort = { order: 'student.name', reverse: false }; + pagination = { + currentPage: 1, + pageSize: 15, + maxSize: 15, + totalSize: 0 + }; + filteredProjects: Project[] = []; + + statusClass!: (key: any) => string; + statusText!: (key: any) => string; + + private showStudentsFilter!: ( + students: readonly Project[], + staffFilter: 'all' | 'mine', + tutorUser: any + ) => readonly Project[]; + private newTaskService: any; + + constructor( + @Inject(analyticsService) private analytics: any, + private injector: Injector, + private router: UIRouter, + private userService: UserService + ) {} + + ngOnInit(): void { + // Grab the old AngularJS filter and task-status helpers + const $filter = (this.injector as any).get('$filter'); + this.showStudentsFilter = $filter('showStudents'); + this.newTaskService = (this.injector as any).get('newTaskService'); + + // Tutors see "mine" by default, other roles see "all" + this.staffFilter = this.unit.unitRole?.role === 'Tutor' ? 'mine' : 'all'; + + this.statusClass = this.newTaskService.statusClass.bind(this.newTaskService); + this.statusText = this.newTaskService.statusText.bind(this.newTaskService); + + this.filteredTypeaheadData = this.unit.studentFilterTypeAheadData.slice(0, 8); + this.applyFilters(); + } + + ngAfterViewInit(): void { + // Keep the existing focus behavior on the search input + this.searchInput?.nativeElement.focus(); + } + + onSearchTextChange(value: string): void { + this.searchText = value; + const term = value.trim().toLowerCase(); + this.filteredTypeaheadData = this.unit.studentFilterTypeAheadData + .filter((t) => t.toLowerCase().includes(term)) + .slice(0, 8); + this.applyFilters(); + } + + staffFilterChanged(newFilter: 'all' | 'mine'): void { + this.staffFilter = newFilter; + this.applyFilters(); + } + + /** Run the full pipeline (filter → search → sort) and return all results */ + private getAllFiltered(): Project[] { + // First apply staff filter using the AngularJS "showStudents" filter + let projects = this.showStudentsFilter( + this.unit.students, + this.staffFilter, + this.userService.currentUser + ) as Project[]; + + // Then apply search text filtering for student name/username or tutor name + if (this.searchText.trim()) { + const term = this.searchText.trim().toLowerCase(); + projects = projects.filter((p) => { + const studentMatch = + p.student.username.toLowerCase().includes(term) || + p.student.name.toLowerCase().includes(term); + let tutorMatch = false; + const anyProject = p as any; + if (anyProject.tutorName && (anyProject.tutorName as string).toLowerCase().includes(term)) { + tutorMatch = true; + } + if (anyProject.tutor && anyProject.tutor.name && (anyProject.tutor.name as string).toLowerCase().includes(term)) { + tutorMatch = true; + } + if (anyProject.tutor && anyProject.tutor.username && (anyProject.tutor.username as string).toLowerCase().includes(term)) { + tutorMatch = true; + } + return studentMatch || tutorMatch; + }); + } + + return this.sortProjects(projects); + } + + applyFilters(): void { + const all = this.getAllFiltered(); + this.pagination.totalSize = all.length; + const start = (this.pagination.currentPage - 1) * this.pagination.pageSize; + this.filteredProjects = all.slice(start, start + this.pagination.pageSize); + } + + private sortProjects(list: Project[]): Project[] { + const { order, reverse } = this.tableSort; + return [...list].sort((a, b) => { + const getValue = (obj: any, path: string) => + path.split('.').reduce((o, key) => (o as any)[key], obj); + const va = getValue(a, order); + const vb = getValue(b, order); + if (va < vb) return reverse ? 1 : -1; + if (va > vb) return reverse ? -1 : 1; + return 0; + }); + } + + sortTableBy(column: string): void { + if (column === 'flags') { + this.showSearchOptions = true; + setTimeout(() => this.searchInput.nativeElement.focus(), 500); + if (['targetGrade', 'similarityFlag', 'portfolioStatus'].includes(this.tableSort.order)) { + this.tableSort.reverse = !this.tableSort.reverse; + this.applyFilters(); + } + return; + } + if (this.tableSort.order === column) { + this.tableSort.reverse = !this.tableSort.reverse; + } else { + this.tableSort.order = column; + this.tableSort.reverse = false; + } + this.applyFilters(); + } + + displayOption(option: string): string { + return option; + } + + totalProgress(project: Project): number { + return (project.taskStats || []).reduce((sum, bar) => sum + bar.value, 0); + } + + getCSVHeader(): string[] { + const header = ['username', 'name', 'email', 'portfolio']; + if (this.unit.tutorialStreamsCache.size > 0) { + this.unit.tutorialStreams.forEach((ts) => header.push(ts.abbreviation)); + } else { + header.push('tutorial'); + } + return header; + } + + /** Map a Project to a plain object whose keys match getCSVHeader() */ + private getCSVRows(projects: Project[]): Record[] { + return projects.map((project) => { + const row: any = { + username: project.student.username, + name: project.student.name, + email: project.student.email, + portfolio: project.portfolioStatus + }; + if (this.unit.tutorialStreamsCache.size > 0) { + this.unit.tutorialStreams.forEach((ts) => { + row[ts.abbreviation] = project.tutorialForStream(ts)?.abbreviation || ''; + }); + } else { + row['tutorial'] = project.tutorials[0]?.abbreviation || ''; + } + return row; + }); + } + + downloadCSV(): void { + // 1) Build header and full data rows + const header = this.getCSVHeader(); + const rows = this.getCSVRows(this.getAllFiltered()); + // 2) Create CSV content + const csvArray = [ + header.join(','), + ...rows.map((r) => + header.map((h) => `"${String(r[h] ?? '')}`.replace(/"/g, '""') + '"').join(',') + ) + ]; + const csvContent = csvArray.join('\r\n'); + // 3) Trigger a download in the browser + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `students-${this.unit.id}.csv`; + a.click(); + window.URL.revokeObjectURL(url); + } + + showEnrolModal(): void { + this.analytics.event( + 'Teacher View - Students Tab', + 'Enrol Student' + ); + const svc = this.injector.get(unitStudentEnrolmentModal); + svc.show(this.unit); + } + + viewStudent(project: Project): void { + this.router.stateService.go('projects/dashboard', { + projectId: project.student.id, + tutor: true, + taskAbbr: '' + }); + } +} diff --git a/src/app/units/states/students-list/students-list.scss b/src/app/units/states/students-list/students-list.scss deleted file mode 100644 index 034083aea9..0000000000 --- a/src/app/units/states/students-list/students-list.scss +++ /dev/null @@ -1,80 +0,0 @@ -#students-list { - // Flag icons - $flag-icon-width: 35px; - $number-of-flags: 3; - table thead th { - vertical-align: middle; - } - a.sort-by-flag-anchor { - &:hover { - text-decoration: none; - .show-link { - text-decoration: underline; - } - } - } - a.sort-by-flag-anchor + .flag-sort-options { - display: flex; - max-width: $flag-icon-width * $number-of-flags; - margin: 0.5em 0; - align-items: center; - justify-content: space-between; - .flag-sort { - i, - grade-icon { - display: inline-block; - } - } - .flag-sort > i:hover { - color: $link-hover-color; - } - .flag-sort > grade-icon:hover span { - background-color: $link-hover-color; - } - } - table { - td.flags-data { - .flags { - max-width: $flag-icon-width * $number-of-flags; - display: flex; - align-items: center; - justify-content: space-between; - } - } - td.task-progress-bar { - padding-right: 3ex; - .progress { - border-radius: 10px; - } - } - td.avatar { - text-align: right; - user-icon { - margin-right: 1ex; - width: 2.5em; - height: 2.5em; - } - } - th.avatar { - width: 05%; - } - th.username { - width: 10%; - } - th.name { - width: 10%; - } - th.stats { - width: 30%; - } - th.flags { - width: 10%; - } - th.tutorial { - width: 15%; - } - th.campus { - width: 10%; - } - } -} diff --git a/src/app/units/states/students-list/students-list.tpl.html b/src/app/units/states/students-list/students-list.tpl.html deleted file mode 100644 index 2ff4736816..0000000000 --- a/src/app/units/states/students-list/students-list.tpl.html +++ /dev/null @@ -1,182 +0,0 @@ -
-
-
-
- - - - -
- -
-
-
- -
-
- - -
-
-
-
- -
-
- - - -
-
-

- Click the button twice to reverse the sort ordering. -

-
-
-
-
-

No students found

-

- No students were found using the filters specified. -

-
-
- - - - - - - - - - - - - - - - - - - - - - - -
- - Username - - - - Name - - - - Stats - - - - Flags - - - - - Campus - - - - Tutorial - -
- - - {{project.student.username || "N/A"}} - - {{project.student.name}} - - - - {{bar.value !== bar.value ? 'No Interaction' : (bar.value + '%')}} - - - - - - - - - - - - - - - - - - -
- -
-
From 96010fec4fa1eee05cb5498966b0ec5056903e95 Mon Sep 17 00:00:00 2001 From: HasinduWelarathne Date: Mon, 19 May 2025 02:10:54 +1000 Subject: [PATCH 2/4] Migrate students-list component --- .../students-list.component.html | 34 +++++++++---------- .../students-list.component.scss | 21 ++++-------- .../students-list/students-list.component.ts | 6 ---- 3 files changed, 24 insertions(+), 37 deletions(-) diff --git a/src/app/units/states/students-list/students-list.component.html b/src/app/units/states/students-list/students-list.component.html index 73b104144d..a055d5eef2 100644 --- a/src/app/units/states/students-list/students-list.component.html +++ b/src/app/units/states/students-list/students-list.component.html @@ -19,7 +19,7 @@ (click)="showSearchOptions = !showSearchOptions" aria-label="Toggle search options" [attr.aria-expanded]="showSearchOptions" - class="w-10 h-10 ml-2 flex items-center justify-center border border-gray-300 rounded focus:outline-none focus:ring-0 bg-white" + class="h-full flex items-center justify-center border-1 border-gray-300 rounded-r focus:outline-none focus:ring-0 bg-white" > {{ showSearchOptions ? 'expand_less' : 'expand_more' }} @@ -42,10 +42,10 @@
- Show Students From… + Show Students From…
-

+

Click a flag sort button twice to reverse its sort order.

diff --git a/src/app/units/states/students-list/students-list.component.ts b/src/app/units/states/students-list/students-list.component.ts index 28e4e172dc..704700a481 100644 --- a/src/app/units/states/students-list/students-list.component.ts +++ b/src/app/units/states/students-list/students-list.component.ts @@ -138,10 +138,6 @@ export class FStudentsListComponent implements OnInit, AfterViewInit { if (column === 'flags') { this.showSearchOptions = true; setTimeout(() => this.searchInput.nativeElement.focus(), 500); - if (['targetGrade', 'similarityFlag', 'portfolioStatus'].includes(this.tableSort.order)) { - this.tableSort.reverse = !this.tableSort.reverse; - this.applyFilters(); - } return; } if (this.tableSort.order === column) {