Add bead separator support to Rosary Builder

- Bead Markers section in library panel with Small, Large, Crucifix cards
- Bead steps render as amber-tinted cards in the sequence (no attribution)
- migrate_v5.php: adds step_type/bead_type columns, makes prayer_id nullable
- Bead steps produce slides with proper bead+bead_index so the ring advances
  during presentation — allows participants to follow along on physical beads
- Prayer steps between beads still show bead_index=null (ring stays on last bead)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-13 21:20:59 -07:00
parent 3c11fd2067
commit 3cc002a6da
6 changed files with 299 additions and 38 deletions
+37 -20
View File
@@ -49,28 +49,40 @@ if ($name === '') json_err('Session name is required');
if (empty($steps)) json_err('Add at least one prayer to your sequence');
$valid_attributions = ['leader_all', 'leader_only', 'all_only', 'none'];
$valid_bead_types = ['small', 'large', 'crucifix'];
// Validate steps
// Validate steps and collect prayer IDs to verify
$prayer_ids = [];
foreach ($steps as $i => $step) {
$pid = (int)($step['prayer_id'] ?? 0);
$att = $step['attribution'] ?? 'leader_all';
if (!$pid) json_err("Step " . ($i + 1) . " is missing a prayer");
if (!in_array($att, $valid_attributions)) json_err("Invalid attribution on step " . ($i + 1));
$type = $step['step_type'] ?? 'prayer';
if ($type === 'bead') {
if (!in_array($step['bead_type'] ?? '', $valid_bead_types)) {
json_err("Invalid bead type on step " . ($i + 1));
}
} else {
$pid = (int)($step['prayer_id'] ?? 0);
$att = $step['attribution'] ?? 'leader_all';
if (!$pid) json_err("Step " . ($i + 1) . " is missing a prayer");
if (!in_array($att, $valid_attributions)) json_err("Invalid attribution on step " . ($i + 1));
$prayer_ids[] = $pid;
}
}
// Verify all prayer_ids exist and are accessible
$prayer_ids = array_unique(array_column($steps, 'prayer_id'));
$in_placeholders = implode(',', array_fill(0, count($prayer_ids), '?'));
$valid_stmt = $pdo->prepare("
SELECT id FROM custom_prayers
WHERE id IN ($in_placeholders)
AND (is_global = 1 OR created_by = ?)
");
$valid_stmt->execute([...$prayer_ids, $uid]);
$valid_ids = array_column($valid_stmt->fetchAll(), 'id');
foreach ($prayer_ids as $pid) {
if (!in_array((string)$pid, array_map('strval', $valid_ids))) {
json_err("Prayer ID {$pid} not found or not accessible");
if (!empty($prayer_ids)) {
$prayer_ids = array_unique($prayer_ids);
$in_placeholders = implode(',', array_fill(0, count($prayer_ids), '?'));
$valid_stmt = $pdo->prepare("
SELECT id FROM custom_prayers
WHERE id IN ($in_placeholders)
AND (is_global = 1 OR created_by = ?)
");
$valid_stmt->execute([...$prayer_ids, $uid]);
$valid_ids = array_column($valid_stmt->fetchAll(), 'id');
foreach ($prayer_ids as $pid) {
if (!in_array((string)$pid, array_map('strval', $valid_ids))) {
json_err("Prayer ID {$pid} not found or not accessible");
}
}
}
@@ -111,11 +123,16 @@ try {
// Insert steps
$step_stmt = $pdo->prepare(
"INSERT INTO builder_steps (session_id, step_order, prayer_id, attribution)
VALUES (?, ?, ?, ?)"
"INSERT INTO builder_steps (session_id, step_type, bead_type, step_order, prayer_id, attribution)
VALUES (?, ?, ?, ?, ?, ?)"
);
foreach ($steps as $order => $step) {
$step_stmt->execute([$session_id, $order, (int)$step['prayer_id'], $step['attribution']]);
$type = $step['step_type'] ?? 'prayer';
if ($type === 'bead') {
$step_stmt->execute([$session_id, 'bead', $step['bead_type'], $order, null, 'none']);
} else {
$step_stmt->execute([$session_id, 'prayer', null, $order, (int)$step['prayer_id'], $step['attribution']]);
}
}
$pdo->commit();