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..71ee0a819f
--- /dev/null
+++ b/src/app/units/states/students-list/students-list.component.html
@@ -0,0 +1,254 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
No students found
+
No students were found using the filters specified.
+
+
+
+
+
0" id="students-list-table" class="min-w-full divide-y divide-gray-200 mb-4">
+
+
+ |
+
+ 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
+ =10&&bar.value===bar.value">
+ {{ bar.value }}%
+
+
+
+ |
+
+
+
+ visibility
+
+ menu_book
+
+
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+
0"
+ id="students-list-paginator"
+ [length]="pagination.totalSize"
+ [pageSize]="pagination.pageSize"
+ [pageSizeOptions]="[5,10,15,20]"
+ (page)="
+ pagination.currentPage=$event.pageIndex+1;
+ pagination.pageSize=$event.pageSize;
+ applyFilters()
+ "
+ >
+
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..a57b7b2060
--- /dev/null
+++ b/src/app/units/states/students-list/students-list.component.scss
@@ -0,0 +1,70 @@
+#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;
+ font-weight: 700;
+ }
+ }
+
+ /* segment colors & text */
+ .not_started,
+ .not-started {
+ background-color: #d1d5db;
+ color: #374151;
+ }
+ .working_on_it,
+ .working-on-it {
+ background-color: #3b82f6;
+ color: #fff;
+ }
+ .ready_for_feedback,
+ .ready-for-feedback {
+ background-color: #f59e0b;
+ color: #fff;
+ }
+ .complete {
+ background-color: #4caf50;
+ color: #fff;
+ }
+ .fail {
+ background-color: #dc3545;
+ 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;
+ }
+
+ /* 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..704700a481
--- /dev/null
+++ b/src/app/units/states/students-list/students-list.component.ts
@@ -0,0 +1,224 @@
+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;
+
+ @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 {
+ 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);
+ return;
+ }
+ if (this.tableSort.order === column) {
+ this.tableSort.reverse = !this.tableSort.reverse;
+ } else {
+ this.tableSort.order = column;
+ this.tableSort.reverse = false;
+ }
+ this.applyFilters();
+ }
+
+ 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 @@
-
-
-
-
-
-
-
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}}
- |
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
- |
-
-
- |
-
-
- |
-
-
-
-
-
-