mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-08-28 17:21:05 +00:00
fix(upcoming assessments): max assessments option
This commit is contained in:
@@ -289,6 +289,69 @@
|
|||||||
{@render Setting(option)}
|
{@render Setting(option)}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
|
<div class="border-none">
|
||||||
|
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
||||||
|
<div class="flex justify-between items-center px-4 py-3">
|
||||||
|
<div class="pr-4">
|
||||||
|
<h2 class="text-sm font-bold">Home Page Assessments</h2>
|
||||||
|
<p class="text-xs">Limit upcoming assessments shown on the home page by subject</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center px-4 py-3 pl-6 border-t border-zinc-100 dark:border-zinc-700/50">
|
||||||
|
<div class="pr-4">
|
||||||
|
<h2 class="text-sm font-bold">Include Past Assessments</h2>
|
||||||
|
<p class="text-xs">Show past-due assessments from the upcoming list, matching the Assessments page</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Switch
|
||||||
|
state={$settingsState.homeUpcomingIncludePast ?? true}
|
||||||
|
onChange={(isOn: boolean) => (settingsState.homeUpcomingIncludePast = isOn)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center px-4 py-3 pl-6 border-t border-zinc-100 dark:border-zinc-700/50">
|
||||||
|
<div class="pr-4">
|
||||||
|
<h2 class="text-sm font-bold">Maximum Subjects</h2>
|
||||||
|
<p class="text-xs">Number of subjects to include, ordered by soonest due date</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Select
|
||||||
|
value={String($settingsState.homeUpcomingSubjectsMax ?? 5)}
|
||||||
|
onChange={(value: string) => (settingsState.homeUpcomingSubjectsMax = Number(value))}
|
||||||
|
options={[
|
||||||
|
{ value: "0", label: "All" },
|
||||||
|
{ value: "3", label: "3" },
|
||||||
|
{ value: "5", label: "5" },
|
||||||
|
{ value: "7", label: "7" },
|
||||||
|
{ value: "10", label: "10" },
|
||||||
|
{ value: "15", label: "15" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center px-4 py-3 pl-6 border-t border-zinc-100 dark:border-zinc-700/50">
|
||||||
|
<div class="pr-4">
|
||||||
|
<h2 class="text-sm font-bold">Maximum Assessments per Subject</h2>
|
||||||
|
<p class="text-xs">Assessments shown for each included subject</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Select
|
||||||
|
value={String($settingsState.homeUpcomingAssessmentsPerSubjectMax ?? 0)}
|
||||||
|
onChange={(value: string) => (settingsState.homeUpcomingAssessmentsPerSubjectMax = Number(value))}
|
||||||
|
options={[
|
||||||
|
{ value: "0", label: "All" },
|
||||||
|
{ value: "1", label: "1" },
|
||||||
|
{ value: "2", label: "2" },
|
||||||
|
{ value: "3", label: "3" },
|
||||||
|
{ value: "5", label: "5" },
|
||||||
|
{ value: "10", label: "10" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="border-none">
|
<div class="border-none">
|
||||||
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
<div class="p-1 my-1 from-white to-zinc-100 bg-gradient-to-br rounded-xl border shadow-sm border-zinc-200/50 dark:border-zinc-700/40 dark:to-zinc-900/50 dark:from-zinc-900/40">
|
||||||
<div class="flex justify-between items-center px-4 py-3">
|
<div class="flex justify-between items-center px-4 py-3">
|
||||||
|
|||||||
@@ -154,6 +154,55 @@ export async function loadHomePage() {
|
|||||||
return cleanup;
|
return cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let upcomingRefreshTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
|
/** Re-render the home page upcoming assessments block when related settings change. */
|
||||||
|
export function refreshHomeUpcomingAssessments() {
|
||||||
|
if (!document.getElementById("upcoming-items")) return;
|
||||||
|
|
||||||
|
if (upcomingRefreshTimeout) clearTimeout(upcomingRefreshTimeout);
|
||||||
|
upcomingRefreshTimeout = setTimeout(() => {
|
||||||
|
upcomingRefreshTimeout = null;
|
||||||
|
void renderHomeUpcomingAssessments();
|
||||||
|
}, 150);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderHomeUpcomingAssessments() {
|
||||||
|
const upcomingItems = document.getElementById("upcoming-items");
|
||||||
|
if (!upcomingItems) return;
|
||||||
|
|
||||||
|
upcomingItems.classList.add("loading");
|
||||||
|
upcomingItems.innerHTML = "";
|
||||||
|
const filterContainer = document.getElementById("upcoming-filters");
|
||||||
|
if (filterContainer) filterContainer.innerHTML = "";
|
||||||
|
|
||||||
|
const [assessments, classes] = await Promise.all([
|
||||||
|
GetUpcomingAssessments(),
|
||||||
|
GetActiveClasses(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const activeSubjects = activeSubjectsFromLearnPayload(classes);
|
||||||
|
const currentAssessments = filterAssessmentsForActiveSubjects(
|
||||||
|
assessments,
|
||||||
|
activeSubjects,
|
||||||
|
).sort(comparedate);
|
||||||
|
|
||||||
|
await CreateUpcomingSection(currentAssessments, activeSubjects);
|
||||||
|
upcomingItems.classList.remove("loading");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerHomeUpcomingSettingsListeners() {
|
||||||
|
const keys = [
|
||||||
|
"homeUpcomingSubjectsMax",
|
||||||
|
"homeUpcomingAssessmentsPerSubjectMax",
|
||||||
|
"homeUpcomingIncludePast",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
for (const key of keys) {
|
||||||
|
settingsState.register(key, () => refreshHomeUpcomingAssessments());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function GetUpcomingAssessments() {
|
async function GetUpcomingAssessments() {
|
||||||
try {
|
try {
|
||||||
return fetch(`${location.origin}/seqta/student/assessment/list/upcoming?`, {
|
return fetch(`${location.origin}/seqta/student/assessment/list/upcoming?`, {
|
||||||
@@ -285,7 +334,13 @@ function debounce<T extends (...args: any[]) => any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function comparedate(obj1: any, obj2: any) {
|
function comparedate(obj1: any, obj2: any) {
|
||||||
return obj1.date < obj2.date ? -1 : obj1.date > obj2.date ? 1 : 0;
|
const d1 = new Date(obj1.due || obj1.date || 0).getTime();
|
||||||
|
const d2 = new Date(obj2.due || obj2.date || 0).getTime();
|
||||||
|
return d1 - d2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startOfDay(date: Date): Date {
|
||||||
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||||
}
|
}
|
||||||
function processNotices(response: any, labelArray: string[]) {
|
function processNotices(response: any, labelArray: string[]) {
|
||||||
const NoticeContainer = document.getElementById("notice-container");
|
const NoticeContainer = document.getElementById("notice-container");
|
||||||
@@ -894,20 +949,73 @@ function CheckUnmarkedAttendance(lessonattendance: any) {
|
|||||||
return lessonattendance ? lessonattendance.label : " ";
|
return lessonattendance ? lessonattendance.label : " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyHomeUpcomingLimits(assessments: any[], activeSubjects: any[]) {
|
||||||
|
const maxSubjects = settingsState.homeUpcomingSubjectsMax ?? 5;
|
||||||
|
const maxPerSubject = settingsState.homeUpcomingAssessmentsPerSubjectMax ?? 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < assessments.length; i++) {
|
||||||
|
const subject = activeSubjectForAssessment(assessments[i], activeSubjects);
|
||||||
|
assessments[i].filterCode = subject?.code ?? assessments[i].code;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bySubject = new Map<string, any[]>();
|
||||||
|
const subjectOrder: string[] = [];
|
||||||
|
|
||||||
|
for (const assessment of assessments) {
|
||||||
|
const code = assessment.filterCode;
|
||||||
|
if (!bySubject.has(code)) {
|
||||||
|
bySubject.set(code, []);
|
||||||
|
subjectOrder.push(code);
|
||||||
|
}
|
||||||
|
bySubject.get(code)!.push(assessment);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const items of bySubject.values()) {
|
||||||
|
items.sort(
|
||||||
|
(a, b) => new Date(a.due).getTime() - new Date(b.due).getTime(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
subjectOrder.sort(
|
||||||
|
(a, b) =>
|
||||||
|
new Date(bySubject.get(a)![0].due).getTime() -
|
||||||
|
new Date(bySubject.get(b)![0].due).getTime(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const subjectLimit =
|
||||||
|
maxSubjects > 0 ? maxSubjects : subjectOrder.length;
|
||||||
|
const allowedSubjects = subjectOrder.slice(0, subjectLimit);
|
||||||
|
|
||||||
|
const limited: any[] = [];
|
||||||
|
for (const code of allowedSubjects) {
|
||||||
|
const items = bySubject.get(code)!;
|
||||||
|
const perSubjectLimit = maxPerSubject > 0 ? maxPerSubject : items.length;
|
||||||
|
limited.push(...items.slice(0, perSubjectLimit));
|
||||||
|
}
|
||||||
|
|
||||||
|
limited.sort(
|
||||||
|
(a, b) => new Date(a.due).getTime() - new Date(b.due).getTime(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assessments.splice(0, assessments.length, ...limited);
|
||||||
|
}
|
||||||
|
|
||||||
async function CreateUpcomingSection(assessments: any, activeSubjects: any) {
|
async function CreateUpcomingSection(assessments: any, activeSubjects: any) {
|
||||||
const upcomingitemcontainer = document.querySelector("#upcoming-items");
|
const upcomingitemcontainer = document.querySelector("#upcoming-items");
|
||||||
const overdueDates = [];
|
|
||||||
const upcomingDates = {};
|
const upcomingDates = {};
|
||||||
const Today = new Date();
|
const Today = new Date();
|
||||||
|
|
||||||
for (let i = 0; i < assessments.length; i++) {
|
if (!(settingsState.homeUpcomingIncludePast ?? true)) {
|
||||||
|
const todayStart = startOfDay(Today);
|
||||||
|
for (let i = assessments.length - 1; i >= 0; i--) {
|
||||||
const assessmentdue = new Date(assessments[i].due);
|
const assessmentdue = new Date(assessments[i].due);
|
||||||
if (assessmentdue < Today && !CheckSpecialDay(Today, assessmentdue)) {
|
if (assessmentdue < todayStart) {
|
||||||
overdueDates.push(assessments[i]);
|
|
||||||
assessments.splice(i, 1);
|
assessments.splice(i, 1);
|
||||||
i--;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyHomeUpcomingLimits(assessments, activeSubjects);
|
||||||
|
|
||||||
const colours = await GetLessonColours();
|
const colours = await GetLessonColours();
|
||||||
|
|
||||||
@@ -945,14 +1053,6 @@ async function CreateUpcomingSection(assessments: any, activeSubjects: any) {
|
|||||||
|
|
||||||
CreateFilters(filterSubjects);
|
CreateFilters(filterSubjects);
|
||||||
|
|
||||||
for (let i = 0; i < assessments.length; i++) {
|
|
||||||
const subject = activeSubjectForAssessment(
|
|
||||||
assessments[i],
|
|
||||||
activeSubjects,
|
|
||||||
);
|
|
||||||
assessments[i].filterCode = subject?.code ?? assessments[i].code;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < assessments.length; i++) {
|
for (let i = 0; i < assessments.length; i++) {
|
||||||
const element: any = assessments[i];
|
const element: any = assessments[i];
|
||||||
if (!upcomingDates[element.due as keyof typeof upcomingDates]) {
|
if (!upcomingDates[element.due as keyof typeof upcomingDates]) {
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ export function getDefaultSettingsState(): SettingsState {
|
|||||||
animations: !isLowEndDevice,
|
animations: !isLowEndDevice,
|
||||||
assessmentsAverage: false,
|
assessmentsAverage: false,
|
||||||
defaultPage: "home",
|
defaultPage: "home",
|
||||||
|
homeUpcomingSubjectsMax: 5,
|
||||||
|
homeUpcomingAssessmentsPerSubjectMax: 0,
|
||||||
|
homeUpcomingIncludePast: true,
|
||||||
shortcuts: [
|
shortcuts: [
|
||||||
{ name: "Outlook", enabled: true },
|
{ name: "Outlook", enabled: true },
|
||||||
{ name: "Office", enabled: true },
|
{ name: "Office", enabled: true },
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ const OPTIONAL_UNSET_MEANS_DEFAULT_KEYS = [
|
|||||||
"verboseLogging",
|
"verboseLogging",
|
||||||
"hideSensitiveContent",
|
"hideSensitiveContent",
|
||||||
"mockNotices",
|
"mockNotices",
|
||||||
|
"homeUpcomingAssessmentsPerSubjectMax",
|
||||||
|
"homeUpcomingIncludePast",
|
||||||
"devGhReleaseVersionOverride",
|
"devGhReleaseVersionOverride",
|
||||||
"lastSeenNightlyPublishedAt",
|
"lastSeenNightlyPublishedAt",
|
||||||
"originalDarkMode",
|
"originalDarkMode",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { applySelectedFont } from "@/seqta/ui/fonts/Manager";
|
|||||||
// Shortcuts rendering
|
// Shortcuts rendering
|
||||||
import { renderShortcuts } from "@/seqta/utils/Render/renderShortcuts";
|
import { renderShortcuts } from "@/seqta/utils/Render/renderShortcuts";
|
||||||
import { FilterUpcomingAssessments } from "@/seqta/utils/FilterUpcomingAssessments";
|
import { FilterUpcomingAssessments } from "@/seqta/utils/FilterUpcomingAssessments";
|
||||||
|
import { registerHomeUpcomingSettingsListeners } from "@/seqta/utils/Loaders/LoadHomePage";
|
||||||
import { applyMenuItemVisibility } from "@/seqta/utils/menuItemVisibility";
|
import { applyMenuItemVisibility } from "@/seqta/utils/menuItemVisibility";
|
||||||
import { ChangeMenuItemPositions } from "@/seqta/utils/Openers/OpenMenuOptions";
|
import { ChangeMenuItemPositions } from "@/seqta/utils/Openers/OpenMenuOptions";
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ export class StorageChangeHandler {
|
|||||||
"subjectfilters",
|
"subjectfilters",
|
||||||
FilterUpcomingAssessments.bind(this),
|
FilterUpcomingAssessments.bind(this),
|
||||||
);
|
);
|
||||||
|
registerHomeUpcomingSettingsListeners();
|
||||||
settingsState.register(
|
settingsState.register(
|
||||||
"iconOnlySidebar",
|
"iconOnlySidebar",
|
||||||
this.handleIconOnlySidebarChange.bind(this),
|
this.handleIconOnlySidebarChange.bind(this),
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ export interface SettingsState {
|
|||||||
timeFormat?: string;
|
timeFormat?: string;
|
||||||
animations: boolean;
|
animations: boolean;
|
||||||
defaultPage: string;
|
defaultPage: string;
|
||||||
|
/** Max subjects with upcoming assessments on the home page; 0 = no limit. */
|
||||||
|
homeUpcomingSubjectsMax?: number;
|
||||||
|
/** Max assessments shown per subject on the home page; 0 = no limit. */
|
||||||
|
homeUpcomingAssessmentsPerSubjectMax?: number;
|
||||||
|
/** When true, show past-due assessments from the upcoming list on the home page. */
|
||||||
|
homeUpcomingIncludePast?: boolean;
|
||||||
devMode?: boolean;
|
devMode?: boolean;
|
||||||
/** Dev-only: emit verboseDebug / verboseInfo / verboseLog output. */
|
/** Dev-only: emit verboseDebug / verboseInfo / verboseLog output. */
|
||||||
verboseLogging?: boolean;
|
verboseLogging?: boolean;
|
||||||
|
|||||||
Reference in New Issue
Block a user