Ryanhub - file viewer
filename: views/public/schedule.php
branch: main
back to repo
<?php
$pageTitle = "Schedule – Seven O'Clock Dinner Club";

// Simple calendar representation: always highlight 7pm dinners.
?>

<?php
// Optional RSVP shortcut: ?rsvp=today (used by the main page button).
// Use a single timezone reference throughout the page to avoid "day off by one" issues.
// Default to New York, but allow overriding during development (e.g. Los Angeles).
$tzName = getenv('APP_TIMEZONE') ?: 'America/New_York';
$tz = new DateTimeZone($tzName);
// Current date boundary in the app's timezone.
$now = new DateTimeImmutable('now', $tz);
$today = $now->setTime(0, 0, 0);

if (!empty($_GET['rsvp']) && isLoggedIn() && $_GET['rsvp'] === 'today') {
    $dateStr = $today->format('Y-m-d');
    $nowStr = (new DateTimeImmutable('now'))->format('Y-m-d H:i:s');
    $stmt = $db->prepare('
        INSERT INTO rsvps (user_id, dinner_date, created_at)
        VALUES (?, ?, ?)
        ON DUPLICATE KEY UPDATE created_at = VALUES(created_at)
    ');
    $uid = (int)currentUserId();
    $stmt->bind_param('iss', $uid, $dateStr, $nowStr);
    $stmt->execute();
    // Avoid repeat RSVP on refresh.
    header('Location: ' . url('schedule'));
    exit;
}
?>

<section class="page-grid">
    <div class="card" data-animate-initial>
        <div class="muted" style="font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 10px;">
            Calendar
        </div>
        <h1 style="font-family: 'Georgia', 'Times New Roman', serif; font-weight: 400; font-size: 26px; margin: 0 0 12px;">
            Give or take ~15 minutes.
        </h1>
    </div>

    <div class="card" data-animate>
        <div class="muted" style="font-size: 12px; text-transform: uppercase; letter-spacing: 0.18em; margin-bottom: 10px;">
            This month at a glance
        </div>
        <div style="display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; font-size: 12px;">
            <?php
            $daysOfWeek = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun'];
            foreach ($daysOfWeek as $dow): ?>
                <div class="muted" style="text-align: center; padding-bottom: 4px; border-bottom: 1px solid rgba(0,0,0,0.06);">
                    <?= htmlspecialchars($dow, ENT_QUOTES, 'UTF-8') ?>
                </div>
            <?php endforeach; ?>
            <?php
            $firstOfMonth = $today->modify('first day of this month');
            $startDow = (int)$firstOfMonth->format('N'); // 1 (Mon) - 7 (Sun)
            for ($i = 1; $i < $startDow; $i++): ?>
                <div></div>
            <?php endfor;

            $daysInMonth = (int)$today->format('t');
            for ($day = 1; $day <= $daysInMonth; $day++):
                $date = $firstOfMonth->setDate((int)$firstOfMonth->format('Y'), (int)$firstOfMonth->format('m'), $day);
                $isToday = $date->format('Y-m-d') === $today->format('Y-m-d');
                ?>
                <div style="border-radius: 10px; padding: 8px 4px; text-align: center; border: 1px solid <?= $isToday ? 'rgba(178,146,99,0.85)' : 'rgba(0,0,0,0.04)' ?>; background: <?= $isToday ? 'rgba(178,146,99,0.22)' : 'rgba(255,255,255,0.7)' ?>; position: relative;">
                    <div style="font-size: 12px;"><?= $day ?></div>
                    <div style="margin-top: 4px; font-size: 10px; text-transform: uppercase; letter-spacing: 0.14em;" class="muted">
                        7&nbsp;pm
                    </div>
                </div>
            <?php endfor; ?>
        </div>
        <p class="muted" style="margin-top: 14px; font-size: 12px;">
            Any changes to the schedule will be communicated.
        </p>
    </div>
</section>

<?php
// Render today's RSVP list.
$todayStr = $today->format('Y-m-d');

$rsvpStmt = $db->prepare('
    SELECT m.display_name, m.photo_url, u.id AS user_id
    FROM rsvps r
    JOIN users u ON u.id = r.user_id
    JOIN member_profiles m ON m.user_id = u.id
    WHERE r.dinner_date = ?
    ORDER BY r.created_at DESC, u.id ASC
');
$rsvpStmt->bind_param('s', $todayStr);
$rsvpStmt->execute();
$rsvps = $rsvpStmt->get_result()->fetch_all(MYSQLI_ASSOC);

$myRsvpStmt = null;
$myRsvp = false;
if (isLoggedIn()) {
    $myRsvpStmt = $db->prepare('SELECT 1 FROM rsvps WHERE user_id = ? AND dinner_date = ? LIMIT 1');
    $myUid = (int)currentUserId();
    $myRsvpStmt->bind_param('is', $myUid, $todayStr);
    $myRsvpStmt->execute();
    $myRsvp = (bool)($myRsvpStmt->get_result()->fetch_assoc());
}
?>

<?php
// Render chat messages for the current date (last 24 hours).
$chatFromTs = $now->modify('-24 hours')->format('Y-m-d H:i:s');
$chatStmt = $db->prepare('
    SELECT
        c.user_id,
        m.display_name,
        m.photo_url,
        c.body,
        c.created_at
    FROM chat_messages c
    JOIN member_profiles m ON m.user_id = c.user_id
    WHERE c.dinner_date = ?
      AND c.created_at >= ?
    ORDER BY c.created_at DESC, c.id DESC
');
$chatStmt->bind_param('ss', $todayStr, $chatFromTs);
$chatStmt->execute();
$initialMessages = $chatStmt->get_result()->fetch_all(MYSQLI_ASSOC);
?>

<section class="page-grid" style="margin-top: 18px;">
    <div class="card" data-animate-initial id="rsvp-today">
        <div class="muted" style="font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 10px;">
            RSVPs for today
        </div>
        <?php if (isLoggedIn()): ?>
            <div style="display:flex; flex-wrap:wrap; align-items:baseline; justify-content:space-between; gap: 12px; margin-bottom: 10px;">
                <h1 style="font-family: 'Georgia', 'Times New Roman', serif; font-weight: 400; font-size: 22px; margin: 0;">
                    <?= htmlspecialchars($today->format('F j, Y'), ENT_QUOTES, 'UTF-8') ?>
                </h1>
                <?php if ($myRsvp): ?>
                    <span
                        class="pill"
                        style="background: rgba(72, 150, 90, 0.18); border: 1px solid rgba(72,150,90,0.55); color: inherit; cursor: not-allowed; white-space:nowrap;"
                        aria-disabled="true"
                    >
                        You're in
                    </span>
                <?php else: ?>
                    <a
                        href="<?= htmlspecialchars(url('schedule?rsvp=today'), ENT_QUOTES, 'UTF-8') ?>"
                        class="pill pill-accent"
                        style="border-style: dashed; text-decoration:none; white-space:nowrap;"
                    >
                        RSVP Today
                    </a>
                <?php endif; ?>
            </div>
        <?php endif; ?>
        <?php if (!isLoggedIn()): ?>
            <h1 style="font-family: 'Georgia', 'Times New Roman', serif; font-weight: 400; font-size: 22px; margin: 0 0 10px;">
                <?= htmlspecialchars($today->format('F j, Y'), ENT_QUOTES, 'UTF-8') ?>
            </h1>
        <?php endif; ?>
        <?php if ($rsvps): ?>
            <div style="display:flex; flex-wrap:wrap; gap: 10px;">
                <?php foreach ($rsvps as $r): ?>
                    <?php $memberUserId = (int)($r['user_id'] ?? 0); ?>
                    <?php if ($memberUserId > 0): ?>
                        <a
                            href="<?= htmlspecialchars(url('members?user_id=' . $memberUserId), ENT_QUOTES, 'UTF-8') ?>"
                            style="display:flex; align-items:center; gap: 8px; padding: 8px 10px; border-radius: 12px; border: 1px solid rgba(0,0,0,0.06); background: rgba(255,255,255,0.65); text-decoration:none; color: inherit;"
                            aria-label="View <?= htmlspecialchars($r['display_name'] ?? 'member', ENT_QUOTES, 'UTF-8') ?> on members page"
                        >
                            <?php $photo = $r['photo_url'] ?? null; ?>
                            <?php if (!empty($photo)): ?>
                                <img src="<?= htmlspecialchars($photo, ENT_QUOTES, 'UTF-8') ?>" alt="" style="width: 28px; height: 28px; border-radius: 999px; object-fit: cover; border: 1px solid rgba(0,0,0,0.08);">
                            <?php else: ?>
                                <div aria-hidden="true" style="width: 28px; height: 28px; border-radius: 999px; background: rgba(0,0,0,0.06); border: 1px solid rgba(0,0,0,0.08);"></div>
                            <?php endif; ?>
                            <div style="display:flex; flex-direction:column; line-height:1.2;">
                                <div style="font-size: 12px; font-weight: 500;">
                                    <?= htmlspecialchars($r['display_name'] ?? 'Unknown', ENT_QUOTES, 'UTF-8') ?>
                                    <?php if (isLoggedIn() && (int)($r['user_id'] ?? 0) === (int)currentUserId()): ?>
                                        <span class="muted" style="font-size: 10px;">(you)</span>
                                    <?php endif; ?>
                                </div>
                            </div>
                        </a>
                    <?php else: ?>
                        <div style="display:flex; align-items:center; gap: 8px; padding: 8px 10px; border-radius: 12px; border: 1px solid rgba(0,0,0,0.06); background: rgba(255,255,255,0.65);">
                            <div class="muted" style="font-size: 12px;">Unknown member</div>
                        </div>
                    <?php endif; ?>
                <?php endforeach; ?>
            </div>
        <?php else: ?>
            <p class="muted" style="font-size: 13px; margin: 0;">
                No RSVPs yet for today.
            </p>
        <?php endif; ?>
        <?php if (!isLoggedIn()): ?>
            <div class="muted" style="margin-top: 8px; font-size: 12px;">
                Sign in to RSVP.
        </div>
        <?php endif; ?>
    </div>
</section>

<section class="page-grid" style="margin-top: 18px;">
    <div class="card" data-animate-initial>
        <div class="muted" style="font-size: 11px; letter-spacing: 0.18em; text-transform: uppercase; margin-bottom: 10px;">
            Chat
        </div>
        <div id="chat-messages"
             style="max-height: 320px; overflow:auto; padding: 12px; border-radius: 14px; border: 1px solid rgba(0,0,0,0.08); background: rgba(255,255,255,0.7); display:flex; flex-direction:column; gap: 10px;">
            <?php if (!$initialMessages): ?>
                <div class="muted" style="font-size: 13px;">No messages yet.</div>
            <?php else: ?>
                <?php foreach ($initialMessages as $m): ?>
                    <div style="display:flex; align-items:flex-start; gap: 10px; padding: 10px 10px; border-radius: 12px; border: 1px solid rgba(0,0,0,0.06); background: rgba(255,255,255,0.65);">
                        <?php $photo = $m['photo_url'] ?? null; ?>
                        <?php if (!empty($photo)): ?>
                            <div style="width:30px; height:30px; border-radius:999px; overflow:hidden; background:rgba(0,0,0,0.06); border:1px solid rgba(0,0,0,0.08); flex:0 0 auto;">
                                <img src="<?= htmlspecialchars($photo, ENT_QUOTES, 'UTF-8') ?>" alt="" style="width:100%; height:100%; object-fit:cover;">
                            </div>
                        <?php else: ?>
                            <div style="width:30px; height:30px; border-radius:999px; background:rgba(0,0,0,0.06); border:1px solid rgba(0,0,0,0.08); flex:0 0 auto; display:flex; align-items:center; justify-content:center; font-family:'Georgia','Times New Roman',serif; font-size:14px;">
                                <?= htmlspecialchars(strtoupper(mb_substr($m['display_name'] ?? '?', 0, 1)), ENT_QUOTES, 'UTF-8') ?>
                            </div>
                        <?php endif; ?>
                        <div style="flex:1; min-width:0;">
                            <div style="display:flex; align-items:flex-start; justify-content:space-between; gap:12px; margin-bottom:3px;">
                                <div style="font-size:12px; font-weight:600;">
                                    <?= htmlspecialchars($m['display_name'] ?? 'Unknown', ENT_QUOTES, 'UTF-8') ?>
                                </div>
                                <div class="muted" style="font-size:11px; margin-top:1px; text-align:right; white-space:nowrap;">
                                    <?= htmlspecialchars((new DateTimeImmutable($m['created_at']))->format('M j, Y g:i A'), ENT_QUOTES, 'UTF-8') ?>
                                </div>
                            </div>
                            <div style="font-size:13px; line-height:1.5;">
                                <?= nl2br(htmlspecialchars($m['body'] ?? '', ENT_QUOTES, 'UTF-8')) ?>
                            </div>
                        </div>
                    </div>
                <?php endforeach; ?>
            <?php endif; ?>
        </div>

        <div style="margin-top: 12px; display:flex; gap: 10px; align-items:flex-start;">
            <?php if (isLoggedIn()): ?>
                <form id="chat-send-form"
                      method="POST"
                      action="<?= htmlspecialchars(url('api/chat-send'), ENT_QUOTES, 'UTF-8') ?>"
                      style="flex:1; display:flex; gap: 10px; align-items:flex-start;">
                    <input type="hidden" name="dinner_date" value="<?= htmlspecialchars($todayStr, ENT_QUOTES, 'UTF-8') ?>">
                    <textarea name="body" rows="2" maxlength="2000" required
                              placeholder="Write a message…"
                              style="flex:1; resize: vertical; min-height: 42px; padding: 10px 12px; border-radius: 14px; border: 1px solid rgba(0,0,0,0.12); background: rgba(255,255,255,0.85);"></textarea>
                    <button type="submit" class="pill pill-accent" style="align-self:stretch; padding: 10px 14px; white-space: nowrap;">
                        Send
                    </button>
                </form>
            <?php else: ?>
                <div class="muted" style="font-size: 13px;">
                    Sign in to send messages.
                </div>
            <?php endif; ?>
        </div>
    </div>
</section>

<?php
// Chat is server-rendered on page load for reliability.
// If we re-introduce polling later, we can do it in a dedicated block.
?>