Diff -- /var/www/vhosts/3dshawn.com/crm.3dshawn.com/TEMPLATES/cf_admin_test_cards.html

O Operator
Diff

/var/www/vhosts/3dshawn.com/crm.3dshawn.com/TEMPLATES/cf_admin_test_cards.html

added on local at 2026-07-11 18:31:58

Added
+0
lines
Removed
-0
lines
Context
501
unchanged
Blobs
from 504ad13f77d0
to 504ad13f77d0
Restore this content →
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.
11<style>
22 /* Self-contained module styling so this template renders the same on
33 every site regardless of host CSS. Falls back to local CSS variables
44 when defined; otherwise uses safe dark-theme defaults. */
55 .module {
66 background:var(--col-surface, #161924);
77 border:1px solid var(--col-border, rgba(255,255,255,.08));
88 border-radius:12px;
99 overflow:hidden;
1010 }
1111 .module-head {
1212 display:flex; align-items:center; gap:14px;
1313 padding:14px 18px;
1414 border-bottom:1px solid var(--col-border, rgba(255,255,255,.06));
1515 background:rgba(255,255,255,.015);
1616 }
1717 .module-head .left {
1818 display:flex; align-items:center; gap:14px; flex:1;
1919 }
2020 .module-icon {
2121 width:36px; height:36px; flex:0 0 auto;
2222 border-radius:8px;
2323 background:rgba(99,102,241,.15);
2424 color:#a5b4fc;
2525 display:flex; align-items:center; justify-content:center;
2626 }
2727 .module-title {
2828 font-size:15px; font-weight:600;
2929 color:var(--col-text, #f1f5f9);
3030 }
3131 .module-sub {
3232 font-size:12px; color:var(--col-text-3, #94a3b8);
3333 margin-top:2px; line-height:1.5;
3434 }
3535 .module-body { padding:14px 18px; }
3636 .mb-3 { margin-bottom:18px; }
3737 .page-head { margin-bottom:18px; }
3838 .page-eyebrow {
3939 display:inline-flex; align-items:center; gap:6px;
4040 font-size:11px; color:#a5b4fc;
4141 text-transform:uppercase; letter-spacing:.08em;
4242 font-weight:600; margin-bottom:8px;
4343 }
4444 .page-eyebrow .dot {
4545 width:6px; height:6px; border-radius:50%;
4646 background:#a5b4fc;
4747 }
4848 .page-title {
4949 margin:0 0 6px; font-size:26px; letter-spacing:-.01em;
5050 color:var(--col-text, #f1f5f9);
5151 }
5252 .page-subtitle {
5353 margin:0 0 0; font-size:13px; color:var(--col-text-2, #cbd5e1);
5454 line-height:1.55; max-width:780px;
5555 }
5656 .tc-card-row {
5757 display:flex; align-items:center; gap:14px;
5858 padding:14px 16px;
5959 background:var(--col-surface-1);
6060 border:1px solid var(--col-border);
6161 border-radius:10px;
6262 margin-bottom:10px;
6363 }
6464 .tc-card-row.is-decline { border-left:3px solid #ef4444; }
6565 .tc-card-row.is-approve { border-left:3px solid #4ade80; }
6666 .tc-card-row.is-inactive {
6767 opacity:.65;
6868 background:rgba(255,255,255,.02);
6969 border-left:3px solid #6b7280;
7070 }
7171 .tc-card-glyph {
7272 width:48px; height:32px; border-radius:4px;
7373 display:grid; place-items:center; flex:0 0 auto;
7474 font:700 10px/1 var(--font-mono, monospace);
7575 color:#fff; text-transform:uppercase;
7676 letter-spacing:1px;
7777 }
7878 .tc-card-glyph.b-visa { background:linear-gradient(135deg, #1a1f71, #2a3499); }
7979 .tc-card-glyph.b-mastercard { background:linear-gradient(135deg, #eb001b, #f79e1b); }
8080 .tc-card-glyph.b-amex { background:linear-gradient(135deg, #2e77bb, #1c4f8c); }
8181 .tc-card-glyph.b-discover { background:linear-gradient(135deg, #ff6000, #d44d00); }
8282 .tc-card-glyph.b-jcb { background:linear-gradient(135deg, #0e4c92, #0a3a72); }
8383 .tc-card-glyph.b-diners { background:linear-gradient(135deg, #0079be, #005c8e); }
8484 .tc-card-glyph.b-unionpay,
8585 .tc-card-glyph.b-default { background:linear-gradient(135deg, #475569, #334155); }
8686 .tc-card-num {
8787 font:600 14px/1.2 var(--font-mono, monospace);
8888 color:var(--col-text);
8989 letter-spacing:0.5px;
9090 }
9191 .tc-card-meta { font-size:12px; color:var(--col-text-3); margin-top:2px; }
9292 .tc-pill {
9393 font:700 10px/1 inherit;
9494 letter-spacing:1px; text-transform:uppercase;
9595 padding:4px 8px; border-radius:999px;
9696 }
9797 .tc-pill.is-approve { background:rgba(34,197,94,0.16); color:#86efac; border:1px solid rgba(34,197,94,0.3); }
9898 .tc-pill.is-decline { background:rgba(239,68,68,0.16); color:#fca5a5; border:1px solid rgba(239,68,68,0.3); }
9999 .tc-pill.is-inactive { background:rgba(107,114,128,0.18); color:#cbd5e1; border:1px solid rgba(107,114,128,0.35); }
100100 .tc-owner-pill {
101101 font-size:11px; padding:3px 8px; border-radius:999px;
102102 background:rgba(139,92,246,0.13); color:#c4b5fd;
103103 border:1px solid rgba(139,92,246,0.30);
104104 margin-left:6px;
105105 }
106106 .tc-card-actions { margin-left:auto; display:flex; gap:6px; align-items:center; flex-wrap:wrap; }
107107 .tc-quickform { display:flex; gap:6px; align-items:center; margin:0; }
108108 .tc-quickform .select, .tc-quickform .input { padding:6px 8px; font-size:12px; min-width:120px; }
109109 .tc-create-form {
110110 display:grid;
111111 grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
112112 gap:12px;
113113 margin-top:10px;
114114 }
115115 .tc-presets { display:flex; flex-wrap:wrap; gap:8px; margin-top:10px; }
116116 .tc-preset-btn {
117117 background:rgba(255,255,255,0.03);
118118 border:1px solid var(--col-border);
119119 color:var(--col-text-2);
120120 padding:6px 10px; border-radius:6px; font-size:12px;
121121 cursor:pointer; transition:background 0.12s, color 0.12s;
122122 }
123123 .tc-preset-btn:hover { background:rgba(99,102,241,0.12); color:#fff; }
124124 .tc-section-title {
125125 font-size:11px; text-transform:uppercase; letter-spacing:1px;
126126 color:var(--col-text-3); font-weight:700;
127127 margin:18px 0 10px;
128128 }
129129 .tc-bulk-row {
130130 display:flex; align-items:center; gap:10px;
131131 padding:10px 14px;
132132 background:rgba(239,68,68,0.04);
133133 border:1px solid rgba(239,68,68,0.18);
134134 border-radius:8px;
135135 margin-bottom:8px;
136136 }
137137 .tc-bulk-row .name { color:#fca5a5; font-weight:600; font-size:13px; }
138138 .tc-bulk-row .count { color:#cbd5e1; font-size:12px; }
139139 .tc-audit-row {
140140 display:flex; align-items:center; gap:12px;
141141 padding:8px 12px;
142142 border-bottom:1px solid rgba(255,255,255,.05);
143143 font-size:12px;
144144 }
145145 .tc-audit-row:last-child { border-bottom:none; }
146146 .tc-audit-action {
147147 font-size:10px; padding:2px 7px; border-radius:4px;
148148 text-transform:uppercase; letter-spacing:.06em; font-weight:700;
149149 background:rgba(99,102,241,.15); color:#a5b4fc;
150150 }
151151 .tc-audit-when { color:var(--col-text-3); font-family:var(--font-mono,monospace); font-size:11px; margin-left:auto; }
152152</style>
153153
154154<div class="page-head">
155155 <div>
156156 <span class="page-eyebrow"><span class="dot"></span> Admin console</span>
157157 <h1 class="page-title">Sandbox Test Credit Cards</h1>
158158 <p class="page-subtitle">Fake credit cards for end-to-end billing QA. Cards attached here bypass Stripe entirely &mdash; a simulated PaymentIntent is returned with the behavior you choose. Tag cards by owner so you can deactivate someone&rsquo;s set in one click when they leave the team.</p>
159159 </div>
160160</div>
161161
162162[if:$has_flash]
163163<div class="banner [if:$flash_is_ok]success[/if][if:$flash_is_err]error[/if] mb-3" style="padding:12px 14px;border-radius:10px">
164164 <div class="text-sm fw-600">$flash_msg</div>
165165</div>
166166[/if]
167167
168168[if:$schema_missing]
169169<div class="banner warn mb-3" style="padding:12px 14px;border-radius:10px;background:rgba(245,158,11,0.10);border:1px solid rgba(245,158,11,0.30)">
170170 <strong>Schema not installed.</strong> Run the latest <code>_test_cards_migration.sql</code> to create the <code>admin_test_cards</code> table.
171171</div>
172172[/if]
173173
174174[if:$schema_ready]
175175
176176<!-- Bulk deactivate-by-owner -->
177177[if:$has_owner_groups]
178178<div class="module mb-3">
179179 <div class="module-head">
180180 <div class="left">
181181 <div class="module-icon" style="background:rgba(239,68,68,0.12);color:#fca5a5"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" style="display:inline-block;vertical-align:middle"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="8.5" cy="7" r="4"/><line x1="17" y1="8" x2="22" y2="13"/><line x1="22" y1="8" x2="17" y2="13"/></svg></div>
182182 <div>
183183 <div class="module-title">Offboard a team member</div>
184184 <div class="module-sub">If someone leaves, deactivate every active card they own in one click. Cards stay in the audit log so you can see who and when.</div>
185185 </div>
186186 </div>
187187 </div>
188188 <div class="module-body">
189189 [loop:@owner_groups]
190190 <form method="POST" action="/admin_test_cards.cgi" class="tc-bulk-row"
191191 data-confirm-title="Deactivate all of $loop1.owner_name's test cards?"
192192 data-confirm-message="All $loop1.n active card(s) owned by $loop1.owner_name will be marked inactive and detached from any user account they're attached to. You can reactivate them later if needed."
193193 data-confirm-label="Deactivate $loop1.n card(s)"
194194 data-confirm-style="danger">
195195 <input type="hidden" name="action" value="bulk_deactivate_owner">
196196 <input type="hidden" name="owner_user_id" value="$loop1.owner_user_id">
197197 <div class="name">$loop1.owner_name</div>
198198 <div class="count">owns $loop1.n active card(s)</div>
199199 <button type="submit" class="btn btn-danger btn-sm" style="margin-left:auto;font-size:11px">Deactivate all</button>
200200 </form>
201201 [/loop]
202202 </div>
203203</div>
204204[/if]
205205
206206<!-- Active cards -->
207207<div class="module mb-3">
208208 <div class="module-head">
209209 <div class="left">
210210 <div class="module-icon"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" style="display:inline-block;vertical-align:middle"><rect x="2" y="5" width="20" height="14" rx="2"/><line x1="2" y1="10" x2="22" y2="10"/></svg></div>
211211 <div>
212212 <div class="module-title">Active test cards ($n_active)</div>
213213 <div class="module-sub">Click <strong>Assign to me</strong> to make any card your own default. The next upgrade or charge runs as the simulated behavior.</div>
214214 </div>
215215 </div>
216216 </div>
217217 <div class="module-body">
218218 [if:$has_active]
219219 [loop:@cards_active]
220220 <div class="tc-card-row [if:$loop1.is_approve]is-approve[/if][if:$loop1.is_decline]is-decline[/if]">
221221 <div class="tc-card-glyph b-$loop1.brand">$loop1.brand_label</div>
222222 <div>
223223 <div class="tc-card-num">$loop1.number_full[if:$loop1.has_owner]<span class="tc-owner-pill">owner: $loop1.owner_name</span>[/if]</div>
224224 <div class="tc-card-meta">$loop1.holder_name &middot; exp $loop1.exp_display &middot; cvc $loop1.cvc[if:$loop1.has_label] &middot; $loop1.label[/if]</div>
225225 </div>
226226 <div class="tc-card-actions">
227227 <span class="tc-pill [if:$loop1.is_approve]is-approve[/if][if:$loop1.is_decline]is-decline[/if]">$loop1.behavior_label</span>
228228
229229 <form method="POST" action="/admin_test_cards.cgi" class="tc-quickform">
230230 <input type="hidden" name="action" value="update_behavior">
231231 <input type="hidden" name="card_id" value="$loop1.id">
232232 <select class="select" name="behavior" onchange="this.form.submit()" title="Change simulated behavior">
233233 [loop:@behavior_options]<option value="$loop2.value"$loop2.selected_attr>$loop2.label</option>[/loop]
234234 </select>
235235 </form>
236236
237237 <form method="POST" action="/admin_test_cards.cgi" class="tc-quickform">
238238 <input type="hidden" name="action" value="assign">
239239 <input type="hidden" name="card_id" value="$loop1.id">
240240 <input type="hidden" name="assign_to" value="me">
241241 <button type="submit" class="btn btn-secondary btn-sm" style="font-size:11px">Assign to me</button>
242242 </form>
243243
244244 [if:$has_users]
245245 <form method="POST" action="/admin_test_cards.cgi" class="tc-quickform">
246246 <input type="hidden" name="action" value="assign">
247247 <input type="hidden" name="card_id" value="$loop1.id">
248248 <select class="select" name="target_user_id" required>
249249 <option value="">Assign to user...</option>
250250 [loop:@user_options]<option value="$loop2.id">$loop2.label</option>[/loop]
251251 </select>
252252 <button type="submit" class="btn btn-secondary btn-sm" style="font-size:11px">Assign</button>
253253 </form>
254254 [/if]
255255
256256 <form method="POST" action="/admin_test_cards.cgi"
257257 data-confirm-title="Deactivate this test card?"
258258 data-confirm-message="The card is marked inactive and detached from every user it was attached to. You can reactivate it later. Use Delete if you want it gone for good."
259259 data-confirm-label="Deactivate"
260260 data-confirm-style="warn"
261261 style="margin:0">
262262 <input type="hidden" name="action" value="deactivate">
263263 <input type="hidden" name="card_id" value="$loop1.id">
264264 <button type="submit" class="btn btn-secondary btn-sm" style="font-size:11px;background:rgba(245,158,11,.12);border-color:rgba(245,158,11,.3);color:#fcd34d">Deactivate</button>
265265 </form>
266266
267267 <form method="POST" action="/admin_test_cards.cgi"
268268 data-confirm-title="Delete this test credit card?"
269269 data-confirm-message="The card is removed and detached from every user it was attached to. The audit log keeps a record. This is permanent."
270270 data-confirm-label="Delete card"
271271 data-confirm-style="danger"
272272 style="margin:0">
273273 <input type="hidden" name="action" value="delete">
274274 <input type="hidden" name="card_id" value="$loop1.id">
275275 <button type="submit" class="btn btn-danger btn-sm" style="font-size:11px">Delete</button>
276276 </form>
277277 </div>
278278 </div>
279279 [/loop]
280280 [/if]
281281 [if:!$has_active]
282282 <div class="text-sm text-dim" style="text-align:center;padding:24px 12px">No active test credit cards. Create one below or use a preset to get started.</div>
283283 [/if]
284284 </div>
285285</div>
286286
287287<!-- Inactive cards -->
288288[if:$has_inactive]
289289<div class="module mb-3">
290290 <div class="module-head">
291291 <div class="left">
292292 <div class="module-icon" style="background:rgba(107,114,128,.15);color:#cbd5e1"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" style="display:inline-block;vertical-align:middle"><rect x="2" y="5" width="20" height="14" rx="2"/><line x1="2" y1="10" x2="22" y2="10"/><line x1="2" y1="2" x2="22" y2="22"/></svg></div>
293293 <div>
294294 <div class="module-title">Deactivated cards ($n_inactive)</div>
295295 <div class="module-sub">Kept for audit history. Reactivate to bring one back, or delete to remove permanently.</div>
296296 </div>
297297 </div>
298298 </div>
299299 <div class="module-body">
300300 [loop:@cards_inactive]
301301 <div class="tc-card-row is-inactive">
302302 <div class="tc-card-glyph b-$loop1.brand">$loop1.brand_label</div>
303303 <div>
304304 <div class="tc-card-num">$loop1.number_full[if:$loop1.has_owner]<span class="tc-owner-pill">owner: $loop1.owner_name</span>[/if]</div>
305305 <div class="tc-card-meta">$loop1.holder_name &middot; exp $loop1.exp_display[if:$loop1.has_deact] &middot; deactivated by $loop1.deact_name on <span class="ts" data-ts="$loop1.deact_epoch" data-fmt="datetime">$loop1.deact_at</span>[/if]</div>
306306 </div>
307307 <div class="tc-card-actions">
308308 <span class="tc-pill is-inactive">Inactive</span>
309309
310310 <form method="POST" action="/admin_test_cards.cgi" style="margin:0">
311311 <input type="hidden" name="action" value="reactivate">
312312 <input type="hidden" name="card_id" value="$loop1.id">
313313 <button type="submit" class="btn btn-secondary btn-sm" style="font-size:11px;background:rgba(34,197,94,.12);border-color:rgba(34,197,94,.3);color:#86efac">Reactivate</button>
314314 </form>
315315
316316 <form method="POST" action="/admin_test_cards.cgi"
317317 data-confirm-title="Delete this test credit card?"
318318 data-confirm-message="Permanently removes the card. The audit log entries stay."
319319 data-confirm-label="Delete card"
320320 data-confirm-style="danger"
321321 style="margin:0">
322322 <input type="hidden" name="action" value="delete">
323323 <input type="hidden" name="card_id" value="$loop1.id">
324324 <button type="submit" class="btn btn-danger btn-sm" style="font-size:11px">Delete</button>
325325 </form>
326326 </div>
327327 </div>
328328 [/loop]
329329 </div>
330330</div>
331331[/if]
332332
333333<!-- Create form -->
334334<div class="module mb-3">
335335 <div class="module-head">
336336 <div class="left">
337337 <div class="module-icon" style="background:rgba(34,197,94,0.15);color:#4ade80"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" style="display:inline-block;vertical-align:middle"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></div>
338338 <div>
339339 <div class="module-title">Add a test credit card</div>
340340 <div class="module-sub">Numbers are fake by design &mdash; nothing leaves the platform. Tag an owner so you can offboard their cards in one click later.</div>
341341 </div>
342342 </div>
343343 </div>
344344 <div class="module-body">
345345 <form method="POST" action="/admin_test_cards.cgi" id="tc-create-form">
346346 <input type="hidden" name="action" value="create">
347347 <div class="tc-create-form">
348348 <label class="text-xs text-dim">
349349 Brand
350350 <select class="select" name="card_brand" id="tc-brand" style="margin-top:4px">
351351 <option value="visa">Visa</option>
352352 <option value="mastercard">Mastercard</option>
353353 <option value="amex">American Express</option>
354354 <option value="discover">Discover</option>
355355 <option value="jcb">JCB</option>
356356 <option value="diners">Diners Club</option>
357357 <option value="unionpay">UnionPay</option>
358358 </select>
359359 </label>
360360 <label class="text-xs text-dim" style="grid-column:span 2">
361361 Card number
362362 <input class="input" type="text" name="card_number" id="tc-number"
363363 placeholder="4242 4242 4242 4242" required maxlength="23"
364364 style="margin-top:4px;font-family:var(--font-mono,monospace);letter-spacing:1px">
365365 </label>
366366 <label class="text-xs text-dim">
367367 Holder name
368368 <input class="input" type="text" name="holder_name" value="Test User"
369369 maxlength="120" style="margin-top:4px">
370370 </label>
371371 <label class="text-xs text-dim">
372372 Exp month
373373 <input class="input" type="number" name="exp_month" value="12" min="1" max="12" style="margin-top:4px">
374374 </label>
375375 <label class="text-xs text-dim">
376376 Exp year
377377 <input class="input" type="number" name="exp_year" value="2030" min="2024" max="2099" style="margin-top:4px">
378378 </label>
379379 <label class="text-xs text-dim">
380380 CVC
381381 <input class="input" type="text" name="cvc" value="123" maxlength="4" style="margin-top:4px">
382382 </label>
383383 <label class="text-xs text-dim">
384384 Behavior
385385 <select class="select" name="behavior" style="margin-top:4px">
386386 [loop:@behavior_options]<option value="$loop1.value">$loop1.label</option>[/loop]
387387 </select>
388388 </label>
389389 [if:$has_users]
390390 <label class="text-xs text-dim" style="grid-column:span 2">
391391 Owner (optional)
392392 <select class="select" name="owner_user_id" style="margin-top:4px">
393393 <option value="">&mdash; No owner / shared &mdash;</option>
394394 [loop:@user_options]<option value="$loop1.id">$loop1.label</option>[/loop]
395395 </select>
396396 </label>
397397 [/if]
398398 <label class="text-xs text-dim" style="grid-column:span 2">
399399 Label (optional)
400400 <input class="input" type="text" name="label" maxlength="120"
401401 placeholder="e.g. Decline path test" style="margin-top:4px">
402402 </label>
403403 </div>
404404
405405 <div class="tc-presets">
406406 <div class="text-xs text-dim" style="display:flex;align-items:center;margin-right:4px">Quick presets:</div>
407407 <button type="button" class="tc-preset-btn" data-preset="visa-approve">Visa &middot; approve</button>
408408 <button type="button" class="tc-preset-btn" data-preset="visa-decline">Visa &middot; decline</button>
409409 <button type="button" class="tc-preset-btn" data-preset="mc-approve">Mastercard &middot; approve</button>
410410 <button type="button" class="tc-preset-btn" data-preset="amex-approve">Amex &middot; approve</button>
411411 <button type="button" class="tc-preset-btn" data-preset="discover-approve">Discover &middot; approve</button>
412412 <button type="button" class="tc-preset-btn" data-preset="insufficient">Visa &middot; insufficient funds</button>
413413 <button type="button" class="tc-preset-btn" data-preset="expired">Visa &middot; expired card</button>
414414 <button type="button" class="tc-preset-btn" data-preset="processing">Visa &middot; processing error</button>
415415 <button type="button" class="tc-preset-btn" data-preset="lost">Visa &middot; lost card</button>
416416 <button type="button" class="tc-preset-btn" data-preset="stolen">Visa &middot; stolen card</button>
417417 <button type="button" class="tc-preset-btn" data-preset="3ds">Visa &middot; 3DS required</button>
418418 </div>
419419
420420 <div style="margin-top:14px">
421421 <button type="submit" class="btn btn-primary">Create test credit card</button>
422422 </div>
423423 </form>
424424 </div>
425425</div>
426426
427427<!-- Audit log -->
428428[if:$has_audit]
429429<div class="module mb-3">
430430 <div class="module-head">
431431 <div class="left">
432432 <div class="module-icon" style="background:rgba(99,102,241,.15);color:#a5b4fc"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" style="display:inline-block;vertical-align:middle"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><line x1="10" y1="9" x2="8" y2="9"/></svg></div>
433433 <div>
434434 <div class="module-title">Recent activity</div>
435435 <div class="module-sub">Last 50 events. Who created / deactivated / assigned each card, and when.</div>
436436 </div>
437437 </div>
438438 </div>
439439 <div class="module-body" style="padding:0">
440440 [loop:@audit_rows]
441441 <div class="tc-audit-row">
442442 <span class="tc-audit-action">$loop1.action</span>
443443 <span style="color:var(--col-text);font-weight:500">$loop1.actor_name</span>
444444 <span style="color:var(--col-text-2)">$loop1.card_label</span>
445445 [if:$loop1.has_detail]<span style="color:var(--col-text-3);font-size:11px">$loop1.detail</span>[/if]
446446 <span class="tc-audit-when"><span class="ts" data-ts="$loop1.created_epoch" data-fmt="datetime">$loop1.created_at</span></span>
447447 </div>
448448 [/loop]
449449 </div>
450450</div>
451451[/if]
452452
453453[/if]
454454
455455<script>
456456(function () {
457457 // Auto-format the card-number input as 4-4-4-4 groups while typing.
458458 var num = document.getElementById('tc-number');
459459 if (num) {
460460 num.addEventListener('input', function () {
461461 var digits = num.value.replace(/[^0-9]/g, '').slice(0, 19);
462462 num.value = digits.replace(/(\d{4})(?=\d)/g, '\$1 ');
463463 });
464464 }
465465
466466 var PRESETS = {
467467 'visa-approve': { brand:'visa', number:'4242 4242 4242 4242', name:'Test Visa Approve', behavior:'approve', label:'Approves on charge' },
468468 'visa-decline': { brand:'visa', number:'4000 0000 0000 0002', name:'Test Visa Decline', behavior:'decline', label:'Generic decline' },
469469 'mc-approve': { brand:'mastercard', number:'5555 5555 5555 4444', name:'Test MC Approve', behavior:'approve', label:'Approves on charge' },
470470 'amex-approve': { brand:'amex', number:'3782 822463 10005', name:'Test Amex Approve', behavior:'approve', label:'Approves on charge' },
471471 'discover-approve': { brand:'discover', number:'6011 0000 0000 0004', name:'Test Discover Approve', behavior:'approve', label:'Approves on charge' },
472472 'insufficient': { brand:'visa', number:'4000 0000 0000 9995', name:'Test Insufficient', behavior:'insufficient_funds', label:'Insufficient funds path' },
473473 'expired': { brand:'visa', number:'4000 0000 0000 0069', name:'Test Expired', behavior:'expired_card', label:'Expired card path' },
474474 'processing': { brand:'visa', number:'4000 0000 0000 0119', name:'Test Processing Err', behavior:'processing_error', label:'Processing error path' },
475475 'lost': { brand:'visa', number:'4000 0000 0000 9987', name:'Test Lost', behavior:'lost_card', label:'Lost card path' },
476476 'stolen': { brand:'visa', number:'4000 0000 0000 9979', name:'Test Stolen', behavior:'stolen_card', label:'Stolen card path' },
477477 '3ds': { brand:'visa', number:'4000 0027 6000 3184', name:'Test 3DS', behavior:'3ds_required', label:'3D Secure auth required' }
478478 };
479479
480480 document.querySelectorAll('.tc-preset-btn').forEach(function (btn) {
481481 btn.addEventListener('click', function () {
482482 var p = PRESETS[btn.getAttribute('data-preset')];
483483 if (!p) return;
484484 var f = document.getElementById('tc-create-form');
485485 f.card_brand.value = p.brand;
486486 f.card_number.value = p.number;
487487 f.holder_name.value = p.name;
488488 f.behavior.value = p.behavior;
489489 f.label.value = p.label;
490490 f.exp_month.value = 12;
491491 f.exp_year.value = 2030;
492492 f.cvc.value = (p.brand === 'amex') ? '1234' : '123';
493493 f.card_number.style.transition = 'background 0.4s';
494494 f.card_number.style.background = 'rgba(99,102,241,0.18)';
495495 setTimeout(function () { f.card_number.style.background = ''; }, 600);
496496 });
497497 });
498498})();
499499</script>
500500
501501<div class="page-foot-spacer"></div>