• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MarkUsProject / Markus / 12976781475

26 Jan 2025 05:20PM UTC coverage: 91.854% (-0.02%) from 91.87%
12976781475

Pull #7392

github

web-flow
Merge 4e5b55b4a into 10a758445
Pull Request #7392: Update React to v18

624 of 1361 branches covered (45.85%)

Branch coverage included in aggregate %.

0 of 16 new or added lines in 8 files covered. (0.0%)

75 existing lines in 4 files now uncovered.

41290 of 44270 relevant lines covered (93.27%)

120.23 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

37.26
/app/javascript/Components/graders_manager.jsx
1
import React from "react";
2
import {createRoot} from "react-dom/client";
3
import {Tab, Tabs, TabList, TabPanel} from "react-tabs";
4
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
5

6
import {withSelection, CheckboxTable} from "./markus_with_selection_hoc";
7
import {selectFilter} from "./Helpers/table_helpers";
8
import {GraderDistributionModal} from "./Modals/graders_distribution_modal";
9
import {SectionDistributionModal} from "./Modals/section_distribution_modal";
10

11
class GradersManager extends React.Component {
12
  constructor(props) {
13
    super(props);
4✔
14
    this.state = {
4✔
15
      graders: [],
16
      groups: [],
17
      criteria: [],
18
      assign_graders_to_criteria: false,
19
      loading: true,
20
      tableName: "groups_table", // The first tab
21
      skip_empty_submissions: true,
22
      anonymize_groups: false,
23
      hide_unassigned_criteria: false,
24
      sections: {},
25
      isGraderDistributionModalOpen: false,
26
      isSectionDistributionModalOpen: false,
27
      show_hidden: false,
28
      show_hidden_groups: false,
29
      hidden_graders_count: 0,
30
      inactive_groups_count: 0,
31
    };
32
  }
33

34
  componentDidMount() {
35
    this.fetchData();
4✔
36
  }
37

38
  openGraderDistributionModal = () => {
4✔
39
    let groups = this.groupsTable ? this.groupsTable.state.selection : [];
×
40
    let criteria = this.criteriaTable ? this.criteriaTable.state.selection : [];
×
41
    let graders = this.gradersTable.state.selection;
×
42
    if (groups.length === 0 && criteria.length === 0) {
×
43
      alert(I18n.t("groups.select_a_group"));
×
44
      return;
×
45
    }
46

47
    if (graders.length === 0) {
×
48
      alert(I18n.t("graders.select_a_grader"));
×
49
      return;
×
50
    }
51

52
    this.setState({
×
53
      isGraderDistributionModalOpen: true,
54
    });
55
  };
56
  openSectionDistributionModal = () => {
4✔
57
    this.setState({
×
58
      isSectionDistributionModalOpen: true,
59
    });
60
  };
61

62
  fetchData = () => {
4✔
63
    fetch(Routes.course_assignment_graders_path(this.props.course_id, this.props.assignment_id), {
4✔
64
      headers: {
65
        Accept: "application/json",
66
      },
67
    })
68
      .then(response => {
69
        if (response.ok) {
4!
70
          return response.json();
4✔
71
        }
72
      })
73
      .then(res => {
74
        if (this.gradersTable) this.gradersTable.resetSelection();
4!
75
        if (this.groupsTable) this.groupsTable.resetSelection();
4!
76
        if (this.criteriaTable) this.criteriaTable.resetSelection();
4!
77

78
        let inactive_groups_count = 0;
4✔
79
        res.groups.forEach(group => {
4✔
80
          if (group.members.length && group.members.every(member => member[2])) {
12✔
81
            group.inactive = true;
4✔
82
            inactive_groups_count += 1;
4✔
83
          } else {
84
            group.inactive = false;
4✔
85
          }
86
        });
87
        this.setState({
4✔
88
          graders: res.graders,
89
          groups: res.groups,
90
          criteria: res.criteria,
91
          assign_graders_to_criteria: res.assign_graders_to_criteria,
92
          loading: false,
93
          sections: res.sections,
94
          anonymize_groups: res.anonymize_groups,
95
          hide_unassigned_criteria: res.hide_unassigned_criteria,
96
          isGraderDistributionModalOpen: false,
97
          isSectionDistributionModalOpen: false,
98
          hidden_graders_count: res.graders.filter(grader => grader.hidden).length,
×
99
          inactive_groups_count: inactive_groups_count,
100
        });
101
      });
102
  };
103

104
  assignAll = () => {
4✔
105
    let groups = this.groupsTable ? this.groupsTable.state.selection : [];
×
106
    let criteria = this.criteriaTable ? this.criteriaTable.state.selection : [];
×
107
    let graders = this.gradersTable.state.selection;
×
108

109
    if (groups.length === 0 && criteria.length === 0) {
×
110
      alert(I18n.t("groups.select_a_group"));
×
111
      return;
×
112
    }
113

114
    if (graders.length === 0) {
×
115
      alert(I18n.t("graders.select_a_grader"));
×
116
      return;
×
117
    }
118

119
    $.post({
×
120
      url: Routes.global_actions_course_assignment_graders_path(
121
        this.props.course_id,
122
        this.props.assignment_id
123
      ),
124
      data: {
125
        global_actions: "assign",
126
        current_table: this.state.tableName,
127
        skip_empty_submissions: this.state.skip_empty_submissions,
128
        groupings: groups,
129
        criteria: criteria,
130
        graders: graders,
131
      },
132
    }).then(this.fetchData);
133
  };
134

135
  assignSections = assignments => {
4✔
136
    let sections = Object.keys(assignments);
×
137
    let graders = Object.values(assignments);
×
138
    $.post({
×
139
      url: Routes.global_actions_course_assignment_graders_path(
140
        this.props.course_id,
141
        this.props.assignment_id
142
      ),
143
      data: {
144
        global_actions: "assign_sections",
145
        current_table: this.state.tableName,
146
        skip_empty_submissions: this.state.skip_empty_submissions,
147
        assignments: assignments,
148
        sections: sections,
149
        graders: graders,
150
      },
151
    }).then(this.fetchData);
152
  };
153

154
  assignRandomly = weightings => {
4✔
155
    let groups = this.groupsTable ? this.groupsTable.state.selection : [];
×
156
    let criteria = this.criteriaTable ? this.criteriaTable.state.selection : [];
×
157
    let graders = Object.keys(weightings);
×
158
    let weights = Object.values(weightings);
×
159

160
    $.post({
×
161
      url: Routes.global_actions_course_assignment_graders_path(
162
        this.props.course_id,
163
        this.props.assignment_id
164
      ),
165
      data: {
166
        global_actions: "random_assign",
167
        current_table: this.state.tableName,
168
        skip_empty_submissions: this.state.skip_empty_submissions,
169
        groupings: groups,
170
        criteria: criteria,
171
        graders: graders,
172
        weightings: weights,
173
      },
174
    }).then(this.fetchData);
175
  };
176

177
  unassignAll = () => {
4✔
178
    let groups = this.groupsTable ? this.groupsTable.state.selection : [];
×
179
    let criteria = this.criteriaTable ? this.criteriaTable.state.selection : [];
×
180
    let graders = this.gradersTable.state.selection;
×
181

182
    if (groups.length === 0 && criteria.length === 0) {
×
183
      alert(I18n.t("groups.select_a_group"));
×
184
      return;
×
185
    }
186

187
    if (graders.length === 0) {
×
188
      alert(I18n.t("graders.select_a_grader"));
×
189
      return;
×
190
    }
191

192
    $.post({
×
193
      url: Routes.global_actions_course_assignment_graders_path(
194
        this.props.course_id,
195
        this.props.assignment_id
196
      ),
197
      data: {
198
        global_actions: "unassign",
199
        current_table: this.state.tableName,
200
        groupings: groups,
201
        criteria: criteria,
202
        graders: graders,
203
      },
204
    }).then(this.fetchData);
205
  };
206

207
  unassignSingle = (id, grader_user_name, origin) => {
4✔
208
    let groups, criteria;
209
    if (origin === "groups_table") {
×
210
      groups = [id];
×
211
      criteria = [];
×
212
    } else {
213
      groups = [];
×
214
      criteria = [id];
×
215
    }
216

217
    $.post({
×
218
      url: Routes.global_actions_course_assignment_graders_path(
219
        this.props.course_id,
220
        this.props.assignment_id
221
      ),
222
      data: {
223
        global_actions: "unassign",
224
        current_table: origin,
225
        groupings: groups,
226
        criteria: criteria,
227
        grader_user_names: [grader_user_name],
228
      },
229
    }).then(this.fetchData);
230
  };
231

232
  toggleSkipEmptySubmissions = () => {
4✔
233
    this.setState({
×
234
      skip_empty_submissions: !this.state.skip_empty_submissions,
235
    });
236
  };
237

238
  toggleAssignGradersToCriteria = () => {
4✔
239
    const assign = !this.state.assign_graders_to_criteria;
×
240
    $.post({
×
241
      url: Routes.set_boolean_graders_options_course_assignment_path(
242
        this.props.course_id,
243
        this.props.assignment_id
244
      ),
245
      data: {
246
        attribute: {
247
          assignment_properties_attributes: {
248
            assign_graders_to_criteria: assign,
249
          },
250
        },
251
      },
252
    }).then(() => this.setState({assign_graders_to_criteria: assign}));
×
253
  };
254

255
  toggleAnonymizeGroups = () => {
4✔
256
    const assign = !this.state.anonymize_groups;
×
257
    $.post({
×
258
      url: Routes.set_boolean_graders_options_course_assignment_path(
259
        this.props.course_id,
260
        this.props.assignment_id
261
      ),
262
      data: {
263
        attribute: {
264
          assignment_properties_attributes: {anonymize_groups: assign},
265
        },
266
      },
267
    }).then(() => this.setState({anonymize_groups: assign}));
×
268
  };
269

270
  toggleHideUnassignedCriteria = () => {
4✔
271
    const assign = !this.state.hide_unassigned_criteria;
×
272
    $.post({
×
273
      url: Routes.set_boolean_graders_options_course_assignment_path(
274
        this.props.course_id,
275
        this.props.assignment_id
276
      ),
277
      data: {
278
        attribute: {
279
          assignment_properties_attributes: {
280
            hide_unassigned_criteria: assign,
281
          },
282
        },
283
      },
284
    }).then(() => this.setState({hide_unassigned_criteria: assign}));
×
285
  };
286

287
  getAssignedGraderObjects = () => {
4✔
288
    return this.state.graders.filter(grader => {
×
289
      return this.gradersTable.state.selection.includes(grader._id);
×
290
    });
291
  };
292

293
  renderHideUnassignedCriteria = () => {
4✔
294
    if (this.state.assign_graders_to_criteria) {
11!
295
      return (
×
296
        <div style={{marginBottom: "1em"}}>
297
          <label>
298
            <input
299
              type="checkbox"
300
              checked={this.state.hide_unassigned_criteria}
301
              onChange={this.toggleHideUnassignedCriteria}
302
              style={{marginRight: "5px"}}
303
            />
304
            {I18n.t("graders.hide_unassigned_criteria")}
305
          </label>
306
        </div>
307
      );
308
    }
309
  };
310

311
  onSelectTable = index => {
4✔
312
    if (index === 0) {
×
313
      this.setState({tableName: "groups_table"});
×
314
    } else {
315
      this.setState({tableName: "criteria_table"});
×
316
    }
317
  };
318

319
  toggleShowHidden = event => {
4✔
320
    let show_hidden = event.target.checked;
×
321
    this.setState({show_hidden});
×
322
  };
323

324
  toggleShowHiddenGroups = event => {
4✔
325
    let show_hidden_groups = event.target.checked;
3✔
326
    this.setState({show_hidden_groups});
3✔
327
  };
328

329
  render() {
330
    return (
11✔
331
      <div>
332
        <GradersActionBox
333
          assignAll={this.assignAll}
334
          openGraderDistributionModal={this.openGraderDistributionModal}
335
          openSectionDistributionModal={this.openSectionDistributionModal}
336
          unassignAll={this.unassignAll}
337
          showHidden={this.state.show_hidden}
338
          showHiddenGroups={this.state.show_hidden_groups}
339
          updateShowHidden={this.toggleShowHidden}
340
          updateShowHiddenGroups={this.toggleShowHiddenGroups}
341
          hiddenGradersCount={this.state.loading ? null : this.state.hidden_graders_count}
11✔
342
          hiddenGroupsCount={this.state.loading ? null : this.state.inactive_groups_count}
11✔
343
        />
344
        <div className="mapping-tables">
345
          <div className="mapping-table">
346
            <GradersTable
347
              ref={r => (this.gradersTable = r)}
22✔
348
              graders={this.state.graders}
349
              loading={this.state.loading}
350
              assign_graders_to_criteria={this.state.assign_graders_to_criteria}
351
              numCriteria={this.state.criteria.length}
352
              showHidden={this.state.show_hidden}
353
            />
354
          </div>
355
          <div className="mapping-table">
356
            <Tabs onSelect={this.onSelectTable}>
357
              <TabList>
358
                <Tab>{I18n.t("activerecord.models.group.other")}</Tab>
359
                <Tab>{I18n.t("activerecord.models.criterion.other")}</Tab>
360
              </TabList>
361

362
              <TabPanel>
363
                <div style={{marginBottom: "1em"}}>
364
                  <label>
365
                    <input
366
                      type="checkbox"
367
                      checked={!this.state.skip_empty_submissions}
368
                      onChange={this.toggleSkipEmptySubmissions}
369
                      style={{marginRight: "5px"}}
370
                    />
371
                    {I18n.t("graders.skip_empty_submissions")}
372
                  </label>
373
                  <div className="inline-help">
374
                    <p>{I18n.t("graders.skip_empty_submissions_tooltip")}</p>
375
                  </div>
376
                </div>
377
                <div style={{marginBottom: "1em"}}>
378
                  <label>
379
                    <input
380
                      type="checkbox"
381
                      checked={this.state.anonymize_groups}
382
                      onChange={this.toggleAnonymizeGroups}
383
                      style={{marginRight: "5px"}}
384
                    />
385
                    {I18n.t("graders.anonymize_groups")}
386
                  </label>
387
                </div>
388
                <GroupsTable
389
                  ref={r => (this.groupsTable = r)}
22✔
390
                  groups={this.state.groups}
391
                  loading={this.state.loading}
392
                  unassignSingle={this.unassignSingle}
393
                  showSections={this.props.showSections}
394
                  sections={this.state.sections}
395
                  numCriteria={this.state.criteria.length}
396
                  showCoverage={this.state.assign_graders_to_criteria}
397
                  showInactive={this.state.show_hidden_groups}
398
                />
399
              </TabPanel>
400
              <TabPanel>
401
                <div style={{marginBottom: "1em"}}>
402
                  <label>
403
                    <input
404
                      type="checkbox"
405
                      onChange={this.toggleAssignGradersToCriteria}
406
                      checked={this.state.assign_graders_to_criteria}
407
                      style={{marginRight: "5px"}}
408
                    />
409
                    {I18n.t("graders.assign_to_criteria")}
410
                  </label>
411
                </div>
412
                {this.renderHideUnassignedCriteria()}
413
                <CriteriaTable
414
                  display={this.state.assign_graders_to_criteria}
415
                  ref={r => (this.criteriaTable = r)}
×
416
                  criteria={this.state.criteria}
417
                  loading={this.state.loading}
418
                  unassignSingle={this.unassignSingle}
419
                  numGroups={this.state.groups.length}
420
                />
421
              </TabPanel>
422
            </Tabs>
423
          </div>
424
        </div>
425
        {this.state.isGraderDistributionModalOpen && (
11!
426
          <GraderDistributionModal
427
            isOpen={this.state.isGraderDistributionModalOpen}
428
            onRequestClose={() =>
429
              this.setState({
×
430
                isGraderDistributionModalOpen: false,
431
              })
432
            }
433
            graders={this.getAssignedGraderObjects()}
434
            onSubmit={this.assignRandomly}
435
          />
436
        )}
437
        {this.state.isSectionDistributionModalOpen && (
11!
438
          <SectionDistributionModal
439
            isOpen={this.state.isSectionDistributionModalOpen}
440
            onRequestClose={() => this.setState({isSectionDistributionModalOpen: false})}
×
441
            onSubmit={this.assignSections}
442
            graders={this.state.graders}
443
            sections={this.state.sections}
444
          />
445
        )}
446
      </div>
447
    );
448
  }
449
}
450

451
class RawGradersTable extends React.Component {
452
  constructor(props) {
453
    super(props);
4✔
454
    this.state = {
4✔
455
      filtered: [],
456
    };
457
  }
458

459
  getColumns = () => [
11✔
460
    {
461
      accessor: "hidden",
462
      id: "hidden",
463
      width: 0,
464
      className: "rt-hidden",
465
      headerClassName: "rt-hidden",
466
      resizable: false,
467
    },
468
    {
469
      show: false,
470
      accessor: "_id",
471
      id: "_id",
472
    },
473
    {
474
      Header: I18n.t("activerecord.attributes.user.user_name"),
475
      accessor: "user_name",
476
      id: "user_name",
477
      Cell: props =>
478
        props.original.hidden
×
479
          ? `${props.value} (${I18n.t("activerecord.attributes.user.hidden")})`
480
          : props.value,
481
      filterMethod: (filter, row) => {
482
        if (filter.value) {
×
483
          return `${row._original.user_name}${
×
484
            row._original.hidden ? `, ${I18n.t("activerecord.attributes.user.hidden")}` : ""
×
485
          }`.includes(filter.value);
486
        } else {
487
          return true;
×
488
        }
489
      },
490
      sortable: true,
491
      minWidth: 90,
492
    },
493
    {
494
      Header: I18n.t("activerecord.attributes.user.full_name"),
495
      Cell: row => `${row.original.first_name} ${row.original.last_name}`,
×
496
      minWidth: 170,
497
    },
498
    {
499
      Header: I18n.t("activerecord.models.group.other"),
500
      accessor: "groups",
501
      className: "number",
502
      filterable: false,
503
    },
504
    {
505
      Header: I18n.t("activerecord.models.criterion.other"),
506
      accessor: "criteria",
507
      filterable: false,
508
      Cell: ({value}) => {
509
        if (this.props.assign_graders_to_criteria) {
×
510
          return (
×
511
            <span>
512
              {value}/{this.props.numCriteria}
513
            </span>
514
          );
515
        } else {
516
          return I18n.t("all");
×
517
        }
518
      },
519
    },
520
  ];
521

522
  static getDerivedStateFromProps(props, state) {
523
    let filtered = [];
11✔
524
    for (let i = 0; i < state.filtered.length; i++) {
11✔
525
      if (state.filtered[i].id !== "hidden") {
7!
526
        filtered.push(state.filtered[i]);
×
527
      }
528
    }
529
    if (!props.showHidden) {
11!
530
      filtered.push({id: "hidden", value: false});
11✔
531
    }
532
    return {filtered};
11✔
533
  }
534

535
  onFilteredChange = filtered => {
4✔
536
    this.setState({filtered});
×
537
  };
538

539
  render() {
540
    return (
11✔
541
      <CheckboxTable
542
        ref={r => (this.checkboxTable = r)}
22✔
543
        data={this.props.graders}
544
        columns={this.getColumns()}
545
        defaultSorted={[
546
          {
547
            id: "user_name",
548
          },
549
        ]}
550
        loading={this.props.loading}
551
        filterable
552
        filtered={this.state.filtered}
553
        onFilteredChange={this.onFilteredChange}
554
        {...this.props.getCheckboxProps()}
555
      />
556
    );
557
  }
558
}
559

560
class RawGroupsTable extends React.Component {
561
  constructor(props) {
562
    super(props);
4✔
563
    this.state = {
4✔
564
      filtered: [],
565
    };
566
  }
567

568
  getColumns = () => {
4✔
569
    return [
11✔
570
      {
571
        accessor: "inactive",
572
        id: "inactive",
573
        width: 0,
574
        className: "rt-hidden",
575
        headerClassName: "rt-hidden",
576
        resizable: false,
577
      },
578
      {
579
        show: false,
580
        accessor: "_id",
581
        id: "_id",
582
      },
583
      {
584
        Header: I18n.t("activerecord.models.section", {count: 1}),
585
        accessor: "section",
586
        id: "section",
587
        show: this.props.showSections || false,
22✔
588
        minWidth: 70,
589
        Cell: ({value}) => {
590
          return this.props.sections[value] || "";
×
591
        },
592
        filterMethod: (filter, row) => {
593
          if (filter.value === "all") {
×
594
            return true;
×
595
          } else {
596
            return this.props.sections[row[filter.id]] === filter.value;
×
597
          }
598
        },
599
        Filter: selectFilter,
600
        filterOptions: Object.entries(this.props.sections).map(kv => ({
×
601
          value: kv[1],
602
          text: kv[1],
603
        })),
604
      },
605
      {
606
        Header: I18n.t("activerecord.models.group.one"),
607
        accessor: "group_name",
608
        id: "group_name",
609
        minWidth: 150,
610
      },
611
      {
612
        Header: I18n.t("activerecord.models.ta.other"),
613
        accessor: "graders",
614
        Cell: row => {
615
          return row.value.map(ta_data => (
13✔
616
            <div key={`${row.original._id}-${ta_data.grader}`}>
×
617
              {ta_data.hidden
×
618
                ? `${ta_data.grader} (${I18n.t("activerecord.attributes.user.hidden")})`
619
                : ta_data.grader}
620
              <a
621
                href="#"
622
                onClick={() =>
623
                  this.props.unassignSingle(row.original._id, ta_data.grader, "groups_table")
×
624
                }
625
                title={I18n.t("graders.actions.unassign_grader")}
626
              >
627
                <FontAwesomeIcon icon="fa-solid fa-trash" className="icon-right" />
628
              </a>
629
            </div>
630
          ));
631
        },
632
        filterable: false,
633
        minWidth: 100,
634
      },
635
      {
636
        Header: I18n.t("graders.coverage"),
637
        accessor: "criteria_coverage_count",
638
        Cell: ({value}) => (
639
          <span>
×
640
            {value || 0}/{this.props.numCriteria}
×
641
          </span>
642
        ),
643
        minWidth: 70,
644
        className: "number",
645
        filterable: false,
646
        show: this.props.showCoverage,
647
      },
648
    ];
649
  };
650

651
  static getDerivedStateFromProps(props, state) {
652
    let filtered = state.filtered.filter(group => group.id !== "inactive");
11✔
653

654
    if (!props.showInactive) {
11✔
655
      filtered.push({id: "inactive", value: false});
9✔
656
    }
657
    return {filtered};
11✔
658
  }
659

660
  onFilteredChange = filtered => {
4✔
661
    this.setState({filtered});
×
662
  };
663

664
  render() {
665
    return (
11✔
666
      <CheckboxTable
667
        ref={r => (this.checkboxTable = r)}
22✔
668
        data={this.props.groups}
669
        columns={this.getColumns()}
670
        defaultSorted={[
671
          {
672
            id: "group_name",
673
          },
674
        ]}
675
        loading={this.props.loading}
676
        filterable
677
        filtered={this.state.filtered}
678
        onFilteredChange={this.onFilteredChange}
679
        {...this.props.getCheckboxProps()}
680
      />
681
    );
682
  }
683
}
684

685
class RawCriteriaTable extends React.Component {
686
  getColumns = () => {
×
687
    return [
×
688
      {
689
        show: false,
690
        accessor: "_id",
691
        id: "_id",
692
      },
693
      {
694
        Header: I18n.t("activerecord.attributes.criterion.name"),
695
        accessor: "name",
696
        minWidth: 150,
697
      },
698
      {
699
        Header: I18n.t("activerecord.models.ta.other"),
700
        accessor: "graders",
701
        Cell: row => {
702
          return row.value.map(ta_data => (
×
703
            <div key={`${row.original._id}-${ta_data.grader}`}>
×
704
              {ta_data.hidden
×
705
                ? `${ta_data.grader} (${I18n.t("activerecord.attributes.user.hidden")})`
706
                : ta_data.grader}
707
              <a
708
                href="#"
709
                onClick={() =>
710
                  this.props.unassignSingle(row.original._id, ta_data.grader, "criteria_table")
×
711
                }
712
                title={I18n.t("graders.actions.unassign_grader")}
713
              >
714
                <FontAwesomeIcon icon="fa-solid fa-trash" className="icon-right" />
715
              </a>
716
            </div>
717
          ));
718
        },
719
        filterable: false,
720
        minWidth: 70,
721
      },
722
      {
723
        Header: I18n.t("graders.coverage"),
724
        accessor: "coverage",
725
        Cell: ({value}) => (
726
          <span>
×
727
            {value}/{this.props.numGroups}
728
          </span>
729
        ),
730
        minWidth: 70,
731
        className: "number",
732
        filterable: false,
733
      },
734
    ];
735
  };
736

737
  render() {
738
    if (this.props.display) {
×
739
      return (
×
740
        <CheckboxTable
741
          ref={r => (this.checkboxTable = r)}
×
742
          data={this.props.criteria}
743
          columns={this.getColumns()}
744
          defaultSorted={[
745
            {
746
              id: "_id",
747
            },
748
          ]}
749
          loading={this.props.loading}
750
          filterable
751
          {...this.props.getCheckboxProps()}
752
        />
753
      );
754
    } else {
755
      return null;
×
756
    }
757
  }
758
}
759

760
const GradersTable = withSelection(RawGradersTable);
1✔
761
const GroupsTable = withSelection(RawGroupsTable);
1✔
762
const CriteriaTable = withSelection(RawCriteriaTable);
1✔
763

764
class GradersActionBox extends React.Component {
765
  render = () => {
4✔
766
    let showHiddenGraderTooltip = "";
11✔
767
    let showHiddenGroupsTooltip = "";
11✔
768
    if (this.props.hiddenGradersCount !== null && this.props.hiddenGroupsCount !== null) {
11✔
769
      showHiddenGraderTooltip = `${I18n.t("graders.inactive_graders_count", {
7✔
770
        count: this.props.hiddenGradersCount,
771
      })}`;
772
      showHiddenGroupsTooltip = `${I18n.t("activerecord.attributes.grouping.inactive_groups", {
7✔
773
        count: this.props.hiddenGroupsCount,
774
      })}`;
775
    }
776

777
    return (
11✔
778
      <div className="rt-action-box">
779
        <span className={"flex-row-expand"}>
780
          <input
781
            id="show_hidden"
782
            name="show_hidden"
783
            type="checkbox"
784
            checked={this.props.showHidden}
785
            onChange={this.props.updateShowHidden}
786
            className={"hide-user-checkbox"}
787
          />
788
          <label title={showHiddenGraderTooltip} htmlFor="show_hidden">
789
            {I18n.t("tas.display_inactive")}
790
          </label>
791
        </span>
792
        <span>
793
          <input
794
            id="show_hidden_groups"
795
            name="show_hidden_groups"
796
            type="checkbox"
797
            checked={this.props.showHiddenGroups}
798
            onChange={this.props.updateShowHiddenGroups}
799
            className={"hide-user-checkbox"}
800
            data-testid={"show_hidden_groups"}
801
          />
802
          <label
803
            title={showHiddenGroupsTooltip}
804
            htmlFor="show_hidden_groups"
805
            data-testid={"show_hidden_groups_tooltip"}
806
          >
807
            {I18n.t("groups.display_inactive")}
808
          </label>
809
        </span>
810
        <button onClick={this.props.assignAll}>
811
          <FontAwesomeIcon icon="fa-solid fa-user-plus" />
812
          {I18n.t("graders.actions.assign_grader")}
813
        </button>
814
        <button onClick={this.props.openGraderDistributionModal}>
815
          <FontAwesomeIcon icon="fa-solid fa-dice" />
816
          {I18n.t("graders.actions.randomly_assign_graders")}
817
        </button>
818
        <button onClick={this.props.openSectionDistributionModal}>
819
          <FontAwesomeIcon icon="fa-solid fa-list" />
820
          {I18n.t("graders.actions.assign_by_section")}
821
        </button>
822
        <button onClick={this.props.unassignAll}>
823
          <FontAwesomeIcon icon="fa-solid fa-user-minus" />
824
          {I18n.t("graders.actions.unassign_grader")}
825
        </button>
826
      </div>
827
    );
828
  };
829
}
830

831
export function makeGradersManager(elem, props) {
NEW
832
  const root = createRoot(elem);
×
NEW
833
  root.render(<GradersManager {...props} />);
×
834
}
835
export {GradersManager};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc