← 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') %>