Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | 3x 3x 3x 14x 14x 14x 3x 4x 4x 4x 4x 1x 14x 14x 6x 6x 6x 4x 6x 14x 6x 14x 1x 4x 2x 1x 4x 5x 2x 5x 5x 1x 4x 5x 14x 14x 14x 6x 14x 67x 14x | /**
* EI-2966 — Student Dashboard personalization helpers.
*
* Pure helpers for managing per-user dashboard widget visibility +
* order via `localStorage`. No backend involved — the preferences
* are intentionally device-local so a student's choice on one device
* doesn't override another. (A future ticket can sync preferences
* server-side; this MVP ships the visible UX first.)
*
* Storage key: `student_dashboard_preferences_v1`
* Schema:
* {
* "hidden": ["widget_id", ...],
* "order": ["widget_id", ...] // canonical order if user reorders
* }
*
* All helpers are defensive: malformed localStorage payloads, parsing
* errors, and quota exceptions are caught and treated as "no
* preferences set" (defaults apply).
*/
export const STUDENT_DASHBOARD_PREFS_KEY = "student_dashboard_preferences_v1";
export const DEFAULT_WIDGET_ORDER = [
"gpa",
"achievements",
"distribution",
"calendar",
"search_teacher",
];
const _safeGet = () => {
try {
Iif (typeof window === "undefined" || !window.localStorage) return null;
return window.localStorage.getItem(STUDENT_DASHBOARD_PREFS_KEY);
} catch {
return null;
}
};
const _safeSet = (json) => {
try {
Iif (typeof window === "undefined" || !window.localStorage) return false;
window.localStorage.setItem(STUDENT_DASHBOARD_PREFS_KEY, json);
return true;
} catch {
// QuotaExceededError, SecurityError, etc. — preferences best-effort.
return false;
}
};
export function loadDashboardPreferences() {
const raw = _safeGet();
if (!raw) return { hidden: [], order: [] };
try {
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== "object") return { hidden: [], order: [] };
const hidden = Array.isArray(parsed.hidden)
? parsed.hidden.filter((s) => typeof s === "string" && s.length > 0)
: [];
const order = Array.isArray(parsed.order)
? parsed.order.filter((s) => typeof s === "string" && s.length > 0)
: [];
return { hidden, order };
} catch {
return { hidden: [], order: [] };
}
}
export function saveDashboardPreferences(prefs) {
const safe = {
hidden: Array.isArray(prefs?.hidden)
? prefs.hidden.filter((s) => typeof s === "string" && s.length > 0)
: [],
order: Array.isArray(prefs?.order)
? prefs.order.filter((s) => typeof s === "string" && s.length > 0)
: [],
};
return _safeSet(JSON.stringify(safe));
}
export function isWidgetHidden(widgetId, prefs) {
if (!widgetId || !prefs || !Array.isArray(prefs.hidden)) return false;
return prefs.hidden.includes(widgetId);
}
export function toggleWidgetVisibility(widgetId, prefs) {
const hidden = new Set(Array.isArray(prefs?.hidden) ? prefs.hidden : []);
if (hidden.has(widgetId)) {
hidden.delete(widgetId);
} else {
hidden.add(widgetId);
}
return {
hidden: Array.from(hidden),
order: Array.isArray(prefs?.order) ? [...prefs.order] : [],
};
}
/**
* Compute the effective rendered order of widgets given the user's
* personalization preferences and the canonical default order.
*
* Rules:
* - User-ordered widgets (in `prefs.order`) come first, in the
* order the user chose.
* - Widgets the user hasn't explicitly ordered come next, in
* `DEFAULT_WIDGET_ORDER` order — so new widgets shipped after
* a user set their preferences still appear.
* - Hidden widgets are filtered out entirely.
* - Unknown widget ids in `prefs.order` (e.g., stale localStorage
* after a widget was removed) are filtered out.
*/
export function resolveWidgetOrder(prefs, knownWidgetIds = DEFAULT_WIDGET_ORDER) {
const known = new Set(knownWidgetIds);
const hidden = new Set(Array.isArray(prefs?.hidden) ? prefs.hidden : []);
const userOrdered = Array.isArray(prefs?.order)
? prefs.order.filter((id) => known.has(id) && !hidden.has(id))
: [];
const seen = new Set(userOrdered);
const fallback = knownWidgetIds.filter((id) => !seen.has(id) && !hidden.has(id));
return [...userOrdered, ...fallback];
}
|