diff --git a/db.js b/db.js
index daa9e25..934914d 100644
--- a/db.js
+++ b/db.js
@@ -244,6 +244,9 @@ const seedSetting = (key, val) => {
if (!db.prepare(`SELECT value FROM settings WHERE key = ?`).get(key))
db.prepare(`INSERT INTO settings (key, value) VALUES (?, ?)`).run(key, val);
};
+seedSetting('school_name', '');
+seedSetting('passing_pct', '80');
+seedSetting('week_start_day', 'monday');
// Seed bundled word lists on first run
if (db.prepare(`SELECT COUNT(*) AS n FROM lists`).get().n === 0) {
diff --git a/public/admin.html b/public/admin.html
index 64cbd18..d7a3ddd 100644
--- a/public/admin.html
+++ b/public/admin.html
@@ -498,11 +498,41 @@
Settings
+
+ Parent PIN
+ School
+
+
diff --git a/public/admin.js b/public/admin.js
index 8f78f94..f200c14 100644
--- a/public/admin.js
+++ b/public/admin.js
@@ -8,6 +8,7 @@ const esc = (s) =>
sessionStorage.removeItem('pin');
let parentPin = null;
+const appSettings = { schoolName: '', passingPct: 80, weekStartDay: 'monday' };
async function api(path, opts = {}) {
const headers = { 'Content-Type': 'application/json' };
@@ -57,7 +58,17 @@ $('#pin-form').addEventListener('submit', async (e) => {
unlock();
});
+async function loadAppSettings() {
+ try {
+ const s = await api('/api/admin/app-settings');
+ appSettings.schoolName = s.school_name || '';
+ appSettings.passingPct = Number(s.passing_pct) || 80;
+ appSettings.weekStartDay = s.week_start_day || 'monday';
+ } catch {}
+}
+
async function unlock() {
+ await loadAppSettings();
$('#pin-gate').hidden = true;
$('#console').hidden = false;
showPanel('kids');
@@ -95,7 +106,7 @@ function showPanel(name) {
const loaders = {
kids: loadKids, courses: loadCourses, planner: loadPlannerPanel,
grading: loadGrading, gradebook: loadGradebookPanel, spelling: loadSpelling, decks: loadDecks,
- settings: () => {},
+ settings: loadSettings,
};
if (loaders[name]) loaders[name]();
if (name === 'course-detail') openCourseDetail(currentCourseId);
@@ -1142,11 +1153,12 @@ $('#ie-ws-parse').addEventListener('click', () => {
// Planner
// ============================================================
-function mondayOf(d) {
+function weekStartOf(d) {
const date = new Date(d);
const day = date.getDay();
- const diff = day === 0 ? -6 : 1 - day;
- date.setDate(date.getDate() + diff);
+ const target = appSettings.weekStartDay === 'sunday' ? 0 : 1;
+ const diff = (day - target + 7) % 7;
+ date.setDate(date.getDate() - diff);
return date.toISOString().slice(0, 10);
}
function addDays(dateStr, n) {
@@ -1155,17 +1167,17 @@ function addDays(dateStr, n) {
return d.toISOString().slice(0, 10);
}
-let plannerWeekStart = mondayOf(new Date());
+let plannerWeekStart = null; // initialized lazily after settings load
let plannerQueue = [];
let plannerStudentId = null;
let plannerCoursesCache = [];
let plannerMode = 'week'; // 'week' | 'month'
let plannerMonthRef = null; // YYYY-MM-01 of the month being viewed
-function firstMondayOfMonth(dateStr) {
+function firstWeekStartOfMonth(dateStr) {
const d = new Date(dateStr + 'T00:00:00');
d.setDate(1);
- return mondayOf(d);
+ return weekStartOf(d);
}
function addMonths(dateStr, n) {
const d = new Date(dateStr + 'T00:00:00');
@@ -1177,6 +1189,7 @@ function monthLabel(dateStr) {
}
async function loadPlannerPanel() {
+ if (!plannerWeekStart) plannerWeekStart = weekStartOf(new Date());
const [students, courses] = await Promise.all([api('/api/students'), api('/api/admin/courses')]);
if (students.length === 0) {
$('#planner-grid').innerHTML = `
Add a kid first.
`;
@@ -1200,7 +1213,7 @@ $('#auto-schedule-btn').addEventListener('click', async () => {
body: { studentId: plannerStudentId, courseId, startDate, itemsPerDay },
});
$('#auto-schedule-msg').textContent = `Scheduled ${scheduled} item${scheduled === 1 ? '' : 's'} starting ${startDate}.`;
- plannerWeekStart = mondayOf(startDate + 'T12:00:00');
+ plannerWeekStart = weekStartOf(startDate + 'T12:00:00');
renderPlanner();
});
@@ -1222,7 +1235,7 @@ $('#planner-view-month').addEventListener('click', () => {
// derive the month from the current week view date
const ref = new Date(plannerWeekStart + 'T00:00:00');
plannerMonthRef = `${ref.getFullYear()}-${String(ref.getMonth() + 1).padStart(2, '0')}-01`;
- plannerWeekStart = firstMondayOfMonth(plannerMonthRef);
+ plannerWeekStart = firstWeekStartOfMonth(plannerMonthRef);
$('#planner-view-month').classList.add('active-view');
$('#planner-view-week').classList.remove('active-view');
$('#planner-copy').hidden = true;
@@ -1231,7 +1244,7 @@ $('#planner-view-month').addEventListener('click', () => {
$('#planner-prev').addEventListener('click', () => {
if (plannerMode === 'month') {
plannerMonthRef = addMonths(plannerMonthRef, -1);
- plannerWeekStart = firstMondayOfMonth(plannerMonthRef);
+ plannerWeekStart = firstWeekStartOfMonth(plannerMonthRef);
} else {
plannerWeekStart = addDays(plannerWeekStart, -7);
}
@@ -1240,7 +1253,7 @@ $('#planner-prev').addEventListener('click', () => {
$('#planner-next').addEventListener('click', () => {
if (plannerMode === 'month') {
plannerMonthRef = addMonths(plannerMonthRef, 1);
- plannerWeekStart = firstMondayOfMonth(plannerMonthRef);
+ plannerWeekStart = firstWeekStartOfMonth(plannerMonthRef);
} else {
plannerWeekStart = addDays(plannerWeekStart, 7);
}
@@ -1259,7 +1272,9 @@ $('#planner-print').addEventListener('click', () => {
else printWeekReport(plannerStudentId, plannerWeekStart);
});
-const DAY_NAMES = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];
+const DAY_NAMES_MON = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];
+const DAY_NAMES_SUN = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday'];
+const DAY_NAMES = () => appSettings.weekStartDay === 'sunday' ? DAY_NAMES_SUN : DAY_NAMES_MON;
function attachPlannerEvents() {
let draggingId = null;
@@ -1332,7 +1347,7 @@ async function renderPlanner() {
const weeks = [];
let cursor = plannerWeekStart;
for (let w = 0; w < 6; w++) {
- const weekDays = DAY_NAMES.map((name, i) => ({ name, date: addDays(cursor, i) }));
+ const weekDays = DAY_NAMES().map((name, i) => ({ name, date: addDays(cursor, i) }));
const fridayMonth = new Date(weekDays[4].date + 'T00:00:00').getMonth();
const mondayMonth = new Date(weekDays[0].date + 'T00:00:00').getMonth();
if (w > 0 && mondayMonth !== month && fridayMonth !== month) break;
@@ -1349,7 +1364,7 @@ async function renderPlanner() {
$('#planner-week-label').textContent = monthLabel(plannerMonthRef);
$('#planner-grid').className = 'planner-grid planner-month';
$('#planner-grid').innerHTML =
- `` +
+ `` +
weeks.map((week) =>
`${week.map((day) => renderDayCell(day, tasks, true, spellingTests)).join('')}
`
).join('');
@@ -1360,7 +1375,7 @@ async function renderPlanner() {
api(`/api/schedule-week/${plannerStudentId}?start=${plannerWeekStart}`),
api(`/api/admin/students/${plannerStudentId}/tests-range?from=${plannerWeekStart}&to=${end}`),
]);
- const days = DAY_NAMES.map((name, i) => ({ name, date: addDays(plannerWeekStart, i) }));
+ const days = DAY_NAMES().map((name, i) => ({ name, date: addDays(plannerWeekStart, i) }));
$('#planner-grid').className = 'planner-grid';
$('#planner-grid').innerHTML = days.map((day) => renderDayCell(day, tasks, false, spellingTests)).join('');
}
@@ -1482,7 +1497,8 @@ async function printWeekReport(studentId, start) {
.map((t) => `| ${esc(t.list)} | ${t.score}/${t.total} |
`)
.join('');
const win = window.open('', '_blank');
- win.document.write(`Weekly Plan — ${esc(r.student)}
+ const schoolPrefix = appSettings.schoolName ? `${esc(appSettings.schoolName)} — ` : '';
+ win.document.write(`${schoolPrefix}Weekly Plan — ${esc(r.student)}
- Weekly Plan — ${esc(r.student)}
+ ${schoolPrefix}Weekly Plan — ${esc(r.student)}
Week of ${r.start}
Schedule
${dayBlocks}
@@ -1519,7 +1535,7 @@ async function printMonthReport(studentId, monthMonday, monthRef) {
const weeks = [];
let cursor = monthMonday;
for (let w = 0; w < 6; w++) {
- const weekDays = DAY_NAMES.map((name, i) => ({ name, date: addDays(cursor, i) }));
+ const weekDays = DAY_NAMES().map((name, i) => ({ name, date: addDays(cursor, i) }));
const fridayMonth = new Date(weekDays[4].date + 'T00:00:00').getMonth();
const mondayMonth = new Date(weekDays[0].date + 'T00:00:00').getMonth();
if (w > 0 && mondayMonth !== month && fridayMonth !== month) break;
@@ -1531,7 +1547,7 @@ async function printMonthReport(studentId, monthMonday, monthRef) {
const student = (await api('/api/students')).find((s) => s.id === studentId);
const { tasks } = await api(`/api/schedule-range/${studentId}?start=${rangeStart}&end=${rangeEnd}`);
- const DAY_SHORT = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'];
+ const DAY_SHORT = DAY_NAMES().map((d) => d.slice(0, 3));
const calRows = weeks.map((week) =>
`${week.map((day) => {
const dayTasks = tasks.filter((t) => t.date === day.date);
@@ -1555,7 +1571,8 @@ async function printMonthReport(studentId, monthMonday, monthRef) {
}).join('');
const win = window.open('', '_blank');
- win.document.write(`${monthLabel(monthRef)} — ${student?.name || ''}
+ const schoolPfx = appSettings.schoolName ? `${esc(appSettings.schoolName)} — ` : '';
+ win.document.write(`${schoolPfx}${monthLabel(monthRef)} — ${student?.name || ''}
- ${monthLabel(monthRef)} — ${esc(student?.name || '')}
+ ${schoolPfx}${monthLabel(monthRef)} — ${esc(student?.name || '')}
${DAY_SHORT.map((d) => `| ${d} | `).join('')}
${calRows}
${detailBlocks ? `Assignment Detail
${detailBlocks}` : ''}