mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 03:34:40 +00:00
feat: assessment overview get letter grades from Assessment Averages plugin
This commit is contained in:
@@ -14,7 +14,7 @@ const assessmentsOverviewPlugin: Plugin<{}> = {
|
|||||||
disableToggle: false,
|
disableToggle: false,
|
||||||
styles,
|
styles,
|
||||||
|
|
||||||
run: async () => {
|
run: async (api) => {
|
||||||
const menu = (await waitForElm('[data-key="assessments"] > .sub > ul', true, 100, 60)) as HTMLElement;
|
const menu = (await waitForElm('[data-key="assessments"] > .sub > ul', true, 100, 60)) as HTMLElement;
|
||||||
const gridItem = document.createElement('li');
|
const gridItem = document.createElement('li');
|
||||||
gridItem.className = 'item';
|
gridItem.className = 'item';
|
||||||
@@ -55,7 +55,7 @@ const assessmentsOverviewPlugin: Plugin<{}> = {
|
|||||||
try {
|
try {
|
||||||
const data = await getAssessmentsData();
|
const data = await getAssessmentsData();
|
||||||
const { renderGrid } = await import('./ui');
|
const { renderGrid } = await import('./ui');
|
||||||
renderGrid(container, data);
|
renderGrid(container, data, api);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to load assessments:', err);
|
console.error('Failed to load assessments:', err);
|
||||||
renderErrorState(container, err instanceof Error ? err.message : 'Unknown error');
|
renderErrorState(container, err instanceof Error ? err.message : 'Unknown error');
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
import { determineStatus, formatDate, getGradeValue } from './utils';
|
import { determineStatus, formatDate, getGradeValue } from './utils';
|
||||||
|
import { settingsState } from '@/seqta/utils/listeners/SettingsState';
|
||||||
|
|
||||||
|
function percentageToLetter(percentage: number): string {
|
||||||
|
const letterMap: Record<number, string> = {
|
||||||
|
100: 'A+', 95: 'A', 90: 'A-', 85: 'B+', 80: 'B', 75: 'B-',
|
||||||
|
70: 'C+', 65: 'C', 60: 'C-', 55: 'D+', 50: 'D', 45: 'D-',
|
||||||
|
40: 'E+', 35: 'E', 30: 'E-', 0: 'F'
|
||||||
|
};
|
||||||
|
|
||||||
|
const rounded = Math.ceil(percentage / 5) * 5;
|
||||||
|
return letterMap[rounded] || 'F';
|
||||||
|
}
|
||||||
|
|
||||||
interface FilterOptions {
|
interface FilterOptions {
|
||||||
subject: string;
|
subject: string;
|
||||||
@@ -10,7 +22,7 @@ let currentFilters: FilterOptions = {
|
|||||||
sortBy: 'due'
|
sortBy: 'due'
|
||||||
};
|
};
|
||||||
|
|
||||||
export function renderGrid(container: HTMLElement, data: any) {
|
export function renderGrid(container: HTMLElement, data: any, api?: any) {
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
container.className = '';
|
container.className = '';
|
||||||
container.id = 'grid-view-container';
|
container.id = 'grid-view-container';
|
||||||
@@ -93,10 +105,10 @@ export function renderGrid(container: HTMLElement, data: any) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderKanbanBoard(contentArea, filteredAssessments, data);
|
renderKanbanBoard(contentArea, filteredAssessments, data, api);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderKanbanBoard(container: HTMLElement, assessments: any[], data: any) {
|
function renderKanbanBoard(container: HTMLElement, assessments: any[], data: any, api?: any) {
|
||||||
// Group assessments by status
|
// Group assessments by status
|
||||||
const statusGroups = {
|
const statusGroups = {
|
||||||
'UPCOMING': [] as any[],
|
'UPCOMING': [] as any[],
|
||||||
@@ -172,7 +184,7 @@ export function renderGrid(container: HTMLElement, data: any) {
|
|||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
assessmentList.forEach(assessment => {
|
assessmentList.forEach(assessment => {
|
||||||
cardsContainer.appendChild(createKanbanCard(assessment, data.colors[assessment.code] || '#6366f1'));
|
cardsContainer.appendChild(createKanbanCard(assessment, data.colors[assessment.code] || '#6366f1', api));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +195,7 @@ export function renderGrid(container: HTMLElement, data: any) {
|
|||||||
container.appendChild(board);
|
container.appendChild(board);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createKanbanCard(assessment: any, color: string): HTMLElement {
|
function createKanbanCard(assessment: any, color: string, api?: any): HTMLElement {
|
||||||
const status = determineStatus(assessment);
|
const status = determineStatus(assessment);
|
||||||
const dueDateClass = getDueDateClass(assessment);
|
const dueDateClass = getDueDateClass(assessment);
|
||||||
|
|
||||||
@@ -201,7 +213,7 @@ export function renderGrid(container: HTMLElement, data: any) {
|
|||||||
<h3 class="assessment-title">${assessment.title}</h3>
|
<h3 class="assessment-title">${assessment.title}</h3>
|
||||||
<div class="assessment-meta">
|
<div class="assessment-meta">
|
||||||
<div class="due-date ${dueDateClass}">
|
<div class="due-date ${dueDateClass}">
|
||||||
📅 ${formatDate(assessment.due)}
|
📅 ${formatDate(assessment.due, assessment.submitted)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${assessment.results
|
${assessment.results
|
||||||
@@ -209,7 +221,15 @@ export function renderGrid(container: HTMLElement, data: any) {
|
|||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<div class="Thermoscore__Thermoscore___WFpL3" style="--fill-colour: ${color}">
|
<div class="Thermoscore__Thermoscore___WFpL3" style="--fill-colour: ${color}">
|
||||||
<div style="width: ${assessment.results.percentage}%" class="Thermoscore__fill___ojxDI">
|
<div style="width: ${assessment.results.percentage}%" class="Thermoscore__fill___ojxDI">
|
||||||
<div title="${assessment.results.percentage}%" class="Thermoscore__text___XSR_M">${assessment.results.percentage}%</div>
|
<div title="${assessment.results.percentage}%" class="Thermoscore__text___XSR_M">
|
||||||
|
${(() => {
|
||||||
|
const allSettings = settingsState.getAll();
|
||||||
|
const letterGradeSetting = allSettings['plugin.assessments-average.settings']?.lettergrade;
|
||||||
|
return letterGradeSetting
|
||||||
|
? percentageToLetter(assessment.results.percentage)
|
||||||
|
: `${assessment.results.percentage}%`;
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
export function formatDate(dateStr: string): string {
|
export function formatDate(dateStr: string, submitted?: boolean): string {
|
||||||
const d = new Date(dateStr);
|
const d = new Date(dateStr);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const diffTime = d.getTime() - now.getTime();
|
const diffTime = d.getTime() - now.getTime();
|
||||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
// If it's overdue
|
// If it's overdue but don't show overdue text for submitted assessments
|
||||||
if (diffDays < 0) {
|
if (diffDays < 0 && !submitted) {
|
||||||
const overdueDays = Math.abs(diffDays);
|
const overdueDays = Math.abs(diffDays);
|
||||||
if (overdueDays === 1) return '1 day overdue';
|
if (overdueDays === 1) return '1 day overdue';
|
||||||
return `${overdueDays} days overdue`;
|
return `${overdueDays} days overdue`;
|
||||||
@@ -42,19 +42,20 @@ export function determineStatus(item: any): string {
|
|||||||
const now = new Date();
|
const now = new Date();
|
||||||
const due = new Date(item.due);
|
const due = new Date(item.due);
|
||||||
|
|
||||||
// Check if overdue, but only if not submitted
|
// Calculate the difference in days (more precise calculation)
|
||||||
if (due.getTime() < now.getTime()) {
|
const diffTime = due.getTime() - now.getTime();
|
||||||
// If it's submitted, treat it as marks pending (upcoming) instead of overdue
|
const diffDays = diffTime / (1000 * 60 * 60 * 24);
|
||||||
|
|
||||||
|
// Check if overdue (more than 1 day past due)
|
||||||
|
if (diffDays < -1) {
|
||||||
|
// If it's submitted but still overdue, treat as DUE_SOON since it's awaiting marking
|
||||||
if (item.submitted) {
|
if (item.submitted) {
|
||||||
return 'UPCOMING';
|
return 'DUE_SOON';
|
||||||
}
|
}
|
||||||
return 'OVERDUE';
|
return 'OVERDUE';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if due soon (within 7 days)
|
// Check if due soon (today through 7 days from now)
|
||||||
const diffTime = due.getTime() - now.getTime();
|
|
||||||
const diffDays = diffTime / (1000 * 60 * 60 * 24);
|
|
||||||
|
|
||||||
if (diffDays <= 7) {
|
if (diffDays <= 7) {
|
||||||
return 'DUE_SOON';
|
return 'DUE_SOON';
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user