Files
pguzman 8c047f5b28 Beads are now a property of prayer steps, not separate steps
Each prayer in the library has an optional default_bead_type (small/large/
crucifix). Standard prayers get sensible defaults: Our Father=large,
Hail Mary=small, Sign of Cross=crucifix, Divine Mercy beads accordingly.

In the sequence, each step card shows a bead selector (—/○/●/✝) so users
can override the default per step. Adding a prayer pre-fills its default.

Bead library icon hints (○●✝) appear on prayer cards in the library.
Modal now includes a Bead selector for creating/editing prayers.

Remove the separate Bead Markers library section — beads live on prayers.
build_slides: prayer steps with bead_type now get a real bead_index so
the ring advances correctly during presentation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 22:07:48 -07:00

280 lines
13 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* admin/builder.php — Rosary Builder
* Available to superuser, admin, superadmin.
* Create or edit a custom prayer-sequence session.
*/
require_once __DIR__ . '/../config/db.php';
require_once __DIR__ . '/../includes/auth.php';
require_role('superuser');
$pdo = get_pdo();
$user = current_user();
$uid = (int)$user['id'];
$is_admin = has_role('admin');
$site_name = get_setting('site_name', APP_NAME);
// ── Load existing session for edit mode ──────────────────────────────────────
$session = null;
$edit_steps = [];
if (isset($_GET['id'])) {
$stmt = $pdo->prepare("SELECT * FROM sessions WHERE id = ? AND occasion = 'custom'");
$stmt->execute([(int)$_GET['id']]);
$session = $stmt->fetch();
if (!$session) {
header('Location: ' . BASE_URL . '/admin/');
exit;
}
if (!$is_admin && (int)$session['user_id'] !== $uid) {
header('Location: ' . BASE_URL . '/admin/');
exit;
}
// Load steps with prayer data
$step_stmt = $pdo->prepare("
SELECT bs.step_type, bs.bead_type, bs.prayer_id, bs.attribution,
cp.name, cp.leader_text, cp.all_text, cp.is_global, cp.created_by,
IF(cp.is_global=1 AND u.role='superadmin','standard',
IF(cp.is_global=1,'global','mine')) AS source_tag
FROM builder_steps bs
LEFT JOIN custom_prayers cp ON cp.id = bs.prayer_id
LEFT JOIN users u ON u.id = cp.created_by
WHERE bs.session_id = ?
ORDER BY bs.step_order ASC
");
$step_stmt->execute([(int)$session['id']]);
$edit_steps = $step_stmt->fetchAll();
}
// ── Load prayer library ───────────────────────────────────────────────────────
$lib_stmt = $pdo->prepare("
SELECT cp.id, cp.name, cp.leader_text, cp.all_text,
cp.default_bead_type, cp.is_global, cp.created_by,
IF(cp.is_global=1 AND u.role='superadmin','standard',
IF(cp.is_global=1,'global','mine')) AS source_tag
FROM custom_prayers cp
LEFT JOIN users u ON u.id = cp.created_by
WHERE cp.is_global = 1 OR cp.created_by = ?
ORDER BY (cp.is_global=1 AND u.role='superadmin') DESC, cp.name ASC
");
$lib_stmt->execute([$uid]);
$prayers_data = $lib_stmt->fetchAll();
$page_title = $session ? 'Edit: ' . htmlspecialchars($session['name']) : 'Rosary Builder';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/svg+xml" href="<?= BASE_URL ?>/favicon.svg">
<title><?= $page_title ?> — <?= htmlspecialchars($site_name) ?></title>
<link rel="stylesheet" href="<?= BASE_URL ?>/assets/css/setup.css">
<link rel="stylesheet" href="<?= BASE_URL ?>/assets/css/builder.css?v=1">
</head>
<body>
<div id="builder-page">
<header class="admin-header" style="flex-shrink:0;padding:0 24px">
<h1 style="font-size:1rem">&#x271D; <?= htmlspecialchars($site_name) ?></h1>
<div class="header-actions">
<a href="<?= BASE_URL ?>/" class="btn btn-ghost" style="font-size:12px">&#x2190; View Site</a>
<?php if ($is_admin): ?>
<a href="<?= BASE_URL ?>/admin/prayers.php" class="btn btn-ghost">Prayer Library</a>
<?php endif; ?>
<a href="<?= BASE_URL ?>/admin/" class="btn btn-ghost">&#x2190; Dashboard</a>
<a href="<?= BASE_URL ?>/logout" class="btn btn-ghost">Logout</a>
</div>
</header>
<div class="builder-wrap">
<!-- Toolbar -->
<div class="builder-toolbar">
<h2>&#x2712; <?= $session ? 'Edit Sequence' : 'Rosary Builder' ?></h2>
<div class="divider"></div>
<input type="text" id="session-name" class="session-name-input"
placeholder="Session label (required)"
value="<?= htmlspecialchars($session['name'] ?? '') ?>">
<div class="toolbar-right">
<label class="public-toggle">
<input type="checkbox" id="is-public"
<?= (!$session || $session['is_public']) ? 'checked' : '' ?>>
Public
</label>
<?php if ($session): ?>
<?php
$owner_stmt = $pdo->prepare("SELECT username FROM users WHERE id = ?");
$owner_stmt->execute([$session['user_id']]);
$owner_uname = $owner_stmt->fetchColumn() ?: '';
$present_url = BASE_URL . '/' . rawurlencode($owner_uname) . '/' . rawurlencode($session['slug'] ?? '');
?>
<a id="present-link" href="<?= htmlspecialchars($present_url) ?>"
target="_blank" class="btn btn-ghost" style="font-size:13px">
Present &#x2197;
</a>
<?php else: ?>
<a id="present-link" href="#" target="_blank"
class="btn btn-ghost" style="font-size:13px;display:none">
Present &#x2197;
</a>
<?php endif; ?>
<button id="btn-save" class="btn btn-primary">Save Session</button>
</div>
</div>
<!-- Message bar -->
<div id="builder-msg"></div>
<!-- Optional subject (for variable substitution in prayers like Eternal Rest) -->
<details class="builder-subject">
<summary>For a specific person (optional — enables {name}, {pronoun} substitutions)</summary>
<div class="builder-subject-fields">
<input type="text" id="subject-name"
placeholder="Full name, e.g. Maria Santos"
value="<?= htmlspecialchars($session['subject_name'] ?? '') ?>">
<select id="subject-pronoun">
<option value="he" <?= ($session['subject_pronoun'] ?? 'he') === 'he' ? 'selected' : '' ?>>He / Him / His</option>
<option value="she" <?= ($session['subject_pronoun'] ?? '') === 'she' ? 'selected' : '' ?>>She / Her / Her</option>
</select>
<input type="text" id="subject-dates"
placeholder="Life dates, e.g. Aug 12, 1949 July 25, 2025"
style="min-width:280px"
value="<?= htmlspecialchars($session['subject_dates'] ?? '') ?>">
</div>
</details>
<!-- Two-panel body -->
<div class="builder-body">
<!-- Left: Sequence -->
<div class="builder-sequence">
<div class="panel-header">
<h3>Your Sequence</h3>
<span id="step-count-badge" class="step-count-badge">0 prayers</span>
</div>
<div id="step-list"></div>
</div>
<!-- Right: Prayer library -->
<div class="builder-library">
<div class="library-header">
<input type="search" id="prayer-search" class="library-search"
placeholder="&#x1F50D; Search prayers by name or text…">
<div class="library-tabs">
<button class="tab-btn active" data-tab="all">All</button>
<button class="tab-btn" data-tab="standard">Standard</button>
<button class="tab-btn" data-tab="mine">My Prayers</button>
</div>
</div>
<div id="prayer-list">
<div class="library-empty" style="grid-column:1/-1;padding:40px;text-align:center;color:var(--muted)">
Loading…
</div>
</div>
<div class="library-footer">
<button id="btn-create-prayer" class="btn btn-secondary">+ Create Custom Prayer</button>
</div>
</div>
</div><!-- /builder-body -->
</div><!-- /builder-wrap -->
</div><!-- /builder-page -->
<!-- ── Create / Edit Prayer Modal ─────────────────────────────── -->
<div id="prayer-modal" class="modal-overlay">
<div class="modal-box">
<h3 id="modal-title">New Custom Prayer</h3>
<div class="form-group">
<label for="modal-name">Prayer Name <span class="required">*</span></label>
<input type="text" id="modal-name" class="form-control"
placeholder="e.g. Opening Prayer, Special Intention…"
style="width:100%;padding:9px 12px;border:1px solid var(--border);border-radius:6px;font-family:var(--font);font-size:14px">
</div>
<div class="form-group">
<label>Attribution</label>
<div class="attr-radios">
<label class="attr-radio-label">
<input type="radio" name="modal-attr" value="leader_all" checked>
<span><strong>Leader / All</strong><br><small>Two voices — leader speaks, congregation responds</small></span>
</label>
<label class="attr-radio-label">
<input type="radio" name="modal-attr" value="leader_only">
<span><strong>Leader only</strong><br><small>Leader speaks the whole prayer</small></span>
</label>
<label class="attr-radio-label">
<input type="radio" name="modal-attr" value="all_only">
<span><strong>All together</strong><br><small>Everyone prays in unison</small></span>
</label>
<label class="attr-radio-label">
<input type="radio" name="modal-attr" value="none">
<span><strong>No attribution</strong><br><small>Plain text — no leader/all labels</small></span>
</label>
</div>
</div>
<div class="form-group">
<label>Bead (optional)</label>
<div class="bead-btn-row" id="modal-bead-row">
<button type="button" class="bead-sel-btn active" data-bead="">No bead</button>
<button type="button" class="bead-sel-btn" data-bead="small">&#x25CB; Small</button>
<button type="button" class="bead-sel-btn" data-bead="large">&#x25CF; Large</button>
<button type="button" class="bead-sel-btn" data-bead="crucifix">&#x271D; Crucifix</button>
</div>
<input type="hidden" id="modal-bead-type" value="">
<p class="help-hint">Sets the default bead when this prayer is added to a sequence.</p>
</div>
<div class="form-group" id="modal-leader-group">
<label for="modal-leader">Leader text</label>
<textarea id="modal-leader" class="modal-textarea" rows="5"
placeholder="What the leader says…"></textarea>
<p class="help-hint">Use line breaks to control pacing. Variables: {name}, {pronoun_obj}, {pronoun_poss}</p>
</div>
<div class="form-group" id="modal-all-group">
<label for="modal-all">All / Response text</label>
<textarea id="modal-all" class="modal-textarea" rows="3"
placeholder="What everyone says together…"></textarea>
</div>
<?php if ($is_admin): ?>
<div class="form-group" id="modal-global-group">
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-weight:normal">
<input type="checkbox" id="modal-global" style="width:16px;height:16px">
Make this prayer available to all users (global library)
</label>
</div>
<?php else: ?>
<input type="hidden" id="modal-global">
<?php endif; ?>
<div class="modal-footer">
<button id="modal-cancel" class="btn btn-ghost">Cancel</button>
<button id="modal-save" class="btn btn-primary">Save Prayer</button>
</div>
</div>
</div>
<script>
var BASE_URL = '<?= BASE_URL ?>';
var IS_ADMIN = <?= $is_admin ? 'true' : 'false' ?>;
var EDIT_SESSION_ID = <?= $session ? (int)$session['id'] : 'null' ?>;
var PRAYERS_DATA = <?= json_encode(array_values($prayers_data)) ?>;
var EXISTING_STEPS = <?= json_encode(array_map(fn($s) => [
'step_type' => $s['step_type'] ?? 'prayer',
'bead_type' => $s['bead_type'] ?? null,
'prayer_id' => $s['prayer_id'] ? (int)$s['prayer_id'] : null,
'attribution' => $s['attribution'] ?? 'leader_all',
], $edit_steps)) ?>;
</script>
<script src="<?= BASE_URL ?>/assets/js/builder.js?v=1"></script>
</body>
</html>