ui: convert admin request page to two-column layout
This commit is contained in:
parent
3e25c3638b
commit
3cb63a6480
1 changed files with 196 additions and 205 deletions
|
|
@ -6,15 +6,11 @@
|
||||||
<title>Request #{{ req.id }} — Admin</title>
|
<title>Request #{{ req.id }} — Admin</title>
|
||||||
<style>
|
<style>
|
||||||
/*
|
/*
|
||||||
Single-request admin detail page.
|
Two-column admin detail page.
|
||||||
Sections:
|
Left column: customer context + workflow.
|
||||||
1. Customer info (with revisions note if any)
|
Right column: operator tools + delivery.
|
||||||
2. Operator notes (internal, operator-only)
|
|
||||||
3. Generate and save Suno prompt
|
|
||||||
4. Upload Version A and Version B MP3s
|
|
||||||
5. Send preview email with private player link
|
|
||||||
6. Mark paid and deliver selected MP3(s)
|
|
||||||
*/
|
*/
|
||||||
|
*{box-sizing:border-box;}
|
||||||
body{
|
body{
|
||||||
font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
|
font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
|
||||||
background:#111827;
|
background:#111827;
|
||||||
|
|
@ -23,8 +19,8 @@
|
||||||
padding:1rem;
|
padding:1rem;
|
||||||
line-height:1.5;
|
line-height:1.5;
|
||||||
}
|
}
|
||||||
.container{max-width:900px;margin:0 auto;}
|
.container{max-width:1200px;margin:0 auto;}
|
||||||
h1,h2{color:#60a5fa;}
|
h1,h2{color:#60a5fa;margin-top:0;}
|
||||||
a{color:#93c5fd;}
|
a{color:#93c5fd;}
|
||||||
.section{
|
.section{
|
||||||
background:#1f2937;
|
background:#1f2937;
|
||||||
|
|
@ -43,7 +39,7 @@
|
||||||
box-sizing:border-box;
|
box-sizing:border-box;
|
||||||
font:inherit;
|
font:inherit;
|
||||||
}
|
}
|
||||||
textarea{min-height:120px;}
|
textarea{min-height:100px;}
|
||||||
button{
|
button{
|
||||||
padding:.7rem 1rem;
|
padding:.7rem 1rem;
|
||||||
border:none;
|
border:none;
|
||||||
|
|
@ -66,13 +62,6 @@
|
||||||
margin-bottom:1rem;
|
margin-bottom:1rem;
|
||||||
}
|
}
|
||||||
.flash.error{background:#450a0a;}
|
.flash.error{background:#450a0a;}
|
||||||
.info-grid{display:grid;grid-template-columns:1fr 1fr;gap:1rem;}
|
|
||||||
.info-grid div{
|
|
||||||
background:#111827;
|
|
||||||
padding:.6rem;
|
|
||||||
border-radius:.5rem;
|
|
||||||
}
|
|
||||||
.approved-box{font-size:1.2rem;font-weight:bold;color:#fbbf24;}
|
|
||||||
.status-badge{
|
.status-badge{
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
padding:.25rem .6rem;
|
padding:.25rem .6rem;
|
||||||
|
|
@ -118,6 +107,17 @@
|
||||||
color:#9ca3af;
|
color:#9ca3af;
|
||||||
margin-left:1.8rem;
|
margin-left:1.8rem;
|
||||||
}
|
}
|
||||||
|
.approved-box{font-size:1.2rem;font-weight:bold;color:#fbbf24;}
|
||||||
|
|
||||||
|
/* Two-column layout */
|
||||||
|
.two-col{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:1fr 1fr;
|
||||||
|
gap:1rem;
|
||||||
|
}
|
||||||
|
@media(max-width:900px){
|
||||||
|
.two-col{grid-template-columns:1fr;}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -131,200 +131,191 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
<!-- Section 1: Customer info summary -->
|
<div class="two-col">
|
||||||
<div class="section">
|
<!-- LEFT COLUMN -->
|
||||||
<h2>Customer Info</h2>
|
<div class="left">
|
||||||
<p class="copy-hint">Operators can correct the customer's email address here.</p>
|
<!-- Customer Info -->
|
||||||
<form method="POST">
|
<div class="section">
|
||||||
<input type="hidden" name="action" value="update_customer_email">
|
<h2>Customer Info</h2>
|
||||||
<label for="customer_email">Email address</label>
|
<p class="copy-hint">Operators can correct the customer's email address here.</p>
|
||||||
<input type="email" id="customer_email" name="email" value="{{ req.email }}" required>
|
<form method="POST">
|
||||||
<div class="actions">
|
<input type="hidden" name="action" value="update_customer_email">
|
||||||
<button type="submit" class="secondary">Update Email</button>
|
<label for="customer_email">Email address</label>
|
||||||
</div>
|
<input type="email" id="customer_email" name="email" value="{{ req.email }}" required>
|
||||||
</form>
|
<div class="actions">
|
||||||
|
<button type="submit" class="secondary">Update Email</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
<div class="info-grid" style="margin-top:1rem">
|
<p style="margin-top:1rem"><strong>Request #:</strong> {{ req.id }}</p>
|
||||||
<div><strong>Request #:</strong> {{ req.id }}</div>
|
<p><strong>Status:</strong> <span class="status-badge">{{ statuses[req.status] }}</span></p>
|
||||||
<div><strong>Status:</strong> {{ statuses[req.status] }}</div>
|
<p><strong>Hobbies:</strong><br>{{ req.hobbies or '-' }}</p>
|
||||||
</div>
|
<p><strong>Notable facts:</strong><br>{{ req.notable_facts or '-' }}</p>
|
||||||
<p><strong>Hobbies:</strong><br>{{ req.hobbies or '-' }}</p>
|
<p><strong>Style / genre:</strong><br>{{ req.style_genre or '-' }}</p>
|
||||||
<p><strong>Notable facts:</strong><br>{{ req.notable_facts or '-' }}</p>
|
<p><strong>Preferred singer voice / gender:</strong><br>{{ req.vocal_gender or 'No preference' }}</p>
|
||||||
<p><strong>Style / genre:</strong><br>{{ req.style_genre or '-' }}</p>
|
<p><strong>Extra requests:</strong><br>{{ req.extra_requests or '-' }}</p>
|
||||||
<p><strong>Preferred singer voice / gender:</strong><br>{{ req.vocal_gender or 'No preference' }}</p>
|
|
||||||
<p><strong>Extra requests:</strong><br>{{ req.extra_requests or '-' }}</p>
|
|
||||||
|
|
||||||
{% if req.revision_note %}
|
{% if req.revision_note %}
|
||||||
<!-- Revisions requested by the customer -->
|
<div class="revision-note">
|
||||||
<div class="revision-note">
|
<h3>📝 Revisions Requested{% if req.revision_count %}<span style="float:right">Revision #{{ req.revision_count }}</span>{% endif %}</h3>
|
||||||
<h3>📝 Revisions Requested{% if req.revision_count %}<span style="float:right">Revision #{{ req.revision_count }}</span>{% endif %}</h3>
|
<p>{{ req.revision_note }}</p>
|
||||||
<p>{{ req.revision_note }}</p>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Section 2: Operator notes -->
|
|
||||||
<div class="section">
|
|
||||||
<h2>Operator Notes</h2>
|
|
||||||
<p class="copy-hint">Internal notes for the booth team. Customers cannot see this.</p>
|
|
||||||
<form method="POST">
|
|
||||||
<input type="hidden" name="action" value="save_operator_notes">
|
|
||||||
<label for="operator_notes">Notes</label>
|
|
||||||
<textarea id="operator_notes" name="operator_notes" rows="4" placeholder="e.g. paid cash, VIP friend, follow up...">{{ req.operator_notes or '' }}</textarea>
|
|
||||||
<div class="actions">
|
|
||||||
<button type="submit" class="secondary">Save Notes</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Section 3: Generate Suno prompt -->
|
|
||||||
<div class="section">
|
|
||||||
<h2>1. Generate & Save Suno Prompt</h2>
|
|
||||||
|
|
||||||
<button type="button" onclick="copyPromptForHermes()">Copy customer info for Hermes</button>
|
|
||||||
<p class="copy-hint">Paste Hermes' Title, Style, and Lyrics directly into the fields below, then save.</p>
|
|
||||||
|
|
||||||
<form method="POST">
|
|
||||||
<input type="hidden" name="action" value="save_prompt">
|
|
||||||
|
|
||||||
<label for="suno_title">Title</label>
|
|
||||||
<input type="text" id="suno_title" name="suno_title" value="{{ req.suno_title or '' }}">
|
|
||||||
|
|
||||||
<label for="suno_style">Style</label>
|
|
||||||
<textarea id="suno_style" name="suno_style">{{ req.suno_style or '' }}</textarea>
|
|
||||||
|
|
||||||
<label for="suno_lyrics">Lyrics (with metatags)</label>
|
|
||||||
<textarea id="suno_lyrics" name="suno_lyrics" rows="12">{{ req.suno_lyrics or '' }}</textarea>
|
|
||||||
|
|
||||||
<div class="actions">
|
|
||||||
<button type="button" class="secondary" onclick="copyToClipboard('suno_title', 'Title')">Copy Title</button>
|
|
||||||
<button type="button" class="secondary" onclick="copyToClipboard('suno_style', 'Style')">Copy Style</button>
|
|
||||||
<button type="button" class="secondary" onclick="copyToClipboard('suno_lyrics', 'Lyrics')">Copy Lyrics</button>
|
|
||||||
<button type="submit">Save Prompt</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Section 4: Upload songs -->
|
|
||||||
<div class="section">
|
|
||||||
<h2>2. Upload Songs</h2>
|
|
||||||
<p>
|
|
||||||
Version A:
|
|
||||||
{% if req.song_a_path and file_exists(req.song_a_path) %}
|
|
||||||
<span class="status-badge ok">✅ Uploaded</span> {{ basename(req.song_a_path) }}
|
|
||||||
{% else %}
|
|
||||||
<span class="status-badge missing">❌ Not uploaded</span>
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Version B:
|
|
||||||
{% if req.song_b_path and file_exists(req.song_b_path) %}
|
|
||||||
<span class="status-badge ok">✅ Uploaded</span> {{ basename(req.song_b_path) }}
|
|
||||||
{% else %}
|
|
||||||
<span class="status-badge missing">❌ Not uploaded</span>
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form method="POST" enctype="multipart/form-data">
|
|
||||||
<input type="hidden" name="action" value="upload_songs">
|
|
||||||
<label for="song_a">Version A MP3</label>
|
|
||||||
<input type="file" id="song_a" name="song_a" accept="audio/mpeg,.mp3">
|
|
||||||
<label for="song_b">Version B MP3</label>
|
|
||||||
<input type="file" id="song_b" name="song_b" accept="audio/mpeg,.mp3">
|
|
||||||
<div class="actions">
|
|
||||||
<button type="submit">Upload Songs</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Section 5: Notify customer -->
|
|
||||||
<div class="section">
|
|
||||||
<h2>3. Notify Customer</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Preview email:
|
|
||||||
{% if req.preview_sent_at %}
|
|
||||||
<span class="status-badge sent">✅ Sent</span> {{ req.preview_sent_at }}
|
|
||||||
{% else %}
|
|
||||||
<span class="status-badge missing">❌ Not sent yet</span>
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
<p>Both songs must be uploaded first.</p>
|
|
||||||
<form method="POST">
|
|
||||||
<input type="hidden" name="action" value="notify_customer">
|
|
||||||
<button type="submit" {% if not (req.song_a_path and req.song_b_path) %}disabled{% endif %}>Send Preview Link</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% if req.song_a_path and req.song_b_path %}
|
|
||||||
<p style="margin-top:.8rem">
|
|
||||||
Operator preview link:
|
|
||||||
<a href="{{ url_for('play', token=req.player_token) }}" target="_blank" rel="noopener noreferrer">Open customer player in new tab →</a>
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Section 6: Payment & delivery -->
|
|
||||||
<div class="section">
|
|
||||||
<h2>4. Payment & Delivery</h2>
|
|
||||||
<p>
|
|
||||||
Customer approved:
|
|
||||||
{% if req.customer_approved == 'none' %}
|
|
||||||
<span class="approved-box">Nothing yet</span>
|
|
||||||
{% else %}
|
|
||||||
<span class="approved-box">{{ req.customer_approved.upper() }}</span>
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Delivery email:
|
|
||||||
{% if req.delivery_sent_at %}
|
|
||||||
<span class="status-badge sent">✅ Sent</span> {{ req.delivery_sent_at }}
|
|
||||||
{% else %}
|
|
||||||
<span class="status-badge missing">❌ Not sent yet</span>
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
<form method="POST">
|
|
||||||
<input type="hidden" name="action" value="mark_paid_deliver">
|
|
||||||
|
|
||||||
<p style="color:#93c5fd;font-weight:600">Select files to deliver:</p>
|
|
||||||
|
|
||||||
{% set all_files = [] %}
|
|
||||||
{% if req.song_a_path and file_exists(req.song_a_path) %}
|
|
||||||
{% set _ = all_files.append(req.song_a_path) %}
|
|
||||||
{% endif %}
|
|
||||||
{% if req.song_b_path and file_exists(req.song_b_path) %}
|
|
||||||
{% set _ = all_files.append(req.song_b_path) %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<!-- List all MP3 files in the request's upload folder (includes revisions) -->
|
|
||||||
{% set files_to_show = all_files + extra_files %}
|
|
||||||
{% for fpath in files_to_show %}
|
|
||||||
<div class="file-select">
|
|
||||||
<input type="checkbox" id="file_{{ loop.index }}" name="deliver_file" value="{{ fpath }}"
|
|
||||||
{% if fpath in all_files %}checked{% endif %}>
|
|
||||||
<label for="file_{{ loop.index }}">{{ basename(fpath) }}</label>
|
|
||||||
{% if not (fpath == req.song_a_path or fpath == req.song_b_path) %}
|
|
||||||
<div class="old-rev">archived / revision file</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% endif %}
|
||||||
<p>No files available. Upload songs first.</p>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<label for="square_payment_ref">Square Payment Reference</label>
|
|
||||||
<input type="text" id="square_payment_ref" name="square_payment_ref" placeholder="e.g. sq0idp-... or receipt number">
|
|
||||||
<div class="actions">
|
|
||||||
<button type="submit" class="success">Mark Paid & Deliver</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
|
<!-- Operator Notes -->
|
||||||
|
<div class="section">
|
||||||
|
<h2>Operator Notes</h2>
|
||||||
|
<p class="copy-hint">Internal notes for the booth team. Customers cannot see this.</p>
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="action" value="save_operator_notes">
|
||||||
|
<label for="operator_notes">Notes</label>
|
||||||
|
<textarea id="operator_notes" name="operator_notes" rows="4" placeholder="e.g. paid cash, VIP friend, follow up...">{{ req.operator_notes or '' }}</textarea>
|
||||||
|
<div class="actions">
|
||||||
|
<button type="submit" class="secondary">Save Notes</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- RIGHT COLUMN -->
|
||||||
|
<div class="right">
|
||||||
|
<!-- Generate Suno Prompt -->
|
||||||
|
<div class="section">
|
||||||
|
<h2>1. Generate & Save Suno Prompt</h2>
|
||||||
|
<button type="button" onclick="copyPromptForHermes()">Copy customer info for Hermes</button>
|
||||||
|
<p class="copy-hint">Paste Hermes' Title, Style, and Lyrics directly into the fields below, then save.</p>
|
||||||
|
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="action" value="save_prompt">
|
||||||
|
<label for="suno_title">Title</label>
|
||||||
|
<input type="text" id="suno_title" name="suno_title" value="{{ req.suno_title or '' }}">
|
||||||
|
<label for="suno_style">Style</label>
|
||||||
|
<textarea id="suno_style" name="suno_style">{{ req.suno_style or '' }}</textarea>
|
||||||
|
<label for="suno_lyrics">Lyrics (with metatags)</label>
|
||||||
|
<textarea id="suno_lyrics" name="suno_lyrics" rows="10">{{ req.suno_lyrics or '' }}</textarea>
|
||||||
|
<div class="actions">
|
||||||
|
<button type="button" class="secondary" onclick="copyToClipboard('suno_title', 'Title')">Copy Title</button>
|
||||||
|
<button type="button" class="secondary" onclick="copyToClipboard('suno_style', 'Style')">Copy Style</button>
|
||||||
|
<button type="button" class="secondary" onclick="copyToClipboard('suno_lyrics', 'Lyrics')">Copy Lyrics</button>
|
||||||
|
<button type="submit">Save Prompt</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Upload Songs -->
|
||||||
|
<div class="section">
|
||||||
|
<h2>2. Upload Songs</h2>
|
||||||
|
<p>
|
||||||
|
Version A:
|
||||||
|
{% if req.song_a_path and file_exists(req.song_a_path) %}
|
||||||
|
<span class="status-badge ok">✅ Uploaded</span> {{ basename(req.song_a_path) }}
|
||||||
|
{% else %}
|
||||||
|
<span class="status-badge missing">❌ Not uploaded</span>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Version B:
|
||||||
|
{% if req.song_b_path and file_exists(req.song_b_path) %}
|
||||||
|
<span class="status-badge ok">✅ Uploaded</span> {{ basename(req.song_b_path) }}
|
||||||
|
{% else %}
|
||||||
|
<span class="status-badge missing">❌ Not uploaded</span>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="POST" enctype="multipart/form-data">
|
||||||
|
<input type="hidden" name="action" value="upload_songs">
|
||||||
|
<label for="song_a">Version A MP3</label>
|
||||||
|
<input type="file" id="song_a" name="song_a" accept="audio/mpeg,.mp3">
|
||||||
|
<label for="song_b">Version B MP3</label>
|
||||||
|
<input type="file" id="song_b" name="song_b" accept="audio/mpeg,.mp3">
|
||||||
|
<div class="actions">
|
||||||
|
<button type="submit">Upload Songs</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Notify Customer -->
|
||||||
|
<div class="section">
|
||||||
|
<h2>3. Notify Customer</h2>
|
||||||
|
<p>
|
||||||
|
Preview email:
|
||||||
|
{% if req.preview_sent_at %}
|
||||||
|
<span class="status-badge sent">✅ Sent</span> {{ req.preview_sent_at }}
|
||||||
|
{% else %}
|
||||||
|
<span class="status-badge missing">❌ Not sent yet</span>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
<p>Both songs must be uploaded first.</p>
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="action" value="notify_customer">
|
||||||
|
<button type="submit" {% if not (req.song_a_path and req.song_b_path) %}disabled{% endif %}>Send Preview Link</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% if req.song_a_path and req.song_b_path %}
|
||||||
|
<p style="margin-top:.8rem">
|
||||||
|
Operator preview link:
|
||||||
|
<a href="{{ url_for('play', token=req.player_token) }}" target="_blank" rel="noopener noreferrer">Open customer player in new tab →</a>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Payment & Delivery -->
|
||||||
|
<div class="section">
|
||||||
|
<h2>4. Payment & Delivery</h2>
|
||||||
|
<p>
|
||||||
|
Customer approved:
|
||||||
|
{% if req.customer_approved == 'none' %}
|
||||||
|
<span class="approved-box">Nothing yet</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="approved-box">{{ req.customer_approved.upper() }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Delivery email:
|
||||||
|
{% if req.delivery_sent_at %}
|
||||||
|
<span class="status-badge sent">✅ Sent</span> {{ req.delivery_sent_at }}
|
||||||
|
{% else %}
|
||||||
|
<span class="status-badge missing">❌ Not sent yet</span>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="action" value="mark_paid_deliver">
|
||||||
|
|
||||||
|
<p style="color:#93c5fd;font-weight:600">Select files to deliver:</p>
|
||||||
|
{% set all_files = [] %}
|
||||||
|
{% if req.song_a_path and file_exists(req.song_a_path) %}
|
||||||
|
{% set _ = all_files.append(req.song_a_path) %}
|
||||||
|
{% endif %}
|
||||||
|
{% if req.song_b_path and file_exists(req.song_b_path) %}
|
||||||
|
{% set _ = all_files.append(req.song_b_path) %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% set files_to_show = all_files + extra_files %}
|
||||||
|
{% for fpath in files_to_show %}
|
||||||
|
<div class="file-select">
|
||||||
|
<input type="checkbox" id="file_{{ loop.index }}" name="deliver_file" value="{{ fpath }}"
|
||||||
|
{% if fpath in all_files %}checked{% endif %}>
|
||||||
|
<label for="file_{{ loop.index }}">{{ basename(fpath) }}</label>
|
||||||
|
{% if not (fpath == req.song_a_path or fpath == req.song_b_path) %}
|
||||||
|
<div class="old-rev">archived / revision file</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<p>No files available. Upload songs first.</p>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<label for="square_payment_ref">Square Payment Reference</label>
|
||||||
|
<input type="text" id="square_payment_ref" name="square_payment_ref" placeholder="e.g. sq0idp-... or receipt number">
|
||||||
|
<div class="actions">
|
||||||
|
<button type="submit" class="success">Mark Paid & Deliver</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/*
|
|
||||||
Client-side helpers for the admin detail page.
|
|
||||||
- copyPromptForHermes(): builds a plain-text prompt and copies it.
|
|
||||||
- copyToClipboard(): copies a field to the clipboard for pasting into Suno.
|
|
||||||
*/
|
|
||||||
|
|
||||||
function copyPromptForHermes() {
|
function copyPromptForHermes() {
|
||||||
const data = {
|
const data = {
|
||||||
name: {{ req.name | tojson }},
|
name: {{ req.name | tojson }},
|
||||||
|
|
|
||||||
Reference in a new issue