← back to NationalPaperHangers
views/admin/booking-detail.ejs
155 lines
<%- include('../partials/head', { title, admin: true }) %>
<%- include('partials/admin-header') %>
<style>
.rc-admin-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:18px}
.rc-admin-card{border:1px solid var(--border,#d4d2c8);border-radius:10px;padding:14px;background:var(--card-bg,#fff)}
.rc-admin-card h3{font-size:15px;margin:0 0 8px}
.rc-admin-photo{display:block;max-width:100%;border-radius:6px}
.rc-admin-wp{margin-top:10px;border-top:1px solid var(--border,#eee);padding-top:10px}
.rc-admin-wp-spec{font-size:13px;margin:0 0 8px}
.rc-admin-preview{width:100%;border-radius:6px;border:1px solid var(--border,#ddd);background-repeat:repeat}
.rc-admin-card .muted{color:var(--muted,#888);font-weight:400;font-size:12px}
</style>
<%
function fmt(v) { return (v === null || v === undefined || v === '') ? '—' : v; }
function pretty(v) { return v ? String(v).replace(/_/g, ' ') : '—'; }
var when = booking.scheduled_start ? new Date(booking.scheduled_start).toLocaleString() : '—';
var dur = (booking.scheduled_start && booking.scheduled_end)
? Math.round((new Date(booking.scheduled_end) - new Date(booking.scheduled_start)) / 60000) + ' min'
: '—';
%>
<section class="admin-main">
<header class="admin-page-head">
<p class="kicker"><a href="/admin/bookings" style="color:inherit">← Back to bookings</a></p>
<h1>Booking — <%= booking.customer_name %></h1>
<span class="status-badge status-<%= booking.status %>"><%= booking.status %></span>
</header>
<div class="admin-grid" style="display:grid;grid-template-columns:repeat(auto-fit, minmax(320px, 1fr));gap:32px">
<section class="admin-section">
<h2>Schedule</h2>
<dl>
<dt>When</dt><dd><%= when %></dd>
<dt>Duration</dt><dd><%= dur %></dd>
<dt>Type</dt><dd><%= pretty(booking.project_type) %></dd>
<dt>Status</dt><dd><%= booking.status %></dd>
<% if (booking.confirmed_at) { %><dt>Confirmed</dt><dd><%= new Date(booking.confirmed_at).toLocaleString() %></dd><% } %>
<% if (booking.completed_at) { %><dt>Completed</dt><dd><%= new Date(booking.completed_at).toLocaleString() %></dd><% } %>
<% if (booking.canceled_at) { %><dt>Canceled</dt><dd><%= new Date(booking.canceled_at).toLocaleString() %></dd><% } %>
</dl>
</section>
<section class="admin-section">
<h2>Customer</h2>
<dl>
<dt>Name</dt><dd><%= booking.customer_name %></dd>
<dt>Email</dt><dd><a href="mailto:<%= booking.customer_email %>"><%= booking.customer_email %></a></dd>
<dt>Phone</dt><dd><%= fmt(booking.customer_phone) %></dd>
</dl>
</section>
<section class="admin-section">
<h2>Project address</h2>
<p>
<%= [booking.address_line1, booking.address_line2].filter(Boolean).join(', ') || '—' %><br>
<%= [booking.city, booking.state, booking.zip].filter(Boolean).join(', ') || '—' %>
</p>
</section>
<section class="admin-section">
<h2>Project brief</h2>
<dl>
<dt>Brand</dt><dd><%= fmt(booking.brand) %></dd>
<dt>Brand SKU / pattern</dt><dd><%= fmt(booking.brand_sku) %></dd>
<dt>Material</dt><dd><%= pretty(booking.material) %></dd>
<dt>Rooms / surfaces</dt><dd><%= fmt(booking.surfaces) %></dd>
<dt>Square feet</dt><dd><%= fmt(booking.square_feet) %></dd>
<dt>Estimated rolls</dt><dd><%= fmt(booking.roll_count_estimate) %></dd>
<dt>Ceiling height</dt><dd><%= booking.ceiling_height_ft ? booking.ceiling_height_ft + ' ft' : '—' %></dd>
<dt>Surface state</dt><dd><%= pretty(booking.surface_state) %></dd>
<dt>Access</dt><dd><%= pretty(booking.access_constraints) %></dd>
<dt>Budget band</dt><dd><%= pretty(booking.budget_band) %></dd>
</dl>
</section>
<% if (booking.room_captures && booking.room_captures.length) { %>
<section class="admin-section" style="grid-column:1/-1">
<h2>Room captures <span class="muted">(<%= booking.room_captures.length %>)</span></h2>
<p class="muted" style="font-size:12px;margin:0 0 14px">Customer-photographed walls with confirmed dimensions — quote from these instead of the typed estimate.</p>
<div class="rc-admin-grid">
<% booking.room_captures.forEach(function (rc) {
var measured = rc.wall_width_ft && rc.wall_height_ft;
var dims = measured ? (rc.wall_width_ft + ' × ' + rc.wall_height_ft + ' ft') : 'not measured';
var wp = rc.wallpaper;
%>
<div class="rc-admin-card">
<h3><%= rc.room || 'Room' %> <span class="muted">· <%= dims %></span></h3>
<% if (rc.photo_url) { %>
<a href="<%= rc.photo_url %>" target="_blank" rel="noopener">
<img src="<%= rc.photo_url %>" alt="Wall photo" class="rc-admin-photo">
</a>
<% } %>
<% if (rc.video_url) { %>
<p style="margin:8px 0 0"><a href="<%= rc.video_url %>" target="_blank" rel="noopener">▶ Room walkthrough video</a></p>
<% } %>
<% if (wp) { %>
<div class="rc-admin-wp">
<p class="rc-admin-wp-spec"><strong>Customer's wallpaper</strong><br>
<%= wp.width_in %>″ panel · <%= wp.repeat_in %>″ repeat · <%= pretty(wp.match_type) %> match</p>
<% if (measured) {
var tw = ((wp.width_in / 12) / rc.wall_width_ft * 100).toFixed(2);
var th = ((wp.repeat_in / 12) / rc.wall_height_ft * 100).toFixed(2);
%>
<div class="rc-admin-preview"
style="aspect-ratio:<%= rc.wall_width_ft %>/<%= rc.wall_height_ft %>;background-image:url('<%= wp.image_url %>');background-size:<%= tw %>% <%= th %>%"></div>
<p class="muted">Shown at true scale on a <%= dims %> wall.</p>
<% } else { %>
<a href="<%= wp.image_url %>" target="_blank" rel="noopener">
<img src="<%= wp.image_url %>" alt="Wallpaper" class="rc-admin-photo"></a>
<% } %>
</div>
<% } %>
</div>
<% }); %>
</div>
</section>
<% } %>
<% if (booking.customer_notes) { %>
<section class="admin-section">
<h2>Customer notes</h2>
<p style="white-space:pre-wrap"><%= booking.customer_notes %></p>
</section>
<% } %>
<% if (booking.installer_notes) { %>
<section class="admin-section">
<h2>Your notes</h2>
<p style="white-space:pre-wrap"><%= booking.installer_notes %></p>
</section>
<% } %>
</div>
<div class="actions" style="margin-top:32px;display:flex;gap:12px;flex-wrap:wrap">
<% if (booking.status === 'pending') { %>
<form method="post" action="/admin/bookings/<%= booking.id %>/confirm" class="inline-form">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<button class="btn btn-primary">Confirm booking</button>
</form>
<form method="post" action="/admin/bookings/<%= booking.id %>/decline" class="inline-form">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<button class="btn btn-ghost">Decline</button>
</form>
<% } else if (booking.status === 'confirmed') { %>
<form method="post" action="/admin/bookings/<%= booking.id %>/complete" class="inline-form">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<button class="btn btn-primary">Mark complete</button>
</form>
<% } %>
</div>
</section>
<%- include('../partials/footer') %>