Expose guided training exercises on the driver's mobile profile link
"Jämna varv" and "Stegträning" previously only lived on the admin Training page. A driver's own personal mobile profile link now offers the same self-service configure/start/stop flow already used for the 5-minute practice timer there, with live progress computed server-side from the driver's own share token so raw lap rows and driver IDs never reach the client. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
38
server.js
38
server.js
@@ -360,7 +360,11 @@ app.get("/public-training/api/:shareToken", (req, res) => {
|
||||
const stored = readStoredAppState();
|
||||
const sinceTimestamp = publicTrainingProfile.normalizePublicTrainingSince(req.query.since, req.query.startedAgoMs);
|
||||
const windowMs = publicTrainingProfile.normalizePublicTrainingWindow(req.query.windowMs);
|
||||
const profile = stored ? publicTrainingProfile.buildPublicTrainingProfile(stored.state, String(req.params.shareToken || ""), stored.updatedAt, sinceTimestamp, windowMs) : null;
|
||||
// Separate "since" markers for the two guided exercises: a driver may run one, both, or
|
||||
// neither independently of the general practice timer above, each started at its own time.
|
||||
const evenLapsSince = publicTrainingProfile.normalizePublicTrainingSince(req.query.evenLapsSince, req.query.evenLapsStartedAgoMs);
|
||||
const stepTrainingSince = publicTrainingProfile.normalizePublicTrainingSince(req.query.stepTrainingSince, req.query.stepTrainingStartedAgoMs);
|
||||
const profile = stored ? publicTrainingProfile.buildPublicTrainingProfile(stored.state, String(req.params.shareToken || ""), stored.updatedAt, sinceTimestamp, windowMs, evenLapsSince, stepTrainingSince) : null;
|
||||
if (!profile) {
|
||||
res.status(404).json({ error: "Profile not found" });
|
||||
return;
|
||||
@@ -388,6 +392,38 @@ app.post("/public-training/api/:shareToken/markers", (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/public-training/api/:shareToken/even-laps", (req, res) => {
|
||||
try {
|
||||
const stored = readStoredAppState();
|
||||
const entry = stored ? publicTrainingProfile.savePublicEvenLapsConfig(stored.state, String(req.params.shareToken || ""), req.body || {}) : null;
|
||||
if (!entry) {
|
||||
res.status(404).json({ error: "Profile, class, or configuration not found" });
|
||||
return;
|
||||
}
|
||||
const nowIso = new Date().toISOString();
|
||||
db.prepare("INSERT INTO app_state (id, state_json, updated_at) VALUES (1, ?, ?) ON CONFLICT(id) DO UPDATE SET state_json = excluded.state_json, updated_at = excluded.updated_at").run(JSON.stringify(stored.state), nowIso);
|
||||
res.status(201).json({ ok: true });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error instanceof Error ? error.message : String(error) });
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/public-training/api/:shareToken/step-training", (req, res) => {
|
||||
try {
|
||||
const stored = readStoredAppState();
|
||||
const entry = stored ? publicTrainingProfile.savePublicStepTrainingConfig(stored.state, String(req.params.shareToken || ""), req.body || {}) : null;
|
||||
if (!entry) {
|
||||
res.status(404).json({ error: "Profile, class, or configuration not found" });
|
||||
return;
|
||||
}
|
||||
const nowIso = new Date().toISOString();
|
||||
db.prepare("INSERT INTO app_state (id, state_json, updated_at) VALUES (1, ?, ?) ON CONFLICT(id) DO UPDATE SET state_json = excluded.state_json, updated_at = excluded.updated_at").run(JSON.stringify(stored.state), nowIso);
|
||||
res.status(201).json({ ok: true });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: error instanceof Error ? error.message : String(error) });
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/public-training/qr/:shareToken", async (req, res) => {
|
||||
try {
|
||||
const stored = readStoredAppState();
|
||||
|
||||
Reference in New Issue
Block a user