Some rows (schema drift, cancelled requests, manual status edits) have customer_approved = NULL instead of the default 'none'. Templates called .upper() on it blindly, causing a Jinja2 UndefinedError / 500 when the customer player or admin pages loaded. Replace all .upper() calls with (value or 'none').upper() and add truthy checks before rendering the approved choice in status.html and dashboard.html.
207 lines
6.9 KiB
HTML
207 lines
6.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Your Theme Song</title>
|
|
<style>
|
|
/*
|
|
Private customer player page.
|
|
Shows two audio players for Version A and Version B, plus approval
|
|
buttons or a revision note form depending on request status.
|
|
After the customer makes a choice, the controls are hidden and a
|
|
confirmation/waiting message is shown. The number of remaining
|
|
customer revisions is displayed when revisions are enabled.
|
|
*/
|
|
body{
|
|
font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
|
|
background:#111827;
|
|
color:#f3f4f6;
|
|
margin:0;
|
|
padding:1rem;
|
|
line-height:1.5;
|
|
}
|
|
.container{
|
|
max-width:640px;
|
|
margin:0 auto;
|
|
background:#1f2937;
|
|
padding:1.5rem;
|
|
border-radius:1rem;
|
|
}
|
|
h1{color:#60a5fa;}
|
|
.player{
|
|
background:#111827;
|
|
padding:1rem;
|
|
border-radius:.5rem;
|
|
margin:1rem 0;
|
|
}
|
|
audio{width:100%;margin-top:.5rem;}
|
|
.actions{display:flex;gap:.5rem;flex-wrap:wrap;margin-top:1rem;}
|
|
button{
|
|
flex:1;
|
|
min-width:120px;
|
|
padding:.8rem;
|
|
border:none;
|
|
border-radius:.5rem;
|
|
background:#3b82f6;
|
|
color:#fff;
|
|
font-weight:700;
|
|
cursor:pointer;
|
|
}
|
|
button:disabled{background:#374151;color:#9ca3af;cursor:not-allowed;}
|
|
button.selected{background:#10b981;}
|
|
button.both{background:#8b5cf6;}
|
|
button.revision{background:#f59e0b;color:#000;}
|
|
form{margin-top:1rem;}
|
|
textarea{
|
|
width:100%;
|
|
padding:.6rem;
|
|
border-radius:.5rem;
|
|
border:1px solid #374151;
|
|
background:#111827;
|
|
color:#f3f4f6;
|
|
box-sizing:border-box;
|
|
min-height:80px;
|
|
}
|
|
.status{
|
|
padding:.8rem;
|
|
background:#064e3b;
|
|
border-radius:.5rem;
|
|
margin-top:1rem;
|
|
}
|
|
.status.waiting{background:#3f3f46;}
|
|
.locked{
|
|
margin-top:1rem;
|
|
padding:1rem;
|
|
background:#111827;
|
|
border-radius:.5rem;
|
|
border:1px solid #374151;
|
|
}
|
|
.locked h2{margin-top:0;color:#fbbf24;}
|
|
.locked p{margin:.3rem 0;}
|
|
.lyrics{
|
|
background:#111827;
|
|
border:1px solid #374151;
|
|
border-radius:.5rem;
|
|
padding:1rem 1.25rem;
|
|
margin:1rem 0;
|
|
line-height:1.3;
|
|
font-size:1rem;
|
|
}
|
|
.lyrics h2{
|
|
margin-top:0;
|
|
margin-bottom:.5rem;
|
|
color:#fbbf24;
|
|
font-size:1.2rem;
|
|
}
|
|
.lyrics .section-label{
|
|
color:#60a5fa;
|
|
font-weight:700;
|
|
display:block;
|
|
margin-top:.5rem;
|
|
margin-bottom:.1rem;
|
|
}
|
|
.lyrics .lyric-line{
|
|
display:block;
|
|
line-height:1.3;
|
|
margin:0;
|
|
padding:0;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🎧 Your Custom Theme Song</h1>
|
|
<p>Hi {{ req.name }}! Listen to both versions and pick the one you want.</p>
|
|
|
|
{% if req.suno_lyrics %}
|
|
<div class="lyrics">
|
|
<h2>🎵 Song Lyrics</h2>
|
|
{% for line in req.suno_lyrics.splitlines() %}
|
|
{% set line = line.strip() %}
|
|
{% if line and not (line.startswith('[') and line.endswith(']')) %}
|
|
<span class="lyric-line">{{ line }}</span>
|
|
{% elif not line %}
|
|
<br>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Version A player -->
|
|
<div class="player">
|
|
<h3>Version A</h3>
|
|
<audio controls controlsList="nodownload" id="player-a"></audio>
|
|
</div>
|
|
|
|
<!-- Version B player -->
|
|
<div class="player">
|
|
<h3>Version B</h3>
|
|
<audio controls controlsList="nodownload" id="player-b"></audio>
|
|
</div>
|
|
|
|
<script>
|
|
(function(){
|
|
// Audio sources are assigned via JS so they do not appear in static HTML source.
|
|
document.getElementById('player-a').src = "{{ url_for('stream_audio', token=req.player_token, version='a') }}";
|
|
document.getElementById('player-b').src = "{{ url_for('stream_audio', token=req.player_token, version='b') }}";
|
|
})();
|
|
</script>
|
|
|
|
{% if req.status == 'revisions_requested' %}
|
|
<!-- Customer asked for changes; lock the page and tell them to wait -->
|
|
<div class="locked waiting">
|
|
<h2>📝 Revision Requested</h2>
|
|
<p>You asked for changes. We will generate a new version and update this page.</p>
|
|
<p><strong>Your note:</strong> {{ req.revision_note }}</p>
|
|
</div>
|
|
|
|
{% elif req.status in ['awaiting_payment','paid','delivered'] %}
|
|
<!-- Customer already picked a version; show their choice and payment instruction -->
|
|
<div class="locked">
|
|
<h2>✅ Choice Received</h2>
|
|
<p>You selected: <strong>{% if req.customer_approved == 'both' %}Both Versions{% else %}Version {{ (req.customer_approved or 'none').upper() }}{% endif %}</strong></p>
|
|
{% if req.status == 'awaiting_payment' %}
|
|
<p>Please return to the booth to finalize payment and collect your files.</p>
|
|
{% elif req.status == 'paid' %}
|
|
<p>Payment recorded. Your files are being prepared.</p>
|
|
{% elif req.status == 'delivered' %}
|
|
<p>Delivered! Check your email for the MP3 attachment(s).</p>
|
|
{% if req.stems_link %}
|
|
<p style="margin-top:.8rem">
|
|
<a href="{{ req.stems_link }}" target="_blank" rel="noopener noreferrer" style="display:inline-block;padding:.7rem 1rem;background:#10b981;color:#000;border-radius:.5rem;text-decoration:none;font-weight:700;">Download stems / extras →</a>
|
|
</p>
|
|
<p style="font-size:.9rem;color:#9ca3af;">This link expires 3 months after delivery. Download soon.</p>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% else %}
|
|
<!-- Approval form: customer picks Version A, B, or both -->
|
|
<form method="POST" action="{{ url_for('approve', token=req.player_token) }}">
|
|
<input type="hidden" name="choice" id="choice">
|
|
<div class="actions">
|
|
<button type="submit" onclick="document.getElementById('choice').value='a'">I want Version A</button>
|
|
<button type="submit" onclick="document.getElementById('choice').value='b'">I want Version B</button>
|
|
<button type="submit" class="both" onclick="document.getElementById('choice').value='both'">I want both</button>
|
|
</div>
|
|
</form>
|
|
|
|
{% if revisions_left > 0 %}
|
|
<!-- Revision form: customer asks for changes -->
|
|
<form method="POST" action="{{ url_for('revise', token=req.player_token) }}">
|
|
<p style="color:#93c5fd;font-weight:600">Revisions remaining: {{ revisions_left }}</p>
|
|
<label for="revision_note">Or ask for changes:</label>
|
|
<textarea id="revision_note" name="revision_note" placeholder="e.g. make the chorus louder, swap a lyric..."></textarea>
|
|
<div class="actions">
|
|
<button type="submit" class="revision">Request Changes</button>
|
|
</div>
|
|
</form>
|
|
{% else %}
|
|
<p style="margin-top:1rem;color:#f87171;font-weight:600">No revisions remaining. Please speak to the booth operator if you need further changes.</p>
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
</div>
|
|
</body>
|
|
</html>
|