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

MarkUsProject / Markus / 21553107611

01 Feb 2026 12:04AM UTC coverage: 91.678%. First build
21553107611

Pull #7808

github

web-flow
Merge b6c301d49 into ab5289caa
Pull Request #7808: TICKET-578: Remove instructor from course

924 of 1817 branches covered (50.85%)

Branch coverage included in aggregate %.

78 of 79 new or added lines in 7 files covered. (98.73%)

44495 of 47725 relevant lines covered (93.23%)

123.65 hits per line

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

87.18
/app/javascript/Components/instructor_table.jsx
1
import React from "react";
2
import {createRoot} from "react-dom/client";
3
import PropTypes from "prop-types";
4

5
import Table from "./table/table";
6
import {createColumnHelper} from "@tanstack/react-table";
7
import {faPencil, faTrashCan} from "@fortawesome/free-solid-svg-icons";
8
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
9

10
class InstructorTable extends React.Component {
11
  constructor() {
12
    super();
8✔
13
    this.state = {
7✔
14
      data: [],
15
      loading: true,
16
    };
17
    this.fetchData = this.fetchData.bind(this);
7✔
18

19
    const columnHelper = createColumnHelper();
7✔
20
    this.columns = [
7✔
21
      columnHelper.accessor("user_name", {
22
        header: () => I18n.t("activerecord.attributes.user.user_name"),
26✔
23
      }),
24
      columnHelper.accessor("first_name", {
25
        header: () => I18n.t("activerecord.attributes.user.first_name"),
26✔
26
      }),
27
      columnHelper.accessor("last_name", {
28
        header: () => I18n.t("activerecord.attributes.user.last_name"),
26✔
29
      }),
30
      columnHelper.accessor("email", {
31
        header: () => I18n.t("activerecord.attributes.user.email"),
26✔
32
      }),
33
      columnHelper.accessor("hidden", {
34
        header: () => I18n.t("roles.active") + "?",
26✔
35
        filterFn: "equalsString",
36
        meta: {
37
          filterVariant: "select",
38
        },
39
      }),
40
      columnHelper.accessor("id", {
41
        id: "id",
42
        enableSorting: false,
43
        enableColumnFilter: false,
44
        header: () => I18n.t("actions"),
26✔
45
        cell: props => (
46
          <span>
12✔
47
            <a
48
              href={Routes.edit_course_instructor_path(this.props.course_id, props.getValue())}
49
              aria-label={I18n.t("edit")}
50
              title={I18n.t("edit")}
51
            >
52
              <FontAwesomeIcon icon={faPencil} />
53
            </a>
54
            {this.props.is_admin && (
16✔
55
              <>
56
                &nbsp;|&nbsp;
57
                <a
58
                  href="#"
59
                  onClick={() => this.removeInstructor(props.getValue())}
1✔
60
                  aria-label={I18n.t("remove")}
61
                  title={I18n.t("remove")}
62
                >
63
                  <FontAwesomeIcon icon={faTrashCan} />
64
                </a>
65
              </>
66
            )}
67
          </span>
68
        ),
69
      }),
70
    ];
71
  }
72

73
  componentDidMount() {
74
    this.setState({loading: true});
7✔
75
    this.fetchData().then(data => this.setState({data: this.processData(data), loading: false}));
7✔
76
  }
77

78
  removeInstructor = instructor_id => {
7✔
79
    fetch(Routes.course_instructor_path(this.props.course_id, instructor_id), {
1✔
80
      method: "DELETE",
81
      headers: {
82
        "Content-Type": "application/json",
83
        "X-CSRF-Token": document.querySelector('[name="csrf-token"]').content,
84
      },
85
    })
86
      .then(response => {
87
        if (response.ok) {
1!
88
          this.fetchData().then(data =>
1✔
89
            this.setState({data: this.processData(data), loading: false})
1✔
90
          );
91
        }
92
      })
93
      .catch(error => {
NEW
94
        console.error("Error removing instructor:", error);
×
95
      });
96
  };
97

98
  fetchData() {
99
    return fetch(Routes.course_instructors_path(this.props.course_id), {
8✔
100
      headers: {
101
        Accept: "application/json",
102
      },
103
    })
104
      .then(response => {
105
        if (response.ok) {
7!
106
          return response.json();
7✔
107
        }
108
      })
109
      .then(json => json.data);
7✔
110
  }
111

112
  processData(data) {
113
    data.forEach(row => (row.hidden = I18n.t(row.hidden ? "roles.inactive" : "roles.active")));
7✔
114
    return data;
7✔
115
  }
116

117
  render() {
118
    return (
20✔
119
      <Table
120
        data={this.state.data}
121
        columns={this.columns}
122
        noDataText={I18n.t("instructors.empty_table")}
123
        loading={this.state.loading}
124
      />
125
    );
126
  }
127
}
128

129
InstructorTable.propTypes = {
1✔
130
  course_id: PropTypes.number,
131
  is_admin: PropTypes.bool,
132
};
133

134
function makeInstructorTable(elem, props) {
135
  const root = createRoot(elem);
×
136
  root.render(<InstructorTable {...props} />);
×
137
}
138

139
export {makeInstructorTable, InstructorTable};
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