Files
Rosary/admin/builder.php
T
pguzman b894e22bdc Fix Builder layout and Loading... initialization bug
Layout: replace admin-container wrapper with #builder-page flex column;
add min-height:0 to builder-wrap, builder-body, both panels so the
inner scroll areas actually shrink correctly in a flex column.

Loading...: scripts at end of body mean DOMContentLoaded may have
already fired by the time the listener is registered. Check
document.readyState first and call init() directly if DOM is ready.

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

265 lines
12 KiB
PHP
Raw 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.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
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.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" hidden>
<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" 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) => [
'prayer_id' => (int)$s['prayer_id'],
'attribution' => $s['attribution'],
], $edit_steps)) ?>;
</script>
<script src="<?= BASE_URL ?>/assets/js/builder.js?v=1"></script>
</body>
</html>