|
|
@@ -247,7 +247,7 @@ module.exports = {
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
configured: pco.configured, serviceTypeSet: Boolean(serviceTypeId),
|
|
|
|
configured: pco.configured, serviceTypeSet: Boolean(serviceTypeId),
|
|
|
|
reachable: false, lastError: '', lastFetched: 0, columns,
|
|
|
|
reachable: false, lastError: '', lastFetched: 0, columns,
|
|
|
|
plan: null,
|
|
|
|
plan: settings.lastPlan || null, // the last synced plan, so hand placements after a restart stamp right
|
|
|
|
slots: effectiveSlots().map((label, index) => ({ index, label, person: null })),
|
|
|
|
slots: effectiveSlots().map((label, index) => ({ index, label, person: null })),
|
|
|
|
library: [], choir: choirOn,
|
|
|
|
library: [], choir: choirOn,
|
|
|
|
};
|
|
|
|
};
|
|
|
@@ -493,7 +493,8 @@ module.exports = {
|
|
|
|
await syncPhotos(); // import the photo source (Drive folder or local path) on every sync
|
|
|
|
await syncPhotos(); // import the photo source (Drive folder or local path) on every sync
|
|
|
|
if (pco.configured && serviceTypeId) {
|
|
|
|
if (pco.configured && serviceTypeId) {
|
|
|
|
const plan = pickPlan(await listPlans(serviceTypeId));
|
|
|
|
const plan = pickPlan(await listPlans(serviceTypeId));
|
|
|
|
latest.plan = plan ? { title: plan.title, dates: plan.dates } : null;
|
|
|
|
latest.plan = plan ? { id: String(plan.id), title: plan.title, dates: plan.dates } : null;
|
|
|
|
|
|
|
|
if (plan) { settings.lastPlan = latest.plan; saveSettings(); }
|
|
|
|
if (!plan) { latest.reachable = true; latest.lastError = 'No plan found for that service type.'; }
|
|
|
|
if (!plan) { latest.reachable = true; latest.lastError = 'No plan found for that service type.'; }
|
|
|
|
else {
|
|
|
|
else {
|
|
|
|
|
|
|
|
|
|
|
@@ -521,12 +522,15 @@ module.exports = {
|
|
|
|
|
|
|
|
|
|
|
|
const libEntries = Object.entries(library).map(([id, v]) => ({ id, name: v.name, file: v.file }));
|
|
|
|
const libEntries = Object.entries(library).map(([id, v]) => ({ id, name: v.name, file: v.file }));
|
|
|
|
|
|
|
|
|
|
|
|
// Slots a person is sitting in by hand — don't fill them, and don't place that same person twice.
|
|
|
|
// Slots a person was placed in by hand FOR THIS PLAN — don't fill them, and don't place that
|
|
|
|
|
|
|
|
// same person twice. A hand placement made for an earlier plan (last week's lineup, or one
|
|
|
|
|
|
|
|
// from before placements were stamped) is stale: Sync replaces it like any synced slot.
|
|
|
|
const eff = effectiveSlots();
|
|
|
|
const eff = effectiveSlots();
|
|
|
|
|
|
|
|
const planId = String(plan.id);
|
|
|
|
const openIdx = []; const manualNames = new Set();
|
|
|
|
const openIdx = []; const manualNames = new Set();
|
|
|
|
for (let i = 0; i < eff.length; i += 1) {
|
|
|
|
for (let i = 0; i < eff.length; i += 1) {
|
|
|
|
const e = board[String(i)];
|
|
|
|
const e = board[String(i)];
|
|
|
|
if (e && (e.personId || e.name)) {
|
|
|
|
if (e && (e.personId || e.name) && e.plan === planId) {
|
|
|
|
const nm = e.personId ? (library[e.personId] && library[e.personId].name) : e.name;
|
|
|
|
const nm = e.personId ? (library[e.personId] && library[e.personId].name) : e.name;
|
|
|
|
if (nm) manualNames.add(normName(nm));
|
|
|
|
if (nm) manualNames.add(normName(nm));
|
|
|
|
} else openIdx.push(i);
|
|
|
|
} else openIdx.push(i);
|
|
|
@@ -540,8 +544,8 @@ module.exports = {
|
|
|
|
const lib = bestLibraryMatch(m.name, libEntries); // folder photo wins over the PCO headshot
|
|
|
|
const lib = bestLibraryMatch(m.name, libEntries); // folder photo wins over the PCO headshot
|
|
|
|
const file = lib && lib.file ? lib.file : await cachePcoPhoto(m.personId, m.photoUrl);
|
|
|
|
const file = lib && lib.file ? lib.file : await cachePcoPhoto(m.personId, m.photoUrl);
|
|
|
|
board[String(i)] = { pco: { name: m.name, file, birthday: m.birthday || '', personId: m.personId } };
|
|
|
|
board[String(i)] = { pco: { name: m.name, file, birthday: m.birthday || '', personId: m.personId } };
|
|
|
|
} else if (board[String(i)] && board[String(i)].pco) {
|
|
|
|
} else if (board[String(i)]) {
|
|
|
|
delete board[String(i)]; // was synced, no longer on the plan
|
|
|
|
delete board[String(i)]; // synced or stale, and no longer on the plan
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
saveBoard();
|
|
|
|
saveBoard();
|
|
|
@@ -580,6 +584,7 @@ module.exports = {
|
|
|
|
current = {
|
|
|
|
current = {
|
|
|
|
streams, getLatest: () => latest, pco, listPlans, photoDir,
|
|
|
|
streams, getLatest: () => latest, pco, listPlans, photoDir,
|
|
|
|
library, board, saveLibrary, saveBoard, rebuildLocal, refresh: () => syncNow(), slots,
|
|
|
|
library, board, saveLibrary, saveBoard, rebuildLocal, refresh: () => syncNow(), slots,
|
|
|
|
|
|
|
|
planId: () => (latest.plan && latest.plan.id) || '',
|
|
|
|
photoUrlFor, libraryList,
|
|
|
|
photoUrlFor, libraryList,
|
|
|
|
setChoir, choirOn: () => choirOn,
|
|
|
|
setChoir, choirOn: () => choirOn,
|
|
|
|
};
|
|
|
|
};
|
|
|
@@ -684,7 +689,7 @@ module.exports = {
|
|
|
|
const personId = String(q.get('personId') || '').trim();
|
|
|
|
const personId = String(q.get('personId') || '').trim();
|
|
|
|
if (!/^\d+$/.test(slot)) return json(res, 400, { error: 'Bad slot.' });
|
|
|
|
if (!/^\d+$/.test(slot)) return json(res, 400, { error: 'Bad slot.' });
|
|
|
|
if (personId && !current.library[personId]) return json(res, 404, { error: 'No such library photo.' });
|
|
|
|
if (personId && !current.library[personId]) return json(res, 404, { error: 'No such library photo.' });
|
|
|
|
if (personId) current.board[slot] = { personId, name: '' };
|
|
|
|
if (personId) current.board[slot] = { personId, name: '', plan: current.planId() };
|
|
|
|
else delete current.board[slot];
|
|
|
|
else delete current.board[slot];
|
|
|
|
current.saveBoard(); current.rebuildLocal();
|
|
|
|
current.saveBoard(); current.rebuildLocal();
|
|
|
|
json(res, 200, { ok: true });
|
|
|
|
json(res, 200, { ok: true });
|
|
|
@@ -702,6 +707,7 @@ module.exports = {
|
|
|
|
const name = String(q.get('name') || '').trim();
|
|
|
|
const name = String(q.get('name') || '').trim();
|
|
|
|
const existing = current.board[index] || { personId: '', name: '' };
|
|
|
|
const existing = current.board[index] || { personId: '', name: '' };
|
|
|
|
existing.name = name;
|
|
|
|
existing.name = name;
|
|
|
|
|
|
|
|
existing.plan = current.planId();
|
|
|
|
if (!existing.personId && !name) delete current.board[index];
|
|
|
|
if (!existing.personId && !name) delete current.board[index];
|
|
|
|
else current.board[index] = existing;
|
|
|
|
else current.board[index] = existing;
|
|
|
|
current.saveBoard(); current.rebuildLocal();
|
|
|
|
current.saveBoard(); current.rebuildLocal();
|
|
|
|