b894e22bdc
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>
265 lines
12 KiB
PHP
265 lines
12 KiB
PHP
<?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">✝ <?= htmlspecialchars($site_name) ?></h1>
|
||
<div class="header-actions">
|
||
<a href="<?= BASE_URL ?>/" class="btn btn-ghost" style="font-size:12px">← 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">← 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>✒ <?= $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 ↗
|
||
</a>
|
||
<?php else: ?>
|
||
<a id="present-link" href="#" target="_blank"
|
||
class="btn btn-ghost" style="font-size:13px;display:none">
|
||
Present ↗
|
||
</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="🔍 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>
|