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

MarkUsProject / Markus / 21598967077

02 Feb 2026 04:45PM UTC coverage: 91.679%. First build
21598967077

Pull #7808

github

web-flow
Merge 87c21776d into bc878d51e
Pull Request #7808: TICKET-578: Remove instructor from course

926 of 1819 branches covered (50.91%)

Branch coverage included in aggregate %.

80 of 81 new or added lines in 7 files covered. (98.77%)

44499 of 47729 relevant lines covered (93.23%)

123.34 hits per line

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

88.37
/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();
9✔
13
    this.state = {
8✔
14
      data: [],
15
      loading: true,
16
    };
17
    this.fetchData = this.fetchData.bind(this);
8✔
18

19
    const columnHelper = createColumnHelper();
8✔
20
    this.columns = [
8✔
21
      columnHelper.accessor("user_name", {
22
        header: () => I18n.t("activerecord.attributes.user.user_name"),
30✔
23
      }),
24
      columnHelper.accessor("first_name", {
25
        header: () => I18n.t("activerecord.attributes.user.first_name"),
30✔
26
      }),
27
      columnHelper.accessor("last_name", {
28
        header: () => I18n.t("activerecord.attributes.user.last_name"),
30✔
29
      }),
30
      columnHelper.accessor("email", {
31
        header: () => I18n.t("activerecord.attributes.user.email"),
30✔
32
      }),
33
      columnHelper.accessor("hidden", {
34
        header: () => I18n.t("roles.active") + "?",
30✔
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"),
30✔
45
        cell: props => (
46
          <span>
14✔
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 && (
20✔
55
              <>
56
                &nbsp;|&nbsp;
57
                <a
58
                  href="#"
59
                  onClick={() => this.removeInstructor(props.getValue())}
2✔
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});
8✔
75
    this.fetchData().then(data => this.setState({data: this.processData(data), loading: false}));
8✔
76
  }
77

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

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

115
  processData(data) {
116
    data.forEach(row => (row.hidden = I18n.t(row.hidden ? "roles.inactive" : "roles.active")));
8✔
117
    return data;
8✔
118
  }
119

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

132
InstructorTable.propTypes = {
1✔
133
  course_id: PropTypes.number,
134
  is_admin: PropTypes.bool,
135
};
136

137
function makeInstructorTable(elem, props) {
138
  const root = createRoot(elem);
×
139
  root.render(<InstructorTable {...props} />);
×
140
}
141

142
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