Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions assets/js/Components/Admin/CoursesPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ export default function CoursePage({
pageNum: 0,
rowsPerPage: (localStorage.getItem('rowsPerPage')) ? localStorage.getItem('rowsPerPage') : '10'
})

const headers = [
{ id: "courseName", text: t('report.header.course_name') },
{ id: "accountName", text: t('report.header.account_name') },
{ id: "lastUpdated", text: t('report.header.last_scanned') },
{ id: "errors", text: t('report.header.issues'), alignText: "center" },
{ id: "barriers", text: t('report.header.issues'), alignText: "center" },
{ id: "suggestions", text: t('report.header.suggestions'), alignText: "center" },
{ id: "contentFixed", text: t('report.header.items_fixed'), alignText: "center" },
{ id: "contentResolved", text: t('report.header.items_resolved'), alignText: "center" },
Expand Down Expand Up @@ -59,6 +58,9 @@ export default function CoursePage({
}

if (!excludeCourse) {
const scanCounts = course.report?.scanCounts || {};
const barriers = scanCounts.errors || 0; // Use scanCounts.errors for "Barriers"
const suggestions = scanCounts.suggestions || 0; // Use scanCounts.suggestions for "Suggestions"
// The Course data from the database is stored in the `course` object.
// The data for the table is converted to the `row` object.
let row = {
Expand All @@ -67,7 +69,10 @@ export default function CoursePage({
courseName: <a href={course.publicUrl} target="_blank" rel="noopener noreferrer">{course.title}</a>,
courseTitle: course.title, // Used for sorting, not displayed outside of courseName element
accountName: course.accountName,
barriers,
suggestions,
lastUpdated: course.lastUpdated,

action: <div class="flex-row gap-1">
<button key={`reportButton${course.id}`}
onClick={() => { !course.loading && handleReportClick(course) }}
Expand All @@ -92,7 +97,7 @@ export default function CoursePage({
</div>

}
tempFilteredCourses.push({...row, ...course.report})
tempFilteredCourses.push({...course.report, ...row,})
}
})

Expand Down
104 changes: 57 additions & 47 deletions assets/js/Components/Admin/ReportsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,61 +6,66 @@ import ReportsTable from '../Reports/ReportsTable'
import IssuesTable from '../Reports/IssuesTable'
import ProgressIcon from '../Icons/ProgressIcon'
import '../ReportsPage.css'
import { analyzeReport } from '../../Services/Report'

export default function ReportsPage({
t,
settings,
filters,
selectedCourse
}) {
const [groupedReports, setGroupedReports] = useState(null)
const [issues, setIssues] = useState(null)

const [reports, setReports] = useState(null)
const [issues, setIssues] = useState(null)
const ISSUE_STATE = {
UNCHANGED: 0,
SAVING: 1,
RESOLVING: 2,
SAVED: 3,
RESOLVED: 4,
ERROR: 5,
}

const getReportHistory = () => {
const api = new Api(settings)
if(selectedCourse === null) {
api.getAdminReportHistory(filters)
.then((responseStr) => responseStr.json())
.then((response) => {
if (!Array.isArray(response.data)) {
let tempReports = Object.values(response.data.reports)
for (let report of tempReports) {
report.id = report.created
}
setReports(tempReports)
setIssues(response.data.issues)
}
else {
setReports(null)
setIssues(null)
}

})
}
else {
api.getCourseReport(selectedCourse.id)
.then((responseStr) => responseStr.json())
.then((response) => {
if (response.data) {
let tempReports = Object.values(response.data.reports)
for (let report of tempReports) {
report.id = report.created
}
setReports(tempReports)
setIssues(response.data.issues)
}
else {
setReports(null)
setIssues(null)
}
})
}
const getReportHistory = () => {
const api = new Api(settings);
api.getAdminReportHistory(filters)
.then((responseStr) => responseStr.json())
.then((response) => {
if (!Array.isArray(response.data)) {

const groupedReports = {}; // Initialize groupedReports
// Iterate through each course
Object.entries(response.data.reports).forEach(([courseName, courseDates]) => {
groupedReports[courseName] = {}; // Initialize course in groupedReports

// Iterate through each date in this course
Object.entries(courseDates).forEach(([date, reportData]) => {
const analyzedReport = analyzeReport(reportData, ISSUE_STATE);

if (date.match(/^\d{4}-\d{2}-\d{2}$/) || date.match(/^\d{2}\/\d{2}\/\d{4}$/)) {
// Update groupedReports with the analyzed report
groupedReports[courseName][date] = {
...analyzedReport,
};
}
});
});

}
// Update state with analyzed reports
setGroupedReports(groupedReports);
setIssues(response.data.issues);
} else {
setGroupedReports(null);
setIssues(null);
}
})
.catch((error) => {
console.error("Error fetching reports:", error);
});
};

useEffect(() => {
if (reports === null) {
if (groupedReports === null) {
getReportHistory()
}
}, [])
Expand All @@ -70,7 +75,7 @@ export default function ReportsPage({
<div className="flex-row justify-content-center mt-3">
<h1 className="mt-0 mb-0 primary-dark">{selectedCourse?.title || t('report.header.all_courses')}</h1>
</div>
{ (reports === null) ? (
{ (groupedReports === null) ? (
<div className="mt-3 mb-3 flex-row justify-content-center">
<div className="flex-column justify-content-center me-3">
<ProgressIcon className="icon-lg udoit-suggestion spinner" />
Expand All @@ -81,7 +86,7 @@ export default function ReportsPage({
</div>
) : (
<div>
{(reports.length === 0) ?
{(groupedReports.length === 0) ?
<div className="flex-row justify-content-center mt-3">
<div>{t('report.label.no_results')}</div>
</div>
Expand All @@ -92,20 +97,25 @@ export default function ReportsPage({
<h2 className="primary-dark mt-0 mb-2">{t('report.title.barriers_remaining')}</h2>
</div>
<div id="resolutionsReport" className="graph-container">
<ResolutionsReport t={t} reports={reports}/>
<ResolutionsReport
t={t}
reports={groupedReports}
selectedCourse={selectedCourse}
/>
</div>
</div>
<div className="mt-3">
<IssuesTable
t={t}
issues={issues}
isAdmin={true}
selectedCourse={selectedCourse}
/>
</div>
<div className="mt-3">
<ReportsTable
t={t}
reports={reports}
reports={groupedReports}
isAdmin={true}
/>
</div>
Expand Down
91 changes: 91 additions & 0 deletions assets/js/Components/Reports/CourseBrowser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React from "react";

const COURSE_LIMIT = 5;

export default function CourseBrowser({
filteredCourseNames,
selectedCourses,
setSelectedCourses,
searchTerm,
setSearchTerm,
selectedCount,
t,
}) {
const handleCheckbox = (name) => {
// Prevent selecting more than COURSE_LIMIT
if (!selectedCourses[name] && selectedCount >= COURSE_LIMIT) return;
setSelectedCourses((prev) => ({
...prev,
[name]: !prev[name],
}));
};

return (
<div
style={{
padding: 0,
}}
>
{/* Search + actions */}
<div
style={{
backgroundColor: "#fff",
zIndex: 1,
padding: 10,
borderBottom: "1px solid #ccc",
}}
>
<input
type="text"
placeholder={t("report.label.search_courses")}
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
style={{ width: "85%", padding: 7 }}
/>
<small style={{ display: "block", marginTop: 5, color: "#666" }}>
{t("report.label.course_limit")}
</small>
<button
onClick={() => setSelectedCourses({})}
style={{
marginTop: 10,
padding: "5px 10px",
backgroundColor: "#f5f5f5",
border: "1px solid #ccc",
borderRadius: 4,
cursor: "pointer",
fontSize: 14,
}}
>
{t("report.button.deselect_all")}
</button>
</div>
{/* Course list */}
<div style={{ padding: 10 }}>
{filteredCourseNames.map((name) => {
const checked = !!selectedCourses[name];
const disabled =
!checked && selectedCount >= COURSE_LIMIT;
return (
<label
key={name}
style={{
display: "block",
marginBottom: 10,
color: disabled ? "#aaa" : undefined,
}}
>
<input
type="checkbox"
checked={checked}
disabled={disabled}
onChange={() => handleCheckbox(name)}
/>{" "}
{name}
</label>
);
})}
</div>
</div>
);
}
7 changes: 4 additions & 3 deletions assets/js/Components/Reports/IssuesTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ export default function IssuesTable({
t,
issues,
quickSearchTerm = null,
isAdmin
isAdmin,
selectedCourse
}) {

const headers = [
Expand All @@ -17,8 +18,8 @@ export default function IssuesTable({
{ id: "resolved", text: t('report.header.resolved'), alignText: 'center' },
]

if (isAdmin) {
headers.push({ id: "courses", text: t('report.header.courses') })
if (isAdmin && (selectedCourse == null)) {
headers.push({ id: "courses", text: t('report.header.courses'), alignText: 'center' })
}

// The "total" is always the last column of the table
Expand Down
Loading